t1Lib.py revision e9601bf9e1253f77b8a66f27685fae453ce98b14
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 287842e56b97ce677b83bdab09cda48bc2d89ac75aJustif os.name == 'mac': 297842e56b97ce677b83bdab09cda48bc2d89ac75aJust import Res 307842e56b97ce677b83bdab09cda48bc2d89ac75aJust import macfs 317842e56b97ce677b83bdab09cda48bc2d89ac75aJust 327842e56b97ce677b83bdab09cda48bc2d89ac75aJusterror = 't1Lib.error' 337842e56b97ce677b83bdab09cda48bc2d89ac75aJust 347842e56b97ce677b83bdab09cda48bc2d89ac75aJust 357842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass T1Font: 367842e56b97ce677b83bdab09cda48bc2d89ac75aJust 373618300613e796ff81abddde967b1092ac1dc915Just """Type 1 font class. 383618300613e796ff81abddde967b1092ac1dc915Just 393618300613e796ff81abddde967b1092ac1dc915Just Uses a minimal interpeter that supports just about enough PS to parse 403618300613e796ff81abddde967b1092ac1dc915Just Type 1 fonts. 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust """ 427842e56b97ce677b83bdab09cda48bc2d89ac75aJust 437842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __init__(self, path=None): 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust if path is not None: 457842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data, type = read(path) 467842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 477842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # XXX 487842e56b97ce677b83bdab09cda48bc2d89ac75aJust 497842e56b97ce677b83bdab09cda48bc2d89ac75aJust def saveAs(self, path, type): 507842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.write(path, self.getData(), type) 517842e56b97ce677b83bdab09cda48bc2d89ac75aJust 527842e56b97ce677b83bdab09cda48bc2d89ac75aJust def getData(self): 533618300613e796ff81abddde967b1092ac1dc915Just # XXX Todo: if the data has been converted to Python object, 543618300613e796ff81abddde967b1092ac1dc915Just # recreate the PS stream 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __getitem__(self, key): 587842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "font"): 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.parse() 603618300613e796ff81abddde967b1092ac1dc915Just return self.font[key] 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust 627842e56b97ce677b83bdab09cda48bc2d89ac75aJust def parse(self): 63528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psLib 64528614e6e254dfe3c501ff440c291c6c55de5e6fJust from fontTools.misc import psCharStrings 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.font = psLib.suckfont(self.data) 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust charStrings = self.font["CharStrings"] 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust lenIV = self.font["Private"].get("lenIV", 4) 687842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert lenIV >= 0 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust for glyphName, charString in charStrings.items(): 70c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(charString, 4330) 717842e56b97ce677b83bdab09cda48bc2d89ac75aJust charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:]) 727842e56b97ce677b83bdab09cda48bc2d89ac75aJust subrs = self.font["Private"]["Subrs"] 737842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(subrs)): 74c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just charString, R = eexec.decrypt(subrs[i], 4330) 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust subrs[i] = psCharStrings.T1CharString(charString[lenIV:]) 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust del self.data 777842e56b97ce677b83bdab09cda48bc2d89ac75aJust 787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust 803618300613e796ff81abddde967b1092ac1dc915Just# low level T1 data read and write functions 817842e56b97ce677b83bdab09cda48bc2d89ac75aJust 827842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef read(path): 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any Type 1 font file, returns raw data""" 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust normpath = string.lower(path) 857842e56b97ce677b83bdab09cda48bc2d89ac75aJust if os.name == 'mac': 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss = macfs.FSSpec(path) 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust creator, type = fss.GetCreatorType() 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust if type == 'LWFN': 895810aa9967488207b039cb2d300fa53c91d4df2fJust return readLWFN(path), 'LWFN' 907842e56b97ce677b83bdab09cda48bc2d89ac75aJust if normpath[-4:] == '.pfb': 915810aa9967488207b039cb2d300fa53c91d4df2fJust return readPFB(path), 'PFB' 927842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 935810aa9967488207b039cb2d300fa53c91d4df2fJust return readOther(path), 'OTHER' 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust 957842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef write(path, data, kind='OTHER', dohex=0): 965810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 977842e56b97ce677b83bdab09cda48bc2d89ac75aJust kind = string.upper(kind) 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 1 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust if kind == 'LWFN': 1055810aa9967488207b039cb2d300fa53c91d4df2fJust writeLWFN(path, data) 1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif kind == 'PFB': 1075810aa9967488207b039cb2d300fa53c91d4df2fJust writePFB(path, data) 1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1095810aa9967488207b039cb2d300fa53c91d4df2fJust writeOther(path, data, dohex) 1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust err = 0 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust if err and not DEBUG: 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust os.remove(path) 1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust except os.error: 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust# -- internal -- 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJustLWFNCHUNKSIZE = 2000 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJustHEXLINELENGTH = 80 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1255810aa9967488207b039cb2d300fa53c91d4df2fJustdef readLWFN(path): 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads an LWFN font file, returns raw data""" 127e9601bf9e1253f77b8a66f27685fae453ce98b14Just resRef = Res.FSpOpenResFile(path, 1) # read-only 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 129e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust n = Res.Count1Resources('POST') 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(501, 501 + n): 1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Get1Resource('POST', i) 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = ord(res.data[0]) 1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ord(res.data[1]) <> 0: 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, 'corrupt LWFN file' 1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(res.data[2:]) 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code in [3, 5]: 1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 4: 1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(f.read()) 1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 0: 1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust pass # comment, ignore 1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, 'bad chunk code: ' + `code` 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 150e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = string.join(data, '') 1525810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1555810aa9967488207b039cb2d300fa53c91d4df2fJustdef readPFB(path, onlyHeader=0): 1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads a PFB font file, returns raw data""" 1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust while 1: 1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust if f.read(1) <> chr(128): 1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, 'corrupt PFB file' 1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = ord(f.read(1)) 1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code in [1, 2]: 1645810aa9967488207b039cb2d300fa53c91d4df2fJust chunklen = stringToLong(f.read(4)) 1653618300613e796ff81abddde967b1092ac1dc915Just chunk = f.read(chunklen) 1663618300613e796ff81abddde967b1092ac1dc915Just assert len(chunk) == chunklen 1673618300613e796ff81abddde967b1092ac1dc915Just data.append(chunk) 1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif code == 3: 1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, 'bad chunk code: ' + `code` 1725810aa9967488207b039cb2d300fa53c91d4df2fJust if onlyHeader: 1735810aa9967488207b039cb2d300fa53c91d4df2fJust break 1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = string.join(data, '') 1765810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1777842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1795810aa9967488207b039cb2d300fa53c91d4df2fJustdef readOther(path): 1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust """reads any (font) file, returns raw data""" 1817842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "rb") 1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = f.read() 1837842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 1845810aa9967488207b039cb2d300fa53c91d4df2fJust assertType1(data) 1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1865810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 1877842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 1885810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 1895810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted and isHex(chunk[:4]): 1905810aa9967488207b039cb2d300fa53c91d4df2fJust data.append(deHexString(chunk)) 1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 1937842e56b97ce677b83bdab09cda48bc2d89ac75aJust return string.join(data, '') 1947842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust# file writing tools 1967842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1975810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeLWFN(path, data): 198e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.FSpCreateResFile(path, "just", "LWFN", 0) 199e9601bf9e1253f77b8a66f27685fae453ce98b14Just resRef = Res.FSpOpenResFile(path, 2) # write-only 2007842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 201e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.UseResFile(resRef) 2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = 501 2035810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2045810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2055810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2077842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Resource(chr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2]) 2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[LWFNCHUNKSIZE - 2:] 2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust resID = resID + 1 2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust res = Res.Resource(chr(5) + '\0') 2157842e56b97ce677b83bdab09cda48bc2d89ac75aJust res.AddResource('POST', resID, '') 2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 217e9601bf9e1253f77b8a66f27685fae453ce98b14Just Res.CloseResFile(resRef) 2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2195810aa9967488207b039cb2d300fa53c91d4df2fJustdef writePFB(path, data): 2205810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2213618300613e796ff81abddde967b1092ac1dc915Just f = open(path, "wb") 2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 2235810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2245810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2257842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2267842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2277842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chr(128) + chr(code)) 2295810aa9967488207b039cb2d300fa53c91d4df2fJust f.write(longToString(len(chunk))) 2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 2317842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chr(128) + chr(3)) 2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 2347842e56b97ce677b83bdab09cda48bc2d89ac75aJust if os.name == 'mac': 2353618300613e796ff81abddde967b1092ac1dc915Just fss = macfs.FSSpec(path) 2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss.SetCreatorType('mdos', 'BINA') 2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2385810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeOther(path, data, dohex = 0): 2395810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2407842e56b97ce677b83bdab09cda48bc2d89ac75aJust f = open(path, "wb") 2417842e56b97ce677b83bdab09cda48bc2d89ac75aJust try: 2427842e56b97ce677b83bdab09cda48bc2d89ac75aJust hexlinelen = HEXLINELENGTH / 2 2435810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2445810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2457842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 2 2467842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = 1 2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust if code == 2 and dohex: 2497842e56b97ce677b83bdab09cda48bc2d89ac75aJust while chunk: 250c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just f.write(eexec.hexString(chunk[:hexlinelen])) 2517842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write('\r') 2527842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = chunk[hexlinelen:] 2537842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2547842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.write(chunk) 2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust finally: 2567842e56b97ce677b83bdab09cda48bc2d89ac75aJust f.close() 2577842e56b97ce677b83bdab09cda48bc2d89ac75aJust if os.name == 'mac': 2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss = macfs.FSSpec(path) 2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust fss.SetCreatorType('R*ch', 'TEXT') # BBEdit text file 2607842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2617842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2627842e56b97ce677b83bdab09cda48bc2d89ac75aJust# decryption tools 2637842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2647842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGIN = "currentfile eexec" 2657842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECEND = '0' * 64 2667842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECINTERNALEND = "currentfile closefile" 2677842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGINMARKER = "%-- eexec start\r" 2687842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECENDMARKER = "%-- eexec end\r" 2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust_ishexRE = re.compile('[0-9A-Fa-f]*$') 2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2725810aa9967488207b039cb2d300fa53c91d4df2fJustdef isHex(text): 2737842e56b97ce677b83bdab09cda48bc2d89ac75aJust return _ishexRE.match(text) is not None 2747842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2757842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2765810aa9967488207b039cb2d300fa53c91d4df2fJustdef decryptType1(data): 2775810aa9967488207b039cb2d300fa53c91d4df2fJust chunks = findEncryptedChunks(data) 2787842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = [] 2795810aa9967488207b039cb2d300fa53c91d4df2fJust for isEncrypted, chunk in chunks: 2805810aa9967488207b039cb2d300fa53c91d4df2fJust if isEncrypted: 2815810aa9967488207b039cb2d300fa53c91d4df2fJust if isHex(chunk[:4]): 2825810aa9967488207b039cb2d300fa53c91d4df2fJust chunk = deHexString(chunk) 283c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just decrypted, R = eexec.decrypt(chunk, 55665) 2847842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[4:] 2857842e56b97ce677b83bdab09cda48bc2d89ac75aJust if decrypted[-len(EEXECINTERNALEND)-1:-1] <> EEXECINTERNALEND \ 2867842e56b97ce677b83bdab09cda48bc2d89ac75aJust and decrypted[-len(EEXECINTERNALEND)-2:-2] <> EEXECINTERNALEND: 2877842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, "invalid end of eexec part" 2887842e56b97ce677b83bdab09cda48bc2d89ac75aJust decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r' 2897842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER) 2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2917842e56b97ce677b83bdab09cda48bc2d89ac75aJust if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN: 2927842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk[:-len(EEXECBEGIN)-1]) 2937842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2947842e56b97ce677b83bdab09cda48bc2d89ac75aJust data.append(chunk) 2957842e56b97ce677b83bdab09cda48bc2d89ac75aJust return string.join(data, '') 2967842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2975810aa9967488207b039cb2d300fa53c91d4df2fJustdef findEncryptedChunks(data): 2987842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks = [] 2997842e56b97ce677b83bdab09cda48bc2d89ac75aJust while 1: 3005810aa9967488207b039cb2d300fa53c91d4df2fJust eBegin = string.find(data, EEXECBEGIN) 3015810aa9967488207b039cb2d300fa53c91d4df2fJust if eBegin < 0: 3027842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 3035810aa9967488207b039cb2d300fa53c91d4df2fJust eEnd = string.find(data, EEXECEND, eBegin) 3045810aa9967488207b039cb2d300fa53c91d4df2fJust if eEnd < 0: 3057842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, "can't find end of eexec part" 3065810aa9967488207b039cb2d300fa53c91d4df2fJust chunks.append((0, data[:eBegin + len(EEXECBEGIN) + 1])) 3075810aa9967488207b039cb2d300fa53c91d4df2fJust chunks.append((1, data[eBegin + len(EEXECBEGIN) + 1:eEnd])) 3085810aa9967488207b039cb2d300fa53c91d4df2fJust data = data[eEnd:] 3097842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunks.append((0, data)) 3107842e56b97ce677b83bdab09cda48bc2d89ac75aJust return chunks 3117842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3125810aa9967488207b039cb2d300fa53c91d4df2fJustdef deHexString(hexstring): 313c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just return eexec.deHexString(string.join(string.split(hexstring), "")) 3147842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3157842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3167842e56b97ce677b83bdab09cda48bc2d89ac75aJust# Type 1 assertion 3177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3187842e56b97ce677b83bdab09cda48bc2d89ac75aJust_fontType1RE = re.compile(r"/FontType\s+1\s+def") 3197842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3205810aa9967488207b039cb2d300fa53c91d4df2fJustdef assertType1(data): 3217842e56b97ce677b83bdab09cda48bc2d89ac75aJust for head in ['%!PS-AdobeFont', '%!FontType1-1.0']: 3227842e56b97ce677b83bdab09cda48bc2d89ac75aJust if data[:len(head)] == head: 3237842e56b97ce677b83bdab09cda48bc2d89ac75aJust break 3247842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3257842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, "not a PostScript font" 3267842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not _fontType1RE.search(data): 3277842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, "not a Type 1 font" 3287842e56b97ce677b83bdab09cda48bc2d89ac75aJust if string.find(data, "currentfile eexec") < 0: 3297842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise error, "not an encrypted Type 1 font" 3307842e56b97ce677b83bdab09cda48bc2d89ac75aJust # XXX what else? 3317842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 3327842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3337842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust# pfb helpers 3357842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3365810aa9967488207b039cb2d300fa53c91d4df2fJustdef longToString(long): 3377842e56b97ce677b83bdab09cda48bc2d89ac75aJust str = "" 3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust str = str + chr((long & (0xff << (i * 8))) >> i * 8) 3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust return str 3417842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3425810aa9967488207b039cb2d300fa53c91d4df2fJustdef stringToLong(str): 3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust if len(str) <> 4: 3447842e56b97ce677b83bdab09cda48bc2d89ac75aJust raise ValueError, 'string must be 4 bytes long' 3457842e56b97ce677b83bdab09cda48bc2d89ac75aJust long = 0 3467842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(4): 3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust long = long + (ord(str[i]) << (i * 8)) 3487842e56b97ce677b83bdab09cda48bc2d89ac75aJust return long 3495810aa9967488207b039cb2d300fa53c91d4df2fJust 350