xref: /aosp_15_r20/external/fonttools/Snippets/svg2glif.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1#!/usr/bin/env python3
2""" Convert SVG paths to UFO glyphs. """
3
4
5__requires__ = ["fontTools"]
6
7from types import SimpleNamespace
8from fontTools.svgLib import SVGPath
9
10from fontTools.pens.pointPen import SegmentToPointPen
11from fontTools.ufoLib.glifLib import writeGlyphToString
12
13
14__all__ = ["svg2glif"]
15
16
17def svg2glif(svg, name, width=0, height=0, unicodes=None, transform=None, version=2):
18    """Convert an SVG outline to a UFO glyph with given 'name', advance
19    'width' and 'height' (int), and 'unicodes' (list of int).
20    Return the resulting string in GLIF format (default: version 2).
21    If 'transform' is provided, apply a transformation matrix before the
22    conversion (must be tuple of 6 floats, or a FontTools Transform object).
23    """
24    glyph = SimpleNamespace(width=width, height=height, unicodes=unicodes)
25    outline = SVGPath.fromstring(svg, transform=transform)
26
27    # writeGlyphToString takes a callable (usually a glyph's drawPoints
28    # method) that accepts a PointPen, however SVGPath currently only has
29    # a draw method that accepts a segment pen. We need to wrap the call
30    # with a converter pen.
31    def drawPoints(pointPen):
32        pen = SegmentToPointPen(pointPen)
33        outline.draw(pen)
34
35    return writeGlyphToString(
36        name, glyphObject=glyph, drawPointsFunc=drawPoints, formatVersion=version
37    )
38
39
40def parse_args(args):
41    import argparse
42
43    def split(arg):
44        return arg.replace(",", " ").split()
45
46    def unicode_hex_list(arg):
47        try:
48            return [int(unihex, 16) for unihex in split(arg)]
49        except ValueError:
50            msg = "Invalid unicode hexadecimal value: %r" % arg
51            raise argparse.ArgumentTypeError(msg)
52
53    def transform_list(arg):
54        try:
55            return [float(n) for n in split(arg)]
56        except ValueError:
57            msg = "Invalid transformation matrix: %r" % arg
58            raise argparse.ArgumentTypeError(msg)
59
60    parser = argparse.ArgumentParser(
61        description="Convert SVG outlines to UFO glyphs (.glif)"
62    )
63    parser.add_argument(
64        "infile",
65        metavar="INPUT.svg",
66        help="Input SVG file containing " '<path> elements with "d" attributes.',
67    )
68    parser.add_argument(
69        "outfile",
70        metavar="OUTPUT.glif",
71        help="Output GLIF file (default: " "print to stdout)",
72        nargs="?",
73    )
74    parser.add_argument(
75        "-n",
76        "--name",
77        help="The glyph name (default: input SVG file "
78        "basename, without the .svg extension)",
79    )
80    parser.add_argument(
81        "-w",
82        "--width",
83        help="The glyph advance width (default: 0)",
84        type=int,
85        default=0,
86    )
87    parser.add_argument(
88        "-H",
89        "--height",
90        help="The glyph vertical advance (optional if " '"width" is defined)',
91        type=int,
92        default=0,
93    )
94    parser.add_argument(
95        "-u",
96        "--unicodes",
97        help="List of Unicode code points as hexadecimal "
98        'numbers (e.g. -u "0041 0042")',
99        type=unicode_hex_list,
100    )
101    parser.add_argument(
102        "-t",
103        "--transform",
104        help="Transformation matrix as a list of six "
105        'float values (e.g. -t "0.1 0 0 -0.1 -50 200")',
106        type=transform_list,
107    )
108    parser.add_argument(
109        "-f",
110        "--format",
111        help="UFO GLIF format version (default: 2)",
112        type=int,
113        choices=(1, 2),
114        default=2,
115    )
116
117    return parser.parse_args(args)
118
119
120def main(args=None):
121    from io import open
122
123    options = parse_args(args)
124
125    svg_file = options.infile
126
127    if options.name:
128        name = options.name
129    else:
130        import os
131
132        name = os.path.splitext(os.path.basename(svg_file))[0]
133
134    with open(svg_file, "r", encoding="utf-8") as f:
135        svg = f.read()
136
137    glif = svg2glif(
138        svg,
139        name,
140        width=options.width,
141        height=options.height,
142        unicodes=options.unicodes,
143        transform=options.transform,
144        version=options.format,
145    )
146
147    if options.outfile is None:
148        print(glif)
149    else:
150        with open(options.outfile, "w", encoding="utf-8") as f:
151            f.write(glif)
152
153
154if __name__ == "__main__":
155    import sys
156
157    sys.exit(main())
158