"""Lee un .kicad_pcb (KiCad 8/9/10) sin KiCad y guarda en JSON lo que hace falta para dibujarlo: pistas, arcos, vĂ­as, pads (con su red y su capa), contorno y zonas rellenadas. Uso: python3 leer_pcb.py tarjeta.kicad_pcb pcb_v56.json""" import re, json, math, sys src = open(sys.argv[1], encoding='utf-8').read() tok_re = re.compile(r'\(|\)|"(?:[^"\\]|\\.)*"|[^\s()"]+') def parse(s): stack=[[]] for m in tok_re.finditer(s): t=m.group(0) if t=='(': stack.append([]) elif t==')': x=stack.pop(); stack[-1].append(x) else: if t.startswith('"'): t=t[1:-1] stack[-1].append(t) return stack[0][0] root=parse(src) def find(node, key): return [c for c in node if isinstance(c,list) and c and c[0]==key] def one(node,key): r=find(node,key); return r[0] if r else None out={'segments':[], 'vias':[], 'pads':[], 'edges':[], 'zones':[], 'arcs':[]} for seg in find(root,'segment'): s=one(seg,'start'); e=one(seg,'end'); w=one(seg,'width'); l=one(seg,'layer'); n=one(seg,'net') out['segments'].append([float(s[1]),float(s[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else '']) for a in find(root,'arc'): s=one(a,'start'); m=one(a,'mid'); e=one(a,'end'); w=one(a,'width'); l=one(a,'layer'); n=one(a,'net') out['arcs'].append([float(s[1]),float(s[2]),float(m[1]),float(m[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else '']) for v in find(root,'via'): at=one(v,'at'); sz=one(v,'size'); n=one(v,'net') out['vias'].append([float(at[1]),float(at[2]),float(sz[1]),n[1] if n else '']) for fp in find(root,'footprint'): at=one(fp,'at'); fx,fy=float(at[1]),float(at[2]); frot=float(at[3]) if len(at)>3 else 0.0 layer=one(fp,'layer')[1] ref='' for p in find(fp,'property'): if len(p)>2 and p[1]=='Reference': ref=p[2] for pad in find(fp,'pad'): pat=one(pad,'at'); px,py=float(pat[1]),float(pat[2]); prot=float(pat[3]) if len(pat)>3 else 0.0 size=one(pad,'size'); w,h=float(size[1]),float(size[2]) shape=pad[3] if len(pad)>3 else 'rect' typ=pad[2] if len(pad)>2 else 'smd' n=one(pad,'net'); net=n[1] if n and len(n)>1 else '' # KiCad: pad position rotated by footprint rotation (clockwise in screen coords since y down) r=math.radians(-frot) ax=fx+px*math.cos(r)-py*math.sin(r); ay=fy+px*math.sin(r)+py*math.cos(r) lay=one(pad,'layers'); lays=lay[1:] if lay else [] out['pads'].append([round(ax,3),round(ay,3),w,h,prot,shape,typ,net,ref,layer,lays]) for g in root: if isinstance(g,list) and g and g[0] in ('gr_line','gr_rect','gr_arc','gr_circle','gr_poly'): l=one(g,'layer') if l and l[1]=='Edge.Cuts': out['edges'].append(g) for z in find(root,'zone'): n=one(z,'net'); net=n[1] if n and len(n)>1 else '' for fpoly in find(z,'filled_polygon'): l=one(fpoly,'layer'); pts=one(fpoly,'pts') xy=[(float(p[1]),float(p[2])) for p in pts[1:] if isinstance(p,list) and p[0]=='xy'] out['zones'].append([l[1] if l else '', net, xy]) json.dump(out, open(sys.argv[2] if len(sys.argv) > 2 else 'pcb.json', 'w')) print({k: len(v) for k, v in out.items()})