xref: /aosp_15_r20/external/fonttools/Tests/t1Lib/t1Lib_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1import unittest
2import os
3import sys
4from fontTools import t1Lib
5from fontTools.pens.basePen import NullPen
6from fontTools.misc.psCharStrings import T1CharString
7import random
8
9
10CWD = os.path.abspath(os.path.dirname(__file__))
11DATADIR = os.path.join(CWD, "data")
12# I used `tx` to convert PFA to LWFN (stored in the data fork)
13LWFN = os.path.join(DATADIR, "TestT1-Regular.lwfn")
14PFA = os.path.join(DATADIR, "TestT1-Regular.pfa")
15PFB = os.path.join(DATADIR, "TestT1-Regular.pfb")
16WEIRD_ZEROS = os.path.join(DATADIR, "TestT1-weird-zeros.pfa")
17# ellipsis is hinted with 55 131 296 131 537 131 vstem3 0 122 hstem
18ELLIPSIS_HINTED = os.path.join(DATADIR, "TestT1-ellipsis-hinted.pfa")
19
20
21class FindEncryptedChunksTest(unittest.TestCase):
22    def test_findEncryptedChunks(self):
23        with open(PFA, "rb") as f:
24            data = f.read()
25        chunks = t1Lib.findEncryptedChunks(data)
26        self.assertEqual(len(chunks), 3)
27        self.assertFalse(chunks[0][0])
28        # the second chunk is encrypted
29        self.assertTrue(chunks[1][0])
30        self.assertFalse(chunks[2][0])
31
32    def test_findEncryptedChunks_weird_zeros(self):
33        with open(WEIRD_ZEROS, "rb") as f:
34            data = f.read()
35
36        # Just assert that this doesn't raise any exception for not finding the
37        # end of eexec
38        t1Lib.findEncryptedChunks(data)
39
40
41class DecryptType1Test(unittest.TestCase):
42    def test_decryptType1(self):
43        with open(PFA, "rb") as f:
44            data = f.read()
45        decrypted = t1Lib.decryptType1(data)
46        self.assertNotEqual(decrypted, data)
47
48
49class ReadWriteTest(unittest.TestCase):
50    def test_read_pfa_write_pfb(self):
51        font = t1Lib.T1Font(PFA)
52        data = self.write(font, "PFB")
53        self.assertEqual(font.getData(), data)
54
55    def test_read_and_parse_pfa_write_pfb(self):
56        font = t1Lib.T1Font(PFA)
57        font.parse()
58        saved_font = self.write(font, "PFB", dohex=False, doparse=True)
59        self.assertTrue(same_dicts(font.font, saved_font))
60
61    def test_read_pfb_write_pfa(self):
62        font = t1Lib.T1Font(PFB)
63        # 'OTHER' == 'PFA'
64        data = self.write(font, "OTHER", dohex=True)
65        self.assertEqual(font.getData(), data)
66
67    def test_read_and_parse_pfb_write_pfa(self):
68        font = t1Lib.T1Font(PFB)
69        font.parse()
70        # 'OTHER' == 'PFA'
71        saved_font = self.write(font, "OTHER", dohex=True, doparse=True)
72        self.assertTrue(same_dicts(font.font, saved_font))
73
74    def test_read_with_path(self):
75        import pathlib
76
77        font = t1Lib.T1Font(pathlib.Path(PFB))
78
79    @staticmethod
80    def write(font, outtype, dohex=False, doparse=False):
81        temp = os.path.join(DATADIR, "temp." + outtype.lower())
82        try:
83            font.saveAs(temp, outtype, dohex=dohex)
84            newfont = t1Lib.T1Font(temp)
85            if doparse:
86                newfont.parse()
87                data = newfont.font
88            else:
89                data = newfont.getData()
90        finally:
91            if os.path.exists(temp):
92                os.remove(temp)
93        return data
94
95
96class T1FontTest(unittest.TestCase):
97    def test_parse_lwfn(self):
98        # the extended attrs are lost on git so we can't auto-detect 'LWFN'
99        font = t1Lib.T1Font(LWFN, kind="LWFN")
100        font.parse()
101        self.assertEqual(font["FontName"], "TestT1-Regular")
102        self.assertTrue("Subrs" in font["Private"])
103
104    def test_parse_pfa(self):
105        font = t1Lib.T1Font(PFA)
106        font.parse()
107        self.assertEqual(font["FontName"], "TestT1-Regular")
108        self.assertTrue("Subrs" in font["Private"])
109
110    def test_parse_pfb(self):
111        font = t1Lib.T1Font(PFB)
112        font.parse()
113        self.assertEqual(font["FontName"], "TestT1-Regular")
114        self.assertTrue("Subrs" in font["Private"])
115
116    def test_getGlyphSet(self):
117        font = t1Lib.T1Font(PFA)
118        glyphs = font.getGlyphSet()
119        i = random.randrange(len(glyphs))
120        aglyph = list(glyphs.values())[i]
121        self.assertTrue(hasattr(aglyph, "draw"))
122        self.assertFalse(hasattr(aglyph, "width"))
123        aglyph.draw(NullPen())
124        self.assertTrue(hasattr(aglyph, "width"))
125
126
127class EditTest(unittest.TestCase):
128    def test_edit_pfa(self):
129        font = t1Lib.T1Font(PFA)
130        ellipsis = font.getGlyphSet()["ellipsis"]
131        ellipsis.decompile()
132        program = []
133        for v in ellipsis.program:
134            try:
135                program.append(int(v))
136            except:
137                program.append(v)
138                if v == "hsbw":
139                    hints = [55, 131, 296, 131, 537, 131, "vstem3", 0, 122, "hstem"]
140                    program.extend(hints)
141        ellipsis.program = program
142        # 'OTHER' == 'PFA'
143        saved_font = self.write(font, "OTHER", dohex=True, doparse=True)
144        hinted_font = t1Lib.T1Font(ELLIPSIS_HINTED)
145        hinted_font.parse()
146        self.assertTrue(same_dicts(hinted_font.font, saved_font))
147
148    @staticmethod
149    def write(font, outtype, dohex=False, doparse=False):
150        temp = os.path.join(DATADIR, "temp." + outtype.lower())
151        try:
152            font.saveAs(temp, outtype, dohex=dohex)
153            newfont = t1Lib.T1Font(temp)
154            if doparse:
155                newfont.parse()
156                data = newfont.font
157            else:
158                data = newfont.getData()
159        finally:
160            if os.path.exists(temp):
161                os.remove(temp)
162        return data
163
164
165def same_dicts(dict1, dict2):
166    if dict1.keys() != dict2.keys():
167        return False
168    for key, value in dict1.items():
169        if isinstance(value, dict):
170            if not same_dicts(value, dict2[key]):
171                return False
172        elif isinstance(value, list):
173            if len(value) != len(dict2[key]):
174                return False
175            for elem1, elem2 in zip(value, dict2[key]):
176                if isinstance(elem1, T1CharString):
177                    elem1.compile()
178                    elem2.compile()
179                    if elem1.bytecode != elem2.bytecode:
180                        return False
181                else:
182                    if elem1 != elem2:
183                        return False
184        elif isinstance(value, T1CharString):
185            value.compile()
186            dict2[key].compile()
187            if value.bytecode != dict2[key].bytecode:
188                return False
189        else:
190            if value != dict2[key]:
191                return False
192    return True
193
194
195if __name__ == "__main__":
196    import sys
197
198    sys.exit(unittest.main())
199