1*e1fe3e4aSElliott Hughesimport os 2*e1fe3e4aSElliott Hughesimport argparse 3*e1fe3e4aSElliott Hughesimport logging 4*e1fe3e4aSElliott Hughesfrom fontTools.misc.cliTools import makeOutputFileName 5*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont 6*e1fe3e4aSElliott Hughesfrom fontTools.pens.qu2cuPen import Qu2CuPen 7*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen 8*e1fe3e4aSElliott Hughesimport fontTools 9*e1fe3e4aSElliott Hughes 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hugheslogger = logging.getLogger("fontTools.qu2cu") 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughes 14*e1fe3e4aSElliott Hughesdef _font_to_cubic(input_path, output_path=None, **kwargs): 15*e1fe3e4aSElliott Hughes font = TTFont(input_path) 16*e1fe3e4aSElliott Hughes logger.info("Converting curves for %s", input_path) 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes stats = {} if kwargs["dump_stats"] else None 19*e1fe3e4aSElliott Hughes qu2cu_kwargs = { 20*e1fe3e4aSElliott Hughes "stats": stats, 21*e1fe3e4aSElliott Hughes "max_err": kwargs["max_err_em"] * font["head"].unitsPerEm, 22*e1fe3e4aSElliott Hughes "all_cubic": kwargs["all_cubic"], 23*e1fe3e4aSElliott Hughes } 24*e1fe3e4aSElliott Hughes 25*e1fe3e4aSElliott Hughes assert "gvar" not in font, "Cannot convert variable font" 26*e1fe3e4aSElliott Hughes glyphSet = font.getGlyphSet() 27*e1fe3e4aSElliott Hughes glyphOrder = font.getGlyphOrder() 28*e1fe3e4aSElliott Hughes glyf = font["glyf"] 29*e1fe3e4aSElliott Hughes for glyphName in glyphOrder: 30*e1fe3e4aSElliott Hughes glyph = glyphSet[glyphName] 31*e1fe3e4aSElliott Hughes ttpen = TTGlyphPen(glyphSet) 32*e1fe3e4aSElliott Hughes pen = Qu2CuPen(ttpen, **qu2cu_kwargs) 33*e1fe3e4aSElliott Hughes glyph.draw(pen) 34*e1fe3e4aSElliott Hughes glyf[glyphName] = ttpen.glyph(dropImpliedOnCurves=True) 35*e1fe3e4aSElliott Hughes 36*e1fe3e4aSElliott Hughes font["head"].glyphDataFormat = 1 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughes if kwargs["dump_stats"]: 39*e1fe3e4aSElliott Hughes logger.info("Stats: %s", stats) 40*e1fe3e4aSElliott Hughes 41*e1fe3e4aSElliott Hughes logger.info("Saving %s", output_path) 42*e1fe3e4aSElliott Hughes font.save(output_path) 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes 45*e1fe3e4aSElliott Hughesdef main(args=None): 46*e1fe3e4aSElliott Hughes """Convert an OpenType font from quadratic to cubic curves""" 47*e1fe3e4aSElliott Hughes parser = argparse.ArgumentParser(prog="qu2cu") 48*e1fe3e4aSElliott Hughes parser.add_argument("--version", action="version", version=fontTools.__version__) 49*e1fe3e4aSElliott Hughes parser.add_argument( 50*e1fe3e4aSElliott Hughes "infiles", 51*e1fe3e4aSElliott Hughes nargs="+", 52*e1fe3e4aSElliott Hughes metavar="INPUT", 53*e1fe3e4aSElliott Hughes help="one or more input TTF source file(s).", 54*e1fe3e4aSElliott Hughes ) 55*e1fe3e4aSElliott Hughes parser.add_argument("-v", "--verbose", action="count", default=0) 56*e1fe3e4aSElliott Hughes parser.add_argument( 57*e1fe3e4aSElliott Hughes "-e", 58*e1fe3e4aSElliott Hughes "--conversion-error", 59*e1fe3e4aSElliott Hughes type=float, 60*e1fe3e4aSElliott Hughes metavar="ERROR", 61*e1fe3e4aSElliott Hughes default=0.001, 62*e1fe3e4aSElliott Hughes help="maxiumum approximation error measured in EM (default: 0.001)", 63*e1fe3e4aSElliott Hughes ) 64*e1fe3e4aSElliott Hughes parser.add_argument( 65*e1fe3e4aSElliott Hughes "-c", 66*e1fe3e4aSElliott Hughes "--all-cubic", 67*e1fe3e4aSElliott Hughes default=False, 68*e1fe3e4aSElliott Hughes action="store_true", 69*e1fe3e4aSElliott Hughes help="whether to only use cubic curves", 70*e1fe3e4aSElliott Hughes ) 71*e1fe3e4aSElliott Hughes 72*e1fe3e4aSElliott Hughes output_parser = parser.add_mutually_exclusive_group() 73*e1fe3e4aSElliott Hughes output_parser.add_argument( 74*e1fe3e4aSElliott Hughes "-o", 75*e1fe3e4aSElliott Hughes "--output-file", 76*e1fe3e4aSElliott Hughes default=None, 77*e1fe3e4aSElliott Hughes metavar="OUTPUT", 78*e1fe3e4aSElliott Hughes help=("output filename for the converted TTF."), 79*e1fe3e4aSElliott Hughes ) 80*e1fe3e4aSElliott Hughes output_parser.add_argument( 81*e1fe3e4aSElliott Hughes "-d", 82*e1fe3e4aSElliott Hughes "--output-dir", 83*e1fe3e4aSElliott Hughes default=None, 84*e1fe3e4aSElliott Hughes metavar="DIRECTORY", 85*e1fe3e4aSElliott Hughes help="output directory where to save converted TTFs", 86*e1fe3e4aSElliott Hughes ) 87*e1fe3e4aSElliott Hughes 88*e1fe3e4aSElliott Hughes options = parser.parse_args(args) 89*e1fe3e4aSElliott Hughes 90*e1fe3e4aSElliott Hughes if not options.verbose: 91*e1fe3e4aSElliott Hughes level = "WARNING" 92*e1fe3e4aSElliott Hughes elif options.verbose == 1: 93*e1fe3e4aSElliott Hughes level = "INFO" 94*e1fe3e4aSElliott Hughes else: 95*e1fe3e4aSElliott Hughes level = "DEBUG" 96*e1fe3e4aSElliott Hughes logging.basicConfig(level=level) 97*e1fe3e4aSElliott Hughes 98*e1fe3e4aSElliott Hughes if len(options.infiles) > 1 and options.output_file: 99*e1fe3e4aSElliott Hughes parser.error("-o/--output-file can't be used with multile inputs") 100*e1fe3e4aSElliott Hughes 101*e1fe3e4aSElliott Hughes if options.output_dir: 102*e1fe3e4aSElliott Hughes output_dir = options.output_dir 103*e1fe3e4aSElliott Hughes if not os.path.exists(output_dir): 104*e1fe3e4aSElliott Hughes os.mkdir(output_dir) 105*e1fe3e4aSElliott Hughes elif not os.path.isdir(output_dir): 106*e1fe3e4aSElliott Hughes parser.error("'%s' is not a directory" % output_dir) 107*e1fe3e4aSElliott Hughes output_paths = [ 108*e1fe3e4aSElliott Hughes os.path.join(output_dir, os.path.basename(p)) for p in options.infiles 109*e1fe3e4aSElliott Hughes ] 110*e1fe3e4aSElliott Hughes elif options.output_file: 111*e1fe3e4aSElliott Hughes output_paths = [options.output_file] 112*e1fe3e4aSElliott Hughes else: 113*e1fe3e4aSElliott Hughes output_paths = [ 114*e1fe3e4aSElliott Hughes makeOutputFileName(p, overWrite=True, suffix=".cubic") 115*e1fe3e4aSElliott Hughes for p in options.infiles 116*e1fe3e4aSElliott Hughes ] 117*e1fe3e4aSElliott Hughes 118*e1fe3e4aSElliott Hughes kwargs = dict( 119*e1fe3e4aSElliott Hughes dump_stats=options.verbose > 0, 120*e1fe3e4aSElliott Hughes max_err_em=options.conversion_error, 121*e1fe3e4aSElliott Hughes all_cubic=options.all_cubic, 122*e1fe3e4aSElliott Hughes ) 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughes for input_path, output_path in zip(options.infiles, output_paths): 125*e1fe3e4aSElliott Hughes _font_to_cubic(input_path, output_path, **kwargs) 126