t1Lib.py revision 05a16f2310e26193557a3dc223ac0efeb166789f
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 115810aa9967488207b039cb2d300fa53c91d4df2fJustwrite(path, data, kind='OTHER', dohex=0) 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""" 187842e56b97ce677b83bdab09cda48bc2d89ac75aJust 197842e56b97ce677b83bdab09cda48bc2d89ac75aJust__author__ = "jvr" 207842e56b97ce677b83bdab09cda48bc2d89ac75aJust__version__ = "1.0b2" 217842e56b97ce677b83bdab09cda48bc2d89ac75aJustDEBUG = 0 227842e56b97ce677b83bdab09cda48bc2d89ac75aJust 23c2be3d982b04c4bbb1c11d9ab8452f78415d6522Justfrom fontTools.misc import eexec 247842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string 257842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport re 267842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport os 277842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2822433b1f52c34a04cf13e21103ee978f7f5b501cjvr 2922433b1f52c34a04cf13e21103ee978f7f5b501cjvrtry: 3025ccb9c3460b05522eae9283a4c9ea783da87aa4jvr try: 3125ccb9c3460b05522eae9283a4c9ea783da87aa4jvr from Carbon import Res 3225ccb9c3460b05522eae9283a4c9ea783da87aa4jvr except ImportError: 3325ccb9c3460b05522eae9283a4c9ea783da87aa4jvr import Res # MacPython < 2.2 34e568dc77d3baccc303d5115e2686f126c96c8a26jvrexcept ImportError: 35e568dc77d3baccc303d5115e2686f126c96c8a26jvr haveMacSupport = 0 36e568dc77d3baccc303d5115e2686f126c96c8a26jvrelse: 37e568dc77d3baccc303d5115e2686f126c96c8a26jvr haveMacSupport = 1 387842e56b97ce677b83bdab09cda48bc2d89ac75aJust import macfs 397842e56b97ce677b83bdab09cda48bc2d89ac75aJust 40e568dc77d3baccc303d5115e2686f126c96c8a26jvrclass T1Error(Exception): pass 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust 427842e56b97ce677b83bdab09cda48bc2d89ac75aJust 437842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass T1Font: 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust 453618300613e796ff81abddde967b1092ac1dc915Just """Type 1 font class. 463618300613e796ff81abddde967b1092ac1dc915Just 473618300613e796ff81abddde967b1092ac1dc915Just Uses a minimal interpeter that supports just about enough PS to parse 483618300613e796ff81abddde967b1092ac1dc915Just Type 1 fonts. 497842e56b97ce677b83bdab09cda48bc2d89ac75aJust """ 507842e56b97ce677b83bdab09cda48bc2d89ac75aJust 517842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __init__(self, path=None): 527842e56b97ce677b83bdab09cda48bc2d89ac75aJust if path is not None: 537842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data, type = read(path) 547842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # XXX 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust def saveAs(self, path, type): 588c74f4639a4494d9d84d1220cbe895de4ffb8aacjvr write(path, self.getData(), type) 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust 607842e56b97ce677b83bdab09cda48bc2d89ac75aJust def getData(self): 613618300613e796ff81abddde967b1092ac1dc915Just # XXX Todo: if the data has been converted to Python object, 623618300613e796ff81abddde967b1092ac1dc915Just # recreate the PS stream 637842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __getitem__(self, key): 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "font"): 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.parse() 683618300613e796ff81abddde967b1092ac1dc915Just return self.font[key] 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust 707842e56b97ce677b83bdab09cda48bc2d89ac75aJust def parse(self): 71528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psLib 72528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psCharStrings 737842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.font = psLib.suckfont(self.data) 747842e56b97ce677b83bdab09cda48bc2d89ac75aJust charStrings = self.font["CharStrings"] 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust lenIV = self.font["Private"].get("lenIV", 4) 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert lenIV >= 0 777842e56b97ce677b83bdab09cda48bc2d89ac75aJust for glyphName, charString in charStrings.items(): 78c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(charString, 4330) 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:]) 807842e56b97ce677b83bdab09cda48bc2d89ac75aJust subrs = self.font["Private"]["Subrs"] 817842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(subrs)): 82c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(subrs[i], 4330) 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust subrs[i] = psCharStrings.T1CharString(charString[lenIV:]) 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust del self.data 857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust 883618300613e796ff81abddde967b1092ac1dc915Just# low level T1 data read and write functions 897842e56b97ce677b83bdab09cda48bc2d89ac75aJust 907842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef read(path): 917842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any Type 1 font file, returns raw data""" 927842e56b97ce677b83bdab09cda48bc2d89ac75aJust normpath = string.lower(path) 9322433b1f52c34a04cf13e21103ee978f7f5b501cjvr if haveMacSupport: 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss = macfs.FSSpec(path) 957842e56b97ce677b83bdab09cda48bc2d89ac75aJust creator, type = fss.GetCreatorType() 967842e56b97ce677b83bdab09cda48bc2d89ac75aJust if type == 'LWFN': 975810aa9967488207b039cb2d300fa53c91d4df2fJust return readLWFN(path), 'LWFN' 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust if normpath[-4:] == '.pfb': 995810aa9967488207b039cb2d300fa53c91d4df2fJust return readPFB(path), 'PFB' 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1015810aa9967488207b039cb2d300fa53c91d4df2fJust return readOther(path), 'OTHER' 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef write(path, data, kind='OTHER', dohex=0): 1045810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust kind = string.upper(kind) 1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 1 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust if kind == 'LWFN': 1135810aa9967488207b039cb2d300fa53c91d4df2fJust writeLWFN(path, data) 1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif kind == 'PFB': 1155810aa9967488207b039cb2d300fa53c91d4df2fJust writePFB(path, data) 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1175810aa9967488207b039cb2d300fa53c91d4df2fJust writeOther(path, data, dohex) 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 0 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust if err and not DEBUG: 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust# -- internal -- 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJustLWFNCHUNKSIZE = 2000 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJustHEXLINELENGTH = 80 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust 133da0d805d2603dd9b83c695006521f887b68d5505jvrdef readLWFN(path, onlyHeader=0): 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads an LWFN font file, returns raw data""" 135e9601bf9e1253f77b8a66f27685fae453ce98b14Just resRef = Res.FSpOpenResFile(path, 1) # read-only 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 137e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust n = Res.Count1Resources('POST') 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(501, 501 + n): 1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Get1Resource('POST', i) 1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = ord(res.data[0]) 1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ord(res.data[1]) <> 0: 144e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, 'corrupt LWFN file' 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 146da0d805d2603dd9b83c695006521f887b68d5505jvr if onlyHeader and code == 2: 147da0d805d2603dd9b83c695006521f887b68d5505jvr break 14805a16f2310e26193557a3dc223ac0efeb166789fjvr data.append(res.data[2:]) 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code in [3, 5]: 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 4: 1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(f.read()) 1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 0: 1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # comment, ignore 1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 158e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, 'bad chunk code: ' + `code` 1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 160e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = string.join(data, '') 1625810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1655810aa9967488207b039cb2d300fa53c91d4df2fJustdef readPFB(path, onlyHeader=0): 1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads a PFB font file, returns raw data""" 1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust while 1: 1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust if f.read(1) <> chr(128): 171e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, 'corrupt PFB file' 1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = ord(f.read(1)) 1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 1745810aa9967488207b039cb2d300fa53c91d4df2fJust chunklen = stringToLong(f.read(4)) 1753618300613e796ff81abddde967b1092ac1dc915Just chunk = f.read(chunklen) 1763618300613e796ff81abddde967b1092ac1dc915Just assert len(chunk) == chunklen 1773618300613e796ff81abddde967b1092ac1dc915Just data.append(chunk) 1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 3: 1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 181e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, 'bad chunk code: ' + `code` 1825810aa9967488207b039cb2d300fa53c91d4df2fJust if onlyHeader: 1835810aa9967488207b039cb2d300fa53c91d4df2fJust break 1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = string.join(data, '') 1865810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1877842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1895810aa9967488207b039cb2d300fa53c91d4df2fJustdef readOther(path): 1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any (font) file, returns raw data""" 1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = f.read() 1937842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1945810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1965810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 1977842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1985810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 1995810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted and isHex(chunk[:4]): 2005810aa9967488207b039cb2d300fa53c91d4df2fJust data.append(deHexString(chunk)) 2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust return string.join(data, '') 2047842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2057842e56b97ce677b83bdab09cda48bc2d89ac75aJust# file writing tools 2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2075810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeLWFN(path, data): 208e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.FSpCreateResFile(path, "just", "LWFN", 0) 209e9601bf9e1253f77b8a66f27685fae453ce98b14Just resRef = Res.FSpOpenResFile(path, 2) # write-only 2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 211e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = 501 2135810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2145810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2155810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2197842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 2207842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Resource(chr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2]) 2217842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[LWFNCHUNKSIZE - 2:] 2237842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = resID + 1 2247842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Resource(chr(5) + '\0') 2257842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2267842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 227e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2295810aa9967488207b039cb2d300fa53c91d4df2fJustdef writePFB(path, data): 2305810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2313618300613e796ff81abddde967b1092ac1dc915Just f = open(path, "wb") 2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 2335810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2345810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2357842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2387842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chr(128) + chr(code)) 2395810aa9967488207b039cb2d300fa53c91d4df2fJust f.write(longToString(len(chunk))) 2407842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 2417842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chr(128) + chr(3)) 2427842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2437842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 24422433b1f52c34a04cf13e21103ee978f7f5b501cjvr if haveMacSupport: 2453618300613e796ff81abddde967b1092ac1dc915Just fss = macfs.FSSpec(path) 2467842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss.SetCreatorType('mdos', 'BINA') 2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2485810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeOther(path, data, dohex = 0): 2495810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2507842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "wb") 2517842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 2527842e56b97ce677b83bdab09cda48bc2d89ac75aJust hexlinelen = HEXLINELENGTH / 2 2535810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2545810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2567842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2577842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code == 2 and dohex: 2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 260c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just f.write(eexec.hexString(chunk[:hexlinelen])) 2617842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write('\r') 2627842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[hexlinelen:] 2637842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 26722433b1f52c34a04cf13e21103ee978f7f5b501cjvr if haveMacSupport: 2687842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss = macfs.FSSpec(path) 2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss.SetCreatorType('R*ch', 'TEXT') # BBEdit text file 2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2727842e56b97ce677b83bdab09cda48bc2d89ac75aJust# decryption tools 2737842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2747842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGIN = "currentfile eexec" 2757842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECEND = '0' * 64 2767842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECINTERNALEND = "currentfile closefile" 2777842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGINMARKER = "%-- eexec start\r" 2787842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECENDMARKER = "%-- eexec end\r" 2797842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2807842e56b97ce677b83bdab09cda48bc2d89ac75aJust_ishexRE = re.compile('[0-9A-Fa-f]*$') 2817842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2825810aa9967488207b039cb2d300fa53c91d4df2fJustdef isHex(text): 2837842e56b97ce677b83bdab09cda48bc2d89ac75aJust return _ishexRE.match(text) is not None 2847842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2865810aa9967488207b039cb2d300fa53c91d4df2fJustdef decryptType1(data): 2875810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2887842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 2895810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2905810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2915810aa9967488207b039cb2d300fa53c91d4df2fJust if isHex(chunk[:4]): 2925810aa9967488207b039cb2d300fa53c91d4df2fJust chunk = deHexString(chunk) 293c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just decrypted, R = eexec.decrypt(chunk, 55665) 2947842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[4:] 2957842e56b97ce677b83bdab09cda48bc2d89ac75aJust if decrypted[-len(EEXECINTERNALEND)-1:-1] <> EEXECINTERNALEND \ 2967842e56b97ce677b83bdab09cda48bc2d89ac75aJust and decrypted[-len(EEXECINTERNALEND)-2:-2] <> EEXECINTERNALEND: 297e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, "invalid end of eexec part" 2987842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r' 2997842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER) 3007842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3017842e56b97ce677b83bdab09cda48bc2d89ac75aJust if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN: 3027842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk[:-len(EEXECBEGIN)-1]) 3037842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3047842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 3057842e56b97ce677b83bdab09cda48bc2d89ac75aJust return string.join(data, '') 3067842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3075810aa9967488207b039cb2d300fa53c91d4df2fJustdef findEncryptedChunks(data): 3087842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks = [] 3097842e56b97ce677b83bdab09cda48bc2d89ac75aJust while 1: 3105810aa9967488207b039cb2d300fa53c91d4df2fJust eBegin = string.find(data, EEXECBEGIN) 3115810aa9967488207b039cb2d300fa53c91d4df2fJust if eBegin < 0: 3127842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 31390290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr eBegin = eBegin + len(EEXECBEGIN) + 1 3145810aa9967488207b039cb2d300fa53c91d4df2fJust eEnd = string.find(data, EEXECEND, eBegin) 3155810aa9967488207b039cb2d300fa53c91d4df2fJust if eEnd < 0: 316e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, "can't find end of eexec part" 317db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr cypherText = data[eBegin:eEnd + 2] 318db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr plainText, R = eexec.decrypt(cypherText, 55665) 319db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr eEndLocal = string.find(plainText, EEXECINTERNALEND) 320db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr if eEndLocal < 0: 321db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr raise T1Error, "can't find end of eexec part" 322db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr eEnd = eBegin + eEndLocal + len(EEXECINTERNALEND) + 1 32390290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr chunks.append((0, data[:eBegin])) 32490290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr chunks.append((1, data[eBegin:eEnd])) 3255810aa9967488207b039cb2d300fa53c91d4df2fJust data = data[eEnd:] 3267842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks.append((0, data)) 3277842e56b97ce677b83bdab09cda48bc2d89ac75aJust return chunks 3287842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3295810aa9967488207b039cb2d300fa53c91d4df2fJustdef deHexString(hexstring): 330c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just return eexec.deHexString(string.join(string.split(hexstring), "")) 3317842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3327842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3337842e56b97ce677b83bdab09cda48bc2d89ac75aJust# Type 1 assertion 3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3357842e56b97ce677b83bdab09cda48bc2d89ac75aJust_fontType1RE = re.compile(r"/FontType\s+1\s+def") 3367842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3375810aa9967488207b039cb2d300fa53c91d4df2fJustdef assertType1(data): 3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust for head in ['%!PS-AdobeFont', '%!FontType1-1.0']: 3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust if data[:len(head)] == head: 3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 3417842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 342e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, "not a PostScript font" 3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not _fontType1RE.search(data): 344e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, "not a Type 1 font" 3457842e56b97ce677b83bdab09cda48bc2d89ac75aJust if string.find(data, "currentfile eexec") < 0: 346e568dc77d3baccc303d5115e2686f126c96c8a26jvr raise T1Error, "not an encrypted Type 1 font" 3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust # XXX what else? 3487842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 3497842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3507842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3517842e56b97ce677b83bdab09cda48bc2d89ac75aJust# pfb helpers 3527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3535810aa9967488207b039cb2d300fa53c91d4df2fJustdef longToString(long): 3547842e56b97ce677b83bdab09cda48bc2d89ac75aJust str = "" 3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 3567842e56b97ce677b83bdab09cda48bc2d89ac75aJust str = str + chr((long & (0xff << (i * 8))) >> i * 8) 3577842e56b97ce677b83bdab09cda48bc2d89ac75aJust return str 3587842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3595810aa9967488207b039cb2d300fa53c91d4df2fJustdef stringToLong(str): 3607842e56b97ce677b83bdab09cda48bc2d89ac75aJust if len(str) <> 4: 3617842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise ValueError, 'string must be 4 bytes long' 3627842e56b97ce677b83bdab09cda48bc2d89ac75aJust long = 0 3637842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 3647842e56b97ce677b83bdab09cda48bc2d89ac75aJust long = long + (ord(str[i]) << (i * 8)) 3657842e56b97ce677b83bdab09cda48bc2d89ac75aJust return long 3665810aa9967488207b039cb2d300fa53c91d4df2fJust 367