722 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			722 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
	
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())
 |