From ba4cafa3a43f3b6655e2cbfbc35d2505f0efccb1 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 22 Jul 2023 14:25:18 +0200 Subject: Add tmtheme support --- gerbonara/cad/kicad/schematic.py | 15 +++---- gerbonara/cad/kicad/schematic_colors.py | 1 + gerbonara/cad/kicad/symbols.py | 4 +- gerbonara/cad/kicad/tmtheme.py | 70 +++++++++++++++++++++++++++++++++ gerbonara/utils.py | 1 + 5 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 gerbonara/cad/kicad/tmtheme.py diff --git a/gerbonara/cad/kicad/schematic.py b/gerbonara/cad/kicad/schematic.py index 0fce9ad..b267743 100644 --- a/gerbonara/cad/kicad/schematic.py +++ b/gerbonara/cad/kicad/schematic.py @@ -194,7 +194,7 @@ class LocalLabel(TextMixin): return (0, -2*self.line_width) def to_svg(self, colorscheme=Colorscheme.KiCad): - yield from TextMixin.to_svg(self, colorscheme.text) + yield from TextMixin.to_svg(self, colorscheme.labels) def label_shape_path_d(shape, w, h): @@ -231,7 +231,7 @@ class GlobalLabel(TextMixin): properties: List(Property) = field(default_factory=list) def to_svg(self, colorscheme=Colorscheme.KiCad): - text = super(TextMixin, self).to_svg(colorscheme.text), + text = super(TextMixin, self).to_svg(colorscheme.labels), text.attrs['transform'] = f'translate({self.size*0.6:.3f} 0)' (x1, y1), (x2, y2) = self.bounding_box() frame = Tag('path', fill='none', stroke_width=0.254, stroke=colorscheme.lines, @@ -249,7 +249,7 @@ class HierarchicalLabel(TextMixin): uuid: UUID = field(default_factory=UUID) def to_svg(self, colorscheme=Colorscheme.KiCad): - text, = TextMixin.to_svg(self, colorscheme.text), + text, = TextMixin.to_svg(self, colorscheme.labels), text.attrs['transform'] = f'translate({self.size*1.2:.3f} 0)' frame = Tag('path', fill='none', stroke_width=0.254, stroke=colorscheme.lines, d=label_shape_path_d(self.shape, self.size, self.size)) @@ -332,7 +332,7 @@ class DrawnProperty(TextMixin): def to_svg(self, colorscheme=Colorscheme.KiCad): if not self.hide: - yield from TextMixin.to_svg(self, colorscheme.text) + yield from TextMixin.to_svg(self, colorscheme.values) @sexp_type('symbol') @@ -418,7 +418,7 @@ class SymbolInstance: yield Tag('g', children=children, transform=at_xform, fill=colorscheme.fill, stroke=colorscheme.lines) for prop in self.properties: - yield from prop.to_svg() + yield from prop.to_svg(colorscheme) @sexp_type('path') @@ -627,12 +627,13 @@ class Schematic: for elem in self.elements: children += elem.to_svg(colorscheme) w, h = KICAD_PAPER_SIZES[self.page_settings.page_format] - return setup_svg(children, ((0, 0), (w, h))) + return setup_svg(children, ((0, 0), (w, h)), pagecolor=colorscheme.background) if __name__ == '__main__': import sys from ...layers import LayerStack + from .tmtheme import TmThemeSchematic sch = Schematic.open(sys.argv[1]) print('Loaded schematic with', len(sch.wires), 'wires and', len(sch.symbols), 'symbols.') for subsh in sch.subsheets: @@ -640,5 +641,5 @@ if __name__ == '__main__': print('Loaded sub-sheet with', len(subsh.wires), 'wires and', len(subsh.symbols), 'symbols.') sch.write('/tmp/test.kicad_sch') - Path('/tmp/test.svg').write_text(str(sch.to_svg())) + Path('/tmp/test.svg').write_text(str(sch.to_svg(TmThemeSchematic(Path('/tmp/witchhazelhypercolor.tmTheme').read_text())))) diff --git a/gerbonara/cad/kicad/schematic_colors.py b/gerbonara/cad/kicad/schematic_colors.py index 4bcbe7b..d38a88d 100644 --- a/gerbonara/cad/kicad/schematic_colors.py +++ b/gerbonara/cad/kicad/schematic_colors.py @@ -9,4 +9,5 @@ class Colorscheme: values = 'black' labels = 'black' fill = '#cccccc' + background = 'white' diff --git a/gerbonara/cad/kicad/symbols.py b/gerbonara/cad/kicad/symbols.py index 5130b29..45f8ec0 100644 --- a/gerbonara/cad/kicad/symbols.py +++ b/gerbonara/cad/kicad/symbols.py @@ -197,7 +197,7 @@ class Pin: h_align=h_align, v_align='middle', rotation=-frot, - stroke=colorscheme.text, + stroke=colorscheme.pin_names, transform=f'translate({lx:.3f} {ly:.3f})', scale=(sx, sy), mirror=(False, False), @@ -211,7 +211,7 @@ class Pin: h_align={'left': 'right', 'right': 'left'}[h_align], v_align='bottom', rotation=-frot, - stroke=colorscheme.text, + stroke=colorscheme.pin_numbers, scale=(sx, sy), transform=f'translate({lx:.3f} {ly:.3f})', mirror=(False, False), diff --git a/gerbonara/cad/kicad/tmtheme.py b/gerbonara/cad/kicad/tmtheme.py new file mode 100644 index 0000000..52d9afd --- /dev/null +++ b/gerbonara/cad/kicad/tmtheme.py @@ -0,0 +1,70 @@ + +from xml.etree import ElementTree +import base64 +from pathlib import Path + +def _map_primitive(element): + match element.tag: + case 'data': + return base64.b64decode(element.text) + case 'date': + return element.text + case 'true': + return True + case 'false': + return False + case 'real': + return float(element.text) + case 'integer': + return int(element.text) + case 'string': + return element.text + case 'array': + return [_map_primitive(child) for child in element] + case 'dict': + children = list(element) + return {k.text: _map_primitive(v) for k, v in zip(children[0::2], children[1::2])} + + +def parse_shitty_json(data): + # Parse apple plist XML + root = ElementTree.fromstring(data) + return _map_primitive(root[0]) + +class TmThemeSchematic: + def __init__(self, data): + self.theme = parse_shitty_json(data) + s = self.theme['settings'][0]['settings'] + by_scope = {} + for elem in self.theme['settings']: + if 'scope' not in elem: + continue + for scope in elem['scope'].split(','): + by_scope[scope.strip()] = elem.get('settings', {}) + + def lookup(default, *scopes): + for scope in scopes: + if not (elem := by_scope.get(scope)): + continue + + if 'foreground' not in elem: + continue + + return elem['foreground'] + return default + + self.background = s.get('background', 'white') + self.bus = lookup('black', 'constant.other', 'storage.type') + self.wire = self.lines = lookup('black', 'constant.other') + self.no_connect = lookup('black', 'constant.language', 'variable') + self.text = lookup('black', 'constant.numeric', 'constant.numeric.hex', 'storage.type.number') + self.pin_numbers = lookup('black', 'constant.character', 'constant.other') + self.pin_names = lookup('black', 'constant.character.format.placeholder', 'constant.other.placeholder') + self.values = lookup('black', 'constant.character.format.placeholder', 'constant.other.placeholder') + self.labels = lookup('black', 'constant.numeric', 'constant.numeric.hex', 'storage.type.number') + self.fill = s.get('background') + print(f'{self.background=} {self.wire=} {self.bus=} {self.lines=} {self.no_connect=} {self.labels=} {self.fill=}') + +if __name__ == '__main__': + print(parse_shitty_json(Path('/tmp/witchhazelhypercolor.tmTheme').read_text())) + diff --git a/gerbonara/utils.py b/gerbonara/utils.py index e2587fa..c7336e6 100644 --- a/gerbonara/utils.py +++ b/gerbonara/utils.py @@ -523,6 +523,7 @@ def setup_svg(tags, bounds, margin=0, arg_unit=MM, svg_unit=MM, pagecolor='white return tag('svg', tags, width=f'{w}{svg_unit}', height=f'{h}{svg_unit}', viewBox=f'{min_x} {min_y} {w} {h}', + style=f'background-color:{pagecolor}', **namespaces, root=True) -- cgit