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