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