aboutsummaryrefslogtreecommitdiff
path: root/generate_scaled_footprints.py
blob: 8ef95e7d1300af17cee0c899fec6173a4abe9f5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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()