• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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