aboutsummaryrefslogtreecommitdiff
path: root/generate_scaled_footprints.py
diff options
context:
space:
mode:
Diffstat (limited to 'generate_scaled_footprints.py')
-rw-r--r--generate_scaled_footprints.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/generate_scaled_footprints.py b/generate_scaled_footprints.py
new file mode 100644
index 0000000..8ef95e7
--- /dev/null
+++ b/generate_scaled_footprints.py
@@ -0,0 +1,115 @@
+#/usr/bin/env python3
+
+import re
+import tempfile
+import os
+import subprocess
+from pathlib import Path
+
+from bs4 import BeautifulSoup
+import click
+
+default_widths = '3mm,5mm,8mm,10mm,12mm,15mm,18mm,20mm,25mm,30mm,35mm,40mm,45mm,50mm,60mm,70mm,80mm,90mm,100mm,120mm,150mm'
+
+
+# Mostly from https://www.w3.org/TR/css-values/#absolute-lengths
+UNIT_FACTORS = {
+ 'm': 1000,
+ 'cm': 10,
+ 'mm': 1,
+ 'Q': 1/4,
+ 'in': 25.4,
+ 'mil': 25.4/1000,
+ 'pc': 25.4/6,
+ 'pt': 25.4/72,
+ 'px': 25.4/96,
+ }
+
+def parse_length(foo, default_unit=None):
+ ''' Parse given physical length, and return result converted to mm. '''
+
+ match = re.fullmatch(r'(.*?)(m|cm|mm|Q|in|mil|pc|pt|px|)', foo.strip().lower())
+ if not match:
+ raise ValueError(f'Invalid length "{foo}"')
+ num, unit = match.groups()
+
+ if not unit:
+ if default_unit:
+ unit = default_unit
+ else:
+ raise ValueError(f'Unit missing from length "{foo}"')
+
+ return float(num) * UNIT_FACTORS[unit]
+
+
+@click.command()
+@click.option('--width')
+@click.option('--height')
+@click.option('--sexp-layer', default='F.SilkS')
+@click.option('--basename', help='Base name for generated symbols and library')
+@click.argument('input_svg')
+def export(width, height, basename, sexp_layer, input_svg):
+ svg_flatten = str(Path(os.environ.get('SVG_FLATTEN', 'svg-flatten')).expanduser())
+ usvg = str(Path(os.environ.get('USVG', 'usvg')).expanduser())
+
+ if not basename:
+ match = re.fullmatch(r'(.*?)(([-_.][0-9.,]+)(m|cm|mm|Q|in|mil|pc|pt|px|))?', Path(input_svg).stem)
+ basename, *rest = match.groups()
+ print(f'No --basename given. Using "{basename}"')
+
+ export_width, export_height = width, height
+ if not export_width or export_height:
+ export_width = default_widths
+
+ elif export_width and export_height:
+ raise click.ClickException('Only one of --width or --height must be given.')
+
+ if export_width:
+ targets = export_width
+ axis = 'width'
+ else:
+ targets = export_height
+ axis = 'height'
+
+ # Determine input document size
+ with tempfile.NamedTemporaryFile() as f:
+ try:
+ subprocess.run([usvg, input_svg, f.name], check=True)
+ except FileNotFoundError:
+ raise click.ClickException('Cannot find usvg binary in PATH. You can give a custom path to the usvg binary by setting the USVG environment variable.')
+
+ soup = BeautifulSoup(f.read(), features='xml')
+ svg = soup.find('svg')
+ doc_w_mm, doc_h_mm = parse_length(svg['width'], default_unit='px'), parse_length(svg['height'], default_unit='px')
+
+ print(f'Input file has dimensions width {doc_w_mm:.1f} mm by height {doc_h_mm:.1f} mm')
+
+ outdir = Path(f'{basename}.pretty')
+ outdir.mkdir(exist_ok=True)
+
+ for target_length in targets.split(','):
+ target_length = parse_length(target_length, default_unit='mm')
+
+ if axis == 'width':
+ scaling_factor = target_length / doc_w_mm
+ else:
+ scaling_factor = target_length / doc_h_mm
+
+ instance_name = f'{basename}_{target_length:.1f}mm'
+ outfile = outdir / f'{instance_name}.kicad_mod'
+ print(f'{outfile}: Scaling to target {axis} {target_length:.1f} mm using scaling factor {scaling_factor:.3f}')
+
+ try:
+ proc = subprocess.run([svg_flatten,
+ '-o', 'sexp',
+ '--sexp-layer', sexp_layer,
+ '--sexp-mod-name', instance_name,
+ '--scale', str(scaling_factor),
+ input_svg], check=True, capture_output=True)
+ outfile.write_bytes(proc.stdout)
+ except FileNotFoundError:
+ raise click.ClickException('Cannot find svg-flatten binary in PATH. You can give a custom path to the svg-flatten binary by setting the SVG_FLATTEN environment variable.')
+
+
+if __name__ == '__main__':
+ export()