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