xref: /aosp_15_r20/external/fonttools/Snippets/otf2ttf.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes#!/usr/bin/env python3
2*e1fe3e4aSElliott Hughes
3*e1fe3e4aSElliott Hughesimport argparse
4*e1fe3e4aSElliott Hughesimport logging
5*e1fe3e4aSElliott Hughesimport os
6*e1fe3e4aSElliott Hughesimport sys
7*e1fe3e4aSElliott Hughes
8*e1fe3e4aSElliott Hughesfrom fontTools.pens.cu2quPen import Cu2QuPen
9*e1fe3e4aSElliott Hughesfrom fontTools import configLogger
10*e1fe3e4aSElliott Hughesfrom fontTools.misc.cliTools import makeOutputFileName
11*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen
12*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont, newTable
13*e1fe3e4aSElliott Hughes
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hugheslog = logging.getLogger()
16*e1fe3e4aSElliott Hughes
17*e1fe3e4aSElliott Hughes# default approximation error, measured in UPEM
18*e1fe3e4aSElliott HughesMAX_ERR = 1.0
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott Hughes# default 'post' table format
21*e1fe3e4aSElliott HughesPOST_FORMAT = 2.0
22*e1fe3e4aSElliott Hughes
23*e1fe3e4aSElliott Hughes# assuming the input contours' direction is correctly set (counter-clockwise),
24*e1fe3e4aSElliott Hughes# we just flip it to clockwise
25*e1fe3e4aSElliott HughesREVERSE_DIRECTION = True
26*e1fe3e4aSElliott Hughes
27*e1fe3e4aSElliott Hughes
28*e1fe3e4aSElliott Hughesdef glyphs_to_quadratic(glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION):
29*e1fe3e4aSElliott Hughes    quadGlyphs = {}
30*e1fe3e4aSElliott Hughes    for gname in glyphs.keys():
31*e1fe3e4aSElliott Hughes        glyph = glyphs[gname]
32*e1fe3e4aSElliott Hughes        ttPen = TTGlyphPen(glyphs)
33*e1fe3e4aSElliott Hughes        cu2quPen = Cu2QuPen(ttPen, max_err, reverse_direction=reverse_direction)
34*e1fe3e4aSElliott Hughes        glyph.draw(cu2quPen)
35*e1fe3e4aSElliott Hughes        quadGlyphs[gname] = ttPen.glyph()
36*e1fe3e4aSElliott Hughes    return quadGlyphs
37*e1fe3e4aSElliott Hughes
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughesdef update_hmtx(ttFont, glyf):
40*e1fe3e4aSElliott Hughes    hmtx = ttFont["hmtx"]
41*e1fe3e4aSElliott Hughes    for glyphName, glyph in glyf.glyphs.items():
42*e1fe3e4aSElliott Hughes        if hasattr(glyph, "xMin"):
43*e1fe3e4aSElliott Hughes            hmtx[glyphName] = (hmtx[glyphName][0], glyph.xMin)
44*e1fe3e4aSElliott Hughes
45*e1fe3e4aSElliott Hughes
46*e1fe3e4aSElliott Hughesdef otf_to_ttf(ttFont, post_format=POST_FORMAT, **kwargs):
47*e1fe3e4aSElliott Hughes    assert ttFont.sfntVersion == "OTTO"
48*e1fe3e4aSElliott Hughes    assert "CFF " in ttFont
49*e1fe3e4aSElliott Hughes
50*e1fe3e4aSElliott Hughes    glyphOrder = ttFont.getGlyphOrder()
51*e1fe3e4aSElliott Hughes
52*e1fe3e4aSElliott Hughes    ttFont["loca"] = newTable("loca")
53*e1fe3e4aSElliott Hughes    ttFont["glyf"] = glyf = newTable("glyf")
54*e1fe3e4aSElliott Hughes    glyf.glyphOrder = glyphOrder
55*e1fe3e4aSElliott Hughes    glyf.glyphs = glyphs_to_quadratic(ttFont.getGlyphSet(), **kwargs)
56*e1fe3e4aSElliott Hughes    del ttFont["CFF "]
57*e1fe3e4aSElliott Hughes    glyf.compile(ttFont)
58*e1fe3e4aSElliott Hughes    update_hmtx(ttFont, glyf)
59*e1fe3e4aSElliott Hughes
60*e1fe3e4aSElliott Hughes    ttFont["maxp"] = maxp = newTable("maxp")
61*e1fe3e4aSElliott Hughes    maxp.tableVersion = 0x00010000
62*e1fe3e4aSElliott Hughes    maxp.maxZones = 1
63*e1fe3e4aSElliott Hughes    maxp.maxTwilightPoints = 0
64*e1fe3e4aSElliott Hughes    maxp.maxStorage = 0
65*e1fe3e4aSElliott Hughes    maxp.maxFunctionDefs = 0
66*e1fe3e4aSElliott Hughes    maxp.maxInstructionDefs = 0
67*e1fe3e4aSElliott Hughes    maxp.maxStackElements = 0
68*e1fe3e4aSElliott Hughes    maxp.maxSizeOfInstructions = 0
69*e1fe3e4aSElliott Hughes    maxp.maxComponentElements = max(
70*e1fe3e4aSElliott Hughes        len(g.components if hasattr(g, "components") else [])
71*e1fe3e4aSElliott Hughes        for g in glyf.glyphs.values()
72*e1fe3e4aSElliott Hughes    )
73*e1fe3e4aSElliott Hughes    maxp.compile(ttFont)
74*e1fe3e4aSElliott Hughes
75*e1fe3e4aSElliott Hughes    post = ttFont["post"]
76*e1fe3e4aSElliott Hughes    post.formatType = post_format
77*e1fe3e4aSElliott Hughes    post.extraNames = []
78*e1fe3e4aSElliott Hughes    post.mapping = {}
79*e1fe3e4aSElliott Hughes    post.glyphOrder = glyphOrder
80*e1fe3e4aSElliott Hughes    try:
81*e1fe3e4aSElliott Hughes        post.compile(ttFont)
82*e1fe3e4aSElliott Hughes    except OverflowError:
83*e1fe3e4aSElliott Hughes        post.formatType = 3
84*e1fe3e4aSElliott Hughes        log.warning("Dropping glyph names, they do not fit in 'post' table.")
85*e1fe3e4aSElliott Hughes
86*e1fe3e4aSElliott Hughes    ttFont.sfntVersion = "\000\001\000\000"
87*e1fe3e4aSElliott Hughes
88*e1fe3e4aSElliott Hughes
89*e1fe3e4aSElliott Hughesdef main(args=None):
90*e1fe3e4aSElliott Hughes    configLogger(logger=log)
91*e1fe3e4aSElliott Hughes
92*e1fe3e4aSElliott Hughes    parser = argparse.ArgumentParser()
93*e1fe3e4aSElliott Hughes    parser.add_argument("input", nargs="+", metavar="INPUT")
94*e1fe3e4aSElliott Hughes    parser.add_argument("-o", "--output")
95*e1fe3e4aSElliott Hughes    parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR)
96*e1fe3e4aSElliott Hughes    parser.add_argument("--post-format", type=float, default=POST_FORMAT)
97*e1fe3e4aSElliott Hughes    parser.add_argument(
98*e1fe3e4aSElliott Hughes        "--keep-direction", dest="reverse_direction", action="store_false"
99*e1fe3e4aSElliott Hughes    )
100*e1fe3e4aSElliott Hughes    parser.add_argument("--face-index", type=int, default=0)
101*e1fe3e4aSElliott Hughes    parser.add_argument("--overwrite", action="store_true")
102*e1fe3e4aSElliott Hughes    options = parser.parse_args(args)
103*e1fe3e4aSElliott Hughes
104*e1fe3e4aSElliott Hughes    if options.output and len(options.input) > 1:
105*e1fe3e4aSElliott Hughes        if not os.path.isdir(options.output):
106*e1fe3e4aSElliott Hughes            parser.error(
107*e1fe3e4aSElliott Hughes                "-o/--output option must be a directory when "
108*e1fe3e4aSElliott Hughes                "processing multiple fonts"
109*e1fe3e4aSElliott Hughes            )
110*e1fe3e4aSElliott Hughes
111*e1fe3e4aSElliott Hughes    for path in options.input:
112*e1fe3e4aSElliott Hughes        if options.output and not os.path.isdir(options.output):
113*e1fe3e4aSElliott Hughes            output = options.output
114*e1fe3e4aSElliott Hughes        else:
115*e1fe3e4aSElliott Hughes            output = makeOutputFileName(
116*e1fe3e4aSElliott Hughes                path,
117*e1fe3e4aSElliott Hughes                outputDir=options.output,
118*e1fe3e4aSElliott Hughes                extension=".ttf",
119*e1fe3e4aSElliott Hughes                overWrite=options.overwrite,
120*e1fe3e4aSElliott Hughes            )
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughes        font = TTFont(path, fontNumber=options.face_index)
123*e1fe3e4aSElliott Hughes        otf_to_ttf(
124*e1fe3e4aSElliott Hughes            font,
125*e1fe3e4aSElliott Hughes            post_format=options.post_format,
126*e1fe3e4aSElliott Hughes            max_err=options.max_error,
127*e1fe3e4aSElliott Hughes            reverse_direction=options.reverse_direction,
128*e1fe3e4aSElliott Hughes        )
129*e1fe3e4aSElliott Hughes        font.save(output)
130*e1fe3e4aSElliott Hughes
131*e1fe3e4aSElliott Hughes
132*e1fe3e4aSElliott Hughesif __name__ == "__main__":
133*e1fe3e4aSElliott Hughes    sys.exit(main())
134