|
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) |
|
|