xref: /aosp_15_r20/external/fonttools/Tests/ttLib/tables/otTables_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML, parseXML, parseXmlInto, FakeFont
2*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import deHexStr, hexStr
3*e1fe3e4aSElliott Hughesfrom fontTools.misc.xmlWriter import XMLWriter
4*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
5*e1fe3e4aSElliott Hughesimport fontTools.ttLib.tables.otTables as otTables
6*e1fe3e4aSElliott Hughesfrom io import StringIO
7*e1fe3e4aSElliott Hughesimport unittest
8*e1fe3e4aSElliott Hughes
9*e1fe3e4aSElliott Hughes
10*e1fe3e4aSElliott Hughesdef makeCoverage(glyphs):
11*e1fe3e4aSElliott Hughes    coverage = otTables.Coverage()
12*e1fe3e4aSElliott Hughes    coverage.glyphs = glyphs
13*e1fe3e4aSElliott Hughes    return coverage
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughes
16*e1fe3e4aSElliott Hughesclass SingleSubstTest(unittest.TestCase):
17*e1fe3e4aSElliott Hughes    def setUp(self):
18*e1fe3e4aSElliott Hughes        self.glyphs = ".notdef A B C D E a b c d e".split()
19*e1fe3e4aSElliott Hughes        self.font = FakeFont(self.glyphs)
20*e1fe3e4aSElliott Hughes
21*e1fe3e4aSElliott Hughes    def test_postRead_format1(self):
22*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
23*e1fe3e4aSElliott Hughes        table.Format = 1
24*e1fe3e4aSElliott Hughes        rawTable = {"Coverage": makeCoverage(["A", "B", "C"]), "DeltaGlyphID": 5}
25*e1fe3e4aSElliott Hughes        table.postRead(rawTable, self.font)
26*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"})
27*e1fe3e4aSElliott Hughes
28*e1fe3e4aSElliott Hughes    def test_postRead_format2(self):
29*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
30*e1fe3e4aSElliott Hughes        table.Format = 2
31*e1fe3e4aSElliott Hughes        rawTable = {
32*e1fe3e4aSElliott Hughes            "Coverage": makeCoverage(["A", "B", "C"]),
33*e1fe3e4aSElliott Hughes            "GlyphCount": 3,
34*e1fe3e4aSElliott Hughes            "Substitute": ["c", "b", "a"],
35*e1fe3e4aSElliott Hughes        }
36*e1fe3e4aSElliott Hughes        table.postRead(rawTable, self.font)
37*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"A": "c", "B": "b", "C": "a"})
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes    def test_postRead_formatUnknown(self):
40*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
41*e1fe3e4aSElliott Hughes        table.Format = 987
42*e1fe3e4aSElliott Hughes        rawTable = {"Coverage": makeCoverage(["A", "B", "C"])}
43*e1fe3e4aSElliott Hughes        self.assertRaises(AssertionError, table.postRead, rawTable, self.font)
44*e1fe3e4aSElliott Hughes
45*e1fe3e4aSElliott Hughes    def test_preWrite_format1(self):
46*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
47*e1fe3e4aSElliott Hughes        table.mapping = {"A": "a", "B": "b", "C": "c"}
48*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
49*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 1)
50*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"])
51*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["DeltaGlyphID"], 5)
52*e1fe3e4aSElliott Hughes
53*e1fe3e4aSElliott Hughes    def test_preWrite_format2(self):
54*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
55*e1fe3e4aSElliott Hughes        table.mapping = {"A": "c", "B": "b", "C": "a"}
56*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
57*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 2)
58*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"])
59*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Substitute"], ["c", "b", "a"])
60*e1fe3e4aSElliott Hughes
61*e1fe3e4aSElliott Hughes    def test_preWrite_emptyMapping(self):
62*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
63*e1fe3e4aSElliott Hughes        table.mapping = {}
64*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
65*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 2)
66*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, [])
67*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Substitute"], [])
68*e1fe3e4aSElliott Hughes
69*e1fe3e4aSElliott Hughes    def test_toXML2(self):
70*e1fe3e4aSElliott Hughes        writer = XMLWriter(StringIO())
71*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
72*e1fe3e4aSElliott Hughes        table.mapping = {"A": "a", "B": "b", "C": "c"}
73*e1fe3e4aSElliott Hughes        table.toXML2(writer, self.font)
74*e1fe3e4aSElliott Hughes        self.assertEqual(
75*e1fe3e4aSElliott Hughes            writer.file.getvalue().splitlines()[1:],
76*e1fe3e4aSElliott Hughes            [
77*e1fe3e4aSElliott Hughes                '<Substitution in="A" out="a"/>',
78*e1fe3e4aSElliott Hughes                '<Substitution in="B" out="b"/>',
79*e1fe3e4aSElliott Hughes                '<Substitution in="C" out="c"/>',
80*e1fe3e4aSElliott Hughes            ],
81*e1fe3e4aSElliott Hughes        )
82*e1fe3e4aSElliott Hughes
83*e1fe3e4aSElliott Hughes    def test_fromXML(self):
84*e1fe3e4aSElliott Hughes        table = otTables.SingleSubst()
85*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
86*e1fe3e4aSElliott Hughes            '<Substitution in="A" out="a"/>'
87*e1fe3e4aSElliott Hughes            '<Substitution in="B" out="b"/>'
88*e1fe3e4aSElliott Hughes            '<Substitution in="C" out="c"/>'
89*e1fe3e4aSElliott Hughes        ):
90*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
91*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"})
92*e1fe3e4aSElliott Hughes
93*e1fe3e4aSElliott Hughes
94*e1fe3e4aSElliott Hughesclass MultipleSubstTest(unittest.TestCase):
95*e1fe3e4aSElliott Hughes    def setUp(self):
96*e1fe3e4aSElliott Hughes        self.glyphs = ".notdef c f i t c_t f_f_i".split()
97*e1fe3e4aSElliott Hughes        self.font = FakeFont(self.glyphs)
98*e1fe3e4aSElliott Hughes
99*e1fe3e4aSElliott Hughes    def test_postRead_format1(self):
100*e1fe3e4aSElliott Hughes        makeSequence = otTables.MultipleSubst.makeSequence_
101*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
102*e1fe3e4aSElliott Hughes        table.Format = 1
103*e1fe3e4aSElliott Hughes        rawTable = {
104*e1fe3e4aSElliott Hughes            "Coverage": makeCoverage(["c_t", "f_f_i"]),
105*e1fe3e4aSElliott Hughes            "Sequence": [makeSequence(["c", "t"]), makeSequence(["f", "f", "i"])],
106*e1fe3e4aSElliott Hughes        }
107*e1fe3e4aSElliott Hughes        table.postRead(rawTable, self.font)
108*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]})
109*e1fe3e4aSElliott Hughes
110*e1fe3e4aSElliott Hughes    def test_postRead_formatUnknown(self):
111*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
112*e1fe3e4aSElliott Hughes        table.Format = 987
113*e1fe3e4aSElliott Hughes        self.assertRaises(AssertionError, table.postRead, {}, self.font)
114*e1fe3e4aSElliott Hughes
115*e1fe3e4aSElliott Hughes    def test_preWrite_format1(self):
116*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
117*e1fe3e4aSElliott Hughes        table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}
118*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
119*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 1)
120*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, ["c_t", "f_f_i"])
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughes    def test_toXML2(self):
123*e1fe3e4aSElliott Hughes        writer = XMLWriter(StringIO())
124*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
125*e1fe3e4aSElliott Hughes        table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}
126*e1fe3e4aSElliott Hughes        table.toXML2(writer, self.font)
127*e1fe3e4aSElliott Hughes        self.assertEqual(
128*e1fe3e4aSElliott Hughes            writer.file.getvalue().splitlines()[1:],
129*e1fe3e4aSElliott Hughes            [
130*e1fe3e4aSElliott Hughes                '<Substitution in="c_t" out="c,t"/>',
131*e1fe3e4aSElliott Hughes                '<Substitution in="f_f_i" out="f,f,i"/>',
132*e1fe3e4aSElliott Hughes            ],
133*e1fe3e4aSElliott Hughes        )
134*e1fe3e4aSElliott Hughes
135*e1fe3e4aSElliott Hughes    def test_fromXML(self):
136*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
137*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
138*e1fe3e4aSElliott Hughes            '<Substitution in="c_t" out="c,t"/>'
139*e1fe3e4aSElliott Hughes            '<Substitution in="f_f_i" out="f,f,i"/>'
140*e1fe3e4aSElliott Hughes        ):
141*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
142*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]})
143*e1fe3e4aSElliott Hughes
144*e1fe3e4aSElliott Hughes    def test_fromXML_oldFormat(self):
145*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
146*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
147*e1fe3e4aSElliott Hughes            "<Coverage>"
148*e1fe3e4aSElliott Hughes            '  <Glyph value="c_t"/>'
149*e1fe3e4aSElliott Hughes            '  <Glyph value="f_f_i"/>'
150*e1fe3e4aSElliott Hughes            "</Coverage>"
151*e1fe3e4aSElliott Hughes            '<Sequence index="0">'
152*e1fe3e4aSElliott Hughes            '  <Substitute index="0" value="c"/>'
153*e1fe3e4aSElliott Hughes            '  <Substitute index="1" value="t"/>'
154*e1fe3e4aSElliott Hughes            "</Sequence>"
155*e1fe3e4aSElliott Hughes            '<Sequence index="1">'
156*e1fe3e4aSElliott Hughes            '  <Substitute index="0" value="f"/>'
157*e1fe3e4aSElliott Hughes            '  <Substitute index="1" value="f"/>'
158*e1fe3e4aSElliott Hughes            '  <Substitute index="2" value="i"/>'
159*e1fe3e4aSElliott Hughes            "</Sequence>"
160*e1fe3e4aSElliott Hughes        ):
161*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
162*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]})
163*e1fe3e4aSElliott Hughes
164*e1fe3e4aSElliott Hughes    def test_fromXML_oldFormat_bug385(self):
165*e1fe3e4aSElliott Hughes        # https://github.com/fonttools/fonttools/issues/385
166*e1fe3e4aSElliott Hughes        table = otTables.MultipleSubst()
167*e1fe3e4aSElliott Hughes        table.Format = 1
168*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
169*e1fe3e4aSElliott Hughes            "<Coverage>"
170*e1fe3e4aSElliott Hughes            '  <Glyph value="o"/>'
171*e1fe3e4aSElliott Hughes            '  <Glyph value="l"/>'
172*e1fe3e4aSElliott Hughes            "</Coverage>"
173*e1fe3e4aSElliott Hughes            "<Sequence>"
174*e1fe3e4aSElliott Hughes            '  <Substitute value="o"/>'
175*e1fe3e4aSElliott Hughes            '  <Substitute value="l"/>'
176*e1fe3e4aSElliott Hughes            '  <Substitute value="o"/>'
177*e1fe3e4aSElliott Hughes            "</Sequence>"
178*e1fe3e4aSElliott Hughes            "<Sequence>"
179*e1fe3e4aSElliott Hughes            '  <Substitute value="o"/>'
180*e1fe3e4aSElliott Hughes            "</Sequence>"
181*e1fe3e4aSElliott Hughes        ):
182*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
183*e1fe3e4aSElliott Hughes        self.assertEqual(table.mapping, {"o": ["o", "l", "o"], "l": ["o"]})
184*e1fe3e4aSElliott Hughes
185*e1fe3e4aSElliott Hughes
186*e1fe3e4aSElliott Hughesclass LigatureSubstTest(unittest.TestCase):
187*e1fe3e4aSElliott Hughes    def setUp(self):
188*e1fe3e4aSElliott Hughes        self.glyphs = ".notdef c f i t c_t f_f f_i f_f_i".split()
189*e1fe3e4aSElliott Hughes        self.font = FakeFont(self.glyphs)
190*e1fe3e4aSElliott Hughes
191*e1fe3e4aSElliott Hughes    def makeLigature(self, s):
192*e1fe3e4aSElliott Hughes        """'ffi' --> Ligature(LigGlyph='f_f_i', Component=['f', 'f', 'i'])"""
193*e1fe3e4aSElliott Hughes        lig = otTables.Ligature()
194*e1fe3e4aSElliott Hughes        lig.Component = list(s)
195*e1fe3e4aSElliott Hughes        lig.LigGlyph = "_".join(lig.Component)
196*e1fe3e4aSElliott Hughes        return lig
197*e1fe3e4aSElliott Hughes
198*e1fe3e4aSElliott Hughes    def makeLigatures(self, s):
199*e1fe3e4aSElliott Hughes        """'ffi fi' --> [otTables.Ligature, otTables.Ligature]"""
200*e1fe3e4aSElliott Hughes        return [self.makeLigature(lig) for lig in s.split()]
201*e1fe3e4aSElliott Hughes
202*e1fe3e4aSElliott Hughes    def test_postRead_format1(self):
203*e1fe3e4aSElliott Hughes        table = otTables.LigatureSubst()
204*e1fe3e4aSElliott Hughes        table.Format = 1
205*e1fe3e4aSElliott Hughes        ligs_c = otTables.LigatureSet()
206*e1fe3e4aSElliott Hughes        ligs_c.Ligature = self.makeLigatures("ct")
207*e1fe3e4aSElliott Hughes        ligs_f = otTables.LigatureSet()
208*e1fe3e4aSElliott Hughes        ligs_f.Ligature = self.makeLigatures("ffi ff fi")
209*e1fe3e4aSElliott Hughes        rawTable = {
210*e1fe3e4aSElliott Hughes            "Coverage": makeCoverage(["c", "f"]),
211*e1fe3e4aSElliott Hughes            "LigatureSet": [ligs_c, ligs_f],
212*e1fe3e4aSElliott Hughes        }
213*e1fe3e4aSElliott Hughes        table.postRead(rawTable, self.font)
214*e1fe3e4aSElliott Hughes        self.assertEqual(set(table.ligatures.keys()), {"c", "f"})
215*e1fe3e4aSElliott Hughes        self.assertEqual(len(table.ligatures["c"]), 1)
216*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["c"][0].LigGlyph, "c_t")
217*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["c"][0].Component, ["c", "t"])
218*e1fe3e4aSElliott Hughes        self.assertEqual(len(table.ligatures["f"]), 3)
219*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][0].LigGlyph, "f_f_i")
220*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][0].Component, ["f", "f", "i"])
221*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][1].LigGlyph, "f_f")
222*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][1].Component, ["f", "f"])
223*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][2].LigGlyph, "f_i")
224*e1fe3e4aSElliott Hughes        self.assertEqual(table.ligatures["f"][2].Component, ["f", "i"])
225*e1fe3e4aSElliott Hughes
226*e1fe3e4aSElliott Hughes    def test_postRead_formatUnknown(self):
227*e1fe3e4aSElliott Hughes        table = otTables.LigatureSubst()
228*e1fe3e4aSElliott Hughes        table.Format = 987
229*e1fe3e4aSElliott Hughes        rawTable = {"Coverage": makeCoverage(["f"])}
230*e1fe3e4aSElliott Hughes        self.assertRaises(AssertionError, table.postRead, rawTable, self.font)
231*e1fe3e4aSElliott Hughes
232*e1fe3e4aSElliott Hughes    def test_preWrite_format1(self):
233*e1fe3e4aSElliott Hughes        table = otTables.LigatureSubst()
234*e1fe3e4aSElliott Hughes        table.ligatures = {
235*e1fe3e4aSElliott Hughes            "c": self.makeLigatures("ct"),
236*e1fe3e4aSElliott Hughes            "f": self.makeLigatures("ffi ff fi"),
237*e1fe3e4aSElliott Hughes        }
238*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
239*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 1)
240*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, ["c", "f"])
241*e1fe3e4aSElliott Hughes        [c, f] = rawTable["LigatureSet"]
242*e1fe3e4aSElliott Hughes        self.assertIsInstance(c, otTables.LigatureSet)
243*e1fe3e4aSElliott Hughes        self.assertIsInstance(f, otTables.LigatureSet)
244*e1fe3e4aSElliott Hughes        [ct] = c.Ligature
245*e1fe3e4aSElliott Hughes        self.assertIsInstance(ct, otTables.Ligature)
246*e1fe3e4aSElliott Hughes        self.assertEqual(ct.LigGlyph, "c_t")
247*e1fe3e4aSElliott Hughes        self.assertEqual(ct.Component, ["c", "t"])
248*e1fe3e4aSElliott Hughes        [ffi, ff, fi] = f.Ligature
249*e1fe3e4aSElliott Hughes        self.assertIsInstance(ffi, otTables.Ligature)
250*e1fe3e4aSElliott Hughes        self.assertEqual(ffi.LigGlyph, "f_f_i")
251*e1fe3e4aSElliott Hughes        self.assertEqual(ffi.Component, ["f", "f", "i"])
252*e1fe3e4aSElliott Hughes        self.assertIsInstance(ff, otTables.Ligature)
253*e1fe3e4aSElliott Hughes        self.assertEqual(ff.LigGlyph, "f_f")
254*e1fe3e4aSElliott Hughes        self.assertEqual(ff.Component, ["f", "f"])
255*e1fe3e4aSElliott Hughes        self.assertIsInstance(fi, otTables.Ligature)
256*e1fe3e4aSElliott Hughes        self.assertEqual(fi.LigGlyph, "f_i")
257*e1fe3e4aSElliott Hughes        self.assertEqual(fi.Component, ["f", "i"])
258*e1fe3e4aSElliott Hughes
259*e1fe3e4aSElliott Hughes    def test_toXML2(self):
260*e1fe3e4aSElliott Hughes        writer = XMLWriter(StringIO())
261*e1fe3e4aSElliott Hughes        table = otTables.LigatureSubst()
262*e1fe3e4aSElliott Hughes        table.ligatures = {
263*e1fe3e4aSElliott Hughes            "c": self.makeLigatures("ct"),
264*e1fe3e4aSElliott Hughes            "f": self.makeLigatures("ffi ff fi"),
265*e1fe3e4aSElliott Hughes        }
266*e1fe3e4aSElliott Hughes        table.toXML2(writer, self.font)
267*e1fe3e4aSElliott Hughes        self.assertEqual(
268*e1fe3e4aSElliott Hughes            writer.file.getvalue().splitlines()[1:],
269*e1fe3e4aSElliott Hughes            [
270*e1fe3e4aSElliott Hughes                '<LigatureSet glyph="c">',
271*e1fe3e4aSElliott Hughes                '  <Ligature components="c,t" glyph="c_t"/>',
272*e1fe3e4aSElliott Hughes                "</LigatureSet>",
273*e1fe3e4aSElliott Hughes                '<LigatureSet glyph="f">',
274*e1fe3e4aSElliott Hughes                '  <Ligature components="f,f,i" glyph="f_f_i"/>',
275*e1fe3e4aSElliott Hughes                '  <Ligature components="f,f" glyph="f_f"/>',
276*e1fe3e4aSElliott Hughes                '  <Ligature components="f,i" glyph="f_i"/>',
277*e1fe3e4aSElliott Hughes                "</LigatureSet>",
278*e1fe3e4aSElliott Hughes            ],
279*e1fe3e4aSElliott Hughes        )
280*e1fe3e4aSElliott Hughes
281*e1fe3e4aSElliott Hughes    def test_fromXML(self):
282*e1fe3e4aSElliott Hughes        table = otTables.LigatureSubst()
283*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
284*e1fe3e4aSElliott Hughes            '<LigatureSet glyph="f">'
285*e1fe3e4aSElliott Hughes            '  <Ligature components="f,f,i" glyph="f_f_i"/>'
286*e1fe3e4aSElliott Hughes            '  <Ligature components="f,f" glyph="f_f"/>'
287*e1fe3e4aSElliott Hughes            "</LigatureSet>"
288*e1fe3e4aSElliott Hughes        ):
289*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
290*e1fe3e4aSElliott Hughes        self.assertEqual(set(table.ligatures.keys()), {"f"})
291*e1fe3e4aSElliott Hughes        [ffi, ff] = table.ligatures["f"]
292*e1fe3e4aSElliott Hughes        self.assertEqual(ffi.LigGlyph, "f_f_i")
293*e1fe3e4aSElliott Hughes        self.assertEqual(ffi.Component, ["f", "f", "i"])
294*e1fe3e4aSElliott Hughes        self.assertEqual(ff.LigGlyph, "f_f")
295*e1fe3e4aSElliott Hughes        self.assertEqual(ff.Component, ["f", "f"])
296*e1fe3e4aSElliott Hughes
297*e1fe3e4aSElliott Hughes
298*e1fe3e4aSElliott Hughesclass AlternateSubstTest(unittest.TestCase):
299*e1fe3e4aSElliott Hughes    def setUp(self):
300*e1fe3e4aSElliott Hughes        self.glyphs = ".notdef G G.alt1 G.alt2 Z Z.fina".split()
301*e1fe3e4aSElliott Hughes        self.font = FakeFont(self.glyphs)
302*e1fe3e4aSElliott Hughes
303*e1fe3e4aSElliott Hughes    def makeAlternateSet(self, s):
304*e1fe3e4aSElliott Hughes        result = otTables.AlternateSet()
305*e1fe3e4aSElliott Hughes        result.Alternate = s.split()
306*e1fe3e4aSElliott Hughes        return result
307*e1fe3e4aSElliott Hughes
308*e1fe3e4aSElliott Hughes    def test_postRead_format1(self):
309*e1fe3e4aSElliott Hughes        table = otTables.AlternateSubst()
310*e1fe3e4aSElliott Hughes        table.Format = 1
311*e1fe3e4aSElliott Hughes        rawTable = {
312*e1fe3e4aSElliott Hughes            "Coverage": makeCoverage(["G", "Z"]),
313*e1fe3e4aSElliott Hughes            "AlternateSet": [
314*e1fe3e4aSElliott Hughes                self.makeAlternateSet("G.alt2 G.alt1"),
315*e1fe3e4aSElliott Hughes                self.makeAlternateSet("Z.fina"),
316*e1fe3e4aSElliott Hughes            ],
317*e1fe3e4aSElliott Hughes        }
318*e1fe3e4aSElliott Hughes        table.postRead(rawTable, self.font)
319*e1fe3e4aSElliott Hughes        self.assertEqual(table.alternates, {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]})
320*e1fe3e4aSElliott Hughes
321*e1fe3e4aSElliott Hughes    def test_postRead_formatUnknown(self):
322*e1fe3e4aSElliott Hughes        table = otTables.AlternateSubst()
323*e1fe3e4aSElliott Hughes        table.Format = 987
324*e1fe3e4aSElliott Hughes        self.assertRaises(AssertionError, table.postRead, {}, self.font)
325*e1fe3e4aSElliott Hughes
326*e1fe3e4aSElliott Hughes    def test_preWrite_format1(self):
327*e1fe3e4aSElliott Hughes        table = otTables.AlternateSubst()
328*e1fe3e4aSElliott Hughes        table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}
329*e1fe3e4aSElliott Hughes        rawTable = table.preWrite(self.font)
330*e1fe3e4aSElliott Hughes        self.assertEqual(table.Format, 1)
331*e1fe3e4aSElliott Hughes        self.assertEqual(rawTable["Coverage"].glyphs, ["G", "Z"])
332*e1fe3e4aSElliott Hughes        [g, z] = rawTable["AlternateSet"]
333*e1fe3e4aSElliott Hughes        self.assertIsInstance(g, otTables.AlternateSet)
334*e1fe3e4aSElliott Hughes        self.assertEqual(g.Alternate, ["G.alt2", "G.alt1"])
335*e1fe3e4aSElliott Hughes        self.assertIsInstance(z, otTables.AlternateSet)
336*e1fe3e4aSElliott Hughes        self.assertEqual(z.Alternate, ["Z.fina"])
337*e1fe3e4aSElliott Hughes
338*e1fe3e4aSElliott Hughes    def test_toXML2(self):
339*e1fe3e4aSElliott Hughes        writer = XMLWriter(StringIO())
340*e1fe3e4aSElliott Hughes        table = otTables.AlternateSubst()
341*e1fe3e4aSElliott Hughes        table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}
342*e1fe3e4aSElliott Hughes        table.toXML2(writer, self.font)
343*e1fe3e4aSElliott Hughes        self.assertEqual(
344*e1fe3e4aSElliott Hughes            writer.file.getvalue().splitlines()[1:],
345*e1fe3e4aSElliott Hughes            [
346*e1fe3e4aSElliott Hughes                '<AlternateSet glyph="G">',
347*e1fe3e4aSElliott Hughes                '  <Alternate glyph="G.alt2"/>',
348*e1fe3e4aSElliott Hughes                '  <Alternate glyph="G.alt1"/>',
349*e1fe3e4aSElliott Hughes                "</AlternateSet>",
350*e1fe3e4aSElliott Hughes                '<AlternateSet glyph="Z">',
351*e1fe3e4aSElliott Hughes                '  <Alternate glyph="Z.fina"/>',
352*e1fe3e4aSElliott Hughes                "</AlternateSet>",
353*e1fe3e4aSElliott Hughes            ],
354*e1fe3e4aSElliott Hughes        )
355*e1fe3e4aSElliott Hughes
356*e1fe3e4aSElliott Hughes    def test_fromXML(self):
357*e1fe3e4aSElliott Hughes        table = otTables.AlternateSubst()
358*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(
359*e1fe3e4aSElliott Hughes            '<AlternateSet glyph="G">'
360*e1fe3e4aSElliott Hughes            '  <Alternate glyph="G.alt2"/>'
361*e1fe3e4aSElliott Hughes            '  <Alternate glyph="G.alt1"/>'
362*e1fe3e4aSElliott Hughes            "</AlternateSet>"
363*e1fe3e4aSElliott Hughes            '<AlternateSet glyph="Z">'
364*e1fe3e4aSElliott Hughes            '  <Alternate glyph="Z.fina"/>'
365*e1fe3e4aSElliott Hughes            "</AlternateSet>"
366*e1fe3e4aSElliott Hughes        ):
367*e1fe3e4aSElliott Hughes            table.fromXML(name, attrs, content, self.font)
368*e1fe3e4aSElliott Hughes        self.assertEqual(table.alternates, {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]})
369*e1fe3e4aSElliott Hughes
370*e1fe3e4aSElliott Hughes
371*e1fe3e4aSElliott Hughesclass RearrangementMorphActionTest(unittest.TestCase):
372*e1fe3e4aSElliott Hughes    def setUp(self):
373*e1fe3e4aSElliott Hughes        self.font = FakeFont([".notdef", "A", "B", "C"])
374*e1fe3e4aSElliott Hughes
375*e1fe3e4aSElliott Hughes    def testCompile(self):
376*e1fe3e4aSElliott Hughes        r = otTables.RearrangementMorphAction()
377*e1fe3e4aSElliott Hughes        r.NewState = 0x1234
378*e1fe3e4aSElliott Hughes        r.MarkFirst = r.DontAdvance = r.MarkLast = True
379*e1fe3e4aSElliott Hughes        r.ReservedFlags, r.Verb = 0x1FF0, 0xD
380*e1fe3e4aSElliott Hughes        writer = OTTableWriter()
381*e1fe3e4aSElliott Hughes        r.compile(writer, self.font, actionIndex=None)
382*e1fe3e4aSElliott Hughes        self.assertEqual(hexStr(writer.getAllData()), "1234fffd")
383*e1fe3e4aSElliott Hughes
384*e1fe3e4aSElliott Hughes    def testCompileActions(self):
385*e1fe3e4aSElliott Hughes        act = otTables.RearrangementMorphAction()
386*e1fe3e4aSElliott Hughes        self.assertEqual(act.compileActions(self.font, []), (None, None))
387*e1fe3e4aSElliott Hughes
388*e1fe3e4aSElliott Hughes    def testDecompileToXML(self):
389*e1fe3e4aSElliott Hughes        r = otTables.RearrangementMorphAction()
390*e1fe3e4aSElliott Hughes        r.decompile(OTTableReader(deHexStr("1234fffd")), self.font, actionReader=None)
391*e1fe3e4aSElliott Hughes        toXML = lambda w, f: r.toXML(w, f, {"Test": "Foo"}, "Transition")
392*e1fe3e4aSElliott Hughes        self.assertEqual(
393*e1fe3e4aSElliott Hughes            getXML(toXML, self.font),
394*e1fe3e4aSElliott Hughes            [
395*e1fe3e4aSElliott Hughes                '<Transition Test="Foo">',
396*e1fe3e4aSElliott Hughes                '  <NewState value="4660"/>',  # 0x1234 = 4660
397*e1fe3e4aSElliott Hughes                '  <Flags value="MarkFirst,DontAdvance,MarkLast"/>',
398*e1fe3e4aSElliott Hughes                '  <ReservedFlags value="0x1FF0"/>',
399*e1fe3e4aSElliott Hughes                '  <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->',
400*e1fe3e4aSElliott Hughes                "</Transition>",
401*e1fe3e4aSElliott Hughes            ],
402*e1fe3e4aSElliott Hughes        )
403*e1fe3e4aSElliott Hughes
404*e1fe3e4aSElliott Hughes
405*e1fe3e4aSElliott Hughesclass ContextualMorphActionTest(unittest.TestCase):
406*e1fe3e4aSElliott Hughes    def setUp(self):
407*e1fe3e4aSElliott Hughes        self.font = FakeFont([".notdef", "A", "B", "C"])
408*e1fe3e4aSElliott Hughes
409*e1fe3e4aSElliott Hughes    def testCompile(self):
410*e1fe3e4aSElliott Hughes        a = otTables.ContextualMorphAction()
411*e1fe3e4aSElliott Hughes        a.NewState = 0x1234
412*e1fe3e4aSElliott Hughes        a.SetMark, a.DontAdvance, a.ReservedFlags = True, True, 0x3117
413*e1fe3e4aSElliott Hughes        a.MarkIndex, a.CurrentIndex = 0xDEAD, 0xBEEF
414*e1fe3e4aSElliott Hughes        writer = OTTableWriter()
415*e1fe3e4aSElliott Hughes        a.compile(writer, self.font, actionIndex=None)
416*e1fe3e4aSElliott Hughes        self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef")
417*e1fe3e4aSElliott Hughes
418*e1fe3e4aSElliott Hughes    def testCompileActions(self):
419*e1fe3e4aSElliott Hughes        act = otTables.ContextualMorphAction()
420*e1fe3e4aSElliott Hughes        self.assertEqual(act.compileActions(self.font, []), (None, None))
421*e1fe3e4aSElliott Hughes
422*e1fe3e4aSElliott Hughes    def testDecompileToXML(self):
423*e1fe3e4aSElliott Hughes        a = otTables.ContextualMorphAction()
424*e1fe3e4aSElliott Hughes        a.decompile(
425*e1fe3e4aSElliott Hughes            OTTableReader(deHexStr("1234f117deadbeef")), self.font, actionReader=None
426*e1fe3e4aSElliott Hughes        )
427*e1fe3e4aSElliott Hughes        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
428*e1fe3e4aSElliott Hughes        self.assertEqual(
429*e1fe3e4aSElliott Hughes            getXML(toXML, self.font),
430*e1fe3e4aSElliott Hughes            [
431*e1fe3e4aSElliott Hughes                '<Transition Test="Foo">',
432*e1fe3e4aSElliott Hughes                '  <NewState value="4660"/>',  # 0x1234 = 4660
433*e1fe3e4aSElliott Hughes                '  <Flags value="SetMark,DontAdvance"/>',
434*e1fe3e4aSElliott Hughes                '  <ReservedFlags value="0x3117"/>',
435*e1fe3e4aSElliott Hughes                '  <MarkIndex value="57005"/>',  # 0xDEAD = 57005
436*e1fe3e4aSElliott Hughes                '  <CurrentIndex value="48879"/>',  # 0xBEEF = 48879
437*e1fe3e4aSElliott Hughes                "</Transition>",
438*e1fe3e4aSElliott Hughes            ],
439*e1fe3e4aSElliott Hughes        )
440*e1fe3e4aSElliott Hughes
441*e1fe3e4aSElliott Hughes
442*e1fe3e4aSElliott Hughesclass LigatureMorphActionTest(unittest.TestCase):
443*e1fe3e4aSElliott Hughes    def setUp(self):
444*e1fe3e4aSElliott Hughes        self.font = FakeFont([".notdef", "A", "B", "C"])
445*e1fe3e4aSElliott Hughes
446*e1fe3e4aSElliott Hughes    def testDecompileToXML(self):
447*e1fe3e4aSElliott Hughes        a = otTables.LigatureMorphAction()
448*e1fe3e4aSElliott Hughes        actionReader = OTTableReader(deHexStr("DEADBEEF 7FFFFFFE 80000003"))
449*e1fe3e4aSElliott Hughes        a.decompile(OTTableReader(deHexStr("1234FAB30001")), self.font, actionReader)
450*e1fe3e4aSElliott Hughes        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
451*e1fe3e4aSElliott Hughes        self.assertEqual(
452*e1fe3e4aSElliott Hughes            getXML(toXML, self.font),
453*e1fe3e4aSElliott Hughes            [
454*e1fe3e4aSElliott Hughes                '<Transition Test="Foo">',
455*e1fe3e4aSElliott Hughes                '  <NewState value="4660"/>',  # 0x1234 = 4660
456*e1fe3e4aSElliott Hughes                '  <Flags value="SetComponent,DontAdvance"/>',
457*e1fe3e4aSElliott Hughes                '  <ReservedFlags value="0x1AB3"/>',
458*e1fe3e4aSElliott Hughes                '  <Action GlyphIndexDelta="-2" Flags="Store"/>',
459*e1fe3e4aSElliott Hughes                '  <Action GlyphIndexDelta="3"/>',
460*e1fe3e4aSElliott Hughes                "</Transition>",
461*e1fe3e4aSElliott Hughes            ],
462*e1fe3e4aSElliott Hughes        )
463*e1fe3e4aSElliott Hughes
464*e1fe3e4aSElliott Hughes    def testCompileActions_empty(self):
465*e1fe3e4aSElliott Hughes        act = otTables.LigatureMorphAction()
466*e1fe3e4aSElliott Hughes        actions, actionIndex = act.compileActions(self.font, [])
467*e1fe3e4aSElliott Hughes        self.assertEqual(actions, b"")
468*e1fe3e4aSElliott Hughes        self.assertEqual(actionIndex, {})
469*e1fe3e4aSElliott Hughes
470*e1fe3e4aSElliott Hughes    def testCompileActions_shouldShareSubsequences(self):
471*e1fe3e4aSElliott Hughes        state = otTables.AATState()
472*e1fe3e4aSElliott Hughes        t = state.Transitions = {i: otTables.LigatureMorphAction() for i in range(3)}
473*e1fe3e4aSElliott Hughes        ligs = [otTables.LigAction() for _ in range(3)]
474*e1fe3e4aSElliott Hughes        for i, lig in enumerate(ligs):
475*e1fe3e4aSElliott Hughes            lig.GlyphIndexDelta = i
476*e1fe3e4aSElliott Hughes        t[0].Actions = ligs[1:2]
477*e1fe3e4aSElliott Hughes        t[1].Actions = ligs[0:3]
478*e1fe3e4aSElliott Hughes        t[2].Actions = ligs[1:3]
479*e1fe3e4aSElliott Hughes        actions, actionIndex = t[0].compileActions(self.font, [state])
480*e1fe3e4aSElliott Hughes        self.assertEqual(actions, deHexStr("00000000 00000001 80000002 80000001"))
481*e1fe3e4aSElliott Hughes        self.assertEqual(
482*e1fe3e4aSElliott Hughes            actionIndex,
483*e1fe3e4aSElliott Hughes            {
484*e1fe3e4aSElliott Hughes                deHexStr("00000000 00000001 80000002"): 0,
485*e1fe3e4aSElliott Hughes                deHexStr("00000001 80000002"): 1,
486*e1fe3e4aSElliott Hughes                deHexStr("80000002"): 2,
487*e1fe3e4aSElliott Hughes                deHexStr("80000001"): 3,
488*e1fe3e4aSElliott Hughes            },
489*e1fe3e4aSElliott Hughes        )
490*e1fe3e4aSElliott Hughes
491*e1fe3e4aSElliott Hughes
492*e1fe3e4aSElliott Hughesclass InsertionMorphActionTest(unittest.TestCase):
493*e1fe3e4aSElliott Hughes    MORPH_ACTION_XML = [
494*e1fe3e4aSElliott Hughes        '<Transition Test="Foo">',
495*e1fe3e4aSElliott Hughes        '  <NewState value="4660"/>',  # 0x1234 = 4660
496*e1fe3e4aSElliott Hughes        '  <Flags value="SetMark,DontAdvance,CurrentIsKashidaLike,'
497*e1fe3e4aSElliott Hughes        'MarkedIsKashidaLike,CurrentInsertBefore,MarkedInsertBefore"/>',
498*e1fe3e4aSElliott Hughes        '  <CurrentInsertionAction glyph="B"/>',
499*e1fe3e4aSElliott Hughes        '  <CurrentInsertionAction glyph="C"/>',
500*e1fe3e4aSElliott Hughes        '  <MarkedInsertionAction glyph="B"/>',
501*e1fe3e4aSElliott Hughes        '  <MarkedInsertionAction glyph="A"/>',
502*e1fe3e4aSElliott Hughes        '  <MarkedInsertionAction glyph="D"/>',
503*e1fe3e4aSElliott Hughes        "</Transition>",
504*e1fe3e4aSElliott Hughes    ]
505*e1fe3e4aSElliott Hughes
506*e1fe3e4aSElliott Hughes    def setUp(self):
507*e1fe3e4aSElliott Hughes        self.font = FakeFont([".notdef", "A", "B", "C", "D"])
508*e1fe3e4aSElliott Hughes        self.maxDiff = None
509*e1fe3e4aSElliott Hughes
510*e1fe3e4aSElliott Hughes    def testDecompileToXML(self):
511*e1fe3e4aSElliott Hughes        a = otTables.InsertionMorphAction()
512*e1fe3e4aSElliott Hughes        actionReader = OTTableReader(
513*e1fe3e4aSElliott Hughes            deHexStr("DEAD BEEF 0002 0001 0004 0002 0003 DEAD BEEF")
514*e1fe3e4aSElliott Hughes        )
515*e1fe3e4aSElliott Hughes        a.decompile(
516*e1fe3e4aSElliott Hughes            OTTableReader(deHexStr("1234 FC43 0005 0002")), self.font, actionReader
517*e1fe3e4aSElliott Hughes        )
518*e1fe3e4aSElliott Hughes        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
519*e1fe3e4aSElliott Hughes        self.assertEqual(getXML(toXML, self.font), self.MORPH_ACTION_XML)
520*e1fe3e4aSElliott Hughes
521*e1fe3e4aSElliott Hughes    def testCompileFromXML(self):
522*e1fe3e4aSElliott Hughes        a = otTables.InsertionMorphAction()
523*e1fe3e4aSElliott Hughes        for name, attrs, content in parseXML(self.MORPH_ACTION_XML):
524*e1fe3e4aSElliott Hughes            a.fromXML(name, attrs, content, self.font)
525*e1fe3e4aSElliott Hughes        writer = OTTableWriter()
526*e1fe3e4aSElliott Hughes        a.compile(
527*e1fe3e4aSElliott Hughes            writer,
528*e1fe3e4aSElliott Hughes            self.font,
529*e1fe3e4aSElliott Hughes            actionIndex={("B", "C"): 9, ("B", "A", "D"): 7},
530*e1fe3e4aSElliott Hughes        )
531*e1fe3e4aSElliott Hughes        self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007")
532*e1fe3e4aSElliott Hughes
533*e1fe3e4aSElliott Hughes    def testCompileActions_empty(self):
534*e1fe3e4aSElliott Hughes        act = otTables.InsertionMorphAction()
535*e1fe3e4aSElliott Hughes        actions, actionIndex = act.compileActions(self.font, [])
536*e1fe3e4aSElliott Hughes        self.assertEqual(actions, b"")
537*e1fe3e4aSElliott Hughes        self.assertEqual(actionIndex, {})
538*e1fe3e4aSElliott Hughes
539*e1fe3e4aSElliott Hughes    def testCompileActions_shouldShareSubsequences(self):
540*e1fe3e4aSElliott Hughes        state = otTables.AATState()
541*e1fe3e4aSElliott Hughes        t = state.Transitions = {i: otTables.InsertionMorphAction() for i in range(3)}
542*e1fe3e4aSElliott Hughes        t[1].CurrentInsertionAction = []
543*e1fe3e4aSElliott Hughes        t[0].MarkedInsertionAction = ["A"]
544*e1fe3e4aSElliott Hughes        t[1].CurrentInsertionAction = ["C", "D"]
545*e1fe3e4aSElliott Hughes        t[1].MarkedInsertionAction = ["B"]
546*e1fe3e4aSElliott Hughes        t[2].CurrentInsertionAction = ["B", "C", "D"]
547*e1fe3e4aSElliott Hughes        t[2].MarkedInsertionAction = ["C", "D"]
548*e1fe3e4aSElliott Hughes        actions, actionIndex = t[0].compileActions(self.font, [state])
549*e1fe3e4aSElliott Hughes        self.assertEqual(actions, deHexStr("0002 0003 0004 0001"))
550*e1fe3e4aSElliott Hughes        self.assertEqual(
551*e1fe3e4aSElliott Hughes            actionIndex,
552*e1fe3e4aSElliott Hughes            {
553*e1fe3e4aSElliott Hughes                ("A",): 3,
554*e1fe3e4aSElliott Hughes                ("B",): 0,
555*e1fe3e4aSElliott Hughes                ("B", "C"): 0,
556*e1fe3e4aSElliott Hughes                ("B", "C", "D"): 0,
557*e1fe3e4aSElliott Hughes                ("C",): 1,
558*e1fe3e4aSElliott Hughes                ("C", "D"): 1,
559*e1fe3e4aSElliott Hughes                ("D",): 2,
560*e1fe3e4aSElliott Hughes            },
561*e1fe3e4aSElliott Hughes        )
562*e1fe3e4aSElliott Hughes
563*e1fe3e4aSElliott Hughes
564*e1fe3e4aSElliott Hughesclass SplitMultipleSubstTest:
565*e1fe3e4aSElliott Hughes    def overflow(self, itemName, itemRecord):
566*e1fe3e4aSElliott Hughes        from fontTools.otlLib.builder import buildMultipleSubstSubtable
567*e1fe3e4aSElliott Hughes        from fontTools.ttLib.tables.otBase import OverflowErrorRecord
568*e1fe3e4aSElliott Hughes
569*e1fe3e4aSElliott Hughes        oldSubTable = buildMultipleSubstSubtable(
570*e1fe3e4aSElliott Hughes            {"e": 1, "a": 2, "b": 3, "c": 4, "d": 5}
571*e1fe3e4aSElliott Hughes        )
572*e1fe3e4aSElliott Hughes        newSubTable = otTables.MultipleSubst()
573*e1fe3e4aSElliott Hughes
574*e1fe3e4aSElliott Hughes        ok = otTables.splitMultipleSubst(
575*e1fe3e4aSElliott Hughes            oldSubTable,
576*e1fe3e4aSElliott Hughes            newSubTable,
577*e1fe3e4aSElliott Hughes            OverflowErrorRecord((None, None, None, itemName, itemRecord)),
578*e1fe3e4aSElliott Hughes        )
579*e1fe3e4aSElliott Hughes
580*e1fe3e4aSElliott Hughes        assert ok
581*e1fe3e4aSElliott Hughes        return oldSubTable.mapping, newSubTable.mapping
582*e1fe3e4aSElliott Hughes
583*e1fe3e4aSElliott Hughes    def test_Coverage(self):
584*e1fe3e4aSElliott Hughes        oldMapping, newMapping = self.overflow("Coverage", None)
585*e1fe3e4aSElliott Hughes        assert oldMapping == {"a": 2, "b": 3}
586*e1fe3e4aSElliott Hughes        assert newMapping == {"c": 4, "d": 5, "e": 1}
587*e1fe3e4aSElliott Hughes
588*e1fe3e4aSElliott Hughes    def test_RangeRecord(self):
589*e1fe3e4aSElliott Hughes        oldMapping, newMapping = self.overflow("RangeRecord", None)
590*e1fe3e4aSElliott Hughes        assert oldMapping == {"a": 2, "b": 3}
591*e1fe3e4aSElliott Hughes        assert newMapping == {"c": 4, "d": 5, "e": 1}
592*e1fe3e4aSElliott Hughes
593*e1fe3e4aSElliott Hughes    def test_Sequence(self):
594*e1fe3e4aSElliott Hughes        oldMapping, newMapping = self.overflow("Sequence", 4)
595*e1fe3e4aSElliott Hughes        assert oldMapping == {"a": 2, "b": 3, "c": 4}
596*e1fe3e4aSElliott Hughes        assert newMapping == {"d": 5, "e": 1}
597*e1fe3e4aSElliott Hughes
598*e1fe3e4aSElliott Hughes
599*e1fe3e4aSElliott Hughesdef test_splitMarkBasePos():
600*e1fe3e4aSElliott Hughes    from fontTools.otlLib.builder import buildAnchor, buildMarkBasePosSubtable
601*e1fe3e4aSElliott Hughes
602*e1fe3e4aSElliott Hughes    marks = {
603*e1fe3e4aSElliott Hughes        "acutecomb": (0, buildAnchor(0, 600)),
604*e1fe3e4aSElliott Hughes        "gravecomb": (0, buildAnchor(0, 590)),
605*e1fe3e4aSElliott Hughes        "cedillacomb": (1, buildAnchor(0, 0)),
606*e1fe3e4aSElliott Hughes    }
607*e1fe3e4aSElliott Hughes    bases = {
608*e1fe3e4aSElliott Hughes        "a": {
609*e1fe3e4aSElliott Hughes            0: buildAnchor(350, 500),
610*e1fe3e4aSElliott Hughes            1: None,
611*e1fe3e4aSElliott Hughes        },
612*e1fe3e4aSElliott Hughes        "c": {
613*e1fe3e4aSElliott Hughes            0: buildAnchor(300, 700),
614*e1fe3e4aSElliott Hughes            1: buildAnchor(300, 0),
615*e1fe3e4aSElliott Hughes        },
616*e1fe3e4aSElliott Hughes    }
617*e1fe3e4aSElliott Hughes    glyphOrder = ["a", "c", "acutecomb", "gravecomb", "cedillacomb"]
618*e1fe3e4aSElliott Hughes    glyphMap = {g: i for i, g in enumerate(glyphOrder)}
619*e1fe3e4aSElliott Hughes
620*e1fe3e4aSElliott Hughes    oldSubTable = buildMarkBasePosSubtable(marks, bases, glyphMap)
621*e1fe3e4aSElliott Hughes    newSubTable = otTables.MarkBasePos()
622*e1fe3e4aSElliott Hughes
623*e1fe3e4aSElliott Hughes    ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None)
624*e1fe3e4aSElliott Hughes
625*e1fe3e4aSElliott Hughes    assert ok
626*e1fe3e4aSElliott Hughes
627*e1fe3e4aSElliott Hughes    assert getXML(oldSubTable.toXML) == [
628*e1fe3e4aSElliott Hughes        '<MarkBasePos Format="1">',
629*e1fe3e4aSElliott Hughes        "  <MarkCoverage>",
630*e1fe3e4aSElliott Hughes        '    <Glyph value="acutecomb"/>',
631*e1fe3e4aSElliott Hughes        '    <Glyph value="gravecomb"/>',
632*e1fe3e4aSElliott Hughes        "  </MarkCoverage>",
633*e1fe3e4aSElliott Hughes        "  <BaseCoverage>",
634*e1fe3e4aSElliott Hughes        '    <Glyph value="a"/>',
635*e1fe3e4aSElliott Hughes        '    <Glyph value="c"/>',
636*e1fe3e4aSElliott Hughes        "  </BaseCoverage>",
637*e1fe3e4aSElliott Hughes        "  <!-- ClassCount=1 -->",
638*e1fe3e4aSElliott Hughes        "  <MarkArray>",
639*e1fe3e4aSElliott Hughes        "    <!-- MarkCount=2 -->",
640*e1fe3e4aSElliott Hughes        '    <MarkRecord index="0">',
641*e1fe3e4aSElliott Hughes        '      <Class value="0"/>',
642*e1fe3e4aSElliott Hughes        '      <MarkAnchor Format="1">',
643*e1fe3e4aSElliott Hughes        '        <XCoordinate value="0"/>',
644*e1fe3e4aSElliott Hughes        '        <YCoordinate value="600"/>',
645*e1fe3e4aSElliott Hughes        "      </MarkAnchor>",
646*e1fe3e4aSElliott Hughes        "    </MarkRecord>",
647*e1fe3e4aSElliott Hughes        '    <MarkRecord index="1">',
648*e1fe3e4aSElliott Hughes        '      <Class value="0"/>',
649*e1fe3e4aSElliott Hughes        '      <MarkAnchor Format="1">',
650*e1fe3e4aSElliott Hughes        '        <XCoordinate value="0"/>',
651*e1fe3e4aSElliott Hughes        '        <YCoordinate value="590"/>',
652*e1fe3e4aSElliott Hughes        "      </MarkAnchor>",
653*e1fe3e4aSElliott Hughes        "    </MarkRecord>",
654*e1fe3e4aSElliott Hughes        "  </MarkArray>",
655*e1fe3e4aSElliott Hughes        "  <BaseArray>",
656*e1fe3e4aSElliott Hughes        "    <!-- BaseCount=2 -->",
657*e1fe3e4aSElliott Hughes        '    <BaseRecord index="0">',
658*e1fe3e4aSElliott Hughes        '      <BaseAnchor index="0" Format="1">',
659*e1fe3e4aSElliott Hughes        '        <XCoordinate value="350"/>',
660*e1fe3e4aSElliott Hughes        '        <YCoordinate value="500"/>',
661*e1fe3e4aSElliott Hughes        "      </BaseAnchor>",
662*e1fe3e4aSElliott Hughes        "    </BaseRecord>",
663*e1fe3e4aSElliott Hughes        '    <BaseRecord index="1">',
664*e1fe3e4aSElliott Hughes        '      <BaseAnchor index="0" Format="1">',
665*e1fe3e4aSElliott Hughes        '        <XCoordinate value="300"/>',
666*e1fe3e4aSElliott Hughes        '        <YCoordinate value="700"/>',
667*e1fe3e4aSElliott Hughes        "      </BaseAnchor>",
668*e1fe3e4aSElliott Hughes        "    </BaseRecord>",
669*e1fe3e4aSElliott Hughes        "  </BaseArray>",
670*e1fe3e4aSElliott Hughes        "</MarkBasePos>",
671*e1fe3e4aSElliott Hughes    ]
672*e1fe3e4aSElliott Hughes
673*e1fe3e4aSElliott Hughes    assert getXML(newSubTable.toXML) == [
674*e1fe3e4aSElliott Hughes        '<MarkBasePos Format="1">',
675*e1fe3e4aSElliott Hughes        "  <MarkCoverage>",
676*e1fe3e4aSElliott Hughes        '    <Glyph value="cedillacomb"/>',
677*e1fe3e4aSElliott Hughes        "  </MarkCoverage>",
678*e1fe3e4aSElliott Hughes        "  <BaseCoverage>",
679*e1fe3e4aSElliott Hughes        '    <Glyph value="a"/>',
680*e1fe3e4aSElliott Hughes        '    <Glyph value="c"/>',
681*e1fe3e4aSElliott Hughes        "  </BaseCoverage>",
682*e1fe3e4aSElliott Hughes        "  <!-- ClassCount=1 -->",
683*e1fe3e4aSElliott Hughes        "  <MarkArray>",
684*e1fe3e4aSElliott Hughes        "    <!-- MarkCount=1 -->",
685*e1fe3e4aSElliott Hughes        '    <MarkRecord index="0">',
686*e1fe3e4aSElliott Hughes        '      <Class value="0"/>',
687*e1fe3e4aSElliott Hughes        '      <MarkAnchor Format="1">',
688*e1fe3e4aSElliott Hughes        '        <XCoordinate value="0"/>',
689*e1fe3e4aSElliott Hughes        '        <YCoordinate value="0"/>',
690*e1fe3e4aSElliott Hughes        "      </MarkAnchor>",
691*e1fe3e4aSElliott Hughes        "    </MarkRecord>",
692*e1fe3e4aSElliott Hughes        "  </MarkArray>",
693*e1fe3e4aSElliott Hughes        "  <BaseArray>",
694*e1fe3e4aSElliott Hughes        "    <!-- BaseCount=2 -->",
695*e1fe3e4aSElliott Hughes        '    <BaseRecord index="0">',
696*e1fe3e4aSElliott Hughes        '      <BaseAnchor index="0" empty="1"/>',
697*e1fe3e4aSElliott Hughes        "    </BaseRecord>",
698*e1fe3e4aSElliott Hughes        '    <BaseRecord index="1">',
699*e1fe3e4aSElliott Hughes        '      <BaseAnchor index="0" Format="1">',
700*e1fe3e4aSElliott Hughes        '        <XCoordinate value="300"/>',
701*e1fe3e4aSElliott Hughes        '        <YCoordinate value="0"/>',
702*e1fe3e4aSElliott Hughes        "      </BaseAnchor>",
703*e1fe3e4aSElliott Hughes        "    </BaseRecord>",
704*e1fe3e4aSElliott Hughes        "  </BaseArray>",
705*e1fe3e4aSElliott Hughes        "</MarkBasePos>",
706*e1fe3e4aSElliott Hughes    ]
707*e1fe3e4aSElliott Hughes
708*e1fe3e4aSElliott Hughes
709*e1fe3e4aSElliott Hughesclass ColrV1Test(unittest.TestCase):
710*e1fe3e4aSElliott Hughes    def setUp(self):
711*e1fe3e4aSElliott Hughes        self.font = FakeFont([".notdef", "meh"])
712*e1fe3e4aSElliott Hughes
713*e1fe3e4aSElliott Hughes    def test_traverseEmptyPaintColrLayersNeedsNoLayerList(self):
714*e1fe3e4aSElliott Hughes        colr = parseXmlInto(
715*e1fe3e4aSElliott Hughes            self.font,
716*e1fe3e4aSElliott Hughes            otTables.COLR(),
717*e1fe3e4aSElliott Hughes            """
718*e1fe3e4aSElliott Hughes          <Version value="1"/>
719*e1fe3e4aSElliott Hughes          <BaseGlyphList>
720*e1fe3e4aSElliott Hughes            <BaseGlyphPaintRecord index="0">
721*e1fe3e4aSElliott Hughes              <BaseGlyph value="meh"/>
722*e1fe3e4aSElliott Hughes              <Paint Format="1"><!-- PaintColrLayers -->
723*e1fe3e4aSElliott Hughes                <NumLayers value="0"/>
724*e1fe3e4aSElliott Hughes                <FirstLayerIndex value="42"/>
725*e1fe3e4aSElliott Hughes              </Paint>
726*e1fe3e4aSElliott Hughes            </BaseGlyphPaintRecord>
727*e1fe3e4aSElliott Hughes          </BaseGlyphList>
728*e1fe3e4aSElliott Hughes          """,
729*e1fe3e4aSElliott Hughes        )
730*e1fe3e4aSElliott Hughes        paint = colr.BaseGlyphList.BaseGlyphPaintRecord[0].Paint
731*e1fe3e4aSElliott Hughes
732*e1fe3e4aSElliott Hughes        # Just want to confirm we don't crash
733*e1fe3e4aSElliott Hughes        visited = []
734*e1fe3e4aSElliott Hughes        paint.traverse(colr, lambda p: visited.append(p))
735*e1fe3e4aSElliott Hughes        assert len(visited) == 1
736*e1fe3e4aSElliott Hughes
737*e1fe3e4aSElliott Hughes
738*e1fe3e4aSElliott Hughesif __name__ == "__main__":
739*e1fe3e4aSElliott Hughes    import sys
740*e1fe3e4aSElliott Hughes
741*e1fe3e4aSElliott Hughes    sys.exit(unittest.main())
742