Skip to content

Commit

Permalink
Add scaled footprint generator script
Browse files Browse the repository at this point in the history
  • Loading branch information
jaseg committed Mar 22, 2023
1 parent b1324e9 commit 70a7a0a
Showing 1 changed file with 115 additions and 0 deletions.
115 changes: 115 additions & 0 deletions generate_scaled_footprints.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 70a7a0a

Please sign in to comment.