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