_c_m_a_p.py revision 1f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3
17842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport DefaultTable 27842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport struct 37842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport array 47842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools import ttLib 57842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval, readHex 622dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvrfrom types import TupleType 77842e56b97ce677b83bdab09cda48bc2d89ac75aJust 87842e56b97ce677b83bdab09cda48bc2d89ac75aJust 97842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass table__c_m_a_p(DefaultTable.DefaultTable): 107842e56b97ce677b83bdab09cda48bc2d89ac75aJust 117842e56b97ce677b83bdab09cda48bc2d89ac75aJust def getcmap(self, platformID, platEncID): 127842e56b97ce677b83bdab09cda48bc2d89ac75aJust for subtable in self.tables: 137842e56b97ce677b83bdab09cda48bc2d89ac75aJust if (subtable.platformID == platformID and 147842e56b97ce677b83bdab09cda48bc2d89ac75aJust subtable.platEncID == platEncID): 157842e56b97ce677b83bdab09cda48bc2d89ac75aJust return subtable 167842e56b97ce677b83bdab09cda48bc2d89ac75aJust return None # not found 177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 187842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 197842e56b97ce677b83bdab09cda48bc2d89ac75aJust tableVersion, numSubTables = struct.unpack(">HH", data[:4]) 207842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tableVersion = int(tableVersion) 217842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables = tables = [] 227842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(numSubTables): 237842e56b97ce677b83bdab09cda48bc2d89ac75aJust platformID, platEncID, offset = struct.unpack( 247842e56b97ce677b83bdab09cda48bc2d89ac75aJust ">HHl", data[4+i*8:4+(i+1)*8]) 257842e56b97ce677b83bdab09cda48bc2d89ac75aJust platformID, platEncID = int(platformID), int(platEncID) 267842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length = struct.unpack(">HH", data[offset:offset+4]) 27ae180248fdc1abcd10f782c8e32b77a62594d349Just if not length: 28ae180248fdc1abcd10f782c8e32b77a62594d349Just continue # bogus cmap subtable? 297842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not cmap_classes.has_key(format): 307842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_format_unknown(format) 317842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 327842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_classes[format](format) 337842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platformID = platformID 347842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platEncID = platEncID 357842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.decompile(data[offset:offset+int(length)], ttFont) 367842e56b97ce677b83bdab09cda48bc2d89ac75aJust tables.append(table) 377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 387842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 397842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables.sort() # sort according to the spec; see CmapSubtable.__cmp__() 407842e56b97ce677b83bdab09cda48bc2d89ac75aJust numSubTables = len(self.tables) 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust totalOffset = 4 + 8 * numSubTables 427842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = struct.pack(">HH", self.tableVersion, numSubTables) 437842e56b97ce677b83bdab09cda48bc2d89ac75aJust tableData = "" 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust done = {} # remember the data so we can reuse the "pointers" 457842e56b97ce677b83bdab09cda48bc2d89ac75aJust for table in self.tables: 467842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = table.compile(ttFont) 477842e56b97ce677b83bdab09cda48bc2d89ac75aJust if done.has_key(chunk): 487842e56b97ce677b83bdab09cda48bc2d89ac75aJust offset = done[chunk] 497842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 507842e56b97ce677b83bdab09cda48bc2d89ac75aJust offset = done[chunk] = totalOffset + len(tableData) 511b850986ef5611934a43bd31104a398e311af713Just tableData = tableData + chunk 527842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset) 537842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data + tableData 547842e56b97ce677b83bdab09cda48bc2d89ac75aJust 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("tableVersion", version=self.tableVersion) 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 587842e56b97ce677b83bdab09cda48bc2d89ac75aJust for table in self.tables: 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.toXML(writer, ttFont) 607842e56b97ce677b83bdab09cda48bc2d89ac75aJust 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 627842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name == "tableVersion": 637842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tableVersion = safeEval(attrs["version"]) 647842e56b97ce677b83bdab09cda48bc2d89ac75aJust return 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name[:12] <> "cmap_format_": 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust return 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "tables"): 687842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables = [] 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust format = safeEval(name[12]) 707842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not cmap_classes.has_key(format): 717842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_format_unknown(format) 727842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 737842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_classes[format](format) 747842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platformID = safeEval(attrs["platformID"]) 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platEncID = safeEval(attrs["platEncID"]) 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.fromXML((name, attrs, content), ttFont) 777842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables.append(table) 787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust 807842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass CmapSubtable: 817842e56b97ce677b83bdab09cda48bc2d89ac75aJust 827842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __init__(self, format): 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.format = format 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust 857842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 897842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 907842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 917842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.dumphex(self.compile(ttFont)) 927842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 937842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust 957842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 967842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.decompile(readHex(content), ttFont) 977842e56b97ce677b83bdab09cda48bc2d89ac75aJust 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __cmp__(self, other): 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust # implemented so that list.sort() sorts according to the cmap spec. 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust selfTuple = ( 1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platformID, 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platEncID, 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version, 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.__dict__) 1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust otherTuple = ( 1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.platformID, 1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.platEncID, 1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.version, 1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.__dict__) 1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust return cmp(selfTuple, otherTuple) 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_0(CmapSubtable): 1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version = struct.unpack(">HHH", data[:6]) 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == 262 == length 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray = array.array("B") 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray.fromstring(data[6:]) 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap = {} 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in range(len(glyphIdArray)): 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[charCode] = ttFont.getGlyphName(glyphIdArray[charCode]) 1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes = self.cmap.keys() 1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes.sort() 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert charCodes == range(256) # charCodes[charCode] == charCode 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in charCodes: 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust # reusing the charCodes list! 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes[charCode] = ttFont.getGlyphID(self.cmap[charCode]) 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray = array.array("B", charCodes) 1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = struct.pack(">HHH", 0, 262, self.version) + glyphIdArray.tostring() 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == 262 1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust items = self.cmap.items() 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust items.sort() 1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in items: 1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 15622dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1647842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_2(CmapSubtable): 1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version = struct.unpack(">HHH", data[:6]) 1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data = data 1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1757842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_format_4_format = ">7H" 1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1771f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 endCode[segCount] # Ending character code for each segment, last = 0xFFFF. 1781f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 reservedPad # This value should be zero 1791f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 startCode[segCount] # Starting character code for each segment 1801f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 idDelta[segCount] # Delta for all character codes in segment 1811f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 idRangeOffset[segCount] # Offset in bytes to glyph indexArray, or 0 1821f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16 glyphIndexArray[variable] # Glyph index array 1837842e56b97ce677b83bdab09cda48bc2d89ac75aJust 184542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvrdef splitRange(startCode, endCode, cmap): 1851f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Try to split a range of character codes into subranges with consecutive 1861f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # glyph IDs in such a way that the cmap4 subtable can be stored "most" 1871f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # efficiently. I can't prove I've got the optimal solution, but it seems 1881f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # to do well with the fonts I tested: none became bigger, many became smaller. 189542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if startCode == endCode: 190542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr return [], [endCode] 191542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 192542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastID = cmap[startCode] 193542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastCode = startCode 194542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr inOrder = None 195542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr orderedBegin = None 1961f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges = [] 197542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 1981f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Gather subranges in which the glyph IDs are consecutive. 199542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr for code in range(startCode + 1, endCode + 1): 200542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr glyphID = cmap[code] 201542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 202542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if glyphID - 1 == lastID: 203542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if inOrder is None or not inOrder: 204542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr inOrder = 1 205542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr orderedBegin = lastCode 206542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr else: 207542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if inOrder: 208542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr inOrder = 0 2091f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges.append((orderedBegin, lastCode)) 210542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr orderedBegin = None 211542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 212542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastID = glyphID 213542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastCode = code 214542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 215542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if inOrder: 2161f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges.append((orderedBegin, lastCode)) 217542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr assert lastCode == endCode 218542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2191f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Now filter out those new subranges that would only make the data bigger. 2201f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # A new segment cost 8 bytes, not using a new segment costs 2 bytes per 2211f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # character. 2221f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr newRanges = [] 2231f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr for b, e in subRanges: 224542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if b == startCode and e == endCode: 225542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr break # the whole range, we're fine 226542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if b == startCode or e == endCode: 227542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr threshold = 4 # split costs one more segment 228542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr else: 229542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr threshold = 8 # split costs two more segments 230542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if (e - b + 1) > threshold: 2311f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr newRanges.append((b, e)) 2321f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges = newRanges 233542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2341f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr if not subRanges: 235542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr return [], [endCode] 236542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2371f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr if subRanges[0][0] != startCode: 2381f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges.insert(0, (startCode, subRanges[0][0] - 1)) 2391f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr if subRanges[-1][1] != endCode: 2401f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges.append((subRanges[-1][1] + 1, endCode)) 2411f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr 2421f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Fill the "holes" in the segments list -- those are the segments in which 2431f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # the glyph IDs are _not_ consecutive. 244542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr i = 1 2451f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr while i < len(subRanges): 2461f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr if subRanges[i-1][1] + 1 != subRanges[i][0]: 2471f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr subRanges.insert(i, (subRanges[i-1][1] + 1, subRanges[i][0] - 1)) 248542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr i = i + 1 249542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr i = i + 1 250542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2511f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Transform the ranges into startCode/endCode lists. 252542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr start = [] 253542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr end = [] 2541f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr for b, e in subRanges: 255542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr start.append(b) 256542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr end.append(e) 257542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr start.pop(0) 258542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 259542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr assert len(start) + 1 == len(end) 260542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr return start, end 261542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 262542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2637842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_4(CmapSubtable): 2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust (format, length, self.version, segCountX2, 2677842e56b97ce677b83bdab09cda48bc2d89ac75aJust searchRange, entrySelector, rangeShift) = \ 2687842e56b97ce677b83bdab09cda48bc2d89ac75aJust struct.unpack(cmap_format_4_format, data[:14]) 2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == length, "corrupt cmap table (%d, %d)" % (len(data), length) 2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCount = segCountX2 / 2 2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust 272542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes = array.array("H") 273542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes.fromstring(data[14:]) 2747842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 275542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes.byteswap() 2767842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2777842e56b97ce677b83bdab09cda48bc2d89ac75aJust # divide the data 278542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr endCode = allCodes[:segCount] 279542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes = allCodes[segCount+1:] # the +1 is skipping the reservedPad field 280542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr startCode = allCodes[:segCount] 281542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes = allCodes[segCount:] 282542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr idDelta = allCodes[:segCount] 283542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes = allCodes[segCount:] 284542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr idRangeOffset = allCodes[:segCount] 285542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr glyphIndexArray = allCodes[segCount:] 286542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr 2877842e56b97ce677b83bdab09cda48bc2d89ac75aJust # build 2-byte character mapping 2887842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap = {} 2897842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(startCode) - 1): # don't do 0xffff! 2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in range(startCode[i], endCode[i] + 1): 2917842e56b97ce677b83bdab09cda48bc2d89ac75aJust rangeOffset = idRangeOffset[i] 2927842e56b97ce677b83bdab09cda48bc2d89ac75aJust if rangeOffset == 0: 2937842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = charCode + idDelta[i] 2947842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2957842e56b97ce677b83bdab09cda48bc2d89ac75aJust # *someone* needs to get killed. 2967842e56b97ce677b83bdab09cda48bc2d89ac75aJust index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset) 2977842e56b97ce677b83bdab09cda48bc2d89ac75aJust if glyphIndexArray[index] <> 0: # if not missing glyph 2987842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = glyphIndexArray[index] + idDelta[i] 2997842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3007842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = 0 # missing glyph 3017842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[charCode] = ttFont.getGlyphName(glyphID % 0x10000) 3027842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap 3037842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3047842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 305ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr from fontTools.ttLib.sfnt import maxPowerOfTwo 3067842e56b97ce677b83bdab09cda48bc2d89ac75aJust 307542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr cmap = {} # code:glyphID mapping 308542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr for code, glyphName in self.cmap.items(): 309542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr cmap[code] = ttFont.getGlyphID(glyphName) 310542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr codes = cmap.keys() 3117842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 3127842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3131f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Build startCode and endCode lists. 3141f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # Split the char codes in ranges of consecutive char codes, then split 3151f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # each range in more ranges of consecutive/not consecutive glyph IDs. 3161f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr # See splitRange(). 317542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastCode = codes[0] 3187842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode = [] 319542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr startCode = [lastCode] 320542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr for charCode in codes[1:]: # skip the first code, it's the first start code 321542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr if charCode == lastCode + 1: 322542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastCode = charCode 3237842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 324542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr start, end = splitRange(startCode[-1], lastCode, cmap) 325542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr startCode.extend(start) 326542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr endCode.extend(end) 3277842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode.append(charCode) 328542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr lastCode = charCode 329542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr endCode.append(lastCode) 3307842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode.append(0xffff) 3317842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode.append(0xffff) 3327842e56b97ce677b83bdab09cda48bc2d89ac75aJust 333542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr # build up rest of cruft 3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta = [] 3357842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset = [] 3367842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = [] 3377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(endCode)-1): # skip the closing codes (0xffff) 3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust indices = [] 340542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr for charCode in range(startCode[i], endCode[i] + 1): 341542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr indices.append(cmap[charCode]) 3427842e56b97ce677b83bdab09cda48bc2d89ac75aJust if indices == range(indices[0], indices[0] + len(indices)): 3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append((indices[0] - startCode[i]) % 0x10000) 3447842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(0) 3457842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 3467842e56b97ce677b83bdab09cda48bc2d89ac75aJust # someone *definitely* needs to get killed. 3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append(0) 3487842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i)) 349542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr glyphIndexArray.extend(indices) 3507842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef 3517842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(0) 3527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3537842e56b97ce677b83bdab09cda48bc2d89ac75aJust # Insane. 3547842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCount = len(endCode) 3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCountX2 = segCount * 2 356542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr maxExponent = maxPowerOfTwo(segCount) 357542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr searchRange = 2 * (2 ** maxExponent) 358542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr entrySelector = maxExponent 3597842e56b97ce677b83bdab09cda48bc2d89ac75aJust rangeShift = 2 * segCount - searchRange 3607842e56b97ce677b83bdab09cda48bc2d89ac75aJust 361542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes = array.array("H", 3627842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode + [0] + startCode + idDelta + idRangeOffset + glyphIndexArray) 3637842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 364542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr allCodes.byteswap() 365542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr data = allCodes.tostring() 3667842e56b97ce677b83bdab09cda48bc2d89ac75aJust length = struct.calcsize(cmap_format_4_format) + len(data) 3677842e56b97ce677b83bdab09cda48bc2d89ac75aJust header = struct.pack(cmap_format_4_format, self.format, length, self.version, 3687842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCountX2, searchRange, entrySelector, rangeShift) 369542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr data = header + data 370542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr return data 3717842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3727842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 3737842e56b97ce677b83bdab09cda48bc2d89ac75aJust from fontTools.unicode import Unicode 3747842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.items() 3757842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 3767842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 3777842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 3787842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 3797842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 3807842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 3817842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3827842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3837842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in codes: 3847842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 3857842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.comment(Unicode[code]) 3867842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3877842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3887842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 3897842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3907842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3917842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 3927842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 3937842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 3947842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 39522dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 3967842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 3977842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 3987842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 3997842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 4007842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 4017842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4027842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4037842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_6(CmapSubtable): 4047842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4057842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 4067842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version, firstCode, entryCount = struct.unpack( 4077842e56b97ce677b83bdab09cda48bc2d89ac75aJust ">HHHHH", data[:10]) 4087842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 4097842e56b97ce677b83bdab09cda48bc2d89ac75aJust firstCode = int(firstCode) 4107842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 4117842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data[10:] 412f6b1563e0dc4e396264d62598cac856b0959c0f7Just #assert len(data) == 2 * entryCount # XXX not true in Apple's Helvetica!!! 4137842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = array.array("H") 41443fa4be9483ec7cfc2f3c183be8bed746862b7f3Just glyphIndexArray.fromstring(data[:2 * int(entryCount)]) 4157842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 4167842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray.byteswap() 4177842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap = {} 4187842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(glyphIndexArray)): 4197842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = glyphIndexArray[i] 4207842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphName = ttFont.getGlyphName(glyphID) 4217842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[i+firstCode] = glyphName 4227842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4237842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 4247842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.keys() 4257842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 4267842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert codes == range(codes[0], codes[0] + len(codes)) 4277842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = array.array("H", [0] * len(codes)) 4287842e56b97ce677b83bdab09cda48bc2d89ac75aJust firstCode = codes[0] 4297842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(codes)): 4307842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = codes[i] 4317842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray[code-firstCode] = ttFont.getGlyphID(self.cmap[code]) 4327842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 4337842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray.byteswap() 4347842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = glyphIndexArray.tostring() 4357842e56b97ce677b83bdab09cda48bc2d89ac75aJust header = struct.pack(">HHHHH", 4367842e56b97ce677b83bdab09cda48bc2d89ac75aJust 6, len(data) + 10, self.version, firstCode, len(self.cmap)) 4377842e56b97ce677b83bdab09cda48bc2d89ac75aJust return header + data 4387842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4397842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 4407842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.items() 4417842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 4427842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 4437842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 4447842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 4457842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 4467842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 4477842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 4487842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4497842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in codes: 4507842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 4517842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 4527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4537842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 4547842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 4557842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4567842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 4577842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 4587842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 4597842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 46022dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 4617842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 4627842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 4637842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 4647842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 4657842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 4667842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4677842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4687842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_unknown(CmapSubtable): 4697842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4707842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 4717842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data = data 4727842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4737842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 4747842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 4757842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4767842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4777842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_classes = { 4787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 0: cmap_format_0, 4797842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2: cmap_format_2, 4807842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4: cmap_format_4, 4817842e56b97ce677b83bdab09cda48bc2d89ac75aJust 6: cmap_format_6, 4827842e56b97ce677b83bdab09cda48bc2d89ac75aJust } 4837842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4847842e56b97ce677b83bdab09cda48bc2d89ac75aJust 485