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