cffLib.py revision e327558aa54f182912a26b8313f3af5a07bc2e77
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com"""cffLib.py -- read/write tools for Adobe CFF fonts.""" 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# $Id: cffLib.py,v 1.9 2002-05-14 12:22:03 jvr Exp $ 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.comimport struct, sstruct 8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.comimport string 98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comimport types 108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comfrom fontTools.misc import psCharStrings 118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comcffHeaderFormat = """ 148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com major: B 158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com minor: B 168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com hdrSize: B 178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com offSize: B 188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com""" 198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comclass CFFFontSet: 218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def __init__(self): 238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.fonts = {} 248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def decompile(self, file): 268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com sstruct.unpack(cffHeaderFormat, file.read(4), self) 278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com assert self.major == 1 and self.minor == 0, \ 288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com "unknown CFF format: %d.%d" % (self.major, self.minor) 298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.fontNames = readINDEX(file) 318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com topDicts = readINDEX(file) 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com strings = IndexedStrings(readINDEX(file)) 338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com globalSubrs = readINDEX(file) 348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.GlobalSubrs = map(psCharStrings.T2CharString, globalSubrs) 358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for i in range(len(topDicts)): 378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com font = self.fonts[self.fontNames[i]] = CFFFont() 388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com font.GlobalSubrs = self.GlobalSubrs # Hmm. 398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com file.seek(0, 0) 408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com font.decompile(file, topDicts[i], strings, self) # maybe only 'on demand'? 418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def compile(self): 438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com strings = IndexedStrings() 448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com XXXX 458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def toXML(self, xmlWriter, progress=None): 478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for fontName in self.fontNames: 498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.begintag("CFFFont", name=fontName) 508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com font = self.fonts[fontName] 528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com font.toXML(xmlWriter, progress) 538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.endtag("CFFFont") 548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.begintag("GlobalSubrs") 578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for i in range(len(self.GlobalSubrs)): 598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.begintag("CharString", id=i) 618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.GlobalSubrs[i].toXML(xmlWriter) 638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.endtag("CharString") 648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.endtag("GlobalSubrs") 678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def fromXML(self, (name, attrs, content)): 718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xxx 728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comclass CFFFont: 758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com defaults = topDictDefaults 778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def __init__(self): 798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com pass 808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def __getattr__(self, attr): 828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if not self.defaults.has_key(attr): 838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com raise AttributeError, attr 848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return self.defaults[attr] 858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def fromDict(self, dict): 878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.__dict__.update(dict) 888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def decompileCID(self, data, strings): 908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com offset = self.FDArray 918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com fontDicts, restdata = readINDEX(data[offset:]) 928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com subFonts = [] 938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for topDictData in fontDicts: 948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com subFont = CFFFont() 958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com subFonts.append(subFont) 968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com subFont.decompile(data, topDictData, strings, None) 978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com raise NotImplementedError 998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def decompile(self, file, topDictData, strings, fontSet): 1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com top = TopDictDecompiler(strings) 102d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com top.decompile(topDictData) 1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.fromDict(top.getDict()) 1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if hasattr(self, "ROS"): 1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com isCID = 1 1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # XXX CID subFonts 1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else: 10964cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org isCID = 0 1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com size, offset = self.Private 1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com file.seek(offset, 0) 1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com privateData = file.read(size) 1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com file.seek(offset, 0) 1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com assert len(privateData) == size 1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.Private = PrivateDict() 1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.Private.decompile(file, privateData, strings) 1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com file.seek(self.CharStrings) 1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com rawCharStrings = readINDEX(file) 1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com nGlyphs = len(rawCharStrings) 1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # get charset (or rather: get glyphNames) 1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if self.charset == 0: 1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xxx # standard charset 1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else: 1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com file.seek(self.charset) 12764cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org format = ord(file.read(1)) 1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if format == 0: 12964cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org xxx 1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com elif format == 1: 1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charset = parseCharsetFormat1(nGlyphs, file, strings, isCID) 1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com elif format == 2: 1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charset = parseCharsetFormat2(nGlyphs, file, strings, isCID) 1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com elif format == 3: 1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xxx 1368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else: 1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xxx 138d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com self.charset = charset 1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com assert len(charset) == nGlyphs 1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.CharStrings = charStrings = {} 1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if self.CharstringType == 2: 1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # Type 2 CharStrings 1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charStringClass = psCharStrings.T2CharString 1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else: 1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # Type 1 CharStrings 1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charStringClass = psCharStrings.T1CharString 1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for i in range(nGlyphs): 1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charStrings[charset[i]] = charStringClass(rawCharStrings[i]) 1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com assert len(charStrings) == nGlyphs 1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # XXX Encoding! 1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com encoding = self.Encoding 1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if encoding not in (0, 1): 1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # encoding is an _offset_ from the beginning of 'data' to an encoding subtable 1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com XXX 1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.Encoding = encoding 1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def getGlyphOrder(self): 1608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return self.charset 1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def setGlyphOrder(self, glyphOrder): 1638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.charset = glyphOrder 1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def decompileAllCharStrings(self): 1668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if self.CharstringType == 2: 1678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # Type 2 CharStrings 1688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com decompiler = psCharStrings.SimpleT2Decompiler(self.Private.Subrs, self.GlobalSubrs) 1698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for charString in self.CharStrings.values(): 1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if charString.needsDecompilation(): 1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com decompiler.reset() 1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com decompiler.execute(charString) 1738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else: 1748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # Type 1 CharStrings 17564cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org for charString in self.CharStrings.values(): 1768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com charString.decompile() 1778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def toXML(self, xmlWriter, progress=None): 1798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 1808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # first dump the simple values 1818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.toXMLSimpleValues(xmlWriter) 1828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # dump charset 1848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # XXX 1858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # decompile all charstrings 1878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if progress: 1888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com progress.setlabel("Decompiling CharStrings...") 1898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.decompileAllCharStrings() 1908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # dump private dict 1928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.begintag("Private") 1938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 1948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.Private.toXML(xmlWriter) 1958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.endtag("Private") 1968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com xmlWriter.newline() 1978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com self.toXMLCharStrings(xmlWriter, progress) 1998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 2008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com def toXMLSimpleValues(self, xmlWriter): 2018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com keys = self.__dict__.keys() 20264cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org keys.remove("CharStrings") 20364cc579efa7e416c7298ed159d76b074b283c0f9senorblanco@chromium.org keys.remove("Private") 2048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com keys.remove("charset") 2058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com keys.remove("GlobalSubrs") 2068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com keys.sort() 2078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for key in keys: 2088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com value = getattr(self, key) 2098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if key == "Encoding": 2108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if value == 0: 2118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com # encoding is (Adobe) Standard Encoding 212 value = "StandardEncoding" 213 elif value == 1: 214 # encoding is Expert Encoding 215 value = "ExpertEncoding" 216 if type(value) == types.ListType: 217 value = string.join(map(str, value), " ") 218 else: 219 value = str(value) 220 xmlWriter.begintag(key) 221 if hasattr(value, "toXML"): 222 xmlWriter.newline() 223 value.toXML(xmlWriter) 224 xmlWriter.newline() 225 else: 226 xmlWriter.write(value) 227 xmlWriter.endtag(key) 228 xmlWriter.newline() 229 xmlWriter.newline() 230 231 def toXMLCharStrings(self, xmlWriter, progress=None): 232 charStrings = self.CharStrings 233 xmlWriter.newline() 234 xmlWriter.begintag("CharStrings") 235 xmlWriter.newline() 236 glyphNames = charStrings.keys() 237 glyphNames.sort() 238 for glyphName in glyphNames: 239 if progress: 240 progress.setlabel("Dumping 'CFF ' table... (%s)" % glyphName) 241 progress.increment() 242 xmlWriter.newline() 243 charString = charStrings[glyphName] 244 xmlWriter.begintag("CharString", name=glyphName) 245 xmlWriter.newline() 246 charString.toXML(xmlWriter) 247 xmlWriter.endtag("CharString") 248 xmlWriter.newline() 249 xmlWriter.newline() 250 xmlWriter.endtag("CharStrings") 251 xmlWriter.newline() 252 253 254class PrivateDict: 255 256 defaults = privateDictDefaults 257 258 def __init__(self): 259 pass 260 261 def decompile(self, data, privateData, strings): 262 p = PrivateDictDecompiler(strings) 263 p.decompile(privateData) 264 self.fromDict(p.getDict()) 265 266 # get local subrs 267 #print "YYY Private.Subrs:", self.Subrs 268 if hasattr(self, "Subrs"): 269 chunk = data[self.Subrs:] 270 localSubrs, restdata = readINDEX(chunk) 271 self.Subrs = map(psCharStrings.T2CharString, localSubrs) 272 else: 273 self.Subrs = [] 274 275 def toXML(self, xmlWriter): 276 xmlWriter.newline() 277 keys = self.__dict__.keys() 278 keys.remove("Subrs") 279 for key in keys: 280 value = getattr(self, key) 281 if type(value) == types.ListType: 282 value = string.join(map(str, value), " ") 283 else: 284 value = str(value) 285 xmlWriter.begintag(key) 286 xmlWriter.write(value) 287 xmlWriter.endtag(key) 288 xmlWriter.newline() 289 # write subroutines 290 xmlWriter.newline() 291 xmlWriter.begintag("Subrs") 292 xmlWriter.newline() 293 for i in range(len(self.Subrs)): 294 xmlWriter.newline() 295 xmlWriter.begintag("CharString", id=i) 296 xmlWriter.newline() 297 self.Subrs[i].toXML(xmlWriter) 298 xmlWriter.endtag("CharString") 299 xmlWriter.newline() 300 xmlWriter.newline() 301 xmlWriter.endtag("Subrs") 302 xmlWriter.newline() 303 xmlWriter.newline() 304 305 def __getattr__(self, attr): 306 if not self.defaults.has_key(attr): 307 raise AttributeError, attr 308 return self.defaults[attr] 309 310 def fromDict(self, dict): 311 self.__dict__.update(dict) 312 313 314def readINDEX(file): 315 count, = struct.unpack(">H", file.read(2)) 316 offSize = ord(file.read(1)) 317 offsets = [] 318 for index in range(count+1): 319 chunk = file.read(offSize) 320 chunk = '\0' * (4 - offSize) + chunk 321 offset, = struct.unpack(">L", chunk) 322 offset = int(offset) 323 offsets.append(offset) 324 prev = offsets[0] 325 stuff = [] 326 next = offsets[0] 327 for next in offsets[1:]: 328 chunk = file.read(next - prev) 329 assert len(chunk) == next - prev 330 stuff.append(chunk) 331 prev = next 332 return stuff 333 334 335def parseCharsetFormat1(nGlyphs, file, strings, isCID): 336 charset = ['.notdef'] 337 count = 1 338 while count < nGlyphs: 339 first, = struct.unpack(">H", file.read(2)) 340 nLeft = ord(file.read(1)) 341 if isCID: 342 for CID in range(first, first+nLeft+1): 343 charset.append(CID) 344 else: 345 for SID in range(first, first+nLeft+1): 346 charset.append(strings[SID]) 347 count = count + nLeft + 1 348 return charset 349 350 351def parseCharsetFormat2(nGlyphs, file, strings, isCID): 352 charset = ['.notdef'] 353 count = 1 354 while count < nGlyphs: 355 first, = struct.unpack(">H", file.read(2)) 356 nLeft, = struct.unpack(">H", file.read(2)) 357 if isCID: 358 for CID in range(first, first+nLeft+1): 359 charset.append(CID) 360 else: 361 for SID in range(first, first+nLeft+1): 362 charset.append(strings[SID]) 363 count = count + nLeft + 1 364 return charset 365 366 367topDictOperators = [ 368# opcode name argument type 369 (0, 'version', 'SID'), 370 (1, 'Notice', 'SID'), 371 (2, 'FullName', 'SID'), 372 (3, 'FamilyName', 'SID'), 373 (4, 'Weight', 'SID'), 374 (5, 'FontBBox', 'array'), 375 (13, 'UniqueID', 'number'), 376 (14, 'XUID', 'array'), 377 (15, 'charset', 'number'), 378 (16, 'Encoding', 'number'), 379 (17, 'CharStrings', 'number'), 380 (18, 'Private', ('number', 'number')), 381 ((12, 0), 'Copyright', 'SID'), 382 ((12, 1), 'isFixedPitch', 'number'), 383 ((12, 2), 'ItalicAngle', 'number'), 384 ((12, 3), 'UnderlinePosition', 'number'), 385 ((12, 4), 'UnderlineThickness', 'number'), 386 ((12, 5), 'PaintType', 'number'), 387 ((12, 6), 'CharstringType', 'number'), 388 ((12, 7), 'FontMatrix', 'array'), 389 ((12, 8), 'StrokeWidth', 'number'), 390 ((12, 20), 'SyntheticBase', 'number'), 391 ((12, 21), 'PostScript', 'SID'), 392 ((12, 22), 'BaseFontName', 'SID'), 393 # CID additions 394 ((12, 30), 'ROS', ('SID', 'SID', 'number')), 395 ((12, 31), 'CIDFontVersion', 'number'), 396 ((12, 32), 'CIDFontRevision', 'number'), 397 ((12, 33), 'CIDFontType', 'number'), 398 ((12, 34), 'CIDCount', 'number'), 399 ((12, 35), 'UIDBase', 'number'), 400 ((12, 36), 'FDArray', 'number'), 401 ((12, 37), 'FDSelect', 'number'), 402 ((12, 38), 'FontName', 'SID'), 403] 404 405topDictDefaults = { 406 'isFixedPitch': 0, 407 'ItalicAngle': 0, 408 'UnderlineThickness': 50, 409 'PaintType': 0, 410 'CharstringType': 2, 411 'FontMatrix': [0.001, 0, 0, 0.001, 0, 0], 412 'FontBBox': [0, 0, 0, 0], 413 'StrokeWidth': 0, 414 'charset': 0, 415 'Encoding': 0, 416 # CID defaults 417 'CIDFontVersion': 0, 418 'CIDFontRevision': 0, 419 'CIDFontType': 0, 420 'CIDCount': 8720, 421} 422 423class TopDictDecompiler(psCharStrings.DictDecompiler): 424 425 operators = psCharStrings.buildOperatorDict(topDictOperators) 426 dictDefaults = topDictDefaults 427 428 429privateDictOperators = [ 430# opcode name argument type 431 (6, 'BlueValues', 'array'), 432 (7, 'OtherBlues', 'array'), 433 (8, 'FamilyBlues', 'array'), 434 (9, 'FamilyOtherBlues', 'array'), 435 (10, 'StdHW', 'number'), 436 (11, 'StdVW', 'number'), 437 (19, 'Subrs', 'number'), 438 (20, 'defaultWidthX', 'number'), 439 (21, 'nominalWidthX', 'number'), 440 ((12, 9), 'BlueScale', 'number'), 441 ((12, 10), 'BlueShift', 'number'), 442 ((12, 11), 'BlueFuzz', 'number'), 443 ((12, 12), 'StemSnapH', 'array'), 444 ((12, 13), 'StemSnapV', 'array'), 445 ((12, 14), 'ForceBold', 'number'), 446 ((12, 17), 'LanguageGroup', 'number'), 447 ((12, 18), 'ExpansionFactor', 'number'), 448 ((12, 19), 'initialRandomSeed', 'number'), 449] 450 451privateDictDefaults = { 452 'defaultWidthX': 0, 453 'nominalWidthX': 0, 454 'BlueScale': 0.039625, 455 'BlueShift': 7, 456 'BlueFuzz': 1, 457 'ForceBold': 0, 458 'LanguageGroup': 0, 459 'ExpansionFactor': 0.06, 460 'initialRandomSeed': 0, 461} 462 463class PrivateDictDecompiler(psCharStrings.DictDecompiler): 464 465 operators = psCharStrings.buildOperatorDict(privateDictOperators) 466 dictDefaults = privateDictDefaults 467 468 469class IndexedStrings: 470 471 def __init__(self, strings=None): 472 if strings is None: 473 strings = [] 474 self.strings = strings 475 476 def __getitem__(self, SID): 477 if SID < cffStandardStringCount: 478 return cffStandardStrings[SID] 479 else: 480 return self.strings[SID - cffStandardStringCount] 481 482 def getSID(self, s): 483 if not hasattr(self, "stringMapping"): 484 self.buildStringMapping() 485 if cffStandardStringMapping.has_key(s): 486 SID = cffStandardStringMapping[s] 487 if self.stringMapping.has_key(s): 488 SID = self.stringMapping[s] 489 else: 490 SID = len(self.strings) + cffStandardStringCount 491 self.strings.append(s) 492 self.stringMapping[s] = SID 493 return SID 494 495 def getStrings(self): 496 return self.strings 497 498 def buildStringMapping(self): 499 self.stringMapping = {} 500 for index in range(len(self.strings)): 501 self.stringMapping[self.strings[index]] = index + cffStandardStringCount 502 503 504# The 391 Standard Strings as used in the CFF format. 505# from Adobe Technical None #5176, version 1.0, 18 March 1998 506 507cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 508 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 509 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 510 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 511 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 512 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 513 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 514 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 515 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 516 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 517 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 518 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 519 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 520 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 521 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 522 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 523 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 524 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 525 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 526 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 527 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 528 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 529 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 530 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 531 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 532 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 533 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 534 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 535 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 536 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 537 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 538 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 539 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 540 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 541 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 542 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 543 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 544 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 545 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 546 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 547 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 548 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 549 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 550 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 551 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 552 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 553 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 554 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 555 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 556 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 557 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 558 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 559 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 560 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 561 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 562 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 563 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 564 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 565 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 566 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 567 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', 568 '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 569 'Semibold' 570] 571 572cffStandardStringCount = 391 573assert len(cffStandardStrings) == cffStandardStringCount 574# build reverse mapping 575cffStandardStringMapping = {} 576for _i in range(cffStandardStringCount): 577 cffStandardStringMapping[cffStandardStrings[_i]] = _i 578 579 580