aboutsummaryrefslogtreecommitdiff
path: root/Snippets/svg2glif.py
blob: c0aa822bf4c792cadaca52d6c9618efe4a862f57 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
""" Convert SVG paths to UFO glyphs. """


__requires__ = ["fontTools"]

from types import SimpleNamespace
from fontTools.svgLib import SVGPath

from fontTools.pens.pointPen import SegmentToPointPen
from fontTools.ufoLib.glifLib import writeGlyphToString


__all__ = ["svg2glif"]


def svg2glif(svg, name, width=0, height=0, unicodes=None, transform=None, version=2):
    """Convert an SVG outline to a UFO glyph with given 'name', advance
    'width' and 'height' (int), and 'unicodes' (list of int).
    Return the resulting string in GLIF format (default: version 2).
    If 'transform' is provided, apply a transformation matrix before the
    conversion (must be tuple of 6 floats, or a FontTools Transform object).
    """
    glyph = SimpleNamespace(width=width, height=height, unicodes=unicodes)
    outline = SVGPath.fromstring(svg, transform=transform)

    # writeGlyphToString takes a callable (usually a glyph's drawPoints
    # method) that accepts a PointPen, however SVGPath currently only has
    # a draw method that accepts a segment pen. We need to wrap the call
    # with a converter pen.
    def drawPoints(pointPen):
        pen = SegmentToPointPen(pointPen)
        outline.draw(pen)

    return writeGlyphToString(
        name, glyphObject=glyph, drawPointsFunc=drawPoints, formatVersion=version
    )


def parse_args(args):
    import argparse

    def split(arg):
        return arg.replace(",", " ").split()

    def unicode_hex_list(arg):
        try:
            return [int(unihex, 16) for unihex in split(arg)]
        except ValueError:
            msg = "Invalid unicode hexadecimal value: %r" % arg
            raise argparse.ArgumentTypeError(msg)

    def transform_list(arg):
        try:
            return [float(n) for n in split(arg)]
        except ValueError:
            msg = "Invalid transformation matrix: %r" % arg
            raise argparse.ArgumentTypeError(msg)

    parser = argparse.ArgumentParser(
        description="Convert SVG outlines to UFO glyphs (.glif)"
    )
    parser.add_argument(
        "infile",
        metavar="INPUT.svg",
        help="Input SVG file containing " '<path> elements with "d" attributes.',
    )
    parser.add_argument(
        "outfile",
        metavar="OUTPUT.glif",
        help="Output GLIF file (default: " "print to stdout)",
        nargs="?",
    )
    parser.add_argument(
        "-n",
        "--name",
        help="The glyph name (default: input SVG file "
        "basename, without the .svg extension)",
    )
    parser.add_argument(
        "-w",
        "--width",
        help="The glyph advance width (default: 0)",
        type=int,
        default=0,
    )
    parser.add_argument(
        "-H",
        "--height",
        help="The glyph vertical advance (optional if " '"width" is defined)',
        type=int,
        default=0,
    )
    parser.add_argument(
        "-u",
        "--unicodes",
        help="List of Unicode code points as hexadecimal "
        'numbers (e.g. -u "0041 0042")',
        type=unicode_hex_list,
    )
    parser.add_argument(
        "-t",
        "--transform",
        help="Transformation matrix as a list of six "
        'float values (e.g. -t "0.1 0 0 -0.1 -50 200")',
        type=transform_list,
    )
    parser.add_argument(
        "-f",
        "--format",
        help="UFO GLIF format version (default: 2)",
        type=int,
        choices=(1, 2),
        default=2,
    )

    return parser.parse_args(args)


def main(args=None):
    from io import open

    options = parse_args(args)

    svg_file = options.infile

    if options.name:
        name = options.name
    else:
        import os

        name = os.path.splitext(os.path.basename(svg_file))[0]

    with open(svg_file, "r", encoding="utf-8") as f:
        svg = f.read()

    glif = svg2glif(
        svg,
        name,
        width=options.width,
        height=options.height,
        unicodes=options.unicodes,
        transform=options.transform,
        version=options.format,
    )

    if options.outfile is None:
        print(glif)
    else:
        with open(options.outfile, "w", encoding="utf-8") as f:
            f.write(glif)


if __name__ == "__main__":
    import sys

    sys.exit(main())