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