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