2015-10-22 19:29:59 +01:00
|
|
|
import unittest
|
|
|
|
import os
|
2018-12-30 12:07:05 +01:00
|
|
|
import sys
|
2015-10-22 19:29:59 +01:00
|
|
|
from fontTools import t1Lib
|
|
|
|
from fontTools.pens.basePen import NullPen
|
2022-02-10 23:53:12 +09:00
|
|
|
from fontTools.misc.psCharStrings import T1CharString
|
2015-10-22 19:29:59 +01:00
|
|
|
import random
|
|
|
|
|
|
|
|
|
|
|
|
CWD = os.path.abspath(os.path.dirname(__file__))
|
2022-12-13 11:26:36 +00:00
|
|
|
DATADIR = os.path.join(CWD, "data")
|
2015-10-26 02:52:57 +00:00
|
|
|
# I used `tx` to convert PFA to LWFN (stored in the data fork)
|
2022-12-13 11:26:36 +00:00
|
|
|
LWFN = os.path.join(DATADIR, "TestT1-Regular.lwfn")
|
|
|
|
PFA = os.path.join(DATADIR, "TestT1-Regular.pfa")
|
|
|
|
PFB = os.path.join(DATADIR, "TestT1-Regular.pfb")
|
|
|
|
WEIRD_ZEROS = os.path.join(DATADIR, "TestT1-weird-zeros.pfa")
|
2022-02-10 23:53:12 +09:00
|
|
|
# ellipsis is hinted with 55 131 296 131 537 131 vstem3 0 122 hstem
|
2022-12-13 11:26:36 +00:00
|
|
|
ELLIPSIS_HINTED = os.path.join(DATADIR, "TestT1-ellipsis-hinted.pfa")
|
2015-10-22 19:29:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
class FindEncryptedChunksTest(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_findEncryptedChunks(self):
|
|
|
|
with open(PFA, "rb") as f:
|
|
|
|
data = f.read()
|
|
|
|
chunks = t1Lib.findEncryptedChunks(data)
|
|
|
|
self.assertEqual(len(chunks), 3)
|
|
|
|
self.assertFalse(chunks[0][0])
|
|
|
|
# the second chunk is encrypted
|
|
|
|
self.assertTrue(chunks[1][0])
|
|
|
|
self.assertFalse(chunks[2][0])
|
2015-10-22 19:29:59 +01:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_findEncryptedChunks_weird_zeros(self):
|
|
|
|
with open(WEIRD_ZEROS, "rb") as f:
|
|
|
|
data = f.read()
|
2015-10-22 19:29:59 +01:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
# Just assert that this doesn't raise any exception for not finding the
|
|
|
|
# end of eexec
|
|
|
|
t1Lib.findEncryptedChunks(data)
|
2019-07-31 23:46:50 -04:00
|
|
|
|
2015-10-22 19:29:59 +01:00
|
|
|
|
|
|
|
class DecryptType1Test(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_decryptType1(self):
|
|
|
|
with open(PFA, "rb") as f:
|
|
|
|
data = f.read()
|
|
|
|
decrypted = t1Lib.decryptType1(data)
|
|
|
|
self.assertNotEqual(decrypted, data)
|
2015-10-22 19:29:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
class ReadWriteTest(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_read_pfa_write_pfb(self):
|
|
|
|
font = t1Lib.T1Font(PFA)
|
|
|
|
data = self.write(font, "PFB")
|
|
|
|
self.assertEqual(font.getData(), data)
|
|
|
|
|
|
|
|
def test_read_and_parse_pfa_write_pfb(self):
|
|
|
|
font = t1Lib.T1Font(PFA)
|
|
|
|
font.parse()
|
|
|
|
saved_font = self.write(font, "PFB", dohex=False, doparse=True)
|
|
|
|
self.assertTrue(same_dicts(font.font, saved_font))
|
|
|
|
|
|
|
|
def test_read_pfb_write_pfa(self):
|
|
|
|
font = t1Lib.T1Font(PFB)
|
|
|
|
# 'OTHER' == 'PFA'
|
|
|
|
data = self.write(font, "OTHER", dohex=True)
|
|
|
|
self.assertEqual(font.getData(), data)
|
|
|
|
|
|
|
|
def test_read_and_parse_pfb_write_pfa(self):
|
|
|
|
font = t1Lib.T1Font(PFB)
|
|
|
|
font.parse()
|
|
|
|
# 'OTHER' == 'PFA'
|
|
|
|
saved_font = self.write(font, "OTHER", dohex=True, doparse=True)
|
|
|
|
self.assertTrue(same_dicts(font.font, saved_font))
|
|
|
|
|
|
|
|
def test_read_with_path(self):
|
|
|
|
import pathlib
|
|
|
|
|
|
|
|
font = t1Lib.T1Font(pathlib.Path(PFB))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def write(font, outtype, dohex=False, doparse=False):
|
|
|
|
temp = os.path.join(DATADIR, "temp." + outtype.lower())
|
|
|
|
try:
|
|
|
|
font.saveAs(temp, outtype, dohex=dohex)
|
|
|
|
newfont = t1Lib.T1Font(temp)
|
|
|
|
if doparse:
|
|
|
|
newfont.parse()
|
|
|
|
data = newfont.font
|
|
|
|
else:
|
|
|
|
data = newfont.getData()
|
|
|
|
finally:
|
|
|
|
if os.path.exists(temp):
|
|
|
|
os.remove(temp)
|
|
|
|
return data
|
2015-10-22 19:29:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
class T1FontTest(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_parse_lwfn(self):
|
|
|
|
# the extended attrs are lost on git so we can't auto-detect 'LWFN'
|
|
|
|
font = t1Lib.T1Font(LWFN, kind="LWFN")
|
|
|
|
font.parse()
|
|
|
|
self.assertEqual(font["FontName"], "TestT1-Regular")
|
|
|
|
self.assertTrue("Subrs" in font["Private"])
|
|
|
|
|
|
|
|
def test_parse_pfa(self):
|
|
|
|
font = t1Lib.T1Font(PFA)
|
|
|
|
font.parse()
|
|
|
|
self.assertEqual(font["FontName"], "TestT1-Regular")
|
|
|
|
self.assertTrue("Subrs" in font["Private"])
|
|
|
|
|
|
|
|
def test_parse_pfb(self):
|
|
|
|
font = t1Lib.T1Font(PFB)
|
|
|
|
font.parse()
|
|
|
|
self.assertEqual(font["FontName"], "TestT1-Regular")
|
|
|
|
self.assertTrue("Subrs" in font["Private"])
|
|
|
|
|
|
|
|
def test_getGlyphSet(self):
|
|
|
|
font = t1Lib.T1Font(PFA)
|
|
|
|
glyphs = font.getGlyphSet()
|
|
|
|
i = random.randrange(len(glyphs))
|
|
|
|
aglyph = list(glyphs.values())[i]
|
|
|
|
self.assertTrue(hasattr(aglyph, "draw"))
|
|
|
|
self.assertFalse(hasattr(aglyph, "width"))
|
|
|
|
aglyph.draw(NullPen())
|
|
|
|
self.assertTrue(hasattr(aglyph, "width"))
|
2015-10-26 05:23:59 +00:00
|
|
|
|
|
|
|
|
2022-02-10 23:53:12 +09:00
|
|
|
class EditTest(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
def test_edit_pfa(self):
|
|
|
|
font = t1Lib.T1Font(PFA)
|
|
|
|
ellipsis = font.getGlyphSet()["ellipsis"]
|
|
|
|
ellipsis.decompile()
|
|
|
|
program = []
|
|
|
|
for v in ellipsis.program:
|
|
|
|
try:
|
|
|
|
program.append(int(v))
|
|
|
|
except:
|
|
|
|
program.append(v)
|
|
|
|
if v == "hsbw":
|
|
|
|
hints = [55, 131, 296, 131, 537, 131, "vstem3", 0, 122, "hstem"]
|
|
|
|
program.extend(hints)
|
|
|
|
ellipsis.program = program
|
|
|
|
# 'OTHER' == 'PFA'
|
|
|
|
saved_font = self.write(font, "OTHER", dohex=True, doparse=True)
|
|
|
|
hinted_font = t1Lib.T1Font(ELLIPSIS_HINTED)
|
|
|
|
hinted_font.parse()
|
|
|
|
self.assertTrue(same_dicts(hinted_font.font, saved_font))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def write(font, outtype, dohex=False, doparse=False):
|
|
|
|
temp = os.path.join(DATADIR, "temp." + outtype.lower())
|
|
|
|
try:
|
|
|
|
font.saveAs(temp, outtype, dohex=dohex)
|
|
|
|
newfont = t1Lib.T1Font(temp)
|
|
|
|
if doparse:
|
|
|
|
newfont.parse()
|
|
|
|
data = newfont.font
|
|
|
|
else:
|
|
|
|
data = newfont.getData()
|
|
|
|
finally:
|
|
|
|
if os.path.exists(temp):
|
|
|
|
os.remove(temp)
|
|
|
|
return data
|
2022-02-10 23:53:12 +09:00
|
|
|
|
|
|
|
|
|
|
|
def same_dicts(dict1, dict2):
|
2022-12-13 11:26:36 +00:00
|
|
|
if dict1.keys() != dict2.keys():
|
|
|
|
return False
|
|
|
|
for key, value in dict1.items():
|
|
|
|
if isinstance(value, dict):
|
|
|
|
if not same_dicts(value, dict2[key]):
|
|
|
|
return False
|
|
|
|
elif isinstance(value, list):
|
|
|
|
if len(value) != len(dict2[key]):
|
|
|
|
return False
|
|
|
|
for elem1, elem2 in zip(value, dict2[key]):
|
|
|
|
if isinstance(elem1, T1CharString):
|
|
|
|
elem1.compile()
|
|
|
|
elem2.compile()
|
|
|
|
if elem1.bytecode != elem2.bytecode:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
if elem1 != elem2:
|
|
|
|
return False
|
|
|
|
elif isinstance(value, T1CharString):
|
|
|
|
value.compile()
|
|
|
|
dict2[key].compile()
|
|
|
|
if value.bytecode != dict2[key].bytecode:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
if value != dict2[key]:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import sys
|
|
|
|
|
|
|
|
sys.exit(unittest.main())
|