xref: /aosp_15_r20/external/fonttools/Lib/fontTools/qu2cu/cli.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
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