xref: /aosp_15_r20/external/fonttools/Tests/fontBuilder/fontBuilder_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesimport os
2*e1fe3e4aSElliott Hughesimport pytest
3*e1fe3e4aSElliott Hughesfrom fontTools.designspaceLib import AxisDescriptor
4*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont
5*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen
6*e1fe3e4aSElliott Hughesfrom fontTools.pens.t2CharStringPen import T2CharStringPen
7*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder
8*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.TupleVariation import TupleVariation
9*e1fe3e4aSElliott Hughesfrom fontTools.misc.psCharStrings import T2CharString
10*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import stripVariableItemsFromTTX
11*e1fe3e4aSElliott Hughes
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hughesdef getTestData(fileName, mode="r"):
14*e1fe3e4aSElliott Hughes    path = os.path.join(os.path.dirname(__file__), "data", fileName)
15*e1fe3e4aSElliott Hughes    with open(path, mode) as f:
16*e1fe3e4aSElliott Hughes        return f.read()
17*e1fe3e4aSElliott Hughes
18*e1fe3e4aSElliott Hughes
19*e1fe3e4aSElliott Hughesdef drawTestGlyph(pen):
20*e1fe3e4aSElliott Hughes    pen.moveTo((100, 100))
21*e1fe3e4aSElliott Hughes    pen.lineTo((100, 1000))
22*e1fe3e4aSElliott Hughes    pen.qCurveTo((200, 900), (400, 900), (500, 1000))
23*e1fe3e4aSElliott Hughes    pen.lineTo((500, 100))
24*e1fe3e4aSElliott Hughes    pen.closePath()
25*e1fe3e4aSElliott Hughes
26*e1fe3e4aSElliott Hughes
27*e1fe3e4aSElliott Hughesdef _setupFontBuilder(isTTF, unitsPerEm=1024):
28*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm, isTTF=isTTF)
29*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
30*e1fe3e4aSElliott Hughes    fb.setupCharacterMap({65: "A", 97: "a"})
31*e1fe3e4aSElliott Hughes
32*e1fe3e4aSElliott Hughes    advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes    familyName = "HelloTestFont"
35*e1fe3e4aSElliott Hughes    styleName = "TotallyNormal"
36*e1fe3e4aSElliott Hughes    nameStrings = dict(
37*e1fe3e4aSElliott Hughes        familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
38*e1fe3e4aSElliott Hughes        styleName=dict(en="TotallyNormal", nl="TotaalNormaal"),
39*e1fe3e4aSElliott Hughes    )
40*e1fe3e4aSElliott Hughes    nameStrings["psName"] = familyName + "-" + styleName
41*e1fe3e4aSElliott Hughes
42*e1fe3e4aSElliott Hughes    return fb, advanceWidths, nameStrings
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughes
45*e1fe3e4aSElliott Hughesdef _setupFontBuilderFvar(fb):
46*e1fe3e4aSElliott Hughes    assert "name" in fb.font, "Must run setupNameTable() first."
47*e1fe3e4aSElliott Hughes
48*e1fe3e4aSElliott Hughes    testAxis = AxisDescriptor()
49*e1fe3e4aSElliott Hughes    testAxis.name = "Test Axis"
50*e1fe3e4aSElliott Hughes    testAxis.tag = "TEST"
51*e1fe3e4aSElliott Hughes    testAxis.minimum = 0
52*e1fe3e4aSElliott Hughes    testAxis.default = 0
53*e1fe3e4aSElliott Hughes    testAxis.maximum = 100
54*e1fe3e4aSElliott Hughes    testAxis.map = [(0, 0), (40, 60), (100, 100)]
55*e1fe3e4aSElliott Hughes    axes = [testAxis]
56*e1fe3e4aSElliott Hughes    instances = [
57*e1fe3e4aSElliott Hughes        dict(location=dict(TEST=0), stylename="TotallyNormal"),
58*e1fe3e4aSElliott Hughes        dict(location=dict(TEST=100), stylename="TotallyTested"),
59*e1fe3e4aSElliott Hughes    ]
60*e1fe3e4aSElliott Hughes    fb.setupFvar(axes, instances)
61*e1fe3e4aSElliott Hughes    fb.setupAvar(axes)
62*e1fe3e4aSElliott Hughes
63*e1fe3e4aSElliott Hughes    return fb
64*e1fe3e4aSElliott Hughes
65*e1fe3e4aSElliott Hughes
66*e1fe3e4aSElliott Hughesdef _setupFontBuilderCFF2(fb):
67*e1fe3e4aSElliott Hughes    assert "fvar" in fb.font, "Must run _setupFontBuilderFvar() first."
68*e1fe3e4aSElliott Hughes
69*e1fe3e4aSElliott Hughes    pen = T2CharStringPen(None, None, CFF2=True)
70*e1fe3e4aSElliott Hughes    drawTestGlyph(pen)
71*e1fe3e4aSElliott Hughes    charString = pen.getCharString()
72*e1fe3e4aSElliott Hughes
73*e1fe3e4aSElliott Hughes    program = [
74*e1fe3e4aSElliott Hughes        200,
75*e1fe3e4aSElliott Hughes        200,
76*e1fe3e4aSElliott Hughes        -200,
77*e1fe3e4aSElliott Hughes        -200,
78*e1fe3e4aSElliott Hughes        2,
79*e1fe3e4aSElliott Hughes        "blend",
80*e1fe3e4aSElliott Hughes        "rmoveto",
81*e1fe3e4aSElliott Hughes        400,
82*e1fe3e4aSElliott Hughes        400,
83*e1fe3e4aSElliott Hughes        1,
84*e1fe3e4aSElliott Hughes        "blend",
85*e1fe3e4aSElliott Hughes        "hlineto",
86*e1fe3e4aSElliott Hughes        400,
87*e1fe3e4aSElliott Hughes        400,
88*e1fe3e4aSElliott Hughes        1,
89*e1fe3e4aSElliott Hughes        "blend",
90*e1fe3e4aSElliott Hughes        "vlineto",
91*e1fe3e4aSElliott Hughes        -400,
92*e1fe3e4aSElliott Hughes        -400,
93*e1fe3e4aSElliott Hughes        1,
94*e1fe3e4aSElliott Hughes        "blend",
95*e1fe3e4aSElliott Hughes        "hlineto",
96*e1fe3e4aSElliott Hughes    ]
97*e1fe3e4aSElliott Hughes    charStringVariable = T2CharString(program=program)
98*e1fe3e4aSElliott Hughes
99*e1fe3e4aSElliott Hughes    charStrings = {
100*e1fe3e4aSElliott Hughes        ".notdef": charString,
101*e1fe3e4aSElliott Hughes        "A": charString,
102*e1fe3e4aSElliott Hughes        "a": charStringVariable,
103*e1fe3e4aSElliott Hughes        ".null": charString,
104*e1fe3e4aSElliott Hughes    }
105*e1fe3e4aSElliott Hughes    fb.setupCFF2(charStrings, regions=[{"TEST": (0, 1, 1)}])
106*e1fe3e4aSElliott Hughes
107*e1fe3e4aSElliott Hughes    return fb
108*e1fe3e4aSElliott Hughes
109*e1fe3e4aSElliott Hughes
110*e1fe3e4aSElliott Hughesdef _verifyOutput(outPath, tables=None):
111*e1fe3e4aSElliott Hughes    f = TTFont(outPath)
112*e1fe3e4aSElliott Hughes    f.saveXML(outPath + ".ttx", tables=tables)
113*e1fe3e4aSElliott Hughes    with open(outPath + ".ttx") as f:
114*e1fe3e4aSElliott Hughes        testData = stripVariableItemsFromTTX(f.read())
115*e1fe3e4aSElliott Hughes    refData = stripVariableItemsFromTTX(getTestData(os.path.basename(outPath) + ".ttx"))
116*e1fe3e4aSElliott Hughes    assert refData == testData
117*e1fe3e4aSElliott Hughes
118*e1fe3e4aSElliott Hughes
119*e1fe3e4aSElliott Hughesdef test_build_ttf(tmpdir):
120*e1fe3e4aSElliott Hughes    outPath = os.path.join(str(tmpdir), "test.ttf")
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughes    fb, advanceWidths, nameStrings = _setupFontBuilder(True)
123*e1fe3e4aSElliott Hughes
124*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
125*e1fe3e4aSElliott Hughes    drawTestGlyph(pen)
126*e1fe3e4aSElliott Hughes    glyph = pen.glyph()
127*e1fe3e4aSElliott Hughes    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
128*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
129*e1fe3e4aSElliott Hughes    metrics = {}
130*e1fe3e4aSElliott Hughes    glyphTable = fb.font["glyf"]
131*e1fe3e4aSElliott Hughes    for gn, advanceWidth in advanceWidths.items():
132*e1fe3e4aSElliott Hughes        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
133*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
134*e1fe3e4aSElliott Hughes
135*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
136*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
137*e1fe3e4aSElliott Hughes    fb.setupOS2()
138*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures("feature salt { sub A by a; } salt;")
139*e1fe3e4aSElliott Hughes    fb.setupPost()
140*e1fe3e4aSElliott Hughes    fb.setupDummyDSIG()
141*e1fe3e4aSElliott Hughes
142*e1fe3e4aSElliott Hughes    fb.save(outPath)
143*e1fe3e4aSElliott Hughes
144*e1fe3e4aSElliott Hughes    _verifyOutput(outPath)
145*e1fe3e4aSElliott Hughes
146*e1fe3e4aSElliott Hughes
147*e1fe3e4aSElliott Hughesdef test_build_cubic_ttf(tmp_path):
148*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
149*e1fe3e4aSElliott Hughes    pen.moveTo((100, 100))
150*e1fe3e4aSElliott Hughes    pen.curveTo((200, 200), (300, 300), (400, 400))
151*e1fe3e4aSElliott Hughes    pen.closePath()
152*e1fe3e4aSElliott Hughes    glyph = pen.glyph()
153*e1fe3e4aSElliott Hughes    glyphs = {"A": glyph}
154*e1fe3e4aSElliott Hughes
155*e1fe3e4aSElliott Hughes    # cubic outlines are not allowed in glyf table format 0
156*e1fe3e4aSElliott Hughes    fb = FontBuilder(1000, isTTF=True, glyphDataFormat=0)
157*e1fe3e4aSElliott Hughes    with pytest.raises(
158*e1fe3e4aSElliott Hughes        ValueError, match="Glyph 'A' has cubic Bezier outlines, but glyphDataFormat=0"
159*e1fe3e4aSElliott Hughes    ):
160*e1fe3e4aSElliott Hughes        fb.setupGlyf(glyphs)
161*e1fe3e4aSElliott Hughes    # can skip check if feeling adventurous
162*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs, validateGlyphFormat=False)
163*e1fe3e4aSElliott Hughes
164*e1fe3e4aSElliott Hughes    # cubics are (will be) allowed in glyf table format 1
165*e1fe3e4aSElliott Hughes    fb = FontBuilder(1000, isTTF=True, glyphDataFormat=1)
166*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
167*e1fe3e4aSElliott Hughes    assert "A" in fb.font["glyf"].glyphs
168*e1fe3e4aSElliott Hughes
169*e1fe3e4aSElliott Hughes
170*e1fe3e4aSElliott Hughesdef test_build_otf(tmpdir):
171*e1fe3e4aSElliott Hughes    outPath = os.path.join(str(tmpdir), "test.otf")
172*e1fe3e4aSElliott Hughes
173*e1fe3e4aSElliott Hughes    fb, advanceWidths, nameStrings = _setupFontBuilder(False)
174*e1fe3e4aSElliott Hughes
175*e1fe3e4aSElliott Hughes    pen = T2CharStringPen(600, None)
176*e1fe3e4aSElliott Hughes    drawTestGlyph(pen)
177*e1fe3e4aSElliott Hughes    charString = pen.getCharString()
178*e1fe3e4aSElliott Hughes    charStrings = {
179*e1fe3e4aSElliott Hughes        ".notdef": charString,
180*e1fe3e4aSElliott Hughes        "A": charString,
181*e1fe3e4aSElliott Hughes        "a": charString,
182*e1fe3e4aSElliott Hughes        ".null": charString,
183*e1fe3e4aSElliott Hughes    }
184*e1fe3e4aSElliott Hughes    fb.setupCFF(
185*e1fe3e4aSElliott Hughes        nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {}
186*e1fe3e4aSElliott Hughes    )
187*e1fe3e4aSElliott Hughes
188*e1fe3e4aSElliott Hughes    lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
189*e1fe3e4aSElliott Hughes    metrics = {}
190*e1fe3e4aSElliott Hughes    for gn, advanceWidth in advanceWidths.items():
191*e1fe3e4aSElliott Hughes        metrics[gn] = (advanceWidth, lsb[gn])
192*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
193*e1fe3e4aSElliott Hughes
194*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
195*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
196*e1fe3e4aSElliott Hughes    fb.setupOS2()
197*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures("feature kern { pos A a -50; } kern;")
198*e1fe3e4aSElliott Hughes    fb.setupPost()
199*e1fe3e4aSElliott Hughes    fb.setupDummyDSIG()
200*e1fe3e4aSElliott Hughes
201*e1fe3e4aSElliott Hughes    fb.save(outPath)
202*e1fe3e4aSElliott Hughes
203*e1fe3e4aSElliott Hughes    _verifyOutput(outPath)
204*e1fe3e4aSElliott Hughes
205*e1fe3e4aSElliott Hughes
206*e1fe3e4aSElliott Hughesdef test_build_var(tmpdir):
207*e1fe3e4aSElliott Hughes    outPath = os.path.join(str(tmpdir), "test_var.ttf")
208*e1fe3e4aSElliott Hughes
209*e1fe3e4aSElliott Hughes    fb, advanceWidths, nameStrings = _setupFontBuilder(True)
210*e1fe3e4aSElliott Hughes
211*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
212*e1fe3e4aSElliott Hughes    pen.moveTo((100, 0))
213*e1fe3e4aSElliott Hughes    pen.lineTo((100, 400))
214*e1fe3e4aSElliott Hughes    pen.lineTo((500, 400))
215*e1fe3e4aSElliott Hughes    pen.lineTo((500, 000))
216*e1fe3e4aSElliott Hughes    pen.closePath()
217*e1fe3e4aSElliott Hughes    glyph1 = pen.glyph()
218*e1fe3e4aSElliott Hughes
219*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
220*e1fe3e4aSElliott Hughes    pen.moveTo((50, 0))
221*e1fe3e4aSElliott Hughes    pen.lineTo((50, 200))
222*e1fe3e4aSElliott Hughes    pen.lineTo((250, 200))
223*e1fe3e4aSElliott Hughes    pen.lineTo((250, 0))
224*e1fe3e4aSElliott Hughes    pen.closePath()
225*e1fe3e4aSElliott Hughes    glyph2 = pen.glyph()
226*e1fe3e4aSElliott Hughes
227*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
228*e1fe3e4aSElliott Hughes    emptyGlyph = pen.glyph()
229*e1fe3e4aSElliott Hughes
230*e1fe3e4aSElliott Hughes    glyphs = {".notdef": emptyGlyph, "A": glyph1, "a": glyph2, ".null": emptyGlyph}
231*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
232*e1fe3e4aSElliott Hughes    metrics = {}
233*e1fe3e4aSElliott Hughes    glyphTable = fb.font["glyf"]
234*e1fe3e4aSElliott Hughes    for gn, advanceWidth in advanceWidths.items():
235*e1fe3e4aSElliott Hughes        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
236*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
237*e1fe3e4aSElliott Hughes
238*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
239*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
240*e1fe3e4aSElliott Hughes
241*e1fe3e4aSElliott Hughes    axes = [
242*e1fe3e4aSElliott Hughes        ("LEFT", 0, 0, 100, "Left"),
243*e1fe3e4aSElliott Hughes        ("RGHT", 0, 0, 100, "Right"),
244*e1fe3e4aSElliott Hughes        ("UPPP", 0, 0, 100, "Up"),
245*e1fe3e4aSElliott Hughes        ("DOWN", 0, 0, 100, "Down"),
246*e1fe3e4aSElliott Hughes    ]
247*e1fe3e4aSElliott Hughes    instances = [
248*e1fe3e4aSElliott Hughes        dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"),
249*e1fe3e4aSElliott Hughes        dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"),
250*e1fe3e4aSElliott Hughes    ]
251*e1fe3e4aSElliott Hughes    fb.setupFvar(axes, instances)
252*e1fe3e4aSElliott Hughes    variations = {}
253*e1fe3e4aSElliott Hughes    # Four (x, y) pairs and four phantom points:
254*e1fe3e4aSElliott Hughes    leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None]
255*e1fe3e4aSElliott Hughes    rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None]
256*e1fe3e4aSElliott Hughes    upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None]
257*e1fe3e4aSElliott Hughes    downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None]
258*e1fe3e4aSElliott Hughes    variations["a"] = [
259*e1fe3e4aSElliott Hughes        TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas),
260*e1fe3e4aSElliott Hughes        TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas),
261*e1fe3e4aSElliott Hughes        TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas),
262*e1fe3e4aSElliott Hughes        TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas),
263*e1fe3e4aSElliott Hughes    ]
264*e1fe3e4aSElliott Hughes    fb.setupGvar(variations)
265*e1fe3e4aSElliott Hughes
266*e1fe3e4aSElliott Hughes    fb.addFeatureVariations(
267*e1fe3e4aSElliott Hughes        [
268*e1fe3e4aSElliott Hughes            (
269*e1fe3e4aSElliott Hughes                [
270*e1fe3e4aSElliott Hughes                    {"LEFT": (0.8, 1), "DOWN": (0.8, 1)},
271*e1fe3e4aSElliott Hughes                    {"RGHT": (0.8, 1), "UPPP": (0.8, 1)},
272*e1fe3e4aSElliott Hughes                ],
273*e1fe3e4aSElliott Hughes                {"A": "a"},
274*e1fe3e4aSElliott Hughes            )
275*e1fe3e4aSElliott Hughes        ],
276*e1fe3e4aSElliott Hughes        featureTag="rclt",
277*e1fe3e4aSElliott Hughes    )
278*e1fe3e4aSElliott Hughes
279*e1fe3e4aSElliott Hughes    statAxes = []
280*e1fe3e4aSElliott Hughes    for tag, minVal, defaultVal, maxVal, name in axes:
281*e1fe3e4aSElliott Hughes        values = [
282*e1fe3e4aSElliott Hughes            dict(name="Neutral", value=defaultVal, flags=0x2),
283*e1fe3e4aSElliott Hughes            dict(name=name, value=maxVal),
284*e1fe3e4aSElliott Hughes        ]
285*e1fe3e4aSElliott Hughes        statAxes.append(dict(tag=tag, name=name, values=values))
286*e1fe3e4aSElliott Hughes    fb.setupStat(statAxes)
287*e1fe3e4aSElliott Hughes
288*e1fe3e4aSElliott Hughes    fb.setupOS2()
289*e1fe3e4aSElliott Hughes    fb.setupPost()
290*e1fe3e4aSElliott Hughes    fb.setupDummyDSIG()
291*e1fe3e4aSElliott Hughes
292*e1fe3e4aSElliott Hughes    fb.save(outPath)
293*e1fe3e4aSElliott Hughes
294*e1fe3e4aSElliott Hughes    _verifyOutput(outPath)
295*e1fe3e4aSElliott Hughes
296*e1fe3e4aSElliott Hughes
297*e1fe3e4aSElliott Hughesdef test_build_cff2(tmpdir):
298*e1fe3e4aSElliott Hughes    outPath = os.path.join(str(tmpdir), "test_var.otf")
299*e1fe3e4aSElliott Hughes
300*e1fe3e4aSElliott Hughes    fb, advanceWidths, nameStrings = _setupFontBuilder(False, 1000)
301*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
302*e1fe3e4aSElliott Hughes    fb = _setupFontBuilderFvar(fb)
303*e1fe3e4aSElliott Hughes    fb = _setupFontBuilderCFF2(fb)
304*e1fe3e4aSElliott Hughes
305*e1fe3e4aSElliott Hughes    metrics = {gn: (advanceWidth, 0) for gn, advanceWidth in advanceWidths.items()}
306*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
307*e1fe3e4aSElliott Hughes
308*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
309*e1fe3e4aSElliott Hughes    fb.setupOS2(
310*e1fe3e4aSElliott Hughes        sTypoAscender=825, sTypoDescender=200, usWinAscent=824, usWinDescent=200
311*e1fe3e4aSElliott Hughes    )
312*e1fe3e4aSElliott Hughes    fb.setupPost()
313*e1fe3e4aSElliott Hughes
314*e1fe3e4aSElliott Hughes    fb.save(outPath)
315*e1fe3e4aSElliott Hughes
316*e1fe3e4aSElliott Hughes    _verifyOutput(outPath)
317*e1fe3e4aSElliott Hughes
318*e1fe3e4aSElliott Hughes
319*e1fe3e4aSElliott Hughesdef test_build_cff_to_cff2(tmpdir):
320*e1fe3e4aSElliott Hughes    fb, _, _ = _setupFontBuilder(False, 1000)
321*e1fe3e4aSElliott Hughes
322*e1fe3e4aSElliott Hughes    pen = T2CharStringPen(600, None)
323*e1fe3e4aSElliott Hughes    drawTestGlyph(pen)
324*e1fe3e4aSElliott Hughes    charString = pen.getCharString()
325*e1fe3e4aSElliott Hughes    charStrings = {
326*e1fe3e4aSElliott Hughes        ".notdef": charString,
327*e1fe3e4aSElliott Hughes        "A": charString,
328*e1fe3e4aSElliott Hughes        "a": charString,
329*e1fe3e4aSElliott Hughes        ".null": charString,
330*e1fe3e4aSElliott Hughes    }
331*e1fe3e4aSElliott Hughes    fb.setupCFF("TestFont", {}, charStrings, {})
332*e1fe3e4aSElliott Hughes
333*e1fe3e4aSElliott Hughes    from fontTools.varLib.cff import convertCFFtoCFF2
334*e1fe3e4aSElliott Hughes
335*e1fe3e4aSElliott Hughes    convertCFFtoCFF2(fb.font)
336*e1fe3e4aSElliott Hughes
337*e1fe3e4aSElliott Hughes
338*e1fe3e4aSElliott Hughesdef test_setupNameTable_no_mac():
339*e1fe3e4aSElliott Hughes    fb, _, nameStrings = _setupFontBuilder(True)
340*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings, mac=False)
341*e1fe3e4aSElliott Hughes
342*e1fe3e4aSElliott Hughes    assert all(n for n in fb.font["name"].names if n.platformID == 3)
343*e1fe3e4aSElliott Hughes    assert not any(n for n in fb.font["name"].names if n.platformID == 1)
344*e1fe3e4aSElliott Hughes
345*e1fe3e4aSElliott Hughes
346*e1fe3e4aSElliott Hughesdef test_setupNameTable_no_windows():
347*e1fe3e4aSElliott Hughes    fb, _, nameStrings = _setupFontBuilder(True)
348*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings, windows=False)
349*e1fe3e4aSElliott Hughes
350*e1fe3e4aSElliott Hughes    assert all(n for n in fb.font["name"].names if n.platformID == 1)
351*e1fe3e4aSElliott Hughes    assert not any(n for n in fb.font["name"].names if n.platformID == 3)
352*e1fe3e4aSElliott Hughes
353*e1fe3e4aSElliott Hughes
354*e1fe3e4aSElliott Hughes@pytest.mark.parametrize(
355*e1fe3e4aSElliott Hughes    "is_ttf, keep_glyph_names, make_cff2, post_format",
356*e1fe3e4aSElliott Hughes    [
357*e1fe3e4aSElliott Hughes        (True, True, False, 2),  # TTF with post table format 2.0
358*e1fe3e4aSElliott Hughes        (True, False, False, 3),  # TTF with post table format 3.0
359*e1fe3e4aSElliott Hughes        (False, True, False, 3),  # CFF with post table format 3.0
360*e1fe3e4aSElliott Hughes        (False, False, False, 3),  # CFF with post table format 3.0
361*e1fe3e4aSElliott Hughes        (False, True, True, 2),  # CFF2 with post table format 2.0
362*e1fe3e4aSElliott Hughes        (False, False, True, 3),  # CFF2 with post table format 3.0
363*e1fe3e4aSElliott Hughes    ],
364*e1fe3e4aSElliott Hughes)
365*e1fe3e4aSElliott Hughesdef test_setupPost(is_ttf, keep_glyph_names, make_cff2, post_format):
366*e1fe3e4aSElliott Hughes    fb, _, nameStrings = _setupFontBuilder(is_ttf)
367*e1fe3e4aSElliott Hughes
368*e1fe3e4aSElliott Hughes    if make_cff2:
369*e1fe3e4aSElliott Hughes        fb.setupNameTable(nameStrings)
370*e1fe3e4aSElliott Hughes        fb = _setupFontBuilderCFF2(_setupFontBuilderFvar(fb))
371*e1fe3e4aSElliott Hughes
372*e1fe3e4aSElliott Hughes    if keep_glyph_names:
373*e1fe3e4aSElliott Hughes        fb.setupPost()
374*e1fe3e4aSElliott Hughes    else:
375*e1fe3e4aSElliott Hughes        fb.setupPost(keepGlyphNames=keep_glyph_names)
376*e1fe3e4aSElliott Hughes
377*e1fe3e4aSElliott Hughes    assert fb.isTTF is is_ttf
378*e1fe3e4aSElliott Hughes    assert ("CFF2" in fb.font) is make_cff2
379*e1fe3e4aSElliott Hughes    assert fb.font["post"].formatType == post_format
380*e1fe3e4aSElliott Hughes
381*e1fe3e4aSElliott Hughes
382*e1fe3e4aSElliott Hughesdef test_unicodeVariationSequences(tmpdir):
383*e1fe3e4aSElliott Hughes    familyName = "UVSTestFont"
384*e1fe3e4aSElliott Hughes    styleName = "Regular"
385*e1fe3e4aSElliott Hughes    nameStrings = dict(familyName=familyName, styleName=styleName)
386*e1fe3e4aSElliott Hughes    nameStrings["psName"] = familyName + "-" + styleName
387*e1fe3e4aSElliott Hughes    glyphOrder = [".notdef", "space", "zero", "zero.slash"]
388*e1fe3e4aSElliott Hughes    cmap = {ord(" "): "space", ord("0"): "zero"}
389*e1fe3e4aSElliott Hughes    uvs = [
390*e1fe3e4aSElliott Hughes        (0x0030, 0xFE00, "zero.slash"),
391*e1fe3e4aSElliott Hughes        (0x0030, 0xFE01, None),  # not an official sequence, just testing
392*e1fe3e4aSElliott Hughes    ]
393*e1fe3e4aSElliott Hughes    metrics = {gn: (600, 0) for gn in glyphOrder}
394*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
395*e1fe3e4aSElliott Hughes    glyph = pen.glyph()  # empty placeholder
396*e1fe3e4aSElliott Hughes    glyphs = {gn: glyph for gn in glyphOrder}
397*e1fe3e4aSElliott Hughes
398*e1fe3e4aSElliott Hughes    fb = FontBuilder(1024, isTTF=True)
399*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder(glyphOrder)
400*e1fe3e4aSElliott Hughes    fb.setupCharacterMap(cmap, uvs)
401*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
402*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
403*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
404*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
405*e1fe3e4aSElliott Hughes    fb.setupOS2()
406*e1fe3e4aSElliott Hughes    fb.setupPost()
407*e1fe3e4aSElliott Hughes
408*e1fe3e4aSElliott Hughes    outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
409*e1fe3e4aSElliott Hughes    fb.save(outPath)
410*e1fe3e4aSElliott Hughes    _verifyOutput(outPath, tables=["cmap"])
411*e1fe3e4aSElliott Hughes
412*e1fe3e4aSElliott Hughes    uvs = [
413*e1fe3e4aSElliott Hughes        (0x0030, 0xFE00, "zero.slash"),
414*e1fe3e4aSElliott Hughes        (
415*e1fe3e4aSElliott Hughes            0x0030,
416*e1fe3e4aSElliott Hughes            0xFE01,
417*e1fe3e4aSElliott Hughes            "zero",
418*e1fe3e4aSElliott Hughes        ),  # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
419*e1fe3e4aSElliott Hughes    ]
420*e1fe3e4aSElliott Hughes    fb.setupCharacterMap(cmap, uvs)
421*e1fe3e4aSElliott Hughes    fb.save(outPath)
422*e1fe3e4aSElliott Hughes    _verifyOutput(outPath, tables=["cmap"])
423*e1fe3e4aSElliott Hughes
424*e1fe3e4aSElliott Hughes
425*e1fe3e4aSElliott Hughesdef test_setupPanose():
426*e1fe3e4aSElliott Hughes    from fontTools.ttLib.tables.O_S_2f_2 import Panose
427*e1fe3e4aSElliott Hughes
428*e1fe3e4aSElliott Hughes    fb, advanceWidths, nameStrings = _setupFontBuilder(True)
429*e1fe3e4aSElliott Hughes
430*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(None)
431*e1fe3e4aSElliott Hughes    drawTestGlyph(pen)
432*e1fe3e4aSElliott Hughes    glyph = pen.glyph()
433*e1fe3e4aSElliott Hughes    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
434*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
435*e1fe3e4aSElliott Hughes    metrics = {}
436*e1fe3e4aSElliott Hughes    glyphTable = fb.font["glyf"]
437*e1fe3e4aSElliott Hughes    for gn, advanceWidth in advanceWidths.items():
438*e1fe3e4aSElliott Hughes        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
439*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(metrics)
440*e1fe3e4aSElliott Hughes
441*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader(ascent=824, descent=200)
442*e1fe3e4aSElliott Hughes    fb.setupNameTable(nameStrings)
443*e1fe3e4aSElliott Hughes    fb.setupOS2()
444*e1fe3e4aSElliott Hughes    fb.setupPost()
445*e1fe3e4aSElliott Hughes
446*e1fe3e4aSElliott Hughes    panoseValues = {  # sample value of Times New Roman from https://www.w3.org/Printing/stevahn.html
447*e1fe3e4aSElliott Hughes        "bFamilyType": 2,
448*e1fe3e4aSElliott Hughes        "bSerifStyle": 2,
449*e1fe3e4aSElliott Hughes        "bWeight": 6,
450*e1fe3e4aSElliott Hughes        "bProportion": 3,
451*e1fe3e4aSElliott Hughes        "bContrast": 5,
452*e1fe3e4aSElliott Hughes        "bStrokeVariation": 4,
453*e1fe3e4aSElliott Hughes        "bArmStyle": 5,
454*e1fe3e4aSElliott Hughes        "bLetterForm": 2,
455*e1fe3e4aSElliott Hughes        "bMidline": 3,
456*e1fe3e4aSElliott Hughes        "bXHeight": 4,
457*e1fe3e4aSElliott Hughes    }
458*e1fe3e4aSElliott Hughes    panoseObj = Panose(**panoseValues)
459*e1fe3e4aSElliott Hughes
460*e1fe3e4aSElliott Hughes    for name in panoseValues:
461*e1fe3e4aSElliott Hughes        assert getattr(fb.font["OS/2"].panose, name) == 0
462*e1fe3e4aSElliott Hughes
463*e1fe3e4aSElliott Hughes    fb.setupOS2(panose=panoseObj)
464*e1fe3e4aSElliott Hughes    fb.setupPost()
465*e1fe3e4aSElliott Hughes
466*e1fe3e4aSElliott Hughes    for name, value in panoseValues.items():
467*e1fe3e4aSElliott Hughes        assert getattr(fb.font["OS/2"].panose, name) == value
468