| from fontTools.misc.testTools import getXML, parseXML, parseXmlInto, FakeFont |
| from fontTools.misc.textTools import deHexStr, hexStr |
| from fontTools.misc.xmlWriter import XMLWriter |
| from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter |
| import fontTools.ttLib.tables.otTables as otTables |
| from io import StringIO |
| import unittest |
| |
| |
| def makeCoverage(glyphs): |
| coverage = otTables.Coverage() |
| coverage.glyphs = glyphs |
| return coverage |
| |
| |
| class SingleSubstTest(unittest.TestCase): |
| def setUp(self): |
| self.glyphs = ".notdef A B C D E a b c d e".split() |
| self.font = FakeFont(self.glyphs) |
| |
| def test_postRead_format1(self): |
| table = otTables.SingleSubst() |
| table.Format = 1 |
| rawTable = {"Coverage": makeCoverage(["A", "B", "C"]), "DeltaGlyphID": 5} |
| table.postRead(rawTable, self.font) |
| self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"}) |
| |
| def test_postRead_format2(self): |
| table = otTables.SingleSubst() |
| table.Format = 2 |
| rawTable = { |
| "Coverage": makeCoverage(["A", "B", "C"]), |
| "GlyphCount": 3, |
| "Substitute": ["c", "b", "a"], |
| } |
| table.postRead(rawTable, self.font) |
| self.assertEqual(table.mapping, {"A": "c", "B": "b", "C": "a"}) |
| |
| def test_postRead_formatUnknown(self): |
| table = otTables.SingleSubst() |
| table.Format = 987 |
| rawTable = {"Coverage": makeCoverage(["A", "B", "C"])} |
| self.assertRaises(AssertionError, table.postRead, rawTable, self.font) |
| |
| def test_preWrite_format1(self): |
| table = otTables.SingleSubst() |
| table.mapping = {"A": "a", "B": "b", "C": "c"} |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 1) |
| self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"]) |
| self.assertEqual(rawTable["DeltaGlyphID"], 5) |
| |
| def test_preWrite_format2(self): |
| table = otTables.SingleSubst() |
| table.mapping = {"A": "c", "B": "b", "C": "a"} |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 2) |
| self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"]) |
| self.assertEqual(rawTable["Substitute"], ["c", "b", "a"]) |
| |
| def test_preWrite_emptyMapping(self): |
| table = otTables.SingleSubst() |
| table.mapping = {} |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 2) |
| self.assertEqual(rawTable["Coverage"].glyphs, []) |
| self.assertEqual(rawTable["Substitute"], []) |
| |
| def test_toXML2(self): |
| writer = XMLWriter(StringIO()) |
| table = otTables.SingleSubst() |
| table.mapping = {"A": "a", "B": "b", "C": "c"} |
| table.toXML2(writer, self.font) |
| self.assertEqual( |
| writer.file.getvalue().splitlines()[1:], |
| [ |
| '<Substitution in="A" out="a"/>', |
| '<Substitution in="B" out="b"/>', |
| '<Substitution in="C" out="c"/>', |
| ], |
| ) |
| |
| def test_fromXML(self): |
| table = otTables.SingleSubst() |
| for name, attrs, content in parseXML( |
| '<Substitution in="A" out="a"/>' |
| '<Substitution in="B" out="b"/>' |
| '<Substitution in="C" out="c"/>' |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"}) |
| |
| |
| class MultipleSubstTest(unittest.TestCase): |
| def setUp(self): |
| self.glyphs = ".notdef c f i t c_t f_f_i".split() |
| self.font = FakeFont(self.glyphs) |
| |
| def test_postRead_format1(self): |
| makeSequence = otTables.MultipleSubst.makeSequence_ |
| table = otTables.MultipleSubst() |
| table.Format = 1 |
| rawTable = { |
| "Coverage": makeCoverage(["c_t", "f_f_i"]), |
| "Sequence": [makeSequence(["c", "t"]), makeSequence(["f", "f", "i"])], |
| } |
| table.postRead(rawTable, self.font) |
| self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}) |
| |
| def test_postRead_formatUnknown(self): |
| table = otTables.MultipleSubst() |
| table.Format = 987 |
| self.assertRaises(AssertionError, table.postRead, {}, self.font) |
| |
| def test_preWrite_format1(self): |
| table = otTables.MultipleSubst() |
| table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]} |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 1) |
| self.assertEqual(rawTable["Coverage"].glyphs, ["c_t", "f_f_i"]) |
| |
| def test_toXML2(self): |
| writer = XMLWriter(StringIO()) |
| table = otTables.MultipleSubst() |
| table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]} |
| table.toXML2(writer, self.font) |
| self.assertEqual( |
| writer.file.getvalue().splitlines()[1:], |
| [ |
| '<Substitution in="c_t" out="c,t"/>', |
| '<Substitution in="f_f_i" out="f,f,i"/>', |
| ], |
| ) |
| |
| def test_fromXML(self): |
| table = otTables.MultipleSubst() |
| for name, attrs, content in parseXML( |
| '<Substitution in="c_t" out="c,t"/>' |
| '<Substitution in="f_f_i" out="f,f,i"/>' |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}) |
| |
| def test_fromXML_oldFormat(self): |
| table = otTables.MultipleSubst() |
| for name, attrs, content in parseXML( |
| "<Coverage>" |
| ' <Glyph value="c_t"/>' |
| ' <Glyph value="f_f_i"/>' |
| "</Coverage>" |
| '<Sequence index="0">' |
| ' <Substitute index="0" value="c"/>' |
| ' <Substitute index="1" value="t"/>' |
| "</Sequence>" |
| '<Sequence index="1">' |
| ' <Substitute index="0" value="f"/>' |
| ' <Substitute index="1" value="f"/>' |
| ' <Substitute index="2" value="i"/>' |
| "</Sequence>" |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(table.mapping, {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}) |
| |
| def test_fromXML_oldFormat_bug385(self): |
| # https://github.com/fonttools/fonttools/issues/385 |
| table = otTables.MultipleSubst() |
| table.Format = 1 |
| for name, attrs, content in parseXML( |
| "<Coverage>" |
| ' <Glyph value="o"/>' |
| ' <Glyph value="l"/>' |
| "</Coverage>" |
| "<Sequence>" |
| ' <Substitute value="o"/>' |
| ' <Substitute value="l"/>' |
| ' <Substitute value="o"/>' |
| "</Sequence>" |
| "<Sequence>" |
| ' <Substitute value="o"/>' |
| "</Sequence>" |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(table.mapping, {"o": ["o", "l", "o"], "l": ["o"]}) |
| |
| |
| class LigatureSubstTest(unittest.TestCase): |
| def setUp(self): |
| self.glyphs = ".notdef c f i t c_t f_f f_i f_f_i".split() |
| self.font = FakeFont(self.glyphs) |
| |
| def makeLigature(self, s): |
| """'ffi' --> Ligature(LigGlyph='f_f_i', Component=['f', 'f', 'i'])""" |
| lig = otTables.Ligature() |
| lig.Component = list(s) |
| lig.LigGlyph = "_".join(lig.Component) |
| return lig |
| |
| def makeLigatures(self, s): |
| """'ffi fi' --> [otTables.Ligature, otTables.Ligature]""" |
| return [self.makeLigature(lig) for lig in s.split()] |
| |
| def test_postRead_format1(self): |
| table = otTables.LigatureSubst() |
| table.Format = 1 |
| ligs_c = otTables.LigatureSet() |
| ligs_c.Ligature = self.makeLigatures("ct") |
| ligs_f = otTables.LigatureSet() |
| ligs_f.Ligature = self.makeLigatures("ffi ff fi") |
| rawTable = { |
| "Coverage": makeCoverage(["c", "f"]), |
| "LigatureSet": [ligs_c, ligs_f], |
| } |
| table.postRead(rawTable, self.font) |
| self.assertEqual(set(table.ligatures.keys()), {"c", "f"}) |
| self.assertEqual(len(table.ligatures["c"]), 1) |
| self.assertEqual(table.ligatures["c"][0].LigGlyph, "c_t") |
| self.assertEqual(table.ligatures["c"][0].Component, ["c", "t"]) |
| self.assertEqual(len(table.ligatures["f"]), 3) |
| self.assertEqual(table.ligatures["f"][0].LigGlyph, "f_f_i") |
| self.assertEqual(table.ligatures["f"][0].Component, ["f", "f", "i"]) |
| self.assertEqual(table.ligatures["f"][1].LigGlyph, "f_f") |
| self.assertEqual(table.ligatures["f"][1].Component, ["f", "f"]) |
| self.assertEqual(table.ligatures["f"][2].LigGlyph, "f_i") |
| self.assertEqual(table.ligatures["f"][2].Component, ["f", "i"]) |
| |
| def test_postRead_formatUnknown(self): |
| table = otTables.LigatureSubst() |
| table.Format = 987 |
| rawTable = {"Coverage": makeCoverage(["f"])} |
| self.assertRaises(AssertionError, table.postRead, rawTable, self.font) |
| |
| def test_preWrite_format1(self): |
| table = otTables.LigatureSubst() |
| table.ligatures = { |
| "c": self.makeLigatures("ct"), |
| "f": self.makeLigatures("ffi ff fi"), |
| } |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 1) |
| self.assertEqual(rawTable["Coverage"].glyphs, ["c", "f"]) |
| [c, f] = rawTable["LigatureSet"] |
| self.assertIsInstance(c, otTables.LigatureSet) |
| self.assertIsInstance(f, otTables.LigatureSet) |
| [ct] = c.Ligature |
| self.assertIsInstance(ct, otTables.Ligature) |
| self.assertEqual(ct.LigGlyph, "c_t") |
| self.assertEqual(ct.Component, ["c", "t"]) |
| [ffi, ff, fi] = f.Ligature |
| self.assertIsInstance(ffi, otTables.Ligature) |
| self.assertEqual(ffi.LigGlyph, "f_f_i") |
| self.assertEqual(ffi.Component, ["f", "f", "i"]) |
| self.assertIsInstance(ff, otTables.Ligature) |
| self.assertEqual(ff.LigGlyph, "f_f") |
| self.assertEqual(ff.Component, ["f", "f"]) |
| self.assertIsInstance(fi, otTables.Ligature) |
| self.assertEqual(fi.LigGlyph, "f_i") |
| self.assertEqual(fi.Component, ["f", "i"]) |
| |
| def test_toXML2(self): |
| writer = XMLWriter(StringIO()) |
| table = otTables.LigatureSubst() |
| table.ligatures = { |
| "c": self.makeLigatures("ct"), |
| "f": self.makeLigatures("ffi ff fi"), |
| } |
| table.toXML2(writer, self.font) |
| self.assertEqual( |
| writer.file.getvalue().splitlines()[1:], |
| [ |
| '<LigatureSet glyph="c">', |
| ' <Ligature components="c,t" glyph="c_t"/>', |
| "</LigatureSet>", |
| '<LigatureSet glyph="f">', |
| ' <Ligature components="f,f,i" glyph="f_f_i"/>', |
| ' <Ligature components="f,f" glyph="f_f"/>', |
| ' <Ligature components="f,i" glyph="f_i"/>', |
| "</LigatureSet>", |
| ], |
| ) |
| |
| def test_fromXML(self): |
| table = otTables.LigatureSubst() |
| for name, attrs, content in parseXML( |
| '<LigatureSet glyph="f">' |
| ' <Ligature components="f,f,i" glyph="f_f_i"/>' |
| ' <Ligature components="f,f" glyph="f_f"/>' |
| "</LigatureSet>" |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(set(table.ligatures.keys()), {"f"}) |
| [ffi, ff] = table.ligatures["f"] |
| self.assertEqual(ffi.LigGlyph, "f_f_i") |
| self.assertEqual(ffi.Component, ["f", "f", "i"]) |
| self.assertEqual(ff.LigGlyph, "f_f") |
| self.assertEqual(ff.Component, ["f", "f"]) |
| |
| |
| class AlternateSubstTest(unittest.TestCase): |
| def setUp(self): |
| self.glyphs = ".notdef G G.alt1 G.alt2 Z Z.fina".split() |
| self.font = FakeFont(self.glyphs) |
| |
| def makeAlternateSet(self, s): |
| result = otTables.AlternateSet() |
| result.Alternate = s.split() |
| return result |
| |
| def test_postRead_format1(self): |
| table = otTables.AlternateSubst() |
| table.Format = 1 |
| rawTable = { |
| "Coverage": makeCoverage(["G", "Z"]), |
| "AlternateSet": [ |
| self.makeAlternateSet("G.alt2 G.alt1"), |
| self.makeAlternateSet("Z.fina"), |
| ], |
| } |
| table.postRead(rawTable, self.font) |
| self.assertEqual(table.alternates, {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}) |
| |
| def test_postRead_formatUnknown(self): |
| table = otTables.AlternateSubst() |
| table.Format = 987 |
| self.assertRaises(AssertionError, table.postRead, {}, self.font) |
| |
| def test_preWrite_format1(self): |
| table = otTables.AlternateSubst() |
| table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]} |
| rawTable = table.preWrite(self.font) |
| self.assertEqual(table.Format, 1) |
| self.assertEqual(rawTable["Coverage"].glyphs, ["G", "Z"]) |
| [g, z] = rawTable["AlternateSet"] |
| self.assertIsInstance(g, otTables.AlternateSet) |
| self.assertEqual(g.Alternate, ["G.alt2", "G.alt1"]) |
| self.assertIsInstance(z, otTables.AlternateSet) |
| self.assertEqual(z.Alternate, ["Z.fina"]) |
| |
| def test_toXML2(self): |
| writer = XMLWriter(StringIO()) |
| table = otTables.AlternateSubst() |
| table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]} |
| table.toXML2(writer, self.font) |
| self.assertEqual( |
| writer.file.getvalue().splitlines()[1:], |
| [ |
| '<AlternateSet glyph="G">', |
| ' <Alternate glyph="G.alt2"/>', |
| ' <Alternate glyph="G.alt1"/>', |
| "</AlternateSet>", |
| '<AlternateSet glyph="Z">', |
| ' <Alternate glyph="Z.fina"/>', |
| "</AlternateSet>", |
| ], |
| ) |
| |
| def test_fromXML(self): |
| table = otTables.AlternateSubst() |
| for name, attrs, content in parseXML( |
| '<AlternateSet glyph="G">' |
| ' <Alternate glyph="G.alt2"/>' |
| ' <Alternate glyph="G.alt1"/>' |
| "</AlternateSet>" |
| '<AlternateSet glyph="Z">' |
| ' <Alternate glyph="Z.fina"/>' |
| "</AlternateSet>" |
| ): |
| table.fromXML(name, attrs, content, self.font) |
| self.assertEqual(table.alternates, {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}) |
| |
| |
| class RearrangementMorphActionTest(unittest.TestCase): |
| def setUp(self): |
| self.font = FakeFont([".notdef", "A", "B", "C"]) |
| |
| def testCompile(self): |
| r = otTables.RearrangementMorphAction() |
| r.NewState = 0x1234 |
| r.MarkFirst = r.DontAdvance = r.MarkLast = True |
| r.ReservedFlags, r.Verb = 0x1FF0, 0xD |
| writer = OTTableWriter() |
| r.compile(writer, self.font, actionIndex=None) |
| self.assertEqual(hexStr(writer.getAllData()), "1234fffd") |
| |
| def testCompileActions(self): |
| act = otTables.RearrangementMorphAction() |
| self.assertEqual(act.compileActions(self.font, []), (None, None)) |
| |
| def testDecompileToXML(self): |
| r = otTables.RearrangementMorphAction() |
| r.decompile(OTTableReader(deHexStr("1234fffd")), self.font, actionReader=None) |
| toXML = lambda w, f: r.toXML(w, f, {"Test": "Foo"}, "Transition") |
| self.assertEqual( |
| getXML(toXML, self.font), |
| [ |
| '<Transition Test="Foo">', |
| ' <NewState value="4660"/>', # 0x1234 = 4660 |
| ' <Flags value="MarkFirst,DontAdvance,MarkLast"/>', |
| ' <ReservedFlags value="0x1FF0"/>', |
| ' <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->', |
| "</Transition>", |
| ], |
| ) |
| |
| |
| class ContextualMorphActionTest(unittest.TestCase): |
| def setUp(self): |
| self.font = FakeFont([".notdef", "A", "B", "C"]) |
| |
| def testCompile(self): |
| a = otTables.ContextualMorphAction() |
| a.NewState = 0x1234 |
| a.SetMark, a.DontAdvance, a.ReservedFlags = True, True, 0x3117 |
| a.MarkIndex, a.CurrentIndex = 0xDEAD, 0xBEEF |
| writer = OTTableWriter() |
| a.compile(writer, self.font, actionIndex=None) |
| self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef") |
| |
| def testCompileActions(self): |
| act = otTables.ContextualMorphAction() |
| self.assertEqual(act.compileActions(self.font, []), (None, None)) |
| |
| def testDecompileToXML(self): |
| a = otTables.ContextualMorphAction() |
| a.decompile( |
| OTTableReader(deHexStr("1234f117deadbeef")), self.font, actionReader=None |
| ) |
| toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition") |
| self.assertEqual( |
| getXML(toXML, self.font), |
| [ |
| '<Transition Test="Foo">', |
| ' <NewState value="4660"/>', # 0x1234 = 4660 |
| ' <Flags value="SetMark,DontAdvance"/>', |
| ' <ReservedFlags value="0x3117"/>', |
| ' <MarkIndex value="57005"/>', # 0xDEAD = 57005 |
| ' <CurrentIndex value="48879"/>', # 0xBEEF = 48879 |
| "</Transition>", |
| ], |
| ) |
| |
| |
| class LigatureMorphActionTest(unittest.TestCase): |
| def setUp(self): |
| self.font = FakeFont([".notdef", "A", "B", "C"]) |
| |
| def testDecompileToXML(self): |
| a = otTables.LigatureMorphAction() |
| actionReader = OTTableReader(deHexStr("DEADBEEF 7FFFFFFE 80000003")) |
| a.decompile(OTTableReader(deHexStr("1234FAB30001")), self.font, actionReader) |
| toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition") |
| self.assertEqual( |
| getXML(toXML, self.font), |
| [ |
| '<Transition Test="Foo">', |
| ' <NewState value="4660"/>', # 0x1234 = 4660 |
| ' <Flags value="SetComponent,DontAdvance"/>', |
| ' <ReservedFlags value="0x1AB3"/>', |
| ' <Action GlyphIndexDelta="-2" Flags="Store"/>', |
| ' <Action GlyphIndexDelta="3"/>', |
| "</Transition>", |
| ], |
| ) |
| |
| def testCompileActions_empty(self): |
| act = otTables.LigatureMorphAction() |
| actions, actionIndex = act.compileActions(self.font, []) |
| self.assertEqual(actions, b"") |
| self.assertEqual(actionIndex, {}) |
| |
| def testCompileActions_shouldShareSubsequences(self): |
| state = otTables.AATState() |
| t = state.Transitions = {i: otTables.LigatureMorphAction() for i in range(3)} |
| ligs = [otTables.LigAction() for _ in range(3)] |
| for i, lig in enumerate(ligs): |
| lig.GlyphIndexDelta = i |
| t[0].Actions = ligs[1:2] |
| t[1].Actions = ligs[0:3] |
| t[2].Actions = ligs[1:3] |
| actions, actionIndex = t[0].compileActions(self.font, [state]) |
| self.assertEqual(actions, deHexStr("00000000 00000001 80000002 80000001")) |
| self.assertEqual( |
| actionIndex, |
| { |
| deHexStr("00000000 00000001 80000002"): 0, |
| deHexStr("00000001 80000002"): 1, |
| deHexStr("80000002"): 2, |
| deHexStr("80000001"): 3, |
| }, |
| ) |
| |
| |
| class InsertionMorphActionTest(unittest.TestCase): |
| MORPH_ACTION_XML = [ |
| '<Transition Test="Foo">', |
| ' <NewState value="4660"/>', # 0x1234 = 4660 |
| ' <Flags value="SetMark,DontAdvance,CurrentIsKashidaLike,' |
| 'MarkedIsKashidaLike,CurrentInsertBefore,MarkedInsertBefore"/>', |
| ' <CurrentInsertionAction glyph="B"/>', |
| ' <CurrentInsertionAction glyph="C"/>', |
| ' <MarkedInsertionAction glyph="B"/>', |
| ' <MarkedInsertionAction glyph="A"/>', |
| ' <MarkedInsertionAction glyph="D"/>', |
| "</Transition>", |
| ] |
| |
| def setUp(self): |
| self.font = FakeFont([".notdef", "A", "B", "C", "D"]) |
| self.maxDiff = None |
| |
| def testDecompileToXML(self): |
| a = otTables.InsertionMorphAction() |
| actionReader = OTTableReader( |
| deHexStr("DEAD BEEF 0002 0001 0004 0002 0003 DEAD BEEF") |
| ) |
| a.decompile( |
| OTTableReader(deHexStr("1234 FC43 0005 0002")), self.font, actionReader |
| ) |
| toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition") |
| self.assertEqual(getXML(toXML, self.font), self.MORPH_ACTION_XML) |
| |
| def testCompileFromXML(self): |
| a = otTables.InsertionMorphAction() |
| for name, attrs, content in parseXML(self.MORPH_ACTION_XML): |
| a.fromXML(name, attrs, content, self.font) |
| writer = OTTableWriter() |
| a.compile( |
| writer, |
| self.font, |
| actionIndex={("B", "C"): 9, ("B", "A", "D"): 7}, |
| ) |
| self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007") |
| |
| def testCompileActions_empty(self): |
| act = otTables.InsertionMorphAction() |
| actions, actionIndex = act.compileActions(self.font, []) |
| self.assertEqual(actions, b"") |
| self.assertEqual(actionIndex, {}) |
| |
| def testCompileActions_shouldShareSubsequences(self): |
| state = otTables.AATState() |
| t = state.Transitions = {i: otTables.InsertionMorphAction() for i in range(3)} |
| t[1].CurrentInsertionAction = [] |
| t[0].MarkedInsertionAction = ["A"] |
| t[1].CurrentInsertionAction = ["C", "D"] |
| t[1].MarkedInsertionAction = ["B"] |
| t[2].CurrentInsertionAction = ["B", "C", "D"] |
| t[2].MarkedInsertionAction = ["C", "D"] |
| actions, actionIndex = t[0].compileActions(self.font, [state]) |
| self.assertEqual(actions, deHexStr("0002 0003 0004 0001")) |
| self.assertEqual( |
| actionIndex, |
| { |
| ("A",): 3, |
| ("B",): 0, |
| ("B", "C"): 0, |
| ("B", "C", "D"): 0, |
| ("C",): 1, |
| ("C", "D"): 1, |
| ("D",): 2, |
| }, |
| ) |
| |
| |
| class SplitMultipleSubstTest: |
| def overflow(self, itemName, itemRecord): |
| from fontTools.otlLib.builder import buildMultipleSubstSubtable |
| from fontTools.ttLib.tables.otBase import OverflowErrorRecord |
| |
| oldSubTable = buildMultipleSubstSubtable( |
| {"e": 1, "a": 2, "b": 3, "c": 4, "d": 5} |
| ) |
| newSubTable = otTables.MultipleSubst() |
| |
| ok = otTables.splitMultipleSubst( |
| oldSubTable, |
| newSubTable, |
| OverflowErrorRecord((None, None, None, itemName, itemRecord)), |
| ) |
| |
| assert ok |
| return oldSubTable.mapping, newSubTable.mapping |
| |
| def test_Coverage(self): |
| oldMapping, newMapping = self.overflow("Coverage", None) |
| assert oldMapping == {"a": 2, "b": 3} |
| assert newMapping == {"c": 4, "d": 5, "e": 1} |
| |
| def test_RangeRecord(self): |
| oldMapping, newMapping = self.overflow("RangeRecord", None) |
| assert oldMapping == {"a": 2, "b": 3} |
| assert newMapping == {"c": 4, "d": 5, "e": 1} |
| |
| def test_Sequence(self): |
| oldMapping, newMapping = self.overflow("Sequence", 4) |
| assert oldMapping == {"a": 2, "b": 3, "c": 4} |
| assert newMapping == {"d": 5, "e": 1} |
| |
| |
| def test_splitMarkBasePos(): |
| from fontTools.otlLib.builder import buildAnchor, buildMarkBasePosSubtable |
| |
| marks = { |
| "acutecomb": (0, buildAnchor(0, 600)), |
| "gravecomb": (0, buildAnchor(0, 590)), |
| "cedillacomb": (1, buildAnchor(0, 0)), |
| } |
| bases = { |
| "a": { |
| 0: buildAnchor(350, 500), |
| 1: None, |
| }, |
| "c": { |
| 0: buildAnchor(300, 700), |
| 1: buildAnchor(300, 0), |
| }, |
| } |
| glyphOrder = ["a", "c", "acutecomb", "gravecomb", "cedillacomb"] |
| glyphMap = {g: i for i, g in enumerate(glyphOrder)} |
| |
| oldSubTable = buildMarkBasePosSubtable(marks, bases, glyphMap) |
| newSubTable = otTables.MarkBasePos() |
| |
| ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None) |
| |
| assert ok |
| |
| assert getXML(oldSubTable.toXML) == [ |
| '<MarkBasePos Format="1">', |
| " <MarkCoverage>", |
| ' <Glyph value="acutecomb"/>', |
| ' <Glyph value="gravecomb"/>', |
| " </MarkCoverage>", |
| " <BaseCoverage>", |
| ' <Glyph value="a"/>', |
| ' <Glyph value="c"/>', |
| " </BaseCoverage>", |
| " <!-- ClassCount=1 -->", |
| " <MarkArray>", |
| " <!-- MarkCount=2 -->", |
| ' <MarkRecord index="0">', |
| ' <Class value="0"/>', |
| ' <MarkAnchor Format="1">', |
| ' <XCoordinate value="0"/>', |
| ' <YCoordinate value="600"/>', |
| " </MarkAnchor>", |
| " </MarkRecord>", |
| ' <MarkRecord index="1">', |
| ' <Class value="0"/>', |
| ' <MarkAnchor Format="1">', |
| ' <XCoordinate value="0"/>', |
| ' <YCoordinate value="590"/>', |
| " </MarkAnchor>", |
| " </MarkRecord>", |
| " </MarkArray>", |
| " <BaseArray>", |
| " <!-- BaseCount=2 -->", |
| ' <BaseRecord index="0">', |
| ' <BaseAnchor index="0" Format="1">', |
| ' <XCoordinate value="350"/>', |
| ' <YCoordinate value="500"/>', |
| " </BaseAnchor>", |
| " </BaseRecord>", |
| ' <BaseRecord index="1">', |
| ' <BaseAnchor index="0" Format="1">', |
| ' <XCoordinate value="300"/>', |
| ' <YCoordinate value="700"/>', |
| " </BaseAnchor>", |
| " </BaseRecord>", |
| " </BaseArray>", |
| "</MarkBasePos>", |
| ] |
| |
| assert getXML(newSubTable.toXML) == [ |
| '<MarkBasePos Format="1">', |
| " <MarkCoverage>", |
| ' <Glyph value="cedillacomb"/>', |
| " </MarkCoverage>", |
| " <BaseCoverage>", |
| ' <Glyph value="a"/>', |
| ' <Glyph value="c"/>', |
| " </BaseCoverage>", |
| " <!-- ClassCount=1 -->", |
| " <MarkArray>", |
| " <!-- MarkCount=1 -->", |
| ' <MarkRecord index="0">', |
| ' <Class value="0"/>', |
| ' <MarkAnchor Format="1">', |
| ' <XCoordinate value="0"/>', |
| ' <YCoordinate value="0"/>', |
| " </MarkAnchor>", |
| " </MarkRecord>", |
| " </MarkArray>", |
| " <BaseArray>", |
| " <!-- BaseCount=2 -->", |
| ' <BaseRecord index="0">', |
| ' <BaseAnchor index="0" empty="1"/>', |
| " </BaseRecord>", |
| ' <BaseRecord index="1">', |
| ' <BaseAnchor index="0" Format="1">', |
| ' <XCoordinate value="300"/>', |
| ' <YCoordinate value="0"/>', |
| " </BaseAnchor>", |
| " </BaseRecord>", |
| " </BaseArray>", |
| "</MarkBasePos>", |
| ] |
| |
| |
| class ColrV1Test(unittest.TestCase): |
| def setUp(self): |
| self.font = FakeFont([".notdef", "meh"]) |
| |
| def test_traverseEmptyPaintColrLayersNeedsNoLayerList(self): |
| colr = parseXmlInto( |
| self.font, |
| otTables.COLR(), |
| """ |
| <Version value="1"/> |
| <BaseGlyphList> |
| <BaseGlyphPaintRecord index="0"> |
| <BaseGlyph value="meh"/> |
| <Paint Format="1"><!-- PaintColrLayers --> |
| <NumLayers value="0"/> |
| <FirstLayerIndex value="42"/> |
| </Paint> |
| </BaseGlyphPaintRecord> |
| </BaseGlyphList> |
| """, |
| ) |
| paint = colr.BaseGlyphList.BaseGlyphPaintRecord[0].Paint |
| |
| # Just want to confirm we don't crash |
| visited = [] |
| paint.traverse(colr, lambda p: visited.append(p)) |
| assert len(visited) == 1 |
| |
| |
| if __name__ == "__main__": |
| import sys |
| |
| sys.exit(unittest.main()) |