xref: /aosp_15_r20/external/fonttools/Tests/otlLib/builder_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesimport io
2*e1fe3e4aSElliott Hughesimport struct
3*e1fe3e4aSElliott Hughesfrom fontTools.misc.fixedTools import floatToFixed, fixedToFloat
4*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML
5*e1fe3e4aSElliott Hughesfrom fontTools.otlLib import builder, error
6*e1fe3e4aSElliott Hughesfrom fontTools import ttLib
7*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables
8*e1fe3e4aSElliott Hughesimport pytest
9*e1fe3e4aSElliott Hughes
10*e1fe3e4aSElliott Hughes
11*e1fe3e4aSElliott Hughesclass BuilderTest(object):
12*e1fe3e4aSElliott Hughes    GLYPHS = (
13*e1fe3e4aSElliott Hughes        ".notdef space zero one two three four five six "
14*e1fe3e4aSElliott Hughes        "A B C a b c grave acute cedilla f_f_i f_i c_t"
15*e1fe3e4aSElliott Hughes    ).split()
16*e1fe3e4aSElliott Hughes    GLYPHMAP = {name: num for num, name in enumerate(GLYPHS)}
17*e1fe3e4aSElliott Hughes
18*e1fe3e4aSElliott Hughes    ANCHOR1 = builder.buildAnchor(11, -11)
19*e1fe3e4aSElliott Hughes    ANCHOR2 = builder.buildAnchor(22, -22)
20*e1fe3e4aSElliott Hughes    ANCHOR3 = builder.buildAnchor(33, -33)
21*e1fe3e4aSElliott Hughes
22*e1fe3e4aSElliott Hughes    def test_buildAnchor_format1(self):
23*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor(23, 42)
24*e1fe3e4aSElliott Hughes        assert getXML(anchor.toXML) == [
25*e1fe3e4aSElliott Hughes            '<Anchor Format="1">',
26*e1fe3e4aSElliott Hughes            '  <XCoordinate value="23"/>',
27*e1fe3e4aSElliott Hughes            '  <YCoordinate value="42"/>',
28*e1fe3e4aSElliott Hughes            "</Anchor>",
29*e1fe3e4aSElliott Hughes        ]
30*e1fe3e4aSElliott Hughes
31*e1fe3e4aSElliott Hughes    def test_buildAnchor_format2(self):
32*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor(23, 42, point=17)
33*e1fe3e4aSElliott Hughes        assert getXML(anchor.toXML) == [
34*e1fe3e4aSElliott Hughes            '<Anchor Format="2">',
35*e1fe3e4aSElliott Hughes            '  <XCoordinate value="23"/>',
36*e1fe3e4aSElliott Hughes            '  <YCoordinate value="42"/>',
37*e1fe3e4aSElliott Hughes            '  <AnchorPoint value="17"/>',
38*e1fe3e4aSElliott Hughes            "</Anchor>",
39*e1fe3e4aSElliott Hughes        ]
40*e1fe3e4aSElliott Hughes
41*e1fe3e4aSElliott Hughes    def test_buildAnchor_format3(self):
42*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor(
43*e1fe3e4aSElliott Hughes            23,
44*e1fe3e4aSElliott Hughes            42,
45*e1fe3e4aSElliott Hughes            deviceX=builder.buildDevice({1: 1, 0: 0}),
46*e1fe3e4aSElliott Hughes            deviceY=builder.buildDevice({7: 7}),
47*e1fe3e4aSElliott Hughes        )
48*e1fe3e4aSElliott Hughes        assert getXML(anchor.toXML) == [
49*e1fe3e4aSElliott Hughes            '<Anchor Format="3">',
50*e1fe3e4aSElliott Hughes            '  <XCoordinate value="23"/>',
51*e1fe3e4aSElliott Hughes            '  <YCoordinate value="42"/>',
52*e1fe3e4aSElliott Hughes            "  <XDeviceTable>",
53*e1fe3e4aSElliott Hughes            '    <StartSize value="0"/>',
54*e1fe3e4aSElliott Hughes            '    <EndSize value="1"/>',
55*e1fe3e4aSElliott Hughes            '    <DeltaFormat value="1"/>',
56*e1fe3e4aSElliott Hughes            '    <DeltaValue value="[0, 1]"/>',
57*e1fe3e4aSElliott Hughes            "  </XDeviceTable>",
58*e1fe3e4aSElliott Hughes            "  <YDeviceTable>",
59*e1fe3e4aSElliott Hughes            '    <StartSize value="7"/>',
60*e1fe3e4aSElliott Hughes            '    <EndSize value="7"/>',
61*e1fe3e4aSElliott Hughes            '    <DeltaFormat value="2"/>',
62*e1fe3e4aSElliott Hughes            '    <DeltaValue value="[7]"/>',
63*e1fe3e4aSElliott Hughes            "  </YDeviceTable>",
64*e1fe3e4aSElliott Hughes            "</Anchor>",
65*e1fe3e4aSElliott Hughes        ]
66*e1fe3e4aSElliott Hughes
67*e1fe3e4aSElliott Hughes    def test_buildAttachList(self):
68*e1fe3e4aSElliott Hughes        attachList = builder.buildAttachList(
69*e1fe3e4aSElliott Hughes            {"zero": [23, 7], "one": [1]}, self.GLYPHMAP
70*e1fe3e4aSElliott Hughes        )
71*e1fe3e4aSElliott Hughes        assert getXML(attachList.toXML) == [
72*e1fe3e4aSElliott Hughes            "<AttachList>",
73*e1fe3e4aSElliott Hughes            "  <Coverage>",
74*e1fe3e4aSElliott Hughes            '    <Glyph value="zero"/>',
75*e1fe3e4aSElliott Hughes            '    <Glyph value="one"/>',
76*e1fe3e4aSElliott Hughes            "  </Coverage>",
77*e1fe3e4aSElliott Hughes            "  <!-- GlyphCount=2 -->",
78*e1fe3e4aSElliott Hughes            '  <AttachPoint index="0">',
79*e1fe3e4aSElliott Hughes            "    <!-- PointCount=2 -->",
80*e1fe3e4aSElliott Hughes            '    <PointIndex index="0" value="7"/>',
81*e1fe3e4aSElliott Hughes            '    <PointIndex index="1" value="23"/>',
82*e1fe3e4aSElliott Hughes            "  </AttachPoint>",
83*e1fe3e4aSElliott Hughes            '  <AttachPoint index="1">',
84*e1fe3e4aSElliott Hughes            "    <!-- PointCount=1 -->",
85*e1fe3e4aSElliott Hughes            '    <PointIndex index="0" value="1"/>',
86*e1fe3e4aSElliott Hughes            "  </AttachPoint>",
87*e1fe3e4aSElliott Hughes            "</AttachList>",
88*e1fe3e4aSElliott Hughes        ]
89*e1fe3e4aSElliott Hughes
90*e1fe3e4aSElliott Hughes    def test_buildAttachList_empty(self):
91*e1fe3e4aSElliott Hughes        assert builder.buildAttachList({}, self.GLYPHMAP) is None
92*e1fe3e4aSElliott Hughes
93*e1fe3e4aSElliott Hughes    def test_buildAttachPoint(self):
94*e1fe3e4aSElliott Hughes        attachPoint = builder.buildAttachPoint([7, 3])
95*e1fe3e4aSElliott Hughes        assert getXML(attachPoint.toXML) == [
96*e1fe3e4aSElliott Hughes            "<AttachPoint>",
97*e1fe3e4aSElliott Hughes            "  <!-- PointCount=2 -->",
98*e1fe3e4aSElliott Hughes            '  <PointIndex index="0" value="3"/>',
99*e1fe3e4aSElliott Hughes            '  <PointIndex index="1" value="7"/>',
100*e1fe3e4aSElliott Hughes            "</AttachPoint>",
101*e1fe3e4aSElliott Hughes        ]
102*e1fe3e4aSElliott Hughes
103*e1fe3e4aSElliott Hughes    def test_buildAttachPoint_empty(self):
104*e1fe3e4aSElliott Hughes        assert builder.buildAttachPoint([]) is None
105*e1fe3e4aSElliott Hughes
106*e1fe3e4aSElliott Hughes    def test_buildAttachPoint_duplicate(self):
107*e1fe3e4aSElliott Hughes        attachPoint = builder.buildAttachPoint([7, 3, 7])
108*e1fe3e4aSElliott Hughes        assert getXML(attachPoint.toXML) == [
109*e1fe3e4aSElliott Hughes            "<AttachPoint>",
110*e1fe3e4aSElliott Hughes            "  <!-- PointCount=2 -->",
111*e1fe3e4aSElliott Hughes            '  <PointIndex index="0" value="3"/>',
112*e1fe3e4aSElliott Hughes            '  <PointIndex index="1" value="7"/>',
113*e1fe3e4aSElliott Hughes            "</AttachPoint>",
114*e1fe3e4aSElliott Hughes        ]
115*e1fe3e4aSElliott Hughes
116*e1fe3e4aSElliott Hughes    def test_buildBaseArray(self):
117*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor
118*e1fe3e4aSElliott Hughes        baseArray = builder.buildBaseArray(
119*e1fe3e4aSElliott Hughes            {"a": {2: anchor(300, 80)}, "c": {1: anchor(300, 80), 2: anchor(300, -20)}},
120*e1fe3e4aSElliott Hughes            numMarkClasses=4,
121*e1fe3e4aSElliott Hughes            glyphMap=self.GLYPHMAP,
122*e1fe3e4aSElliott Hughes        )
123*e1fe3e4aSElliott Hughes        assert getXML(baseArray.toXML) == [
124*e1fe3e4aSElliott Hughes            "<BaseArray>",
125*e1fe3e4aSElliott Hughes            "  <!-- BaseCount=2 -->",
126*e1fe3e4aSElliott Hughes            '  <BaseRecord index="0">',
127*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="0" empty="1"/>',
128*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="1" empty="1"/>',
129*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="2" Format="1">',
130*e1fe3e4aSElliott Hughes            '      <XCoordinate value="300"/>',
131*e1fe3e4aSElliott Hughes            '      <YCoordinate value="80"/>',
132*e1fe3e4aSElliott Hughes            "    </BaseAnchor>",
133*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="3" empty="1"/>',
134*e1fe3e4aSElliott Hughes            "  </BaseRecord>",
135*e1fe3e4aSElliott Hughes            '  <BaseRecord index="1">',
136*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="0" empty="1"/>',
137*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="1" Format="1">',
138*e1fe3e4aSElliott Hughes            '      <XCoordinate value="300"/>',
139*e1fe3e4aSElliott Hughes            '      <YCoordinate value="80"/>',
140*e1fe3e4aSElliott Hughes            "    </BaseAnchor>",
141*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="2" Format="1">',
142*e1fe3e4aSElliott Hughes            '      <XCoordinate value="300"/>',
143*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-20"/>',
144*e1fe3e4aSElliott Hughes            "    </BaseAnchor>",
145*e1fe3e4aSElliott Hughes            '    <BaseAnchor index="3" empty="1"/>',
146*e1fe3e4aSElliott Hughes            "  </BaseRecord>",
147*e1fe3e4aSElliott Hughes            "</BaseArray>",
148*e1fe3e4aSElliott Hughes        ]
149*e1fe3e4aSElliott Hughes
150*e1fe3e4aSElliott Hughes    def test_buildBaseRecord(self):
151*e1fe3e4aSElliott Hughes        a = builder.buildAnchor
152*e1fe3e4aSElliott Hughes        rec = builder.buildBaseRecord([a(500, -20), None, a(300, -15)])
153*e1fe3e4aSElliott Hughes        assert getXML(rec.toXML) == [
154*e1fe3e4aSElliott Hughes            "<BaseRecord>",
155*e1fe3e4aSElliott Hughes            '  <BaseAnchor index="0" Format="1">',
156*e1fe3e4aSElliott Hughes            '    <XCoordinate value="500"/>',
157*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-20"/>',
158*e1fe3e4aSElliott Hughes            "  </BaseAnchor>",
159*e1fe3e4aSElliott Hughes            '  <BaseAnchor index="1" empty="1"/>',
160*e1fe3e4aSElliott Hughes            '  <BaseAnchor index="2" Format="1">',
161*e1fe3e4aSElliott Hughes            '    <XCoordinate value="300"/>',
162*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-15"/>',
163*e1fe3e4aSElliott Hughes            "  </BaseAnchor>",
164*e1fe3e4aSElliott Hughes            "</BaseRecord>",
165*e1fe3e4aSElliott Hughes        ]
166*e1fe3e4aSElliott Hughes
167*e1fe3e4aSElliott Hughes    def test_buildCaretValueForCoord(self):
168*e1fe3e4aSElliott Hughes        caret = builder.buildCaretValueForCoord(500)
169*e1fe3e4aSElliott Hughes        assert getXML(caret.toXML) == [
170*e1fe3e4aSElliott Hughes            '<CaretValue Format="1">',
171*e1fe3e4aSElliott Hughes            '  <Coordinate value="500"/>',
172*e1fe3e4aSElliott Hughes            "</CaretValue>",
173*e1fe3e4aSElliott Hughes        ]
174*e1fe3e4aSElliott Hughes
175*e1fe3e4aSElliott Hughes    def test_buildCaretValueForPoint(self):
176*e1fe3e4aSElliott Hughes        caret = builder.buildCaretValueForPoint(23)
177*e1fe3e4aSElliott Hughes        assert getXML(caret.toXML) == [
178*e1fe3e4aSElliott Hughes            '<CaretValue Format="2">',
179*e1fe3e4aSElliott Hughes            '  <CaretValuePoint value="23"/>',
180*e1fe3e4aSElliott Hughes            "</CaretValue>",
181*e1fe3e4aSElliott Hughes        ]
182*e1fe3e4aSElliott Hughes
183*e1fe3e4aSElliott Hughes    def test_buildComponentRecord(self):
184*e1fe3e4aSElliott Hughes        a = builder.buildAnchor
185*e1fe3e4aSElliott Hughes        rec = builder.buildComponentRecord([a(500, -20), None, a(300, -15)])
186*e1fe3e4aSElliott Hughes        assert getXML(rec.toXML) == [
187*e1fe3e4aSElliott Hughes            "<ComponentRecord>",
188*e1fe3e4aSElliott Hughes            '  <LigatureAnchor index="0" Format="1">',
189*e1fe3e4aSElliott Hughes            '    <XCoordinate value="500"/>',
190*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-20"/>',
191*e1fe3e4aSElliott Hughes            "  </LigatureAnchor>",
192*e1fe3e4aSElliott Hughes            '  <LigatureAnchor index="1" empty="1"/>',
193*e1fe3e4aSElliott Hughes            '  <LigatureAnchor index="2" Format="1">',
194*e1fe3e4aSElliott Hughes            '    <XCoordinate value="300"/>',
195*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-15"/>',
196*e1fe3e4aSElliott Hughes            "  </LigatureAnchor>",
197*e1fe3e4aSElliott Hughes            "</ComponentRecord>",
198*e1fe3e4aSElliott Hughes        ]
199*e1fe3e4aSElliott Hughes
200*e1fe3e4aSElliott Hughes    def test_buildComponentRecord_empty(self):
201*e1fe3e4aSElliott Hughes        assert builder.buildComponentRecord([]) is None
202*e1fe3e4aSElliott Hughes
203*e1fe3e4aSElliott Hughes    def test_buildComponentRecord_None(self):
204*e1fe3e4aSElliott Hughes        assert builder.buildComponentRecord(None) is None
205*e1fe3e4aSElliott Hughes
206*e1fe3e4aSElliott Hughes    def test_buildCoverage(self):
207*e1fe3e4aSElliott Hughes        cov = builder.buildCoverage(("two", "four", "two"), {"two": 2, "four": 4})
208*e1fe3e4aSElliott Hughes        assert getXML(cov.toXML) == [
209*e1fe3e4aSElliott Hughes            "<Coverage>",
210*e1fe3e4aSElliott Hughes            '  <Glyph value="two"/>',
211*e1fe3e4aSElliott Hughes            '  <Glyph value="four"/>',
212*e1fe3e4aSElliott Hughes            "</Coverage>",
213*e1fe3e4aSElliott Hughes        ]
214*e1fe3e4aSElliott Hughes
215*e1fe3e4aSElliott Hughes    def test_buildCursivePos(self):
216*e1fe3e4aSElliott Hughes        pos = builder.buildCursivePosSubtable(
217*e1fe3e4aSElliott Hughes            {"two": (self.ANCHOR1, self.ANCHOR2), "four": (self.ANCHOR3, self.ANCHOR1)},
218*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
219*e1fe3e4aSElliott Hughes        )
220*e1fe3e4aSElliott Hughes        assert getXML(pos.toXML) == [
221*e1fe3e4aSElliott Hughes            '<CursivePos Format="1">',
222*e1fe3e4aSElliott Hughes            "  <Coverage>",
223*e1fe3e4aSElliott Hughes            '    <Glyph value="two"/>',
224*e1fe3e4aSElliott Hughes            '    <Glyph value="four"/>',
225*e1fe3e4aSElliott Hughes            "  </Coverage>",
226*e1fe3e4aSElliott Hughes            "  <!-- EntryExitCount=2 -->",
227*e1fe3e4aSElliott Hughes            '  <EntryExitRecord index="0">',
228*e1fe3e4aSElliott Hughes            '    <EntryAnchor Format="1">',
229*e1fe3e4aSElliott Hughes            '      <XCoordinate value="11"/>',
230*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-11"/>',
231*e1fe3e4aSElliott Hughes            "    </EntryAnchor>",
232*e1fe3e4aSElliott Hughes            '    <ExitAnchor Format="1">',
233*e1fe3e4aSElliott Hughes            '      <XCoordinate value="22"/>',
234*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-22"/>',
235*e1fe3e4aSElliott Hughes            "    </ExitAnchor>",
236*e1fe3e4aSElliott Hughes            "  </EntryExitRecord>",
237*e1fe3e4aSElliott Hughes            '  <EntryExitRecord index="1">',
238*e1fe3e4aSElliott Hughes            '    <EntryAnchor Format="1">',
239*e1fe3e4aSElliott Hughes            '      <XCoordinate value="33"/>',
240*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-33"/>',
241*e1fe3e4aSElliott Hughes            "    </EntryAnchor>",
242*e1fe3e4aSElliott Hughes            '    <ExitAnchor Format="1">',
243*e1fe3e4aSElliott Hughes            '      <XCoordinate value="11"/>',
244*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-11"/>',
245*e1fe3e4aSElliott Hughes            "    </ExitAnchor>",
246*e1fe3e4aSElliott Hughes            "  </EntryExitRecord>",
247*e1fe3e4aSElliott Hughes            "</CursivePos>",
248*e1fe3e4aSElliott Hughes        ]
249*e1fe3e4aSElliott Hughes
250*e1fe3e4aSElliott Hughes    def test_buildDevice_format1(self):
251*e1fe3e4aSElliott Hughes        device = builder.buildDevice({1: 1, 0: 0})
252*e1fe3e4aSElliott Hughes        assert getXML(device.toXML) == [
253*e1fe3e4aSElliott Hughes            "<Device>",
254*e1fe3e4aSElliott Hughes            '  <StartSize value="0"/>',
255*e1fe3e4aSElliott Hughes            '  <EndSize value="1"/>',
256*e1fe3e4aSElliott Hughes            '  <DeltaFormat value="1"/>',
257*e1fe3e4aSElliott Hughes            '  <DeltaValue value="[0, 1]"/>',
258*e1fe3e4aSElliott Hughes            "</Device>",
259*e1fe3e4aSElliott Hughes        ]
260*e1fe3e4aSElliott Hughes
261*e1fe3e4aSElliott Hughes    def test_buildDevice_format2(self):
262*e1fe3e4aSElliott Hughes        device = builder.buildDevice({2: 2, 0: 1, 1: 0})
263*e1fe3e4aSElliott Hughes        assert getXML(device.toXML) == [
264*e1fe3e4aSElliott Hughes            "<Device>",
265*e1fe3e4aSElliott Hughes            '  <StartSize value="0"/>',
266*e1fe3e4aSElliott Hughes            '  <EndSize value="2"/>',
267*e1fe3e4aSElliott Hughes            '  <DeltaFormat value="2"/>',
268*e1fe3e4aSElliott Hughes            '  <DeltaValue value="[1, 0, 2]"/>',
269*e1fe3e4aSElliott Hughes            "</Device>",
270*e1fe3e4aSElliott Hughes        ]
271*e1fe3e4aSElliott Hughes
272*e1fe3e4aSElliott Hughes    def test_buildDevice_format3(self):
273*e1fe3e4aSElliott Hughes        device = builder.buildDevice({5: 3, 1: 77})
274*e1fe3e4aSElliott Hughes        assert getXML(device.toXML) == [
275*e1fe3e4aSElliott Hughes            "<Device>",
276*e1fe3e4aSElliott Hughes            '  <StartSize value="1"/>',
277*e1fe3e4aSElliott Hughes            '  <EndSize value="5"/>',
278*e1fe3e4aSElliott Hughes            '  <DeltaFormat value="3"/>',
279*e1fe3e4aSElliott Hughes            '  <DeltaValue value="[77, 0, 0, 0, 3]"/>',
280*e1fe3e4aSElliott Hughes            "</Device>",
281*e1fe3e4aSElliott Hughes        ]
282*e1fe3e4aSElliott Hughes
283*e1fe3e4aSElliott Hughes    def test_buildLigatureArray(self):
284*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor
285*e1fe3e4aSElliott Hughes        ligatureArray = builder.buildLigatureArray(
286*e1fe3e4aSElliott Hughes            {
287*e1fe3e4aSElliott Hughes                "f_i": [{2: anchor(300, -20)}, {}],
288*e1fe3e4aSElliott Hughes                "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}],
289*e1fe3e4aSElliott Hughes            },
290*e1fe3e4aSElliott Hughes            numMarkClasses=4,
291*e1fe3e4aSElliott Hughes            glyphMap=self.GLYPHMAP,
292*e1fe3e4aSElliott Hughes        )
293*e1fe3e4aSElliott Hughes        assert getXML(ligatureArray.toXML) == [
294*e1fe3e4aSElliott Hughes            "<LigatureArray>",
295*e1fe3e4aSElliott Hughes            "  <!-- LigatureCount=2 -->",
296*e1fe3e4aSElliott Hughes            '  <LigatureAttach index="0">',  # f_i
297*e1fe3e4aSElliott Hughes            "    <!-- ComponentCount=2 -->",
298*e1fe3e4aSElliott Hughes            '    <ComponentRecord index="0">',
299*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="0" empty="1"/>',
300*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="1" empty="1"/>',
301*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="2" Format="1">',
302*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
303*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-20"/>',
304*e1fe3e4aSElliott Hughes            "      </LigatureAnchor>",
305*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="3" empty="1"/>',
306*e1fe3e4aSElliott Hughes            "    </ComponentRecord>",
307*e1fe3e4aSElliott Hughes            '    <ComponentRecord index="1">',
308*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="0" empty="1"/>',
309*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="1" empty="1"/>',
310*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="2" empty="1"/>',
311*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="3" empty="1"/>',
312*e1fe3e4aSElliott Hughes            "    </ComponentRecord>",
313*e1fe3e4aSElliott Hughes            "  </LigatureAttach>",
314*e1fe3e4aSElliott Hughes            '  <LigatureAttach index="1">',
315*e1fe3e4aSElliott Hughes            "    <!-- ComponentCount=2 -->",
316*e1fe3e4aSElliott Hughes            '    <ComponentRecord index="0">',
317*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="0" empty="1"/>',
318*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="1" empty="1"/>',
319*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="2" empty="1"/>',
320*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="3" empty="1"/>',
321*e1fe3e4aSElliott Hughes            "    </ComponentRecord>",
322*e1fe3e4aSElliott Hughes            '    <ComponentRecord index="1">',
323*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="0" empty="1"/>',
324*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="1" Format="1">',
325*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
326*e1fe3e4aSElliott Hughes            '        <YCoordinate value="350"/>',
327*e1fe3e4aSElliott Hughes            "      </LigatureAnchor>",
328*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="2" Format="1">',
329*e1fe3e4aSElliott Hughes            '        <XCoordinate value="1300"/>',
330*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-20"/>',
331*e1fe3e4aSElliott Hughes            "      </LigatureAnchor>",
332*e1fe3e4aSElliott Hughes            '      <LigatureAnchor index="3" empty="1"/>',
333*e1fe3e4aSElliott Hughes            "    </ComponentRecord>",
334*e1fe3e4aSElliott Hughes            "  </LigatureAttach>",
335*e1fe3e4aSElliott Hughes            "</LigatureArray>",
336*e1fe3e4aSElliott Hughes        ]
337*e1fe3e4aSElliott Hughes
338*e1fe3e4aSElliott Hughes    def test_buildLigatureAttach(self):
339*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor
340*e1fe3e4aSElliott Hughes        attach = builder.buildLigatureAttach(
341*e1fe3e4aSElliott Hughes            [[anchor(500, -10), None], [None, anchor(300, -20), None]]
342*e1fe3e4aSElliott Hughes        )
343*e1fe3e4aSElliott Hughes        assert getXML(attach.toXML) == [
344*e1fe3e4aSElliott Hughes            "<LigatureAttach>",
345*e1fe3e4aSElliott Hughes            "  <!-- ComponentCount=2 -->",
346*e1fe3e4aSElliott Hughes            '  <ComponentRecord index="0">',
347*e1fe3e4aSElliott Hughes            '    <LigatureAnchor index="0" Format="1">',
348*e1fe3e4aSElliott Hughes            '      <XCoordinate value="500"/>',
349*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-10"/>',
350*e1fe3e4aSElliott Hughes            "    </LigatureAnchor>",
351*e1fe3e4aSElliott Hughes            '    <LigatureAnchor index="1" empty="1"/>',
352*e1fe3e4aSElliott Hughes            "  </ComponentRecord>",
353*e1fe3e4aSElliott Hughes            '  <ComponentRecord index="1">',
354*e1fe3e4aSElliott Hughes            '    <LigatureAnchor index="0" empty="1"/>',
355*e1fe3e4aSElliott Hughes            '    <LigatureAnchor index="1" Format="1">',
356*e1fe3e4aSElliott Hughes            '      <XCoordinate value="300"/>',
357*e1fe3e4aSElliott Hughes            '      <YCoordinate value="-20"/>',
358*e1fe3e4aSElliott Hughes            "    </LigatureAnchor>",
359*e1fe3e4aSElliott Hughes            '    <LigatureAnchor index="2" empty="1"/>',
360*e1fe3e4aSElliott Hughes            "  </ComponentRecord>",
361*e1fe3e4aSElliott Hughes            "</LigatureAttach>",
362*e1fe3e4aSElliott Hughes        ]
363*e1fe3e4aSElliott Hughes
364*e1fe3e4aSElliott Hughes    def test_buildLigatureAttach_emptyComponents(self):
365*e1fe3e4aSElliott Hughes        attach = builder.buildLigatureAttach([[], None])
366*e1fe3e4aSElliott Hughes        assert getXML(attach.toXML) == [
367*e1fe3e4aSElliott Hughes            "<LigatureAttach>",
368*e1fe3e4aSElliott Hughes            "  <!-- ComponentCount=2 -->",
369*e1fe3e4aSElliott Hughes            '  <ComponentRecord index="0" empty="1"/>',
370*e1fe3e4aSElliott Hughes            '  <ComponentRecord index="1" empty="1"/>',
371*e1fe3e4aSElliott Hughes            "</LigatureAttach>",
372*e1fe3e4aSElliott Hughes        ]
373*e1fe3e4aSElliott Hughes
374*e1fe3e4aSElliott Hughes    def test_buildLigatureAttach_noComponents(self):
375*e1fe3e4aSElliott Hughes        attach = builder.buildLigatureAttach([])
376*e1fe3e4aSElliott Hughes        assert getXML(attach.toXML) == [
377*e1fe3e4aSElliott Hughes            "<LigatureAttach>",
378*e1fe3e4aSElliott Hughes            "  <!-- ComponentCount=0 -->",
379*e1fe3e4aSElliott Hughes            "</LigatureAttach>",
380*e1fe3e4aSElliott Hughes        ]
381*e1fe3e4aSElliott Hughes
382*e1fe3e4aSElliott Hughes    def test_buildLigCaretList(self):
383*e1fe3e4aSElliott Hughes        carets = builder.buildLigCaretList(
384*e1fe3e4aSElliott Hughes            {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP
385*e1fe3e4aSElliott Hughes        )
386*e1fe3e4aSElliott Hughes        assert getXML(carets.toXML) == [
387*e1fe3e4aSElliott Hughes            "<LigCaretList>",
388*e1fe3e4aSElliott Hughes            "  <Coverage>",
389*e1fe3e4aSElliott Hughes            '    <Glyph value="f_f_i"/>',
390*e1fe3e4aSElliott Hughes            '    <Glyph value="c_t"/>',
391*e1fe3e4aSElliott Hughes            "  </Coverage>",
392*e1fe3e4aSElliott Hughes            "  <!-- LigGlyphCount=2 -->",
393*e1fe3e4aSElliott Hughes            '  <LigGlyph index="0">',
394*e1fe3e4aSElliott Hughes            "    <!-- CaretCount=2 -->",
395*e1fe3e4aSElliott Hughes            '    <CaretValue index="0" Format="1">',
396*e1fe3e4aSElliott Hughes            '      <Coordinate value="300"/>',
397*e1fe3e4aSElliott Hughes            "    </CaretValue>",
398*e1fe3e4aSElliott Hughes            '    <CaretValue index="1" Format="1">',
399*e1fe3e4aSElliott Hughes            '      <Coordinate value="600"/>',
400*e1fe3e4aSElliott Hughes            "    </CaretValue>",
401*e1fe3e4aSElliott Hughes            "  </LigGlyph>",
402*e1fe3e4aSElliott Hughes            '  <LigGlyph index="1">',
403*e1fe3e4aSElliott Hughes            "    <!-- CaretCount=1 -->",
404*e1fe3e4aSElliott Hughes            '    <CaretValue index="0" Format="2">',
405*e1fe3e4aSElliott Hughes            '      <CaretValuePoint value="42"/>',
406*e1fe3e4aSElliott Hughes            "    </CaretValue>",
407*e1fe3e4aSElliott Hughes            "  </LigGlyph>",
408*e1fe3e4aSElliott Hughes            "</LigCaretList>",
409*e1fe3e4aSElliott Hughes        ]
410*e1fe3e4aSElliott Hughes
411*e1fe3e4aSElliott Hughes    def test_buildLigCaretList_bothCoordsAndPointsForSameGlyph(self):
412*e1fe3e4aSElliott Hughes        carets = builder.buildLigCaretList(
413*e1fe3e4aSElliott Hughes            {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP
414*e1fe3e4aSElliott Hughes        )
415*e1fe3e4aSElliott Hughes        assert getXML(carets.toXML) == [
416*e1fe3e4aSElliott Hughes            "<LigCaretList>",
417*e1fe3e4aSElliott Hughes            "  <Coverage>",
418*e1fe3e4aSElliott Hughes            '    <Glyph value="f_f_i"/>',
419*e1fe3e4aSElliott Hughes            "  </Coverage>",
420*e1fe3e4aSElliott Hughes            "  <!-- LigGlyphCount=1 -->",
421*e1fe3e4aSElliott Hughes            '  <LigGlyph index="0">',
422*e1fe3e4aSElliott Hughes            "    <!-- CaretCount=2 -->",
423*e1fe3e4aSElliott Hughes            '    <CaretValue index="0" Format="1">',
424*e1fe3e4aSElliott Hughes            '      <Coordinate value="300"/>',
425*e1fe3e4aSElliott Hughes            "    </CaretValue>",
426*e1fe3e4aSElliott Hughes            '    <CaretValue index="1" Format="2">',
427*e1fe3e4aSElliott Hughes            '      <CaretValuePoint value="7"/>',
428*e1fe3e4aSElliott Hughes            "    </CaretValue>",
429*e1fe3e4aSElliott Hughes            "  </LigGlyph>",
430*e1fe3e4aSElliott Hughes            "</LigCaretList>",
431*e1fe3e4aSElliott Hughes        ]
432*e1fe3e4aSElliott Hughes
433*e1fe3e4aSElliott Hughes    def test_buildLigCaretList_empty(self):
434*e1fe3e4aSElliott Hughes        assert builder.buildLigCaretList({}, {}, self.GLYPHMAP) is None
435*e1fe3e4aSElliott Hughes
436*e1fe3e4aSElliott Hughes    def test_buildLigCaretList_None(self):
437*e1fe3e4aSElliott Hughes        assert builder.buildLigCaretList(None, None, self.GLYPHMAP) is None
438*e1fe3e4aSElliott Hughes
439*e1fe3e4aSElliott Hughes    def test_buildLigGlyph_coords(self):
440*e1fe3e4aSElliott Hughes        lig = builder.buildLigGlyph([500, 800], None)
441*e1fe3e4aSElliott Hughes        assert getXML(lig.toXML) == [
442*e1fe3e4aSElliott Hughes            "<LigGlyph>",
443*e1fe3e4aSElliott Hughes            "  <!-- CaretCount=2 -->",
444*e1fe3e4aSElliott Hughes            '  <CaretValue index="0" Format="1">',
445*e1fe3e4aSElliott Hughes            '    <Coordinate value="500"/>',
446*e1fe3e4aSElliott Hughes            "  </CaretValue>",
447*e1fe3e4aSElliott Hughes            '  <CaretValue index="1" Format="1">',
448*e1fe3e4aSElliott Hughes            '    <Coordinate value="800"/>',
449*e1fe3e4aSElliott Hughes            "  </CaretValue>",
450*e1fe3e4aSElliott Hughes            "</LigGlyph>",
451*e1fe3e4aSElliott Hughes        ]
452*e1fe3e4aSElliott Hughes
453*e1fe3e4aSElliott Hughes    def test_buildLigGlyph_empty(self):
454*e1fe3e4aSElliott Hughes        assert builder.buildLigGlyph([], []) is None
455*e1fe3e4aSElliott Hughes
456*e1fe3e4aSElliott Hughes    def test_buildLigGlyph_None(self):
457*e1fe3e4aSElliott Hughes        assert builder.buildLigGlyph(None, None) is None
458*e1fe3e4aSElliott Hughes
459*e1fe3e4aSElliott Hughes    def test_buildLigGlyph_points(self):
460*e1fe3e4aSElliott Hughes        lig = builder.buildLigGlyph(None, [2])
461*e1fe3e4aSElliott Hughes        assert getXML(lig.toXML) == [
462*e1fe3e4aSElliott Hughes            "<LigGlyph>",
463*e1fe3e4aSElliott Hughes            "  <!-- CaretCount=1 -->",
464*e1fe3e4aSElliott Hughes            '  <CaretValue index="0" Format="2">',
465*e1fe3e4aSElliott Hughes            '    <CaretValuePoint value="2"/>',
466*e1fe3e4aSElliott Hughes            "  </CaretValue>",
467*e1fe3e4aSElliott Hughes            "</LigGlyph>",
468*e1fe3e4aSElliott Hughes        ]
469*e1fe3e4aSElliott Hughes
470*e1fe3e4aSElliott Hughes    def test_buildLookup(self):
471*e1fe3e4aSElliott Hughes        s1 = builder.buildSingleSubstSubtable({"one": "two"})
472*e1fe3e4aSElliott Hughes        s2 = builder.buildSingleSubstSubtable({"three": "four"})
473*e1fe3e4aSElliott Hughes        lookup = builder.buildLookup([s1, s2], flags=7)
474*e1fe3e4aSElliott Hughes        assert getXML(lookup.toXML) == [
475*e1fe3e4aSElliott Hughes            "<Lookup>",
476*e1fe3e4aSElliott Hughes            '  <LookupType value="1"/>',
477*e1fe3e4aSElliott Hughes            '  <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->',
478*e1fe3e4aSElliott Hughes            "  <!-- SubTableCount=2 -->",
479*e1fe3e4aSElliott Hughes            '  <SingleSubst index="0">',
480*e1fe3e4aSElliott Hughes            '    <Substitution in="one" out="two"/>',
481*e1fe3e4aSElliott Hughes            "  </SingleSubst>",
482*e1fe3e4aSElliott Hughes            '  <SingleSubst index="1">',
483*e1fe3e4aSElliott Hughes            '    <Substitution in="three" out="four"/>',
484*e1fe3e4aSElliott Hughes            "  </SingleSubst>",
485*e1fe3e4aSElliott Hughes            "</Lookup>",
486*e1fe3e4aSElliott Hughes        ]
487*e1fe3e4aSElliott Hughes
488*e1fe3e4aSElliott Hughes    def test_buildLookup_badFlags(self):
489*e1fe3e4aSElliott Hughes        s = builder.buildSingleSubstSubtable({"one": "two"})
490*e1fe3e4aSElliott Hughes        with pytest.raises(
491*e1fe3e4aSElliott Hughes            AssertionError,
492*e1fe3e4aSElliott Hughes            match=(
493*e1fe3e4aSElliott Hughes                "if markFilterSet is None, flags must not set "
494*e1fe3e4aSElliott Hughes                "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x0010"
495*e1fe3e4aSElliott Hughes            ),
496*e1fe3e4aSElliott Hughes        ) as excinfo:
497*e1fe3e4aSElliott Hughes            builder.buildLookup([s], builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None)
498*e1fe3e4aSElliott Hughes
499*e1fe3e4aSElliott Hughes    def test_buildLookup_conflictingSubtableTypes(self):
500*e1fe3e4aSElliott Hughes        s1 = builder.buildSingleSubstSubtable({"one": "two"})
501*e1fe3e4aSElliott Hughes        s2 = builder.buildAlternateSubstSubtable({"one": ["two", "three"]})
502*e1fe3e4aSElliott Hughes        with pytest.raises(
503*e1fe3e4aSElliott Hughes            AssertionError, match="all subtables must have the same LookupType"
504*e1fe3e4aSElliott Hughes        ) as excinfo:
505*e1fe3e4aSElliott Hughes            builder.buildLookup([s1, s2])
506*e1fe3e4aSElliott Hughes
507*e1fe3e4aSElliott Hughes    def test_buildLookup_noSubtables(self):
508*e1fe3e4aSElliott Hughes        assert builder.buildLookup([]) is None
509*e1fe3e4aSElliott Hughes        assert builder.buildLookup(None) is None
510*e1fe3e4aSElliott Hughes        assert builder.buildLookup([None]) is None
511*e1fe3e4aSElliott Hughes        assert builder.buildLookup([None, None]) is None
512*e1fe3e4aSElliott Hughes
513*e1fe3e4aSElliott Hughes    def test_buildLookup_markFilterSet(self):
514*e1fe3e4aSElliott Hughes        s = builder.buildSingleSubstSubtable({"one": "two"})
515*e1fe3e4aSElliott Hughes        flags = (
516*e1fe3e4aSElliott Hughes            builder.LOOKUP_FLAG_RIGHT_TO_LEFT
517*e1fe3e4aSElliott Hughes            | builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET
518*e1fe3e4aSElliott Hughes        )
519*e1fe3e4aSElliott Hughes        lookup = builder.buildLookup([s], flags, markFilterSet=999)
520*e1fe3e4aSElliott Hughes        assert getXML(lookup.toXML) == [
521*e1fe3e4aSElliott Hughes            "<Lookup>",
522*e1fe3e4aSElliott Hughes            '  <LookupType value="1"/>',
523*e1fe3e4aSElliott Hughes            '  <LookupFlag value="17"/><!-- rightToLeft useMarkFilteringSet -->',
524*e1fe3e4aSElliott Hughes            "  <!-- SubTableCount=1 -->",
525*e1fe3e4aSElliott Hughes            '  <SingleSubst index="0">',
526*e1fe3e4aSElliott Hughes            '    <Substitution in="one" out="two"/>',
527*e1fe3e4aSElliott Hughes            "  </SingleSubst>",
528*e1fe3e4aSElliott Hughes            '  <MarkFilteringSet value="999"/>',
529*e1fe3e4aSElliott Hughes            "</Lookup>",
530*e1fe3e4aSElliott Hughes        ]
531*e1fe3e4aSElliott Hughes
532*e1fe3e4aSElliott Hughes    def test_buildMarkArray(self):
533*e1fe3e4aSElliott Hughes        markArray = builder.buildMarkArray(
534*e1fe3e4aSElliott Hughes            {
535*e1fe3e4aSElliott Hughes                "acute": (7, builder.buildAnchor(300, 800)),
536*e1fe3e4aSElliott Hughes                "grave": (2, builder.buildAnchor(10, 80)),
537*e1fe3e4aSElliott Hughes            },
538*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
539*e1fe3e4aSElliott Hughes        )
540*e1fe3e4aSElliott Hughes        assert self.GLYPHMAP["grave"] < self.GLYPHMAP["acute"]
541*e1fe3e4aSElliott Hughes        assert getXML(markArray.toXML) == [
542*e1fe3e4aSElliott Hughes            "<MarkArray>",
543*e1fe3e4aSElliott Hughes            "  <!-- MarkCount=2 -->",
544*e1fe3e4aSElliott Hughes            '  <MarkRecord index="0">',
545*e1fe3e4aSElliott Hughes            '    <Class value="2"/>',
546*e1fe3e4aSElliott Hughes            '    <MarkAnchor Format="1">',
547*e1fe3e4aSElliott Hughes            '      <XCoordinate value="10"/>',
548*e1fe3e4aSElliott Hughes            '      <YCoordinate value="80"/>',
549*e1fe3e4aSElliott Hughes            "    </MarkAnchor>",
550*e1fe3e4aSElliott Hughes            "  </MarkRecord>",
551*e1fe3e4aSElliott Hughes            '  <MarkRecord index="1">',
552*e1fe3e4aSElliott Hughes            '    <Class value="7"/>',
553*e1fe3e4aSElliott Hughes            '    <MarkAnchor Format="1">',
554*e1fe3e4aSElliott Hughes            '      <XCoordinate value="300"/>',
555*e1fe3e4aSElliott Hughes            '      <YCoordinate value="800"/>',
556*e1fe3e4aSElliott Hughes            "    </MarkAnchor>",
557*e1fe3e4aSElliott Hughes            "  </MarkRecord>",
558*e1fe3e4aSElliott Hughes            "</MarkArray>",
559*e1fe3e4aSElliott Hughes        ]
560*e1fe3e4aSElliott Hughes
561*e1fe3e4aSElliott Hughes    def test_buildMarkBasePosSubtable(self):
562*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor
563*e1fe3e4aSElliott Hughes        marks = {
564*e1fe3e4aSElliott Hughes            "acute": (0, anchor(300, 700)),
565*e1fe3e4aSElliott Hughes            "cedilla": (1, anchor(300, -100)),
566*e1fe3e4aSElliott Hughes            "grave": (0, anchor(300, 700)),
567*e1fe3e4aSElliott Hughes        }
568*e1fe3e4aSElliott Hughes        bases = {
569*e1fe3e4aSElliott Hughes            # Make sure we can handle missing entries.
570*e1fe3e4aSElliott Hughes            "A": {},  # no entry for any markClass
571*e1fe3e4aSElliott Hughes            "B": {0: anchor(500, 900)},  # only markClass 0 specified
572*e1fe3e4aSElliott Hughes            "C": {1: anchor(500, -10)},  # only markClass 1 specified
573*e1fe3e4aSElliott Hughes            "a": {0: anchor(500, 400), 1: anchor(500, -20)},
574*e1fe3e4aSElliott Hughes            "b": {0: anchor(500, 800), 1: anchor(500, -20)},
575*e1fe3e4aSElliott Hughes        }
576*e1fe3e4aSElliott Hughes        table = builder.buildMarkBasePosSubtable(marks, bases, self.GLYPHMAP)
577*e1fe3e4aSElliott Hughes        assert getXML(table.toXML) == [
578*e1fe3e4aSElliott Hughes            '<MarkBasePos Format="1">',
579*e1fe3e4aSElliott Hughes            "  <MarkCoverage>",
580*e1fe3e4aSElliott Hughes            '    <Glyph value="grave"/>',
581*e1fe3e4aSElliott Hughes            '    <Glyph value="acute"/>',
582*e1fe3e4aSElliott Hughes            '    <Glyph value="cedilla"/>',
583*e1fe3e4aSElliott Hughes            "  </MarkCoverage>",
584*e1fe3e4aSElliott Hughes            "  <BaseCoverage>",
585*e1fe3e4aSElliott Hughes            '    <Glyph value="A"/>',
586*e1fe3e4aSElliott Hughes            '    <Glyph value="B"/>',
587*e1fe3e4aSElliott Hughes            '    <Glyph value="C"/>',
588*e1fe3e4aSElliott Hughes            '    <Glyph value="a"/>',
589*e1fe3e4aSElliott Hughes            '    <Glyph value="b"/>',
590*e1fe3e4aSElliott Hughes            "  </BaseCoverage>",
591*e1fe3e4aSElliott Hughes            "  <!-- ClassCount=2 -->",
592*e1fe3e4aSElliott Hughes            "  <MarkArray>",
593*e1fe3e4aSElliott Hughes            "    <!-- MarkCount=3 -->",
594*e1fe3e4aSElliott Hughes            '    <MarkRecord index="0">',  # grave
595*e1fe3e4aSElliott Hughes            '      <Class value="0"/>',
596*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
597*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
598*e1fe3e4aSElliott Hughes            '        <YCoordinate value="700"/>',
599*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
600*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
601*e1fe3e4aSElliott Hughes            '    <MarkRecord index="1">',  # acute
602*e1fe3e4aSElliott Hughes            '      <Class value="0"/>',
603*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
604*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
605*e1fe3e4aSElliott Hughes            '        <YCoordinate value="700"/>',
606*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
607*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
608*e1fe3e4aSElliott Hughes            '    <MarkRecord index="2">',  # cedilla
609*e1fe3e4aSElliott Hughes            '      <Class value="1"/>',
610*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
611*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
612*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-100"/>',
613*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
614*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
615*e1fe3e4aSElliott Hughes            "  </MarkArray>",
616*e1fe3e4aSElliott Hughes            "  <BaseArray>",
617*e1fe3e4aSElliott Hughes            "    <!-- BaseCount=5 -->",
618*e1fe3e4aSElliott Hughes            '    <BaseRecord index="0">',  # A
619*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="0" empty="1"/>',
620*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="1" empty="1"/>',
621*e1fe3e4aSElliott Hughes            "    </BaseRecord>",
622*e1fe3e4aSElliott Hughes            '    <BaseRecord index="1">',  # B
623*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="0" Format="1">',
624*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
625*e1fe3e4aSElliott Hughes            '        <YCoordinate value="900"/>',
626*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
627*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="1" empty="1"/>',
628*e1fe3e4aSElliott Hughes            "    </BaseRecord>",
629*e1fe3e4aSElliott Hughes            '    <BaseRecord index="2">',  # C
630*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="0" empty="1"/>',
631*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="1" Format="1">',
632*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
633*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-10"/>',
634*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
635*e1fe3e4aSElliott Hughes            "    </BaseRecord>",
636*e1fe3e4aSElliott Hughes            '    <BaseRecord index="3">',  # a
637*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="0" Format="1">',
638*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
639*e1fe3e4aSElliott Hughes            '        <YCoordinate value="400"/>',
640*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
641*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="1" Format="1">',
642*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
643*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-20"/>',
644*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
645*e1fe3e4aSElliott Hughes            "    </BaseRecord>",
646*e1fe3e4aSElliott Hughes            '    <BaseRecord index="4">',  # b
647*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="0" Format="1">',
648*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
649*e1fe3e4aSElliott Hughes            '        <YCoordinate value="800"/>',
650*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
651*e1fe3e4aSElliott Hughes            '      <BaseAnchor index="1" Format="1">',
652*e1fe3e4aSElliott Hughes            '        <XCoordinate value="500"/>',
653*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-20"/>',
654*e1fe3e4aSElliott Hughes            "      </BaseAnchor>",
655*e1fe3e4aSElliott Hughes            "    </BaseRecord>",
656*e1fe3e4aSElliott Hughes            "  </BaseArray>",
657*e1fe3e4aSElliott Hughes            "</MarkBasePos>",
658*e1fe3e4aSElliott Hughes        ]
659*e1fe3e4aSElliott Hughes
660*e1fe3e4aSElliott Hughes    def test_buildMarkGlyphSetsDef(self):
661*e1fe3e4aSElliott Hughes        marksets = builder.buildMarkGlyphSetsDef(
662*e1fe3e4aSElliott Hughes            [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP
663*e1fe3e4aSElliott Hughes        )
664*e1fe3e4aSElliott Hughes        assert getXML(marksets.toXML) == [
665*e1fe3e4aSElliott Hughes            "<MarkGlyphSetsDef>",
666*e1fe3e4aSElliott Hughes            '  <MarkSetTableFormat value="1"/>',
667*e1fe3e4aSElliott Hughes            "  <!-- MarkSetCount=2 -->",
668*e1fe3e4aSElliott Hughes            '  <Coverage index="0">',
669*e1fe3e4aSElliott Hughes            '    <Glyph value="grave"/>',
670*e1fe3e4aSElliott Hughes            '    <Glyph value="acute"/>',
671*e1fe3e4aSElliott Hughes            "  </Coverage>",
672*e1fe3e4aSElliott Hughes            '  <Coverage index="1">',
673*e1fe3e4aSElliott Hughes            '    <Glyph value="grave"/>',
674*e1fe3e4aSElliott Hughes            '    <Glyph value="cedilla"/>',
675*e1fe3e4aSElliott Hughes            "  </Coverage>",
676*e1fe3e4aSElliott Hughes            "</MarkGlyphSetsDef>",
677*e1fe3e4aSElliott Hughes        ]
678*e1fe3e4aSElliott Hughes
679*e1fe3e4aSElliott Hughes    def test_buildMarkGlyphSetsDef_empty(self):
680*e1fe3e4aSElliott Hughes        assert builder.buildMarkGlyphSetsDef([], self.GLYPHMAP) is None
681*e1fe3e4aSElliott Hughes
682*e1fe3e4aSElliott Hughes    def test_buildMarkGlyphSetsDef_None(self):
683*e1fe3e4aSElliott Hughes        assert builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP) is None
684*e1fe3e4aSElliott Hughes
685*e1fe3e4aSElliott Hughes    def test_buildMarkLigPosSubtable(self):
686*e1fe3e4aSElliott Hughes        anchor = builder.buildAnchor
687*e1fe3e4aSElliott Hughes        marks = {
688*e1fe3e4aSElliott Hughes            "acute": (0, anchor(300, 700)),
689*e1fe3e4aSElliott Hughes            "cedilla": (1, anchor(300, -100)),
690*e1fe3e4aSElliott Hughes            "grave": (0, anchor(300, 700)),
691*e1fe3e4aSElliott Hughes        }
692*e1fe3e4aSElliott Hughes        bases = {
693*e1fe3e4aSElliott Hughes            "f_i": [{}, {0: anchor(200, 400)}],  # nothing on f; only 1 on i
694*e1fe3e4aSElliott Hughes            "c_t": [
695*e1fe3e4aSElliott Hughes                {0: anchor(500, 600), 1: anchor(500, -20)},  # c
696*e1fe3e4aSElliott Hughes                {0: anchor(1300, 800), 1: anchor(1300, -20)},  # t
697*e1fe3e4aSElliott Hughes            ],
698*e1fe3e4aSElliott Hughes        }
699*e1fe3e4aSElliott Hughes        table = builder.buildMarkLigPosSubtable(marks, bases, self.GLYPHMAP)
700*e1fe3e4aSElliott Hughes        assert getXML(table.toXML) == [
701*e1fe3e4aSElliott Hughes            '<MarkLigPos Format="1">',
702*e1fe3e4aSElliott Hughes            "  <MarkCoverage>",
703*e1fe3e4aSElliott Hughes            '    <Glyph value="grave"/>',
704*e1fe3e4aSElliott Hughes            '    <Glyph value="acute"/>',
705*e1fe3e4aSElliott Hughes            '    <Glyph value="cedilla"/>',
706*e1fe3e4aSElliott Hughes            "  </MarkCoverage>",
707*e1fe3e4aSElliott Hughes            "  <LigatureCoverage>",
708*e1fe3e4aSElliott Hughes            '    <Glyph value="f_i"/>',
709*e1fe3e4aSElliott Hughes            '    <Glyph value="c_t"/>',
710*e1fe3e4aSElliott Hughes            "  </LigatureCoverage>",
711*e1fe3e4aSElliott Hughes            "  <!-- ClassCount=2 -->",
712*e1fe3e4aSElliott Hughes            "  <MarkArray>",
713*e1fe3e4aSElliott Hughes            "    <!-- MarkCount=3 -->",
714*e1fe3e4aSElliott Hughes            '    <MarkRecord index="0">',
715*e1fe3e4aSElliott Hughes            '      <Class value="0"/>',
716*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
717*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
718*e1fe3e4aSElliott Hughes            '        <YCoordinate value="700"/>',
719*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
720*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
721*e1fe3e4aSElliott Hughes            '    <MarkRecord index="1">',
722*e1fe3e4aSElliott Hughes            '      <Class value="0"/>',
723*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
724*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
725*e1fe3e4aSElliott Hughes            '        <YCoordinate value="700"/>',
726*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
727*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
728*e1fe3e4aSElliott Hughes            '    <MarkRecord index="2">',
729*e1fe3e4aSElliott Hughes            '      <Class value="1"/>',
730*e1fe3e4aSElliott Hughes            '      <MarkAnchor Format="1">',
731*e1fe3e4aSElliott Hughes            '        <XCoordinate value="300"/>',
732*e1fe3e4aSElliott Hughes            '        <YCoordinate value="-100"/>',
733*e1fe3e4aSElliott Hughes            "      </MarkAnchor>",
734*e1fe3e4aSElliott Hughes            "    </MarkRecord>",
735*e1fe3e4aSElliott Hughes            "  </MarkArray>",
736*e1fe3e4aSElliott Hughes            "  <LigatureArray>",
737*e1fe3e4aSElliott Hughes            "    <!-- LigatureCount=2 -->",
738*e1fe3e4aSElliott Hughes            '    <LigatureAttach index="0">',
739*e1fe3e4aSElliott Hughes            "      <!-- ComponentCount=2 -->",
740*e1fe3e4aSElliott Hughes            '      <ComponentRecord index="0">',
741*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="0" empty="1"/>',
742*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="1" empty="1"/>',
743*e1fe3e4aSElliott Hughes            "      </ComponentRecord>",
744*e1fe3e4aSElliott Hughes            '      <ComponentRecord index="1">',
745*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="0" Format="1">',
746*e1fe3e4aSElliott Hughes            '          <XCoordinate value="200"/>',
747*e1fe3e4aSElliott Hughes            '          <YCoordinate value="400"/>',
748*e1fe3e4aSElliott Hughes            "        </LigatureAnchor>",
749*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="1" empty="1"/>',
750*e1fe3e4aSElliott Hughes            "      </ComponentRecord>",
751*e1fe3e4aSElliott Hughes            "    </LigatureAttach>",
752*e1fe3e4aSElliott Hughes            '    <LigatureAttach index="1">',
753*e1fe3e4aSElliott Hughes            "      <!-- ComponentCount=2 -->",
754*e1fe3e4aSElliott Hughes            '      <ComponentRecord index="0">',
755*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="0" Format="1">',
756*e1fe3e4aSElliott Hughes            '          <XCoordinate value="500"/>',
757*e1fe3e4aSElliott Hughes            '          <YCoordinate value="600"/>',
758*e1fe3e4aSElliott Hughes            "        </LigatureAnchor>",
759*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="1" Format="1">',
760*e1fe3e4aSElliott Hughes            '          <XCoordinate value="500"/>',
761*e1fe3e4aSElliott Hughes            '          <YCoordinate value="-20"/>',
762*e1fe3e4aSElliott Hughes            "        </LigatureAnchor>",
763*e1fe3e4aSElliott Hughes            "      </ComponentRecord>",
764*e1fe3e4aSElliott Hughes            '      <ComponentRecord index="1">',
765*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="0" Format="1">',
766*e1fe3e4aSElliott Hughes            '          <XCoordinate value="1300"/>',
767*e1fe3e4aSElliott Hughes            '          <YCoordinate value="800"/>',
768*e1fe3e4aSElliott Hughes            "        </LigatureAnchor>",
769*e1fe3e4aSElliott Hughes            '        <LigatureAnchor index="1" Format="1">',
770*e1fe3e4aSElliott Hughes            '          <XCoordinate value="1300"/>',
771*e1fe3e4aSElliott Hughes            '          <YCoordinate value="-20"/>',
772*e1fe3e4aSElliott Hughes            "        </LigatureAnchor>",
773*e1fe3e4aSElliott Hughes            "      </ComponentRecord>",
774*e1fe3e4aSElliott Hughes            "    </LigatureAttach>",
775*e1fe3e4aSElliott Hughes            "  </LigatureArray>",
776*e1fe3e4aSElliott Hughes            "</MarkLigPos>",
777*e1fe3e4aSElliott Hughes        ]
778*e1fe3e4aSElliott Hughes
779*e1fe3e4aSElliott Hughes    def test_buildMarkRecord(self):
780*e1fe3e4aSElliott Hughes        rec = builder.buildMarkRecord(17, builder.buildAnchor(500, -20))
781*e1fe3e4aSElliott Hughes        assert getXML(rec.toXML) == [
782*e1fe3e4aSElliott Hughes            "<MarkRecord>",
783*e1fe3e4aSElliott Hughes            '  <Class value="17"/>',
784*e1fe3e4aSElliott Hughes            '  <MarkAnchor Format="1">',
785*e1fe3e4aSElliott Hughes            '    <XCoordinate value="500"/>',
786*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-20"/>',
787*e1fe3e4aSElliott Hughes            "  </MarkAnchor>",
788*e1fe3e4aSElliott Hughes            "</MarkRecord>",
789*e1fe3e4aSElliott Hughes        ]
790*e1fe3e4aSElliott Hughes
791*e1fe3e4aSElliott Hughes    def test_buildMark2Record(self):
792*e1fe3e4aSElliott Hughes        a = builder.buildAnchor
793*e1fe3e4aSElliott Hughes        rec = builder.buildMark2Record([a(500, -20), None, a(300, -15)])
794*e1fe3e4aSElliott Hughes        assert getXML(rec.toXML) == [
795*e1fe3e4aSElliott Hughes            "<Mark2Record>",
796*e1fe3e4aSElliott Hughes            '  <Mark2Anchor index="0" Format="1">',
797*e1fe3e4aSElliott Hughes            '    <XCoordinate value="500"/>',
798*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-20"/>',
799*e1fe3e4aSElliott Hughes            "  </Mark2Anchor>",
800*e1fe3e4aSElliott Hughes            '  <Mark2Anchor index="1" empty="1"/>',
801*e1fe3e4aSElliott Hughes            '  <Mark2Anchor index="2" Format="1">',
802*e1fe3e4aSElliott Hughes            '    <XCoordinate value="300"/>',
803*e1fe3e4aSElliott Hughes            '    <YCoordinate value="-15"/>',
804*e1fe3e4aSElliott Hughes            "  </Mark2Anchor>",
805*e1fe3e4aSElliott Hughes            "</Mark2Record>",
806*e1fe3e4aSElliott Hughes        ]
807*e1fe3e4aSElliott Hughes
808*e1fe3e4aSElliott Hughes    def test_buildPairPosClassesSubtable(self):
809*e1fe3e4aSElliott Hughes        d20 = builder.buildValue({"XPlacement": -20})
810*e1fe3e4aSElliott Hughes        d50 = builder.buildValue({"XPlacement": -50})
811*e1fe3e4aSElliott Hughes        d0 = builder.buildValue({})
812*e1fe3e4aSElliott Hughes        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
813*e1fe3e4aSElliott Hughes        subtable = builder.buildPairPosClassesSubtable(
814*e1fe3e4aSElliott Hughes            {
815*e1fe3e4aSElliott Hughes                (tuple("A"), tuple(["zero"])): (d0, d50),
816*e1fe3e4aSElliott Hughes                (tuple("A"), tuple(["one", "two"])): (None, d20),
817*e1fe3e4aSElliott Hughes                (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50),
818*e1fe3e4aSElliott Hughes            },
819*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
820*e1fe3e4aSElliott Hughes        )
821*e1fe3e4aSElliott Hughes        assert getXML(subtable.toXML) == [
822*e1fe3e4aSElliott Hughes            '<PairPos Format="2">',
823*e1fe3e4aSElliott Hughes            "  <Coverage>",
824*e1fe3e4aSElliott Hughes            '    <Glyph value="A"/>',
825*e1fe3e4aSElliott Hughes            '    <Glyph value="B"/>',
826*e1fe3e4aSElliott Hughes            '    <Glyph value="C"/>',
827*e1fe3e4aSElliott Hughes            "  </Coverage>",
828*e1fe3e4aSElliott Hughes            '  <ValueFormat1 value="3"/>',
829*e1fe3e4aSElliott Hughes            '  <ValueFormat2 value="1"/>',
830*e1fe3e4aSElliott Hughes            "  <ClassDef1>",
831*e1fe3e4aSElliott Hughes            '    <ClassDef glyph="A" class="1"/>',
832*e1fe3e4aSElliott Hughes            "  </ClassDef1>",
833*e1fe3e4aSElliott Hughes            "  <ClassDef2>",
834*e1fe3e4aSElliott Hughes            '    <ClassDef glyph="one" class="1"/>',
835*e1fe3e4aSElliott Hughes            '    <ClassDef glyph="two" class="1"/>',
836*e1fe3e4aSElliott Hughes            '    <ClassDef glyph="zero" class="2"/>',
837*e1fe3e4aSElliott Hughes            "  </ClassDef2>",
838*e1fe3e4aSElliott Hughes            "  <!-- Class1Count=2 -->",
839*e1fe3e4aSElliott Hughes            "  <!-- Class2Count=3 -->",
840*e1fe3e4aSElliott Hughes            '  <Class1Record index="0">',
841*e1fe3e4aSElliott Hughes            '    <Class2Record index="0">',
842*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
843*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="0"/>',
844*e1fe3e4aSElliott Hughes            "    </Class2Record>",
845*e1fe3e4aSElliott Hughes            '    <Class2Record index="1">',
846*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
847*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="0"/>',
848*e1fe3e4aSElliott Hughes            "    </Class2Record>",
849*e1fe3e4aSElliott Hughes            '    <Class2Record index="2">',
850*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="-80" YPlacement="-20"/>',
851*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
852*e1fe3e4aSElliott Hughes            "    </Class2Record>",
853*e1fe3e4aSElliott Hughes            "  </Class1Record>",
854*e1fe3e4aSElliott Hughes            '  <Class1Record index="1">',
855*e1fe3e4aSElliott Hughes            '    <Class2Record index="0">',
856*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
857*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="0"/>',
858*e1fe3e4aSElliott Hughes            "    </Class2Record>",
859*e1fe3e4aSElliott Hughes            '    <Class2Record index="1">',
860*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
861*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-20"/>',
862*e1fe3e4aSElliott Hughes            "    </Class2Record>",
863*e1fe3e4aSElliott Hughes            '    <Class2Record index="2">',
864*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
865*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
866*e1fe3e4aSElliott Hughes            "    </Class2Record>",
867*e1fe3e4aSElliott Hughes            "  </Class1Record>",
868*e1fe3e4aSElliott Hughes            "</PairPos>",
869*e1fe3e4aSElliott Hughes        ]
870*e1fe3e4aSElliott Hughes
871*e1fe3e4aSElliott Hughes    def test_buildPairPosGlyphs(self):
872*e1fe3e4aSElliott Hughes        d50 = builder.buildValue({"XPlacement": -50})
873*e1fe3e4aSElliott Hughes        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
874*e1fe3e4aSElliott Hughes        subtables = builder.buildPairPosGlyphs(
875*e1fe3e4aSElliott Hughes            {("A", "zero"): (None, d50), ("A", "one"): (d8020, d50)}, self.GLYPHMAP
876*e1fe3e4aSElliott Hughes        )
877*e1fe3e4aSElliott Hughes        assert sum([getXML(t.toXML) for t in subtables], []) == [
878*e1fe3e4aSElliott Hughes            '<PairPos Format="1">',
879*e1fe3e4aSElliott Hughes            "  <Coverage>",
880*e1fe3e4aSElliott Hughes            '    <Glyph value="A"/>',
881*e1fe3e4aSElliott Hughes            "  </Coverage>",
882*e1fe3e4aSElliott Hughes            '  <ValueFormat1 value="0"/>',
883*e1fe3e4aSElliott Hughes            '  <ValueFormat2 value="1"/>',
884*e1fe3e4aSElliott Hughes            "  <!-- PairSetCount=1 -->",
885*e1fe3e4aSElliott Hughes            '  <PairSet index="0">',
886*e1fe3e4aSElliott Hughes            "    <!-- PairValueCount=1 -->",
887*e1fe3e4aSElliott Hughes            '    <PairValueRecord index="0">',
888*e1fe3e4aSElliott Hughes            '      <SecondGlyph value="zero"/>',
889*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
890*e1fe3e4aSElliott Hughes            "    </PairValueRecord>",
891*e1fe3e4aSElliott Hughes            "  </PairSet>",
892*e1fe3e4aSElliott Hughes            "</PairPos>",
893*e1fe3e4aSElliott Hughes            '<PairPos Format="1">',
894*e1fe3e4aSElliott Hughes            "  <Coverage>",
895*e1fe3e4aSElliott Hughes            '    <Glyph value="A"/>',
896*e1fe3e4aSElliott Hughes            "  </Coverage>",
897*e1fe3e4aSElliott Hughes            '  <ValueFormat1 value="3"/>',
898*e1fe3e4aSElliott Hughes            '  <ValueFormat2 value="1"/>',
899*e1fe3e4aSElliott Hughes            "  <!-- PairSetCount=1 -->",
900*e1fe3e4aSElliott Hughes            '  <PairSet index="0">',
901*e1fe3e4aSElliott Hughes            "    <!-- PairValueCount=1 -->",
902*e1fe3e4aSElliott Hughes            '    <PairValueRecord index="0">',
903*e1fe3e4aSElliott Hughes            '      <SecondGlyph value="one"/>',
904*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="-80" YPlacement="-20"/>',
905*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
906*e1fe3e4aSElliott Hughes            "    </PairValueRecord>",
907*e1fe3e4aSElliott Hughes            "  </PairSet>",
908*e1fe3e4aSElliott Hughes            "</PairPos>",
909*e1fe3e4aSElliott Hughes        ]
910*e1fe3e4aSElliott Hughes
911*e1fe3e4aSElliott Hughes    def test_buildPairPosGlyphsSubtable(self):
912*e1fe3e4aSElliott Hughes        d20 = builder.buildValue({"XPlacement": -20})
913*e1fe3e4aSElliott Hughes        d50 = builder.buildValue({"XPlacement": -50})
914*e1fe3e4aSElliott Hughes        d0 = builder.buildValue({})
915*e1fe3e4aSElliott Hughes        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
916*e1fe3e4aSElliott Hughes        subtable = builder.buildPairPosGlyphsSubtable(
917*e1fe3e4aSElliott Hughes            {
918*e1fe3e4aSElliott Hughes                ("A", "zero"): (d0, d50),
919*e1fe3e4aSElliott Hughes                ("A", "one"): (None, d20),
920*e1fe3e4aSElliott Hughes                ("B", "five"): (d8020, d50),
921*e1fe3e4aSElliott Hughes            },
922*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
923*e1fe3e4aSElliott Hughes        )
924*e1fe3e4aSElliott Hughes
925*e1fe3e4aSElliott Hughes        assert getXML(subtable.toXML) == [
926*e1fe3e4aSElliott Hughes            '<PairPos Format="1">',
927*e1fe3e4aSElliott Hughes            "  <Coverage>",
928*e1fe3e4aSElliott Hughes            '    <Glyph value="A"/>',
929*e1fe3e4aSElliott Hughes            '    <Glyph value="B"/>',
930*e1fe3e4aSElliott Hughes            "  </Coverage>",
931*e1fe3e4aSElliott Hughes            '  <ValueFormat1 value="3"/>',
932*e1fe3e4aSElliott Hughes            '  <ValueFormat2 value="1"/>',
933*e1fe3e4aSElliott Hughes            "  <!-- PairSetCount=2 -->",
934*e1fe3e4aSElliott Hughes            '  <PairSet index="0">',
935*e1fe3e4aSElliott Hughes            "    <!-- PairValueCount=2 -->",
936*e1fe3e4aSElliott Hughes            '    <PairValueRecord index="0">',
937*e1fe3e4aSElliott Hughes            '      <SecondGlyph value="zero"/>',
938*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
939*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
940*e1fe3e4aSElliott Hughes            "    </PairValueRecord>",
941*e1fe3e4aSElliott Hughes            '    <PairValueRecord index="1">',
942*e1fe3e4aSElliott Hughes            '      <SecondGlyph value="one"/>',
943*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="0" YPlacement="0"/>',
944*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-20"/>',
945*e1fe3e4aSElliott Hughes            "    </PairValueRecord>",
946*e1fe3e4aSElliott Hughes            "  </PairSet>",
947*e1fe3e4aSElliott Hughes            '  <PairSet index="1">',
948*e1fe3e4aSElliott Hughes            "    <!-- PairValueCount=1 -->",
949*e1fe3e4aSElliott Hughes            '    <PairValueRecord index="0">',
950*e1fe3e4aSElliott Hughes            '      <SecondGlyph value="five"/>',
951*e1fe3e4aSElliott Hughes            '      <Value1 XPlacement="-80" YPlacement="-20"/>',
952*e1fe3e4aSElliott Hughes            '      <Value2 XPlacement="-50"/>',
953*e1fe3e4aSElliott Hughes            "    </PairValueRecord>",
954*e1fe3e4aSElliott Hughes            "  </PairSet>",
955*e1fe3e4aSElliott Hughes            "</PairPos>",
956*e1fe3e4aSElliott Hughes        ]
957*e1fe3e4aSElliott Hughes
958*e1fe3e4aSElliott Hughes    def test_buildSinglePos(self):
959*e1fe3e4aSElliott Hughes        subtables = builder.buildSinglePos(
960*e1fe3e4aSElliott Hughes            {
961*e1fe3e4aSElliott Hughes                "one": builder.buildValue({"XPlacement": 500}),
962*e1fe3e4aSElliott Hughes                "two": builder.buildValue({"XPlacement": 500}),
963*e1fe3e4aSElliott Hughes                "three": builder.buildValue({"XPlacement": 200}),
964*e1fe3e4aSElliott Hughes                "four": builder.buildValue({"XPlacement": 400}),
965*e1fe3e4aSElliott Hughes                "five": builder.buildValue({"XPlacement": 500}),
966*e1fe3e4aSElliott Hughes                "six": builder.buildValue({"YPlacement": -6}),
967*e1fe3e4aSElliott Hughes            },
968*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
969*e1fe3e4aSElliott Hughes        )
970*e1fe3e4aSElliott Hughes        assert sum([getXML(t.toXML) for t in subtables], []) == [
971*e1fe3e4aSElliott Hughes            '<SinglePos Format="2">',
972*e1fe3e4aSElliott Hughes            "  <Coverage>",
973*e1fe3e4aSElliott Hughes            '    <Glyph value="one"/>',
974*e1fe3e4aSElliott Hughes            '    <Glyph value="two"/>',
975*e1fe3e4aSElliott Hughes            '    <Glyph value="three"/>',
976*e1fe3e4aSElliott Hughes            '    <Glyph value="four"/>',
977*e1fe3e4aSElliott Hughes            '    <Glyph value="five"/>',
978*e1fe3e4aSElliott Hughes            "  </Coverage>",
979*e1fe3e4aSElliott Hughes            '  <ValueFormat value="1"/>',
980*e1fe3e4aSElliott Hughes            "  <!-- ValueCount=5 -->",
981*e1fe3e4aSElliott Hughes            '  <Value index="0" XPlacement="500"/>',
982*e1fe3e4aSElliott Hughes            '  <Value index="1" XPlacement="500"/>',
983*e1fe3e4aSElliott Hughes            '  <Value index="2" XPlacement="200"/>',
984*e1fe3e4aSElliott Hughes            '  <Value index="3" XPlacement="400"/>',
985*e1fe3e4aSElliott Hughes            '  <Value index="4" XPlacement="500"/>',
986*e1fe3e4aSElliott Hughes            "</SinglePos>",
987*e1fe3e4aSElliott Hughes            '<SinglePos Format="1">',
988*e1fe3e4aSElliott Hughes            "  <Coverage>",
989*e1fe3e4aSElliott Hughes            '    <Glyph value="six"/>',
990*e1fe3e4aSElliott Hughes            "  </Coverage>",
991*e1fe3e4aSElliott Hughes            '  <ValueFormat value="2"/>',
992*e1fe3e4aSElliott Hughes            '  <Value YPlacement="-6"/>',
993*e1fe3e4aSElliott Hughes            "</SinglePos>",
994*e1fe3e4aSElliott Hughes        ]
995*e1fe3e4aSElliott Hughes
996*e1fe3e4aSElliott Hughes    def test_buildSinglePos_ValueFormat0(self):
997*e1fe3e4aSElliott Hughes        subtables = builder.buildSinglePos(
998*e1fe3e4aSElliott Hughes            {"zero": builder.buildValue({})}, self.GLYPHMAP
999*e1fe3e4aSElliott Hughes        )
1000*e1fe3e4aSElliott Hughes        assert sum([getXML(t.toXML) for t in subtables], []) == [
1001*e1fe3e4aSElliott Hughes            '<SinglePos Format="1">',
1002*e1fe3e4aSElliott Hughes            "  <Coverage>",
1003*e1fe3e4aSElliott Hughes            '    <Glyph value="zero"/>',
1004*e1fe3e4aSElliott Hughes            "  </Coverage>",
1005*e1fe3e4aSElliott Hughes            '  <ValueFormat value="0"/>',
1006*e1fe3e4aSElliott Hughes            "</SinglePos>",
1007*e1fe3e4aSElliott Hughes        ]
1008*e1fe3e4aSElliott Hughes
1009*e1fe3e4aSElliott Hughes    def test_buildSinglePosSubtable_format1(self):
1010*e1fe3e4aSElliott Hughes        subtable = builder.buildSinglePosSubtable(
1011*e1fe3e4aSElliott Hughes            {
1012*e1fe3e4aSElliott Hughes                "one": builder.buildValue({"XPlacement": 777}),
1013*e1fe3e4aSElliott Hughes                "two": builder.buildValue({"XPlacement": 777}),
1014*e1fe3e4aSElliott Hughes            },
1015*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
1016*e1fe3e4aSElliott Hughes        )
1017*e1fe3e4aSElliott Hughes        assert getXML(subtable.toXML) == [
1018*e1fe3e4aSElliott Hughes            '<SinglePos Format="1">',
1019*e1fe3e4aSElliott Hughes            "  <Coverage>",
1020*e1fe3e4aSElliott Hughes            '    <Glyph value="one"/>',
1021*e1fe3e4aSElliott Hughes            '    <Glyph value="two"/>',
1022*e1fe3e4aSElliott Hughes            "  </Coverage>",
1023*e1fe3e4aSElliott Hughes            '  <ValueFormat value="1"/>',
1024*e1fe3e4aSElliott Hughes            '  <Value XPlacement="777"/>',
1025*e1fe3e4aSElliott Hughes            "</SinglePos>",
1026*e1fe3e4aSElliott Hughes        ]
1027*e1fe3e4aSElliott Hughes
1028*e1fe3e4aSElliott Hughes    def test_buildSinglePosSubtable_format2(self):
1029*e1fe3e4aSElliott Hughes        subtable = builder.buildSinglePosSubtable(
1030*e1fe3e4aSElliott Hughes            {
1031*e1fe3e4aSElliott Hughes                "one": builder.buildValue({"XPlacement": 777}),
1032*e1fe3e4aSElliott Hughes                "two": builder.buildValue({"YPlacement": -888}),
1033*e1fe3e4aSElliott Hughes            },
1034*e1fe3e4aSElliott Hughes            self.GLYPHMAP,
1035*e1fe3e4aSElliott Hughes        )
1036*e1fe3e4aSElliott Hughes        assert getXML(subtable.toXML) == [
1037*e1fe3e4aSElliott Hughes            '<SinglePos Format="2">',
1038*e1fe3e4aSElliott Hughes            "  <Coverage>",
1039*e1fe3e4aSElliott Hughes            '    <Glyph value="one"/>',
1040*e1fe3e4aSElliott Hughes            '    <Glyph value="two"/>',
1041*e1fe3e4aSElliott Hughes            "  </Coverage>",
1042*e1fe3e4aSElliott Hughes            '  <ValueFormat value="3"/>',
1043*e1fe3e4aSElliott Hughes            "  <!-- ValueCount=2 -->",
1044*e1fe3e4aSElliott Hughes            '  <Value index="0" XPlacement="777" YPlacement="0"/>',
1045*e1fe3e4aSElliott Hughes            '  <Value index="1" XPlacement="0" YPlacement="-888"/>',
1046*e1fe3e4aSElliott Hughes            "</SinglePos>",
1047*e1fe3e4aSElliott Hughes        ]
1048*e1fe3e4aSElliott Hughes
1049*e1fe3e4aSElliott Hughes    def test_buildValue(self):
1050*e1fe3e4aSElliott Hughes        value = builder.buildValue({"XPlacement": 7, "YPlacement": 23})
1051*e1fe3e4aSElliott Hughes        func = lambda writer, font: value.toXML(writer, font, valueName="Val")
1052*e1fe3e4aSElliott Hughes        assert getXML(func) == ['<Val XPlacement="7" YPlacement="23"/>']
1053*e1fe3e4aSElliott Hughes
1054*e1fe3e4aSElliott Hughes    def test_getLigatureSortKey(self):
1055*e1fe3e4aSElliott Hughes        components = lambda s: [tuple(word) for word in s.split()]
1056*e1fe3e4aSElliott Hughes        c = components("fi fl ff ffi fff")
1057*e1fe3e4aSElliott Hughes        c.sort(key=otTables.LigatureSubst._getLigatureSortKey)
1058*e1fe3e4aSElliott Hughes        assert c == components("ffi fff fi fl ff")
1059*e1fe3e4aSElliott Hughes
1060*e1fe3e4aSElliott Hughes    def test_getSinglePosValueKey(self):
1061*e1fe3e4aSElliott Hughes        device = builder.buildDevice({10: 1, 11: 3})
1062*e1fe3e4aSElliott Hughes        a1 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device})
1063*e1fe3e4aSElliott Hughes        a2 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device})
1064*e1fe3e4aSElliott Hughes        b = builder.buildValue({"XPlacement": 500})
1065*e1fe3e4aSElliott Hughes        keyA1 = builder._getSinglePosValueKey(a1)
1066*e1fe3e4aSElliott Hughes        keyA2 = builder._getSinglePosValueKey(a1)
1067*e1fe3e4aSElliott Hughes        keyB = builder._getSinglePosValueKey(b)
1068*e1fe3e4aSElliott Hughes        assert keyA1 == keyA2
1069*e1fe3e4aSElliott Hughes        assert hash(keyA1) == hash(keyA2)
1070*e1fe3e4aSElliott Hughes        assert keyA1 != keyB
1071*e1fe3e4aSElliott Hughes        assert hash(keyA1) != hash(keyB)
1072*e1fe3e4aSElliott Hughes
1073*e1fe3e4aSElliott Hughes
1074*e1fe3e4aSElliott Hughesclass ClassDefBuilderTest(object):
1075*e1fe3e4aSElliott Hughes    def test_build_usingClass0(self):
1076*e1fe3e4aSElliott Hughes        b = builder.ClassDefBuilder(useClass0=True)
1077*e1fe3e4aSElliott Hughes        b.add({"aa", "bb"})
1078*e1fe3e4aSElliott Hughes        b.add({"a", "b"})
1079*e1fe3e4aSElliott Hughes        b.add({"c"})
1080*e1fe3e4aSElliott Hughes        b.add({"e", "f", "g", "h"})
1081*e1fe3e4aSElliott Hughes        cdef = b.build()
1082*e1fe3e4aSElliott Hughes        assert isinstance(cdef, otTables.ClassDef)
1083*e1fe3e4aSElliott Hughes        assert cdef.classDefs == {"a": 1, "b": 1, "c": 3, "aa": 2, "bb": 2}
1084*e1fe3e4aSElliott Hughes
1085*e1fe3e4aSElliott Hughes    def test_build_notUsingClass0(self):
1086*e1fe3e4aSElliott Hughes        b = builder.ClassDefBuilder(useClass0=False)
1087*e1fe3e4aSElliott Hughes        b.add({"a", "b"})
1088*e1fe3e4aSElliott Hughes        b.add({"c"})
1089*e1fe3e4aSElliott Hughes        b.add({"e", "f", "g", "h"})
1090*e1fe3e4aSElliott Hughes        cdef = b.build()
1091*e1fe3e4aSElliott Hughes        assert isinstance(cdef, otTables.ClassDef)
1092*e1fe3e4aSElliott Hughes        assert cdef.classDefs == {
1093*e1fe3e4aSElliott Hughes            "a": 2,
1094*e1fe3e4aSElliott Hughes            "b": 2,
1095*e1fe3e4aSElliott Hughes            "c": 3,
1096*e1fe3e4aSElliott Hughes            "e": 1,
1097*e1fe3e4aSElliott Hughes            "f": 1,
1098*e1fe3e4aSElliott Hughes            "g": 1,
1099*e1fe3e4aSElliott Hughes            "h": 1,
1100*e1fe3e4aSElliott Hughes        }
1101*e1fe3e4aSElliott Hughes
1102*e1fe3e4aSElliott Hughes    def test_canAdd(self):
1103*e1fe3e4aSElliott Hughes        b = builder.ClassDefBuilder(useClass0=True)
1104*e1fe3e4aSElliott Hughes        b.add({"a", "b", "c", "d"})
1105*e1fe3e4aSElliott Hughes        b.add({"e", "f"})
1106*e1fe3e4aSElliott Hughes        assert b.canAdd({"a", "b", "c", "d"})
1107*e1fe3e4aSElliott Hughes        assert b.canAdd({"e", "f"})
1108*e1fe3e4aSElliott Hughes        assert b.canAdd({"g", "h", "i"})
1109*e1fe3e4aSElliott Hughes        assert not b.canAdd({"b", "c", "d"})
1110*e1fe3e4aSElliott Hughes        assert not b.canAdd({"a", "b", "c", "d", "e", "f"})
1111*e1fe3e4aSElliott Hughes        assert not b.canAdd({"d", "e", "f"})
1112*e1fe3e4aSElliott Hughes        assert not b.canAdd({"f"})
1113*e1fe3e4aSElliott Hughes
1114*e1fe3e4aSElliott Hughes    def test_add_exception(self):
1115*e1fe3e4aSElliott Hughes        b = builder.ClassDefBuilder(useClass0=True)
1116*e1fe3e4aSElliott Hughes        b.add({"a", "b", "c"})
1117*e1fe3e4aSElliott Hughes        with pytest.raises(error.OpenTypeLibError):
1118*e1fe3e4aSElliott Hughes            b.add({"a", "d"})
1119*e1fe3e4aSElliott Hughes
1120*e1fe3e4aSElliott Hughes
1121*e1fe3e4aSElliott HughesbuildStatTable_test_data = [
1122*e1fe3e4aSElliott Hughes    (
1123*e1fe3e4aSElliott Hughes        [
1124*e1fe3e4aSElliott Hughes            dict(
1125*e1fe3e4aSElliott Hughes                tag="wght",
1126*e1fe3e4aSElliott Hughes                name="Weight",
1127*e1fe3e4aSElliott Hughes                values=[
1128*e1fe3e4aSElliott Hughes                    dict(value=100, name="Thin"),
1129*e1fe3e4aSElliott Hughes                    dict(value=400, name="Regular", flags=0x2),
1130*e1fe3e4aSElliott Hughes                    dict(value=900, name="Black"),
1131*e1fe3e4aSElliott Hughes                ],
1132*e1fe3e4aSElliott Hughes            )
1133*e1fe3e4aSElliott Hughes        ],
1134*e1fe3e4aSElliott Hughes        None,
1135*e1fe3e4aSElliott Hughes        "Regular",
1136*e1fe3e4aSElliott Hughes        [
1137*e1fe3e4aSElliott Hughes            "  <STAT>",
1138*e1fe3e4aSElliott Hughes            '    <Version value="0x00010001"/>',
1139*e1fe3e4aSElliott Hughes            '    <DesignAxisRecordSize value="8"/>',
1140*e1fe3e4aSElliott Hughes            "    <!-- DesignAxisCount=1 -->",
1141*e1fe3e4aSElliott Hughes            "    <DesignAxisRecord>",
1142*e1fe3e4aSElliott Hughes            '      <Axis index="0">',
1143*e1fe3e4aSElliott Hughes            '        <AxisTag value="wght"/>',
1144*e1fe3e4aSElliott Hughes            '        <AxisNameID value="257"/>  <!-- Weight -->',
1145*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="0"/>',
1146*e1fe3e4aSElliott Hughes            "      </Axis>",
1147*e1fe3e4aSElliott Hughes            "    </DesignAxisRecord>",
1148*e1fe3e4aSElliott Hughes            "    <!-- AxisValueCount=3 -->",
1149*e1fe3e4aSElliott Hughes            "    <AxisValueArray>",
1150*e1fe3e4aSElliott Hughes            '      <AxisValue index="0" Format="1">',
1151*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1152*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1153*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Thin -->',
1154*e1fe3e4aSElliott Hughes            '        <Value value="100.0"/>',
1155*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1156*e1fe3e4aSElliott Hughes            '      <AxisValue index="1" Format="1">',
1157*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1158*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1159*e1fe3e4aSElliott Hughes            '        <ValueNameID value="256"/>  <!-- Regular -->',
1160*e1fe3e4aSElliott Hughes            '        <Value value="400.0"/>',
1161*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1162*e1fe3e4aSElliott Hughes            '      <AxisValue index="2" Format="1">',
1163*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1164*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1165*e1fe3e4aSElliott Hughes            '        <ValueNameID value="259"/>  <!-- Black -->',
1166*e1fe3e4aSElliott Hughes            '        <Value value="900.0"/>',
1167*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1168*e1fe3e4aSElliott Hughes            "    </AxisValueArray>",
1169*e1fe3e4aSElliott Hughes            '    <ElidedFallbackNameID value="256"/>  <!-- Regular -->',
1170*e1fe3e4aSElliott Hughes            "  </STAT>",
1171*e1fe3e4aSElliott Hughes        ],
1172*e1fe3e4aSElliott Hughes    ),
1173*e1fe3e4aSElliott Hughes    (
1174*e1fe3e4aSElliott Hughes        [
1175*e1fe3e4aSElliott Hughes            dict(
1176*e1fe3e4aSElliott Hughes                tag="wght",
1177*e1fe3e4aSElliott Hughes                name=dict(en="Weight", nl="Gewicht"),
1178*e1fe3e4aSElliott Hughes                values=[
1179*e1fe3e4aSElliott Hughes                    dict(value=100, name=dict(en="Thin", nl="Dun")),
1180*e1fe3e4aSElliott Hughes                    dict(value=400, name="Regular", flags=0x2),
1181*e1fe3e4aSElliott Hughes                    dict(value=900, name="Black"),
1182*e1fe3e4aSElliott Hughes                ],
1183*e1fe3e4aSElliott Hughes            ),
1184*e1fe3e4aSElliott Hughes            dict(
1185*e1fe3e4aSElliott Hughes                tag="wdth",
1186*e1fe3e4aSElliott Hughes                name="Width",
1187*e1fe3e4aSElliott Hughes                values=[
1188*e1fe3e4aSElliott Hughes                    dict(value=50, name="Condensed"),
1189*e1fe3e4aSElliott Hughes                    dict(value=100, name="Regular", flags=0x2),
1190*e1fe3e4aSElliott Hughes                    dict(value=200, name="Extended"),
1191*e1fe3e4aSElliott Hughes                ],
1192*e1fe3e4aSElliott Hughes            ),
1193*e1fe3e4aSElliott Hughes        ],
1194*e1fe3e4aSElliott Hughes        None,
1195*e1fe3e4aSElliott Hughes        2,
1196*e1fe3e4aSElliott Hughes        [
1197*e1fe3e4aSElliott Hughes            "  <STAT>",
1198*e1fe3e4aSElliott Hughes            '    <Version value="0x00010001"/>',
1199*e1fe3e4aSElliott Hughes            '    <DesignAxisRecordSize value="8"/>',
1200*e1fe3e4aSElliott Hughes            "    <!-- DesignAxisCount=2 -->",
1201*e1fe3e4aSElliott Hughes            "    <DesignAxisRecord>",
1202*e1fe3e4aSElliott Hughes            '      <Axis index="0">',
1203*e1fe3e4aSElliott Hughes            '        <AxisTag value="wght"/>',
1204*e1fe3e4aSElliott Hughes            '        <AxisNameID value="256"/>  <!-- Weight -->',
1205*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="0"/>',
1206*e1fe3e4aSElliott Hughes            "      </Axis>",
1207*e1fe3e4aSElliott Hughes            '      <Axis index="1">',
1208*e1fe3e4aSElliott Hughes            '        <AxisTag value="wdth"/>',
1209*e1fe3e4aSElliott Hughes            '        <AxisNameID value="260"/>  <!-- Width -->',
1210*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="1"/>',
1211*e1fe3e4aSElliott Hughes            "      </Axis>",
1212*e1fe3e4aSElliott Hughes            "    </DesignAxisRecord>",
1213*e1fe3e4aSElliott Hughes            "    <!-- AxisValueCount=6 -->",
1214*e1fe3e4aSElliott Hughes            "    <AxisValueArray>",
1215*e1fe3e4aSElliott Hughes            '      <AxisValue index="0" Format="1">',
1216*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1217*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1218*e1fe3e4aSElliott Hughes            '        <ValueNameID value="257"/>  <!-- Thin -->',
1219*e1fe3e4aSElliott Hughes            '        <Value value="100.0"/>',
1220*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1221*e1fe3e4aSElliott Hughes            '      <AxisValue index="1" Format="1">',
1222*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1223*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1224*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Regular -->',
1225*e1fe3e4aSElliott Hughes            '        <Value value="400.0"/>',
1226*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1227*e1fe3e4aSElliott Hughes            '      <AxisValue index="2" Format="1">',
1228*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1229*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1230*e1fe3e4aSElliott Hughes            '        <ValueNameID value="259"/>  <!-- Black -->',
1231*e1fe3e4aSElliott Hughes            '        <Value value="900.0"/>',
1232*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1233*e1fe3e4aSElliott Hughes            '      <AxisValue index="3" Format="1">',
1234*e1fe3e4aSElliott Hughes            '        <AxisIndex value="1"/>',
1235*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1236*e1fe3e4aSElliott Hughes            '        <ValueNameID value="261"/>  <!-- Condensed -->',
1237*e1fe3e4aSElliott Hughes            '        <Value value="50.0"/>',
1238*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1239*e1fe3e4aSElliott Hughes            '      <AxisValue index="4" Format="1">',
1240*e1fe3e4aSElliott Hughes            '        <AxisIndex value="1"/>',
1241*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1242*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Regular -->',
1243*e1fe3e4aSElliott Hughes            '        <Value value="100.0"/>',
1244*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1245*e1fe3e4aSElliott Hughes            '      <AxisValue index="5" Format="1">',
1246*e1fe3e4aSElliott Hughes            '        <AxisIndex value="1"/>',
1247*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1248*e1fe3e4aSElliott Hughes            '        <ValueNameID value="262"/>  <!-- Extended -->',
1249*e1fe3e4aSElliott Hughes            '        <Value value="200.0"/>',
1250*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1251*e1fe3e4aSElliott Hughes            "    </AxisValueArray>",
1252*e1fe3e4aSElliott Hughes            '    <ElidedFallbackNameID value="2"/>  <!-- missing from name table -->',
1253*e1fe3e4aSElliott Hughes            "  </STAT>",
1254*e1fe3e4aSElliott Hughes        ],
1255*e1fe3e4aSElliott Hughes    ),
1256*e1fe3e4aSElliott Hughes    (
1257*e1fe3e4aSElliott Hughes        [
1258*e1fe3e4aSElliott Hughes            dict(
1259*e1fe3e4aSElliott Hughes                tag="wght",
1260*e1fe3e4aSElliott Hughes                name="Weight",
1261*e1fe3e4aSElliott Hughes                values=[
1262*e1fe3e4aSElliott Hughes                    dict(value=400, name="Regular", flags=0x2),
1263*e1fe3e4aSElliott Hughes                    dict(value=600, linkedValue=650, name="Bold"),
1264*e1fe3e4aSElliott Hughes                ],
1265*e1fe3e4aSElliott Hughes            )
1266*e1fe3e4aSElliott Hughes        ],
1267*e1fe3e4aSElliott Hughes        None,
1268*e1fe3e4aSElliott Hughes        18,
1269*e1fe3e4aSElliott Hughes        [
1270*e1fe3e4aSElliott Hughes            "  <STAT>",
1271*e1fe3e4aSElliott Hughes            '    <Version value="0x00010001"/>',
1272*e1fe3e4aSElliott Hughes            '    <DesignAxisRecordSize value="8"/>',
1273*e1fe3e4aSElliott Hughes            "    <!-- DesignAxisCount=1 -->",
1274*e1fe3e4aSElliott Hughes            "    <DesignAxisRecord>",
1275*e1fe3e4aSElliott Hughes            '      <Axis index="0">',
1276*e1fe3e4aSElliott Hughes            '        <AxisTag value="wght"/>',
1277*e1fe3e4aSElliott Hughes            '        <AxisNameID value="256"/>  <!-- Weight -->',
1278*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="0"/>',
1279*e1fe3e4aSElliott Hughes            "      </Axis>",
1280*e1fe3e4aSElliott Hughes            "    </DesignAxisRecord>",
1281*e1fe3e4aSElliott Hughes            "    <!-- AxisValueCount=2 -->",
1282*e1fe3e4aSElliott Hughes            "    <AxisValueArray>",
1283*e1fe3e4aSElliott Hughes            '      <AxisValue index="0" Format="1">',
1284*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1285*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1286*e1fe3e4aSElliott Hughes            '        <ValueNameID value="257"/>  <!-- Regular -->',
1287*e1fe3e4aSElliott Hughes            '        <Value value="400.0"/>',
1288*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1289*e1fe3e4aSElliott Hughes            '      <AxisValue index="1" Format="3">',
1290*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1291*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1292*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Bold -->',
1293*e1fe3e4aSElliott Hughes            '        <Value value="600.0"/>',
1294*e1fe3e4aSElliott Hughes            '        <LinkedValue value="650.0"/>',
1295*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1296*e1fe3e4aSElliott Hughes            "    </AxisValueArray>",
1297*e1fe3e4aSElliott Hughes            '    <ElidedFallbackNameID value="18"/>  <!-- missing from name table -->',
1298*e1fe3e4aSElliott Hughes            "  </STAT>",
1299*e1fe3e4aSElliott Hughes        ],
1300*e1fe3e4aSElliott Hughes    ),
1301*e1fe3e4aSElliott Hughes    (
1302*e1fe3e4aSElliott Hughes        [
1303*e1fe3e4aSElliott Hughes            dict(
1304*e1fe3e4aSElliott Hughes                tag="opsz",
1305*e1fe3e4aSElliott Hughes                name="Optical Size",
1306*e1fe3e4aSElliott Hughes                values=[
1307*e1fe3e4aSElliott Hughes                    dict(nominalValue=6, rangeMaxValue=10, name="Small"),
1308*e1fe3e4aSElliott Hughes                    dict(
1309*e1fe3e4aSElliott Hughes                        rangeMinValue=10,
1310*e1fe3e4aSElliott Hughes                        nominalValue=14,
1311*e1fe3e4aSElliott Hughes                        rangeMaxValue=24,
1312*e1fe3e4aSElliott Hughes                        name="Text",
1313*e1fe3e4aSElliott Hughes                        flags=0x2,
1314*e1fe3e4aSElliott Hughes                    ),
1315*e1fe3e4aSElliott Hughes                    dict(rangeMinValue=24, nominalValue=600, name="Display"),
1316*e1fe3e4aSElliott Hughes                ],
1317*e1fe3e4aSElliott Hughes            )
1318*e1fe3e4aSElliott Hughes        ],
1319*e1fe3e4aSElliott Hughes        None,
1320*e1fe3e4aSElliott Hughes        2,
1321*e1fe3e4aSElliott Hughes        [
1322*e1fe3e4aSElliott Hughes            "  <STAT>",
1323*e1fe3e4aSElliott Hughes            '    <Version value="0x00010001"/>',
1324*e1fe3e4aSElliott Hughes            '    <DesignAxisRecordSize value="8"/>',
1325*e1fe3e4aSElliott Hughes            "    <!-- DesignAxisCount=1 -->",
1326*e1fe3e4aSElliott Hughes            "    <DesignAxisRecord>",
1327*e1fe3e4aSElliott Hughes            '      <Axis index="0">',
1328*e1fe3e4aSElliott Hughes            '        <AxisTag value="opsz"/>',
1329*e1fe3e4aSElliott Hughes            '        <AxisNameID value="256"/>  <!-- Optical Size -->',
1330*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="0"/>',
1331*e1fe3e4aSElliott Hughes            "      </Axis>",
1332*e1fe3e4aSElliott Hughes            "    </DesignAxisRecord>",
1333*e1fe3e4aSElliott Hughes            "    <!-- AxisValueCount=3 -->",
1334*e1fe3e4aSElliott Hughes            "    <AxisValueArray>",
1335*e1fe3e4aSElliott Hughes            '      <AxisValue index="0" Format="2">',
1336*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1337*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1338*e1fe3e4aSElliott Hughes            '        <ValueNameID value="257"/>  <!-- Small -->',
1339*e1fe3e4aSElliott Hughes            '        <NominalValue value="6.0"/>',
1340*e1fe3e4aSElliott Hughes            '        <RangeMinValue value="-32768.0"/>',
1341*e1fe3e4aSElliott Hughes            '        <RangeMaxValue value="10.0"/>',
1342*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1343*e1fe3e4aSElliott Hughes            '      <AxisValue index="1" Format="2">',
1344*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1345*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1346*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Text -->',
1347*e1fe3e4aSElliott Hughes            '        <NominalValue value="14.0"/>',
1348*e1fe3e4aSElliott Hughes            '        <RangeMinValue value="10.0"/>',
1349*e1fe3e4aSElliott Hughes            '        <RangeMaxValue value="24.0"/>',
1350*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1351*e1fe3e4aSElliott Hughes            '      <AxisValue index="2" Format="2">',
1352*e1fe3e4aSElliott Hughes            '        <AxisIndex value="0"/>',
1353*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1354*e1fe3e4aSElliott Hughes            '        <ValueNameID value="259"/>  <!-- Display -->',
1355*e1fe3e4aSElliott Hughes            '        <NominalValue value="600.0"/>',
1356*e1fe3e4aSElliott Hughes            '        <RangeMinValue value="24.0"/>',
1357*e1fe3e4aSElliott Hughes            '        <RangeMaxValue value="32767.99998"/>',
1358*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1359*e1fe3e4aSElliott Hughes            "    </AxisValueArray>",
1360*e1fe3e4aSElliott Hughes            '    <ElidedFallbackNameID value="2"/>  <!-- missing from name table -->',
1361*e1fe3e4aSElliott Hughes            "  </STAT>",
1362*e1fe3e4aSElliott Hughes        ],
1363*e1fe3e4aSElliott Hughes    ),
1364*e1fe3e4aSElliott Hughes    (
1365*e1fe3e4aSElliott Hughes        [
1366*e1fe3e4aSElliott Hughes            dict(tag="wght", name="Weight", ordering=1, values=[]),
1367*e1fe3e4aSElliott Hughes            dict(
1368*e1fe3e4aSElliott Hughes                tag="ABCD",
1369*e1fe3e4aSElliott Hughes                name="ABCDTest",
1370*e1fe3e4aSElliott Hughes                ordering=0,
1371*e1fe3e4aSElliott Hughes                values=[dict(value=100, name="Regular", flags=0x2)],
1372*e1fe3e4aSElliott Hughes            ),
1373*e1fe3e4aSElliott Hughes        ],
1374*e1fe3e4aSElliott Hughes        [dict(location=dict(wght=300, ABCD=100), name="Regular ABCD")],
1375*e1fe3e4aSElliott Hughes        18,
1376*e1fe3e4aSElliott Hughes        [
1377*e1fe3e4aSElliott Hughes            "  <STAT>",
1378*e1fe3e4aSElliott Hughes            '    <Version value="0x00010002"/>',
1379*e1fe3e4aSElliott Hughes            '    <DesignAxisRecordSize value="8"/>',
1380*e1fe3e4aSElliott Hughes            "    <!-- DesignAxisCount=2 -->",
1381*e1fe3e4aSElliott Hughes            "    <DesignAxisRecord>",
1382*e1fe3e4aSElliott Hughes            '      <Axis index="0">',
1383*e1fe3e4aSElliott Hughes            '        <AxisTag value="wght"/>',
1384*e1fe3e4aSElliott Hughes            '        <AxisNameID value="256"/>  <!-- Weight -->',
1385*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="1"/>',
1386*e1fe3e4aSElliott Hughes            "      </Axis>",
1387*e1fe3e4aSElliott Hughes            '      <Axis index="1">',
1388*e1fe3e4aSElliott Hughes            '        <AxisTag value="ABCD"/>',
1389*e1fe3e4aSElliott Hughes            '        <AxisNameID value="257"/>  <!-- ABCDTest -->',
1390*e1fe3e4aSElliott Hughes            '        <AxisOrdering value="0"/>',
1391*e1fe3e4aSElliott Hughes            "      </Axis>",
1392*e1fe3e4aSElliott Hughes            "    </DesignAxisRecord>",
1393*e1fe3e4aSElliott Hughes            "    <!-- AxisValueCount=2 -->",
1394*e1fe3e4aSElliott Hughes            "    <AxisValueArray>",
1395*e1fe3e4aSElliott Hughes            '      <AxisValue index="0" Format="4">',
1396*e1fe3e4aSElliott Hughes            "        <!-- AxisCount=2 -->",
1397*e1fe3e4aSElliott Hughes            '        <Flags value="0"/>',
1398*e1fe3e4aSElliott Hughes            '        <ValueNameID value="259"/>  <!-- Regular ABCD -->',
1399*e1fe3e4aSElliott Hughes            '        <AxisValueRecord index="0">',
1400*e1fe3e4aSElliott Hughes            '          <AxisIndex value="0"/>',
1401*e1fe3e4aSElliott Hughes            '          <Value value="300.0"/>',
1402*e1fe3e4aSElliott Hughes            "        </AxisValueRecord>",
1403*e1fe3e4aSElliott Hughes            '        <AxisValueRecord index="1">',
1404*e1fe3e4aSElliott Hughes            '          <AxisIndex value="1"/>',
1405*e1fe3e4aSElliott Hughes            '          <Value value="100.0"/>',
1406*e1fe3e4aSElliott Hughes            "        </AxisValueRecord>",
1407*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1408*e1fe3e4aSElliott Hughes            '      <AxisValue index="1" Format="1">',
1409*e1fe3e4aSElliott Hughes            '        <AxisIndex value="1"/>',
1410*e1fe3e4aSElliott Hughes            '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
1411*e1fe3e4aSElliott Hughes            '        <ValueNameID value="258"/>  <!-- Regular -->',
1412*e1fe3e4aSElliott Hughes            '        <Value value="100.0"/>',
1413*e1fe3e4aSElliott Hughes            "      </AxisValue>",
1414*e1fe3e4aSElliott Hughes            "    </AxisValueArray>",
1415*e1fe3e4aSElliott Hughes            '    <ElidedFallbackNameID value="18"/>  <!-- missing from name table -->',
1416*e1fe3e4aSElliott Hughes            "  </STAT>",
1417*e1fe3e4aSElliott Hughes        ],
1418*e1fe3e4aSElliott Hughes    ),
1419*e1fe3e4aSElliott Hughes]
1420*e1fe3e4aSElliott Hughes
1421*e1fe3e4aSElliott Hughes
1422*e1fe3e4aSElliott Hughes@pytest.mark.parametrize(
1423*e1fe3e4aSElliott Hughes    "axes, axisValues, elidedFallbackName, expected_ttx", buildStatTable_test_data
1424*e1fe3e4aSElliott Hughes)
1425*e1fe3e4aSElliott Hughesdef test_buildStatTable(axes, axisValues, elidedFallbackName, expected_ttx):
1426*e1fe3e4aSElliott Hughes    font = ttLib.TTFont()
1427*e1fe3e4aSElliott Hughes    font["name"] = ttLib.newTable("name")
1428*e1fe3e4aSElliott Hughes    font["name"].names = []
1429*e1fe3e4aSElliott Hughes    # https://github.com/fonttools/fonttools/issues/1985
1430*e1fe3e4aSElliott Hughes    # Add nameID < 256 that matches a test axis name, to test whether
1431*e1fe3e4aSElliott Hughes    # the nameID is not reused: AxisNameIDs must be > 255 according
1432*e1fe3e4aSElliott Hughes    # to the spec.
1433*e1fe3e4aSElliott Hughes    font["name"].addMultilingualName(dict(en="ABCDTest"), nameID=6)
1434*e1fe3e4aSElliott Hughes    builder.buildStatTable(font, axes, axisValues, elidedFallbackName)
1435*e1fe3e4aSElliott Hughes    f = io.StringIO()
1436*e1fe3e4aSElliott Hughes    font.saveXML(f, tables=["STAT"])
1437*e1fe3e4aSElliott Hughes    ttx = f.getvalue().splitlines()
1438*e1fe3e4aSElliott Hughes    ttx = ttx[3:-2]  # strip XML header and <ttFont> element
1439*e1fe3e4aSElliott Hughes    assert expected_ttx == ttx
1440*e1fe3e4aSElliott Hughes    # Compile and round-trip
1441*e1fe3e4aSElliott Hughes    f = io.BytesIO()
1442*e1fe3e4aSElliott Hughes    font.save(f)
1443*e1fe3e4aSElliott Hughes    font = ttLib.TTFont(f)
1444*e1fe3e4aSElliott Hughes    f = io.StringIO()
1445*e1fe3e4aSElliott Hughes    font.saveXML(f, tables=["STAT"])
1446*e1fe3e4aSElliott Hughes    ttx = f.getvalue().splitlines()
1447*e1fe3e4aSElliott Hughes    ttx = ttx[3:-2]  # strip XML header and <ttFont> element
1448*e1fe3e4aSElliott Hughes    assert expected_ttx == ttx
1449*e1fe3e4aSElliott Hughes
1450*e1fe3e4aSElliott Hughes
1451*e1fe3e4aSElliott Hughesdef test_buildStatTable_platform_specific_names():
1452*e1fe3e4aSElliott Hughes    # PR: https://github.com/fonttools/fonttools/pull/2528
1453*e1fe3e4aSElliott Hughes    # Introduce new 'platform' feature for creating a STAT table.
1454*e1fe3e4aSElliott Hughes    # Set windowsNames and or macNames to create name table entries
1455*e1fe3e4aSElliott Hughes    # in the specified platforms
1456*e1fe3e4aSElliott Hughes    font_obj = ttLib.TTFont()
1457*e1fe3e4aSElliott Hughes    font_obj["name"] = ttLib.newTable("name")
1458*e1fe3e4aSElliott Hughes    font_obj["name"].names = []
1459*e1fe3e4aSElliott Hughes
1460*e1fe3e4aSElliott Hughes    wght_values = [
1461*e1fe3e4aSElliott Hughes        dict(nominalValue=200, rangeMinValue=200, rangeMaxValue=250, name="ExtraLight"),
1462*e1fe3e4aSElliott Hughes        dict(nominalValue=300, rangeMinValue=250, rangeMaxValue=350, name="Light"),
1463*e1fe3e4aSElliott Hughes        dict(
1464*e1fe3e4aSElliott Hughes            nominalValue=400,
1465*e1fe3e4aSElliott Hughes            rangeMinValue=350,
1466*e1fe3e4aSElliott Hughes            rangeMaxValue=450,
1467*e1fe3e4aSElliott Hughes            name="Regular",
1468*e1fe3e4aSElliott Hughes            flags=0x2,
1469*e1fe3e4aSElliott Hughes        ),
1470*e1fe3e4aSElliott Hughes        dict(nominalValue=500, rangeMinValue=450, rangeMaxValue=650, name="Medium"),
1471*e1fe3e4aSElliott Hughes        dict(nominalValue=700, rangeMinValue=650, rangeMaxValue=750, name="Bold"),
1472*e1fe3e4aSElliott Hughes        dict(nominalValue=800, rangeMinValue=750, rangeMaxValue=850, name="ExtraBold"),
1473*e1fe3e4aSElliott Hughes        dict(nominalValue=900, rangeMinValue=850, rangeMaxValue=900, name="Black"),
1474*e1fe3e4aSElliott Hughes    ]
1475*e1fe3e4aSElliott Hughes
1476*e1fe3e4aSElliott Hughes    AXES = [
1477*e1fe3e4aSElliott Hughes        dict(
1478*e1fe3e4aSElliott Hughes            tag="wght",
1479*e1fe3e4aSElliott Hughes            name="Weight",
1480*e1fe3e4aSElliott Hughes            ordering=1,
1481*e1fe3e4aSElliott Hughes            values=wght_values,
1482*e1fe3e4aSElliott Hughes        ),
1483*e1fe3e4aSElliott Hughes    ]
1484*e1fe3e4aSElliott Hughes
1485*e1fe3e4aSElliott Hughes    font_obj["name"].setName("ExtraLight", 260, 3, 1, 0x409)
1486*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Light", 261, 3, 1, 0x409)
1487*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Regular", 262, 3, 1, 0x409)
1488*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Medium", 263, 3, 1, 0x409)
1489*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Bold", 264, 3, 1, 0x409)
1490*e1fe3e4aSElliott Hughes    font_obj["name"].setName("ExtraBold", 265, 3, 1, 0x409)
1491*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Black", 266, 3, 1, 0x409)
1492*e1fe3e4aSElliott Hughes
1493*e1fe3e4aSElliott Hughes    font_obj["name"].setName("Weight", 270, 3, 1, 0x409)
1494*e1fe3e4aSElliott Hughes
1495*e1fe3e4aSElliott Hughes    expected_names = [x.string for x in font_obj["name"].names]
1496*e1fe3e4aSElliott Hughes
1497*e1fe3e4aSElliott Hughes    builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=False)
1498*e1fe3e4aSElliott Hughes    actual_names = [x.string for x in font_obj["name"].names]
1499*e1fe3e4aSElliott Hughes
1500*e1fe3e4aSElliott Hughes    # no new name records were added by buildStatTable
1501*e1fe3e4aSElliott Hughes    # because windows-only names with the same strings were already present
1502*e1fe3e4aSElliott Hughes    assert expected_names == actual_names
1503*e1fe3e4aSElliott Hughes
1504*e1fe3e4aSElliott Hughes    font_obj["name"].removeNames(nameID=270)
1505*e1fe3e4aSElliott Hughes    expected_names = [x.string for x in font_obj["name"].names] + ["Weight"]
1506*e1fe3e4aSElliott Hughes
1507*e1fe3e4aSElliott Hughes    builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=False)
1508*e1fe3e4aSElliott Hughes    actual_names = [x.string for x in font_obj["name"].names]
1509*e1fe3e4aSElliott Hughes    # One new name records 'Weight' were added by buildStatTable
1510*e1fe3e4aSElliott Hughes    assert expected_names == actual_names
1511*e1fe3e4aSElliott Hughes
1512*e1fe3e4aSElliott Hughes    builder.buildStatTable(font_obj, AXES, windowsNames=True, macNames=True)
1513*e1fe3e4aSElliott Hughes    actual_names = [x.string for x in font_obj["name"].names]
1514*e1fe3e4aSElliott Hughes    expected_names = [
1515*e1fe3e4aSElliott Hughes        "Weight",
1516*e1fe3e4aSElliott Hughes        "Weight",
1517*e1fe3e4aSElliott Hughes        "Weight",
1518*e1fe3e4aSElliott Hughes        "ExtraLight",
1519*e1fe3e4aSElliott Hughes        "ExtraLight",
1520*e1fe3e4aSElliott Hughes        "ExtraLight",
1521*e1fe3e4aSElliott Hughes        "Light",
1522*e1fe3e4aSElliott Hughes        "Light",
1523*e1fe3e4aSElliott Hughes        "Light",
1524*e1fe3e4aSElliott Hughes        "Regular",
1525*e1fe3e4aSElliott Hughes        "Regular",
1526*e1fe3e4aSElliott Hughes        "Regular",
1527*e1fe3e4aSElliott Hughes        "Medium",
1528*e1fe3e4aSElliott Hughes        "Medium",
1529*e1fe3e4aSElliott Hughes        "Medium",
1530*e1fe3e4aSElliott Hughes        "Bold",
1531*e1fe3e4aSElliott Hughes        "Bold",
1532*e1fe3e4aSElliott Hughes        "Bold",
1533*e1fe3e4aSElliott Hughes        "ExtraBold",
1534*e1fe3e4aSElliott Hughes        "ExtraBold",
1535*e1fe3e4aSElliott Hughes        "ExtraBold",
1536*e1fe3e4aSElliott Hughes        "Black",
1537*e1fe3e4aSElliott Hughes        "Black",
1538*e1fe3e4aSElliott Hughes        "Black",
1539*e1fe3e4aSElliott Hughes    ]
1540*e1fe3e4aSElliott Hughes    # Because there is an inconsistency in the names add new name IDs
1541*e1fe3e4aSElliott Hughes    # for each platform -> windowsNames=True, macNames=True
1542*e1fe3e4aSElliott Hughes    assert sorted(expected_names) == sorted(actual_names)
1543*e1fe3e4aSElliott Hughes
1544*e1fe3e4aSElliott Hughes
1545*e1fe3e4aSElliott Hughesdef test_stat_infinities():
1546*e1fe3e4aSElliott Hughes    negInf = floatToFixed(builder.AXIS_VALUE_NEGATIVE_INFINITY, 16)
1547*e1fe3e4aSElliott Hughes    assert struct.pack(">l", negInf) == b"\x80\x00\x00\x00"
1548*e1fe3e4aSElliott Hughes    posInf = floatToFixed(builder.AXIS_VALUE_POSITIVE_INFINITY, 16)
1549*e1fe3e4aSElliott Hughes    assert struct.pack(">l", posInf) == b"\x7f\xff\xff\xff"
1550*e1fe3e4aSElliott Hughes
1551*e1fe3e4aSElliott Hughes
1552*e1fe3e4aSElliott Hughesdef test_buildMathTable_empty():
1553*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1554*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder([])
1555*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont)
1556*e1fe3e4aSElliott Hughes
1557*e1fe3e4aSElliott Hughes    assert "MATH" in ttFont
1558*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1559*e1fe3e4aSElliott Hughes    assert mathTable.Version == 0x00010000
1560*e1fe3e4aSElliott Hughes
1561*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1562*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1563*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1564*e1fe3e4aSElliott Hughes
1565*e1fe3e4aSElliott Hughes
1566*e1fe3e4aSElliott Hughesdef test_buildMathTable_constants():
1567*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1568*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder([])
1569*e1fe3e4aSElliott Hughes    constants = {
1570*e1fe3e4aSElliott Hughes        "AccentBaseHeight": 516,
1571*e1fe3e4aSElliott Hughes        "AxisHeight": 262,
1572*e1fe3e4aSElliott Hughes        "DelimitedSubFormulaMinHeight": 1500,
1573*e1fe3e4aSElliott Hughes        "DisplayOperatorMinHeight": 2339,
1574*e1fe3e4aSElliott Hughes        "FlattenedAccentBaseHeight": 698,
1575*e1fe3e4aSElliott Hughes        "FractionDenomDisplayStyleGapMin": 198,
1576*e1fe3e4aSElliott Hughes        "FractionDenominatorDisplayStyleShiftDown": 698,
1577*e1fe3e4aSElliott Hughes        "FractionDenominatorGapMin": 66,
1578*e1fe3e4aSElliott Hughes        "FractionDenominatorShiftDown": 465,
1579*e1fe3e4aSElliott Hughes        "FractionNumDisplayStyleGapMin": 198,
1580*e1fe3e4aSElliott Hughes        "FractionNumeratorDisplayStyleShiftUp": 774,
1581*e1fe3e4aSElliott Hughes        "FractionNumeratorGapMin": 66,
1582*e1fe3e4aSElliott Hughes        "FractionNumeratorShiftUp": 516,
1583*e1fe3e4aSElliott Hughes        "FractionRuleThickness": 66,
1584*e1fe3e4aSElliott Hughes        "LowerLimitBaselineDropMin": 585,
1585*e1fe3e4aSElliott Hughes        "LowerLimitGapMin": 132,
1586*e1fe3e4aSElliott Hughes        "MathLeading": 300,
1587*e1fe3e4aSElliott Hughes        "OverbarExtraAscender": 66,
1588*e1fe3e4aSElliott Hughes        "OverbarRuleThickness": 66,
1589*e1fe3e4aSElliott Hughes        "OverbarVerticalGap": 198,
1590*e1fe3e4aSElliott Hughes        "RadicalDegreeBottomRaisePercent": 75,
1591*e1fe3e4aSElliott Hughes        "RadicalDisplayStyleVerticalGap": 195,
1592*e1fe3e4aSElliott Hughes        "RadicalExtraAscender": 66,
1593*e1fe3e4aSElliott Hughes        "RadicalKernAfterDegree": -556,
1594*e1fe3e4aSElliott Hughes        "RadicalKernBeforeDegree": 278,
1595*e1fe3e4aSElliott Hughes        "RadicalRuleThickness": 66,
1596*e1fe3e4aSElliott Hughes        "RadicalVerticalGap": 82,
1597*e1fe3e4aSElliott Hughes        "ScriptPercentScaleDown": 70,
1598*e1fe3e4aSElliott Hughes        "ScriptScriptPercentScaleDown": 55,
1599*e1fe3e4aSElliott Hughes        "SkewedFractionHorizontalGap": 66,
1600*e1fe3e4aSElliott Hughes        "SkewedFractionVerticalGap": 77,
1601*e1fe3e4aSElliott Hughes        "SpaceAfterScript": 42,
1602*e1fe3e4aSElliott Hughes        "StackBottomDisplayStyleShiftDown": 698,
1603*e1fe3e4aSElliott Hughes        "StackBottomShiftDown": 465,
1604*e1fe3e4aSElliott Hughes        "StackDisplayStyleGapMin": 462,
1605*e1fe3e4aSElliott Hughes        "StackGapMin": 198,
1606*e1fe3e4aSElliott Hughes        "StackTopDisplayStyleShiftUp": 774,
1607*e1fe3e4aSElliott Hughes        "StackTopShiftUp": 516,
1608*e1fe3e4aSElliott Hughes        "StretchStackBottomShiftDown": 585,
1609*e1fe3e4aSElliott Hughes        "StretchStackGapAboveMin": 132,
1610*e1fe3e4aSElliott Hughes        "StretchStackGapBelowMin": 132,
1611*e1fe3e4aSElliott Hughes        "StretchStackTopShiftUp": 165,
1612*e1fe3e4aSElliott Hughes        "SubSuperscriptGapMin": 264,
1613*e1fe3e4aSElliott Hughes        "SubscriptBaselineDropMin": 105,
1614*e1fe3e4aSElliott Hughes        "SubscriptShiftDown": 140,
1615*e1fe3e4aSElliott Hughes        "SubscriptTopMax": 413,
1616*e1fe3e4aSElliott Hughes        "SuperscriptBaselineDropMax": 221,
1617*e1fe3e4aSElliott Hughes        "SuperscriptBottomMaxWithSubscript": 413,
1618*e1fe3e4aSElliott Hughes        "SuperscriptBottomMin": 129,
1619*e1fe3e4aSElliott Hughes        "SuperscriptShiftUp": 477,
1620*e1fe3e4aSElliott Hughes        "SuperscriptShiftUpCramped": 358,
1621*e1fe3e4aSElliott Hughes        "UnderbarExtraDescender": 66,
1622*e1fe3e4aSElliott Hughes        "UnderbarRuleThickness": 66,
1623*e1fe3e4aSElliott Hughes        "UnderbarVerticalGap": 198,
1624*e1fe3e4aSElliott Hughes        "UpperLimitBaselineRiseMin": 165,
1625*e1fe3e4aSElliott Hughes        "UpperLimitGapMin": 132,
1626*e1fe3e4aSElliott Hughes    }
1627*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, constants=constants)
1628*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1629*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants
1630*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1631*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1632*e1fe3e4aSElliott Hughes    for k, v in constants.items():
1633*e1fe3e4aSElliott Hughes        r = getattr(mathTable.MathConstants, k)
1634*e1fe3e4aSElliott Hughes        try:
1635*e1fe3e4aSElliott Hughes            r = r.Value
1636*e1fe3e4aSElliott Hughes        except AttributeError:
1637*e1fe3e4aSElliott Hughes            pass
1638*e1fe3e4aSElliott Hughes        assert r == v
1639*e1fe3e4aSElliott Hughes
1640*e1fe3e4aSElliott Hughes
1641*e1fe3e4aSElliott Hughesdef test_buildMathTable_italicsCorrection():
1642*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1643*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"])
1644*e1fe3e4aSElliott Hughes    italicsCorrections = {"A": 100, "C": 300, "D": 400, "E": 500}
1645*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, italicsCorrections=italicsCorrections)
1646*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1647*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1648*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo
1649*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1650*e1fe3e4aSElliott Hughes    assert set(
1651*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.Coverage.glyphs
1652*e1fe3e4aSElliott Hughes    ) == set(italicsCorrections.keys())
1653*e1fe3e4aSElliott Hughes    for glyph, correction in zip(
1654*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.Coverage.glyphs,
1655*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.ItalicsCorrection,
1656*e1fe3e4aSElliott Hughes    ):
1657*e1fe3e4aSElliott Hughes        assert correction.Value == italicsCorrections[glyph]
1658*e1fe3e4aSElliott Hughes
1659*e1fe3e4aSElliott Hughes
1660*e1fe3e4aSElliott Hughesdef test_buildMathTable_topAccentAttachment():
1661*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1662*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"])
1663*e1fe3e4aSElliott Hughes    topAccentAttachments = {"A": 10, "B": 20, "C": 30, "E": 50}
1664*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, topAccentAttachments=topAccentAttachments)
1665*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1666*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1667*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo
1668*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1669*e1fe3e4aSElliott Hughes    assert set(
1670*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentCoverage.glyphs
1671*e1fe3e4aSElliott Hughes    ) == set(topAccentAttachments.keys())
1672*e1fe3e4aSElliott Hughes    for glyph, attachment in zip(
1673*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentCoverage.glyphs,
1674*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathTopAccentAttachment.TopAccentAttachment,
1675*e1fe3e4aSElliott Hughes    ):
1676*e1fe3e4aSElliott Hughes        assert attachment.Value == topAccentAttachments[glyph]
1677*e1fe3e4aSElliott Hughes
1678*e1fe3e4aSElliott Hughes
1679*e1fe3e4aSElliott Hughesdef test_buildMathTable_extendedShape():
1680*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1681*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"])
1682*e1fe3e4aSElliott Hughes    extendedShapes = {"A", "C", "E", "F"}
1683*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, extendedShapes=extendedShapes)
1684*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1685*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1686*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo
1687*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1688*e1fe3e4aSElliott Hughes    assert set(mathTable.MathGlyphInfo.ExtendedShapeCoverage.glyphs) == extendedShapes
1689*e1fe3e4aSElliott Hughes
1690*e1fe3e4aSElliott Hughes
1691*e1fe3e4aSElliott Hughesdef test_buildMathTable_mathKern():
1692*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1693*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "B"])
1694*e1fe3e4aSElliott Hughes    mathKerns = {
1695*e1fe3e4aSElliott Hughes        "A": {
1696*e1fe3e4aSElliott Hughes            "TopRight": ([10, 20], [10, 20, 30]),
1697*e1fe3e4aSElliott Hughes            "BottomRight": ([], [10]),
1698*e1fe3e4aSElliott Hughes            "TopLeft": ([10], [0, 20]),
1699*e1fe3e4aSElliott Hughes            "BottomLeft": ([-10, 0], [0, 10, 20]),
1700*e1fe3e4aSElliott Hughes        },
1701*e1fe3e4aSElliott Hughes    }
1702*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, mathKerns=mathKerns)
1703*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1704*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1705*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo
1706*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants is None
1707*e1fe3e4aSElliott Hughes    assert set(mathTable.MathGlyphInfo.MathKernInfo.MathKernCoverage.glyphs) == set(
1708*e1fe3e4aSElliott Hughes        mathKerns.keys()
1709*e1fe3e4aSElliott Hughes    )
1710*e1fe3e4aSElliott Hughes    for glyph, record in zip(
1711*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathKernInfo.MathKernCoverage.glyphs,
1712*e1fe3e4aSElliott Hughes        mathTable.MathGlyphInfo.MathKernInfo.MathKernInfoRecords,
1713*e1fe3e4aSElliott Hughes    ):
1714*e1fe3e4aSElliott Hughes        h, k = mathKerns[glyph]["TopRight"]
1715*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.TopRightMathKern.CorrectionHeight] == h
1716*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.TopRightMathKern.KernValue] == k
1717*e1fe3e4aSElliott Hughes        h, k = mathKerns[glyph]["BottomRight"]
1718*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.BottomRightMathKern.CorrectionHeight] == h
1719*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.BottomRightMathKern.KernValue] == k
1720*e1fe3e4aSElliott Hughes        h, k = mathKerns[glyph]["TopLeft"]
1721*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.TopLeftMathKern.CorrectionHeight] == h
1722*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.TopLeftMathKern.KernValue] == k
1723*e1fe3e4aSElliott Hughes        h, k = mathKerns[glyph]["BottomLeft"]
1724*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.BottomLeftMathKern.CorrectionHeight] == h
1725*e1fe3e4aSElliott Hughes        assert [v.Value for v in record.BottomLeftMathKern.KernValue] == k
1726*e1fe3e4aSElliott Hughes
1727*e1fe3e4aSElliott Hughes
1728*e1fe3e4aSElliott Hughesdef test_buildMathTable_vertVariants():
1729*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1730*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "A.size1", "A.size2"])
1731*e1fe3e4aSElliott Hughes    vertGlyphVariants = {"A": [("A.size1", 100), ("A.size2", 200)]}
1732*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, vertGlyphVariants=vertGlyphVariants)
1733*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1734*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1735*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1736*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants
1737*e1fe3e4aSElliott Hughes    assert set(mathTable.MathVariants.VertGlyphCoverage.glyphs) == set(
1738*e1fe3e4aSElliott Hughes        vertGlyphVariants.keys()
1739*e1fe3e4aSElliott Hughes    )
1740*e1fe3e4aSElliott Hughes    for glyph, construction in zip(
1741*e1fe3e4aSElliott Hughes        mathTable.MathVariants.VertGlyphCoverage.glyphs,
1742*e1fe3e4aSElliott Hughes        mathTable.MathVariants.VertGlyphConstruction,
1743*e1fe3e4aSElliott Hughes    ):
1744*e1fe3e4aSElliott Hughes        assert [
1745*e1fe3e4aSElliott Hughes            (r.VariantGlyph, r.AdvanceMeasurement)
1746*e1fe3e4aSElliott Hughes            for r in construction.MathGlyphVariantRecord
1747*e1fe3e4aSElliott Hughes        ] == vertGlyphVariants[glyph]
1748*e1fe3e4aSElliott Hughes
1749*e1fe3e4aSElliott Hughes
1750*e1fe3e4aSElliott Hughesdef test_buildMathTable_horizVariants():
1751*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1752*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "A.size1", "A.size2"])
1753*e1fe3e4aSElliott Hughes    horizGlyphVariants = {"A": [("A.size1", 100), ("A.size2", 200)]}
1754*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, horizGlyphVariants=horizGlyphVariants)
1755*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1756*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1757*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1758*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants
1759*e1fe3e4aSElliott Hughes    assert set(mathTable.MathVariants.HorizGlyphCoverage.glyphs) == set(
1760*e1fe3e4aSElliott Hughes        horizGlyphVariants.keys()
1761*e1fe3e4aSElliott Hughes    )
1762*e1fe3e4aSElliott Hughes    for glyph, construction in zip(
1763*e1fe3e4aSElliott Hughes        mathTable.MathVariants.HorizGlyphCoverage.glyphs,
1764*e1fe3e4aSElliott Hughes        mathTable.MathVariants.HorizGlyphConstruction,
1765*e1fe3e4aSElliott Hughes    ):
1766*e1fe3e4aSElliott Hughes        assert [
1767*e1fe3e4aSElliott Hughes            (r.VariantGlyph, r.AdvanceMeasurement)
1768*e1fe3e4aSElliott Hughes            for r in construction.MathGlyphVariantRecord
1769*e1fe3e4aSElliott Hughes        ] == horizGlyphVariants[glyph]
1770*e1fe3e4aSElliott Hughes
1771*e1fe3e4aSElliott Hughes
1772*e1fe3e4aSElliott Hughesdef test_buildMathTable_vertAssembly():
1773*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1774*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "A.top", "A.middle", "A.bottom", "A.extender"])
1775*e1fe3e4aSElliott Hughes    vertGlyphAssembly = {
1776*e1fe3e4aSElliott Hughes        "A": [
1777*e1fe3e4aSElliott Hughes            [
1778*e1fe3e4aSElliott Hughes                ("A.bottom", 0, 0, 100, 200),
1779*e1fe3e4aSElliott Hughes                ("A.extender", 1, 50, 50, 100),
1780*e1fe3e4aSElliott Hughes                ("A.middle", 0, 100, 100, 200),
1781*e1fe3e4aSElliott Hughes                ("A.extender", 1, 50, 50, 100),
1782*e1fe3e4aSElliott Hughes                ("A.top", 0, 100, 0, 200),
1783*e1fe3e4aSElliott Hughes            ],
1784*e1fe3e4aSElliott Hughes            10,
1785*e1fe3e4aSElliott Hughes        ],
1786*e1fe3e4aSElliott Hughes    }
1787*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, vertGlyphAssembly=vertGlyphAssembly)
1788*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1789*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1790*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1791*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants
1792*e1fe3e4aSElliott Hughes    assert set(mathTable.MathVariants.VertGlyphCoverage.glyphs) == set(
1793*e1fe3e4aSElliott Hughes        vertGlyphAssembly.keys()
1794*e1fe3e4aSElliott Hughes    )
1795*e1fe3e4aSElliott Hughes    for glyph, construction in zip(
1796*e1fe3e4aSElliott Hughes        mathTable.MathVariants.VertGlyphCoverage.glyphs,
1797*e1fe3e4aSElliott Hughes        mathTable.MathVariants.VertGlyphConstruction,
1798*e1fe3e4aSElliott Hughes    ):
1799*e1fe3e4aSElliott Hughes        assert [
1800*e1fe3e4aSElliott Hughes            [
1801*e1fe3e4aSElliott Hughes                (
1802*e1fe3e4aSElliott Hughes                    r.glyph,
1803*e1fe3e4aSElliott Hughes                    r.PartFlags,
1804*e1fe3e4aSElliott Hughes                    r.StartConnectorLength,
1805*e1fe3e4aSElliott Hughes                    r.EndConnectorLength,
1806*e1fe3e4aSElliott Hughes                    r.FullAdvance,
1807*e1fe3e4aSElliott Hughes                )
1808*e1fe3e4aSElliott Hughes                for r in construction.GlyphAssembly.PartRecords
1809*e1fe3e4aSElliott Hughes            ],
1810*e1fe3e4aSElliott Hughes            construction.GlyphAssembly.ItalicsCorrection.Value,
1811*e1fe3e4aSElliott Hughes        ] == vertGlyphAssembly[glyph]
1812*e1fe3e4aSElliott Hughes
1813*e1fe3e4aSElliott Hughes
1814*e1fe3e4aSElliott Hughesdef test_buildMathTable_horizAssembly():
1815*e1fe3e4aSElliott Hughes    ttFont = ttLib.TTFont()
1816*e1fe3e4aSElliott Hughes    ttFont.setGlyphOrder(["A", "A.top", "A.middle", "A.bottom", "A.extender"])
1817*e1fe3e4aSElliott Hughes    horizGlyphAssembly = {
1818*e1fe3e4aSElliott Hughes        "A": [
1819*e1fe3e4aSElliott Hughes            [
1820*e1fe3e4aSElliott Hughes                ("A.bottom", 0, 0, 100, 200),
1821*e1fe3e4aSElliott Hughes                ("A.extender", 1, 50, 50, 100),
1822*e1fe3e4aSElliott Hughes                ("A.middle", 0, 100, 100, 200),
1823*e1fe3e4aSElliott Hughes                ("A.extender", 1, 50, 50, 100),
1824*e1fe3e4aSElliott Hughes                ("A.top", 0, 100, 0, 200),
1825*e1fe3e4aSElliott Hughes            ],
1826*e1fe3e4aSElliott Hughes            10,
1827*e1fe3e4aSElliott Hughes        ],
1828*e1fe3e4aSElliott Hughes    }
1829*e1fe3e4aSElliott Hughes    builder.buildMathTable(ttFont, horizGlyphAssembly=horizGlyphAssembly)
1830*e1fe3e4aSElliott Hughes    mathTable = ttFont["MATH"].table
1831*e1fe3e4aSElliott Hughes    assert mathTable.MathConstants is None
1832*e1fe3e4aSElliott Hughes    assert mathTable.MathGlyphInfo is None
1833*e1fe3e4aSElliott Hughes    assert mathTable.MathVariants
1834*e1fe3e4aSElliott Hughes    assert set(mathTable.MathVariants.HorizGlyphCoverage.glyphs) == set(
1835*e1fe3e4aSElliott Hughes        horizGlyphAssembly.keys()
1836*e1fe3e4aSElliott Hughes    )
1837*e1fe3e4aSElliott Hughes    for glyph, construction in zip(
1838*e1fe3e4aSElliott Hughes        mathTable.MathVariants.HorizGlyphCoverage.glyphs,
1839*e1fe3e4aSElliott Hughes        mathTable.MathVariants.HorizGlyphConstruction,
1840*e1fe3e4aSElliott Hughes    ):
1841*e1fe3e4aSElliott Hughes        assert [
1842*e1fe3e4aSElliott Hughes            [
1843*e1fe3e4aSElliott Hughes                (
1844*e1fe3e4aSElliott Hughes                    r.glyph,
1845*e1fe3e4aSElliott Hughes                    r.PartFlags,
1846*e1fe3e4aSElliott Hughes                    r.StartConnectorLength,
1847*e1fe3e4aSElliott Hughes                    r.EndConnectorLength,
1848*e1fe3e4aSElliott Hughes                    r.FullAdvance,
1849*e1fe3e4aSElliott Hughes                )
1850*e1fe3e4aSElliott Hughes                for r in construction.GlyphAssembly.PartRecords
1851*e1fe3e4aSElliott Hughes            ],
1852*e1fe3e4aSElliott Hughes            construction.GlyphAssembly.ItalicsCorrection.Value,
1853*e1fe3e4aSElliott Hughes        ] == horizGlyphAssembly[glyph]
1854*e1fe3e4aSElliott Hughes
1855*e1fe3e4aSElliott Hughes
1856*e1fe3e4aSElliott Hughesclass ChainContextualRulesetTest(object):
1857*e1fe3e4aSElliott Hughes    def test_makeRulesets(self):
1858*e1fe3e4aSElliott Hughes        font = ttLib.TTFont()
1859*e1fe3e4aSElliott Hughes        font.setGlyphOrder(["a", "b", "c", "d", "A", "B", "C", "D", "E"])
1860*e1fe3e4aSElliott Hughes        sb = builder.ChainContextSubstBuilder(font, None)
1861*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [["a"], ["b"]], [["c"]], [], [None]
1862*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1863*e1fe3e4aSElliott Hughes
1864*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [["a"], ["d"]], [["c"]], [], [None]
1865*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1866*e1fe3e4aSElliott Hughes
1867*e1fe3e4aSElliott Hughes        sb.add_subtable_break(None)
1868*e1fe3e4aSElliott Hughes
1869*e1fe3e4aSElliott Hughes        # Second subtable has some glyph classes
1870*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [["A"]], [["E"]], [], [None]
1871*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1872*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [["A"]], [["C", "D"]], [], [None]
1873*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1874*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [["A", "B"]], [["E"]], [], [None]
1875*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1876*e1fe3e4aSElliott Hughes
1877*e1fe3e4aSElliott Hughes        sb.add_subtable_break(None)
1878*e1fe3e4aSElliott Hughes
1879*e1fe3e4aSElliott Hughes        # Third subtable has no pre/post context
1880*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [], [["E"]], [], [None]
1881*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1882*e1fe3e4aSElliott Hughes        prefix, input_, suffix, lookups = [], [["C", "D"]], [], [None]
1883*e1fe3e4aSElliott Hughes        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
1884*e1fe3e4aSElliott Hughes
1885*e1fe3e4aSElliott Hughes        rulesets = sb.rulesets()
1886*e1fe3e4aSElliott Hughes        assert len(rulesets) == 3
1887*e1fe3e4aSElliott Hughes        assert rulesets[0].hasPrefixOrSuffix
1888*e1fe3e4aSElliott Hughes        assert not rulesets[0].hasAnyGlyphClasses
1889*e1fe3e4aSElliott Hughes        cd = rulesets[0].format2ClassDefs()
1890*e1fe3e4aSElliott Hughes        assert set(cd[0].classes()[1:]) == set([("d",), ("b",), ("a",)])
1891*e1fe3e4aSElliott Hughes        assert set(cd[1].classes()[1:]) == set([("c",)])
1892*e1fe3e4aSElliott Hughes        assert set(cd[2].classes()[1:]) == set()
1893*e1fe3e4aSElliott Hughes
1894*e1fe3e4aSElliott Hughes        assert rulesets[1].hasPrefixOrSuffix
1895*e1fe3e4aSElliott Hughes        assert rulesets[1].hasAnyGlyphClasses
1896*e1fe3e4aSElliott Hughes        assert not rulesets[1].format2ClassDefs()
1897*e1fe3e4aSElliott Hughes
1898*e1fe3e4aSElliott Hughes        assert not rulesets[2].hasPrefixOrSuffix
1899*e1fe3e4aSElliott Hughes        assert rulesets[2].hasAnyGlyphClasses
1900*e1fe3e4aSElliott Hughes        assert rulesets[2].format2ClassDefs()
1901*e1fe3e4aSElliott Hughes        cd = rulesets[2].format2ClassDefs()
1902*e1fe3e4aSElliott Hughes        assert set(cd[0].classes()[1:]) == set()
1903*e1fe3e4aSElliott Hughes        assert set(cd[1].classes()[1:]) == set([("C", "D"), ("E",)])
1904*e1fe3e4aSElliott Hughes        assert set(cd[2].classes()[1:]) == set()
1905*e1fe3e4aSElliott Hughes
1906*e1fe3e4aSElliott Hughes
1907*e1fe3e4aSElliott Hughesif __name__ == "__main__":
1908*e1fe3e4aSElliott Hughes    import sys
1909*e1fe3e4aSElliott Hughes
1910*e1fe3e4aSElliott Hughes    sys.exit(pytest.main(sys.argv))
1911