import os import argparse import logging from fontTools.misc.cliTools import makeOutputFileName from fontTools.ttLib import TTFont from fontTools.pens.qu2cuPen import Qu2CuPen from fontTools.pens.ttGlyphPen import TTGlyphPen import fontTools logger = logging.getLogger("fontTools.qu2cu") def _font_to_cubic(input_path, output_path=None, **kwargs): font = TTFont(input_path) logger.info("Converting curves for %s", input_path) stats = {} if kwargs["dump_stats"] else None qu2cu_kwargs = { "stats": stats, "max_err": kwargs["max_err_em"] * font["head"].unitsPerEm, "all_cubic": kwargs["all_cubic"], } assert "gvar" not in font, "Cannot convert variable font" glyphSet = font.getGlyphSet() glyphOrder = font.getGlyphOrder() glyf = font["glyf"] for glyphName in glyphOrder: glyph = glyphSet[glyphName] ttpen = TTGlyphPen(glyphSet) pen = Qu2CuPen(ttpen, **qu2cu_kwargs) glyph.draw(pen) glyf[glyphName] = ttpen.glyph(dropImpliedOnCurves=True) font["head"].glyphDataFormat = 1 if kwargs["dump_stats"]: logger.info("Stats: %s", stats) logger.info("Saving %s", output_path) font.save(output_path) def main(args=None): """Convert an OpenType font from quadratic to cubic curves""" parser = argparse.ArgumentParser(prog="qu2cu") parser.add_argument("--version", action="version", version=fontTools.__version__) parser.add_argument( "infiles", nargs="+", metavar="INPUT", help="one or more input TTF source file(s).", ) parser.add_argument("-v", "--verbose", action="count", default=0) parser.add_argument( "-e", "--conversion-error", type=float, metavar="ERROR", default=0.001, help="maxiumum approximation error measured in EM (default: 0.001)", ) parser.add_argument( "-c", "--all-cubic", default=False, action="store_true", help="whether to only use cubic curves", ) output_parser = parser.add_mutually_exclusive_group() output_parser.add_argument( "-o", "--output-file", default=None, metavar="OUTPUT", help=("output filename for the converted TTF."), ) output_parser.add_argument( "-d", "--output-dir", default=None, metavar="DIRECTORY", help="output directory where to save converted TTFs", ) options = parser.parse_args(args) if not options.verbose: level = "WARNING" elif options.verbose == 1: level = "INFO" else: level = "DEBUG" logging.basicConfig(level=level) if len(options.infiles) > 1 and options.output_file: parser.error("-o/--output-file can't be used with multile inputs") if options.output_dir: output_dir = options.output_dir if not os.path.exists(output_dir): os.mkdir(output_dir) elif not os.path.isdir(output_dir): parser.error("'%s' is not a directory" % output_dir) output_paths = [ os.path.join(output_dir, os.path.basename(p)) for p in options.infiles ] elif options.output_file: output_paths = [options.output_file] else: output_paths = [ makeOutputFileName(p, overWrite=True, suffix=".cubic") for p in options.infiles ] kwargs = dict( dump_stats=options.verbose > 0, max_err_em=options.conversion_error, all_cubic=options.all_cubic, ) for input_path, output_path in zip(options.infiles, output_paths): _font_to_cubic(input_path, output_path, **kwargs)