17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""fontTools.t1Lib.py -- Tools for PostScript Type 1 fonts 27842e56b97ce677b83bdab09cda48bc2d89ac75aJust 37842e56b97ce677b83bdab09cda48bc2d89ac75aJustFunctions for reading and writing raw Type 1 data: 47842e56b97ce677b83bdab09cda48bc2d89ac75aJust 57842e56b97ce677b83bdab09cda48bc2d89ac75aJustread(path) 67842e56b97ce677b83bdab09cda48bc2d89ac75aJust reads any Type 1 font file, returns the raw data and a type indicator: 77842e56b97ce677b83bdab09cda48bc2d89ac75aJust 'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed 87842e56b97ce677b83bdab09cda48bc2d89ac75aJust to by 'path'. 97842e56b97ce677b83bdab09cda48bc2d89ac75aJust Raises an error when the file does not contain valid Type 1 data. 107842e56b97ce677b83bdab09cda48bc2d89ac75aJust 11dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbodwrite(path, data, kind='OTHER', dohex=False) 127842e56b97ce677b83bdab09cda48bc2d89ac75aJust writes raw Type 1 data to the file pointed to by 'path'. 137842e56b97ce677b83bdab09cda48bc2d89ac75aJust 'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'. 147842e56b97ce677b83bdab09cda48bc2d89ac75aJust 'dohex' is a flag which determines whether the eexec encrypted 157842e56b97ce677b83bdab09cda48bc2d89ac75aJust part should be written as hexadecimal or binary, but only if kind 167842e56b97ce677b83bdab09cda48bc2d89ac75aJust is 'LWFN' or 'PFB'. 177842e56b97ce677b83bdab09cda48bc2d89ac75aJust""" 181ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 1930e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 20c2be3d982b04c4bbb1c11d9ab8452f78415d6522Justfrom fontTools.misc import eexec 2145d1f3b3b552297484bc2b8e9a2e999630bb5e50jvrfrom fontTools.misc.macCreatorType import getMacCreatorAndType 227842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport os 2330e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport re 247842e56b97ce677b83bdab09cda48bc2d89ac75aJust 25af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye__author__ = "jvr" 26af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye__version__ = "1.0b2" 27af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis JacqueryeDEBUG = 0 28af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye 2922433b1f52c34a04cf13e21103ee978f7f5b501cjvr 3022433b1f52c34a04cf13e21103ee978f7f5b501cjvrtry: 3125ccb9c3460b05522eae9283a4c9ea783da87aa4jvr try: 3225ccb9c3460b05522eae9283a4c9ea783da87aa4jvr from Carbon import Res 3325ccb9c3460b05522eae9283a4c9ea783da87aa4jvr except ImportError: 3425ccb9c3460b05522eae9283a4c9ea783da87aa4jvr import Res # MacPython < 2.2 35e568dc77d3baccc303d5115e2686f126c96c8a26jvrexcept ImportError: 36e568dc77d3baccc303d5115e2686f126c96c8a26jvr haveMacSupport = 0 37e568dc77d3baccc303d5115e2686f126c96c8a26jvrelse: 38e568dc77d3baccc303d5115e2686f126c96c8a26jvr haveMacSupport = 1 39b19141e48930ddd3106a6fe303fa2e6b76d35f47jvr import MacOS 4059afba7684d4c2f7760be5dbc1aeffceb8ced46fjvr 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust 42e568dc77d3baccc303d5115e2686f126c96c8a26jvrclass T1Error(Exception): pass 437842e56b97ce677b83bdab09cda48bc2d89ac75aJust 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust 45e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass T1Font(object): 467842e56b97ce677b83bdab09cda48bc2d89ac75aJust 473618300613e796ff81abddde967b1092ac1dc915Just """Type 1 font class. 483618300613e796ff81abddde967b1092ac1dc915Just 493618300613e796ff81abddde967b1092ac1dc915Just Uses a minimal interpeter that supports just about enough PS to parse 503618300613e796ff81abddde967b1092ac1dc915Just Type 1 fonts. 517842e56b97ce677b83bdab09cda48bc2d89ac75aJust """ 527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 537842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __init__(self, path=None): 547842e56b97ce677b83bdab09cda48bc2d89ac75aJust if path is not None: 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data, type = read(path) 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # XXX 587842e56b97ce677b83bdab09cda48bc2d89ac75aJust 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust def saveAs(self, path, type): 608c74f4639a4494d9d84d1220cbe895de4ffb8aacjvr write(path, self.getData(), type) 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust 627842e56b97ce677b83bdab09cda48bc2d89ac75aJust def getData(self): 633618300613e796ff81abddde967b1092ac1dc915Just # XXX Todo: if the data has been converted to Python object, 643618300613e796ff81abddde967b1092ac1dc915Just # recreate the PS stream 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust 677d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr def getGlyphSet(self): 687d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr """Return a generic GlyphSet, which is a dict-like object 697d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr mapping glyph names to glyph objects. The returned glyph objects 707d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr have a .draw() method that supports the Pen protocol, and will 717d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr have an attribute named 'width', but only *after* the .draw() method 727d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr has been called. 737d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr 747d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr In the case of Type 1, the GlyphSet is simply the CharStrings dict. 757d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr """ 767d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr return self["CharStrings"] 777d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr 787842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __getitem__(self, key): 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "font"): 807842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.parse() 813618300613e796ff81abddde967b1092ac1dc915Just return self.font[key] 827842e56b97ce677b83bdab09cda48bc2d89ac75aJust 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust def parse(self): 84528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psLib 85528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psCharStrings 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.font = psLib.suckfont(self.data) 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust charStrings = self.font["CharStrings"] 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust lenIV = self.font["Private"].get("lenIV", 4) 897842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert lenIV >= 0 90489d76a340845361def6af9ab7d9152f8e66f417jvr subrs = self.font["Private"]["Subrs"] 917842e56b97ce677b83bdab09cda48bc2d89ac75aJust for glyphName, charString in charStrings.items(): 92c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(charString, 4330) 93489d76a340845361def6af9ab7d9152f8e66f417jvr charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:], 94489d76a340845361def6af9ab7d9152f8e66f417jvr subrs=subrs) 957842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(subrs)): 96c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(subrs[i], 4330) 97489d76a340845361def6af9ab7d9152f8e66f417jvr subrs[i] = psCharStrings.T1CharString(charString[lenIV:], subrs=subrs) 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust del self.data 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1013618300613e796ff81abddde967b1092ac1dc915Just# low level T1 data read and write functions 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust 103dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef read(path, onlyHeader=False): 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any Type 1 font file, returns raw data""" 10514fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod normpath = path.lower() 106153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod creator, typ = getMacCreatorAndType(path) 107153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod if typ == 'LWFN': 10845d1f3b3b552297484bc2b8e9a2e999630bb5e50jvr return readLWFN(path, onlyHeader), 'LWFN' 1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust if normpath[-4:] == '.pfb': 11010fd22a9890ce20091a20e70660677db779d4b64jvr return readPFB(path, onlyHeader), 'PFB' 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1125810aa9967488207b039cb2d300fa53c91d4df2fJust return readOther(path), 'OTHER' 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust 114dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef write(path, data, kind='OTHER', dohex=False): 1155810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 11614fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod kind = kind.upper() 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 1 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust if kind == 'LWFN': 1245810aa9967488207b039cb2d300fa53c91d4df2fJust writeLWFN(path, data) 1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif kind == 'PFB': 1265810aa9967488207b039cb2d300fa53c91d4df2fJust writePFB(path, data) 1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1285810aa9967488207b039cb2d300fa53c91d4df2fJust writeOther(path, data, dohex) 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 0 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust if err and not DEBUG: 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust# -- internal -- 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1407842e56b97ce677b83bdab09cda48bc2d89ac75aJustLWFNCHUNKSIZE = 2000 1417842e56b97ce677b83bdab09cda48bc2d89ac75aJustHEXLINELENGTH = 80 1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust 144dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef readLWFN(path, onlyHeader=False): 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads an LWFN font file, returns raw data""" 14691bca4244286fb519c93fe92329da96b0e6f32eejvr resRef = Res.FSOpenResFile(path, 1) # read-only 1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 148e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust n = Res.Count1Resources('POST') 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(501, 501 + n): 1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Get1Resource('POST', i) 153319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod code = byteord(res.data[0]) 154319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod if byteord(res.data[1]) != 0: 155cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error('corrupt LWFN file') 1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 157da0d805d2603dd9b83c695006521f887b68d5505jvr if onlyHeader and code == 2: 158da0d805d2603dd9b83c695006521f887b68d5505jvr break 15905a16f2310e26193557a3dc223ac0efeb166789fjvr data.append(res.data[2:]) 1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code in [3, 5]: 1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 4: 1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(f.read()) 1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 0: 1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # comment, ignore 1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 169dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod raise T1Error('bad chunk code: ' + repr(code)) 1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 171e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 17218316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod data = bytesjoin(data) 1735810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust 176dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef readPFB(path, onlyHeader=False): 1777842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads a PFB font file, returns raw data""" 1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 180ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod while True: 181b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod if f.read(1) != bytechr(128): 182cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error('corrupt PFB file') 183319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod code = byteord(f.read(1)) 1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 1855810aa9967488207b039cb2d300fa53c91d4df2fJust chunklen = stringToLong(f.read(4)) 1863618300613e796ff81abddde967b1092ac1dc915Just chunk = f.read(chunklen) 1873618300613e796ff81abddde967b1092ac1dc915Just assert len(chunk) == chunklen 1883618300613e796ff81abddde967b1092ac1dc915Just data.append(chunk) 1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 3: 1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 192dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod raise T1Error('bad chunk code: ' + repr(code)) 1935810aa9967488207b039cb2d300fa53c91d4df2fJust if onlyHeader: 1945810aa9967488207b039cb2d300fa53c91d4df2fJust break 1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 19618316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod data = bytesjoin(data) 1975810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1987842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1997842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2005810aa9967488207b039cb2d300fa53c91d4df2fJustdef readOther(path): 2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any (font) file, returns raw data""" 2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = f.read() 2047842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 2055810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2075810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 2095810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2105810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted and isHex(chunk[:4]): 2115810aa9967488207b039cb2d300fa53c91d4df2fJust data.append(deHexString(chunk)) 2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 21418316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod return bytesjoin(data) 2157842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust# file writing tools 2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2185810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeLWFN(path, data): 219e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.FSpCreateResFile(path, "just", "LWFN", 0) 22091bca4244286fb519c93fe92329da96b0e6f32eejvr resRef = Res.FSOpenResFile(path, 2) # write-only 2217842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 222e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 2237842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = 501 2245810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2255810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2265810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2277842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2297842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 231b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2]) 2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[LWFNCHUNKSIZE - 2:] 2347842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = resID + 1 235b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod res = Res.Resource(bytechr(5) + '\0') 2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 238e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 2397842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2405810aa9967488207b039cb2d300fa53c91d4df2fJustdef writePFB(path, data): 2415810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2423618300613e796ff81abddde967b1092ac1dc915Just f = open(path, "wb") 2437842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 2445810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2455810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2467842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 249b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod f.write(bytechr(128) + bytechr(code)) 2505810aa9967488207b039cb2d300fa53c91d4df2fJust f.write(longToString(len(chunk))) 2517842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 252b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod f.write(bytechr(128) + bytechr(3)) 2537842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2547842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust 256dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef writeOther(path, data, dohex=False): 2575810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "wb") 2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 26032c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod hexlinelen = HEXLINELENGTH // 2 2615810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2625810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2637842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code == 2 and dohex: 2677842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 268c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just f.write(eexec.hexString(chunk[:hexlinelen])) 2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write('\r') 2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[hexlinelen:] 2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2727842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 2737842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2747842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 2757842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2767842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2777842e56b97ce677b83bdab09cda48bc2d89ac75aJust# decryption tools 2787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2797842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGIN = "currentfile eexec" 2807842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECEND = '0' * 64 2817842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECINTERNALEND = "currentfile closefile" 2827842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGINMARKER = "%-- eexec start\r" 2837842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECENDMARKER = "%-- eexec end\r" 2847842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2857842e56b97ce677b83bdab09cda48bc2d89ac75aJust_ishexRE = re.compile('[0-9A-Fa-f]*$') 2867842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2875810aa9967488207b039cb2d300fa53c91d4df2fJustdef isHex(text): 2887842e56b97ce677b83bdab09cda48bc2d89ac75aJust return _ishexRE.match(text) is not None 2897842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2915810aa9967488207b039cb2d300fa53c91d4df2fJustdef decryptType1(data): 2925810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2937842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 2945810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2955810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2965810aa9967488207b039cb2d300fa53c91d4df2fJust if isHex(chunk[:4]): 2975810aa9967488207b039cb2d300fa53c91d4df2fJust chunk = deHexString(chunk) 298c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just decrypted, R = eexec.decrypt(chunk, 55665) 2997842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[4:] 300180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \ 301180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND: 302cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("invalid end of eexec part") 3037842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r' 3047842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER) 3057842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3067842e56b97ce677b83bdab09cda48bc2d89ac75aJust if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN: 3077842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk[:-len(EEXECBEGIN)-1]) 3087842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3097842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 31018316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod return bytesjoin(data) 3117842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3125810aa9967488207b039cb2d300fa53c91d4df2fJustdef findEncryptedChunks(data): 3137842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks = [] 314ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod while True: 31514fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod eBegin = data.find(EEXECBEGIN) 3165810aa9967488207b039cb2d300fa53c91d4df2fJust if eBegin < 0: 3177842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 31890290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr eBegin = eBegin + len(EEXECBEGIN) + 1 31914fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod eEnd = data.find(EEXECEND, eBegin) 3205810aa9967488207b039cb2d300fa53c91d4df2fJust if eEnd < 0: 321cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("can't find end of eexec part") 322db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr cypherText = data[eBegin:eEnd + 2] 323e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr if isHex(cypherText[:4]): 324e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr cypherText = deHexString(cypherText) 325db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr plainText, R = eexec.decrypt(cypherText, 55665) 32614fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod eEndLocal = plainText.find(EEXECINTERNALEND) 327db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr if eEndLocal < 0: 328cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("can't find end of eexec part") 32990290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr chunks.append((0, data[:eBegin])) 330e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1])) 3315810aa9967488207b039cb2d300fa53c91d4df2fJust data = data[eEnd:] 3327842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks.append((0, data)) 3337842e56b97ce677b83bdab09cda48bc2d89ac75aJust return chunks 3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3355810aa9967488207b039cb2d300fa53c91d4df2fJustdef deHexString(hexstring): 33618316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod return eexec.deHexString(strjoin(hexstring.split())) 3377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust# Type 1 assertion 3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust 341dc0ce0b498912287a557bdad58301ec7611c569eBehdad Esfahbod_fontType1RE = re.compile(br"/FontType\s+1\s+def") 3427842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3435810aa9967488207b039cb2d300fa53c91d4df2fJustdef assertType1(data): 344dc0ce0b498912287a557bdad58301ec7611c569eBehdad Esfahbod for head in [b'%!PS-AdobeFont', b'%!FontType1']: 3457842e56b97ce677b83bdab09cda48bc2d89ac75aJust if data[:len(head)] == head: 3467842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 348cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("not a PostScript font") 3497842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not _fontType1RE.search(data): 350cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("not a Type 1 font") 351dc0ce0b498912287a557bdad58301ec7611c569eBehdad Esfahbod if data.find(b"currentfile eexec") < 0: 352cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise T1Error("not an encrypted Type 1 font") 3537842e56b97ce677b83bdab09cda48bc2d89ac75aJust # XXX what else? 3547842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3567842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3577842e56b97ce677b83bdab09cda48bc2d89ac75aJust# pfb helpers 3587842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3595810aa9967488207b039cb2d300fa53c91d4df2fJustdef longToString(long): 360153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod s = "" 3617842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 362153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod s += bytechr((long & (0xff << (i * 8))) >> i * 8) 363153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod return s 3647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 365153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahboddef stringToLong(s): 366153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod if len(s) != 4: 367cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise ValueError('string must be 4 bytes long') 368153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod l = 0 3697842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 370153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod l += byteord(s[i]) << (i * 8) 371153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod return l 3725810aa9967488207b039cb2d300fa53c91d4df2fJust 373