_c_m_a_p.py revision 22dcb9e6f9a9d087e87cece6caca6aa5d92f4d91
17842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport DefaultTable 27842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport struct 37842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string 47842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport array 57842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools import ttLib 67842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval, readHex 722dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvrfrom types import TupleType 87842e56b97ce677b83bdab09cda48bc2d89ac75aJust 97842e56b97ce677b83bdab09cda48bc2d89ac75aJust 107842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass table__c_m_a_p(DefaultTable.DefaultTable): 117842e56b97ce677b83bdab09cda48bc2d89ac75aJust 127842e56b97ce677b83bdab09cda48bc2d89ac75aJust def getcmap(self, platformID, platEncID): 137842e56b97ce677b83bdab09cda48bc2d89ac75aJust for subtable in self.tables: 147842e56b97ce677b83bdab09cda48bc2d89ac75aJust if (subtable.platformID == platformID and 157842e56b97ce677b83bdab09cda48bc2d89ac75aJust subtable.platEncID == platEncID): 167842e56b97ce677b83bdab09cda48bc2d89ac75aJust return subtable 177842e56b97ce677b83bdab09cda48bc2d89ac75aJust return None # not found 187842e56b97ce677b83bdab09cda48bc2d89ac75aJust 197842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 207842e56b97ce677b83bdab09cda48bc2d89ac75aJust tableVersion, numSubTables = struct.unpack(">HH", data[:4]) 217842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tableVersion = int(tableVersion) 227842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables = tables = [] 237842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(numSubTables): 247842e56b97ce677b83bdab09cda48bc2d89ac75aJust platformID, platEncID, offset = struct.unpack( 257842e56b97ce677b83bdab09cda48bc2d89ac75aJust ">HHl", data[4+i*8:4+(i+1)*8]) 267842e56b97ce677b83bdab09cda48bc2d89ac75aJust platformID, platEncID = int(platformID), int(platEncID) 277842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length = struct.unpack(">HH", data[offset:offset+4]) 28ae180248fdc1abcd10f782c8e32b77a62594d349Just if not length: 29ae180248fdc1abcd10f782c8e32b77a62594d349Just continue # bogus cmap subtable? 307842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not cmap_classes.has_key(format): 317842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_format_unknown(format) 327842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 337842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_classes[format](format) 347842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platformID = platformID 357842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platEncID = platEncID 367842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.decompile(data[offset:offset+int(length)], ttFont) 377842e56b97ce677b83bdab09cda48bc2d89ac75aJust tables.append(table) 387842e56b97ce677b83bdab09cda48bc2d89ac75aJust 397842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 407842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables.sort() # sort according to the spec; see CmapSubtable.__cmp__() 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust numSubTables = len(self.tables) 427842e56b97ce677b83bdab09cda48bc2d89ac75aJust totalOffset = 4 + 8 * numSubTables 437842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = struct.pack(">HH", self.tableVersion, numSubTables) 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust tableData = "" 457842e56b97ce677b83bdab09cda48bc2d89ac75aJust done = {} # remember the data so we can reuse the "pointers" 467842e56b97ce677b83bdab09cda48bc2d89ac75aJust for table in self.tables: 477842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunk = table.compile(ttFont) 487842e56b97ce677b83bdab09cda48bc2d89ac75aJust if done.has_key(chunk): 497842e56b97ce677b83bdab09cda48bc2d89ac75aJust offset = done[chunk] 507842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 517842e56b97ce677b83bdab09cda48bc2d89ac75aJust offset = done[chunk] = totalOffset + len(tableData) 521b850986ef5611934a43bd31104a398e311af713Just tableData = tableData + chunk 537842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset) 547842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data + tableData 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("tableVersion", version=self.tableVersion) 587842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust for table in self.tables: 607842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.toXML(writer, ttFont) 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust 627842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 637842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name == "tableVersion": 647842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tableVersion = safeEval(attrs["version"]) 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust return 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name[:12] <> "cmap_format_": 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust return 687842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "tables"): 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables = [] 707842e56b97ce677b83bdab09cda48bc2d89ac75aJust format = safeEval(name[12]) 717842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not cmap_classes.has_key(format): 727842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_format_unknown(format) 737842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 747842e56b97ce677b83bdab09cda48bc2d89ac75aJust table = cmap_classes[format](format) 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platformID = safeEval(attrs["platformID"]) 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.platEncID = safeEval(attrs["platEncID"]) 777842e56b97ce677b83bdab09cda48bc2d89ac75aJust table.fromXML((name, attrs, content), ttFont) 787842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.tables.append(table) 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust 807842e56b97ce677b83bdab09cda48bc2d89ac75aJust 817842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass CmapSubtable: 827842e56b97ce677b83bdab09cda48bc2d89ac75aJust 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __init__(self, format): 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.format = format 857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 897842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 907842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 917842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 927842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.dumphex(self.compile(ttFont)) 937842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 957842e56b97ce677b83bdab09cda48bc2d89ac75aJust 967842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 977842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.decompile(readHex(content), ttFont) 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __cmp__(self, other): 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust # implemented so that list.sort() sorts according to the cmap spec. 1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust selfTuple = ( 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platformID, 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platEncID, 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version, 1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.__dict__) 1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust otherTuple = ( 1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.platformID, 1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.platEncID, 1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.version, 1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust other.__dict__) 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust return cmp(selfTuple, otherTuple) 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1147842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_0(CmapSubtable): 1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version = struct.unpack(">HHH", data[:6]) 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == 262 == length 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray = array.array("B") 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray.fromstring(data[6:]) 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap = {} 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in range(len(glyphIdArray)): 1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[charCode] = ttFont.getGlyphName(glyphIdArray[charCode]) 1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes = self.cmap.keys() 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes.sort() 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert charCodes == range(256) # charCodes[charCode] == charCode 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in charCodes: 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust # reusing the charCodes list! 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust charCodes[charCode] = ttFont.getGlyphID(self.cmap[charCode]) 1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIdArray = array.array("B", charCodes) 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = struct.pack(">HHH", 0, 262, self.version) + glyphIdArray.tostring() 1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == 262 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust items = self.cmap.items() 1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust items.sort() 1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in items: 1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 15722dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1667842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_2(CmapSubtable): 1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version = struct.unpack(">HHH", data[:6]) 1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data = data 1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1777842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_format_4_format = ">7H" 1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 endCode[segCount] # Ending character code for each segment, last = 0xFFFF. 1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 reservedPad # This value should be zero 1817842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 startCode[segCount] # Starting character code for each segment 1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 idDelta[segCount] # Delta for all character codes in segment 1837842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 idRangeOffset[segCount] # Offset in bytes to glyph indexArray, or 0 1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust#uint16 glyphIndexArray[variable] # Glyph index array 1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1867842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_4(CmapSubtable): 1877842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust (format, length, self.version, segCountX2, 1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust searchRange, entrySelector, rangeShift) = \ 1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust struct.unpack(cmap_format_4_format, data[:14]) 1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(data) == length, "corrupt cmap table (%d, %d)" % (len(data), length) 1937842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data[14:] 1947842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCountX2 = int(segCountX2) 1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCount = segCountX2 / 2 1967842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1977842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes = array.array("H") 1987842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes.fromstring(data) 1997842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 2007842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes.byteswap() 2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust # divide the data 2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode = allcodes[:segCount] 2047842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes = allcodes[segCount+1:] 2057842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode = allcodes[:segCount] 2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes = allcodes[segCount:] 2077842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta = allcodes[:segCount] 2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes = allcodes[segCount:] 2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset = allcodes[:segCount] 2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = allcodes[segCount:] 2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust # build 2-byte character mapping 2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap = {} 2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(startCode) - 1): # don't do 0xffff! 2157842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in range(startCode[i], endCode[i] + 1): 2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust rangeOffset = idRangeOffset[i] 2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust if rangeOffset == 0: 2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = charCode + idDelta[i] 2197842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2207842e56b97ce677b83bdab09cda48bc2d89ac75aJust # *someone* needs to get killed. 2217842e56b97ce677b83bdab09cda48bc2d89ac75aJust index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset) 2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust if glyphIndexArray[index] <> 0: # if not missing glyph 2237842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = glyphIndexArray[index] + idDelta[i] 2247842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2257842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = 0 # missing glyph 2267842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[charCode] = ttFont.getGlyphName(glyphID % 0x10000) 2277842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap 2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2297842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust from fontTools.ttLib.sfnt import maxpoweroftwo 2317842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.items() 2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 2347842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2357842e56b97ce677b83bdab09cda48bc2d89ac75aJust # build startCode and endCode lists 2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust last = codes[0][0] 2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode = [] 2387842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode = [last] 2397842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode, glyphName in codes[1:]: # skip the first code, it's the first start code 2407842e56b97ce677b83bdab09cda48bc2d89ac75aJust if charCode == last + 1: 2417842e56b97ce677b83bdab09cda48bc2d89ac75aJust last = charCode 2427842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 2437842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode.append(last) 2447842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode.append(charCode) 2457842e56b97ce677b83bdab09cda48bc2d89ac75aJust last = charCode 2467842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode.append(last) 2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust startCode.append(0xffff) 2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode.append(0xffff) 2497842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2507842e56b97ce677b83bdab09cda48bc2d89ac75aJust # build up rest of cruft. 2517842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta = [] 2527842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset = [] 2537842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = [] 2547842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(endCode)-1): # skip the closing codes (0xffff) 2567842e56b97ce677b83bdab09cda48bc2d89ac75aJust indices = [] 2577842e56b97ce677b83bdab09cda48bc2d89ac75aJust for charCode in range(startCode[i], endCode[i]+1): 2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust indices.append(ttFont.getGlyphID(self.cmap[charCode])) 2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust if indices == range(indices[0], indices[0] + len(indices)): 2607842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append((indices[0] - startCode[i]) % 0x10000) 2617842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(0) 2627842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 2637842e56b97ce677b83bdab09cda48bc2d89ac75aJust # someone *definitely* needs to get killed. 2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append(0) 2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i)) 2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = glyphIndexArray + indices 2677842e56b97ce677b83bdab09cda48bc2d89ac75aJust idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef 2687842e56b97ce677b83bdab09cda48bc2d89ac75aJust idRangeOffset.append(0) 2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust # Insane. 2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCount = len(endCode) 2727842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCountX2 = segCount * 2 2737842e56b97ce677b83bdab09cda48bc2d89ac75aJust maxexponent = maxpoweroftwo(segCount) 2747842e56b97ce677b83bdab09cda48bc2d89ac75aJust searchRange = 2 * (2 ** maxexponent) 2757842e56b97ce677b83bdab09cda48bc2d89ac75aJust entrySelector = maxexponent 2767842e56b97ce677b83bdab09cda48bc2d89ac75aJust rangeShift = 2 * segCount - searchRange 2777842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2787842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes = array.array("H", 2797842e56b97ce677b83bdab09cda48bc2d89ac75aJust endCode + [0] + startCode + idDelta + idRangeOffset + glyphIndexArray) 2807842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 2817842e56b97ce677b83bdab09cda48bc2d89ac75aJust allcodes.byteswap() 2827842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = allcodes.tostring() 2837842e56b97ce677b83bdab09cda48bc2d89ac75aJust length = struct.calcsize(cmap_format_4_format) + len(data) 2847842e56b97ce677b83bdab09cda48bc2d89ac75aJust header = struct.pack(cmap_format_4_format, self.format, length, self.version, 2857842e56b97ce677b83bdab09cda48bc2d89ac75aJust segCountX2, searchRange, entrySelector, rangeShift) 2867842e56b97ce677b83bdab09cda48bc2d89ac75aJust return header + data 2877842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2887842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 2897842e56b97ce677b83bdab09cda48bc2d89ac75aJust from fontTools.unicode import Unicode 2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.items() 2917842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 2927842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 2937842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 2947842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 2957842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 2967842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 2977842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 2987842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2997842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in codes: 3007842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 3017842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.comment(Unicode[code]) 3027842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3037842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3047842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 3057842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3067842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3077842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 3087842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 3097842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 3107842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 31122dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 3127842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 3137842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 3147842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 3157842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 3167842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 3177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3187842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3197842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_6(CmapSubtable): 3207842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3217842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 3227842e56b97ce677b83bdab09cda48bc2d89ac75aJust format, length, version, firstCode, entryCount = struct.unpack( 3237842e56b97ce677b83bdab09cda48bc2d89ac75aJust ">HHHHH", data[:10]) 3247842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 3257842e56b97ce677b83bdab09cda48bc2d89ac75aJust firstCode = int(firstCode) 3267842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = int(version) 3277842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data[10:] 328f6b1563e0dc4e396264d62598cac856b0959c0f7Just #assert len(data) == 2 * entryCount # XXX not true in Apple's Helvetica!!! 3297842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = array.array("H") 33043fa4be9483ec7cfc2f3c183be8bed746862b7f3Just glyphIndexArray.fromstring(data[:2 * int(entryCount)]) 3317842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 3327842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray.byteswap() 3337842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = cmap = {} 3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(glyphIndexArray)): 3357842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphID = glyphIndexArray[i] 3367842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphName = ttFont.getGlyphName(glyphID) 3377842e56b97ce677b83bdab09cda48bc2d89ac75aJust cmap[i+firstCode] = glyphName 3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.keys() 3417842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 3427842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert codes == range(codes[0], codes[0] + len(codes)) 3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray = array.array("H", [0] * len(codes)) 3447842e56b97ce677b83bdab09cda48bc2d89ac75aJust firstCode = codes[0] 3457842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(len(codes)): 3467842e56b97ce677b83bdab09cda48bc2d89ac75aJust code = codes[i] 3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray[code-firstCode] = ttFont.getGlyphID(self.cmap[code]) 3487842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ttLib.endian <> "big": 3497842e56b97ce677b83bdab09cda48bc2d89ac75aJust glyphIndexArray.byteswap() 3507842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = glyphIndexArray.tostring() 3517842e56b97ce677b83bdab09cda48bc2d89ac75aJust header = struct.pack(">HHHHH", 3527842e56b97ce677b83bdab09cda48bc2d89ac75aJust 6, len(data) + 10, self.version, firstCode, len(self.cmap)) 3537842e56b97ce677b83bdab09cda48bc2d89ac75aJust return header + data 3547842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 3567842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes = self.cmap.items() 3577842e56b97ce677b83bdab09cda48bc2d89ac75aJust codes.sort() 3587842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag(self.__class__.__name__, [ 3597842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 3607842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 3617842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("version", self.version), 3627842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 3637842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3647842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3657842e56b97ce677b83bdab09cda48bc2d89ac75aJust for code, name in codes: 3667842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.simpletag("map", code=hex(code), name=name) 3677842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3687842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3697842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag(self.__class__.__name__) 3707842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 3717842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3727842e56b97ce677b83bdab09cda48bc2d89ac75aJust def fromXML(self, (name, attrs, content), ttFont): 3737842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.version = safeEval(attrs["version"]) 3747842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap = {} 3757842e56b97ce677b83bdab09cda48bc2d89ac75aJust for element in content: 37622dcb9e6f9a9d087e87cece6caca6aa5d92f4d91jvr if type(element) <> TupleType: 3777842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 3787842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, attrs, content = element 3797842e56b97ce677b83bdab09cda48bc2d89ac75aJust if name <> "map": 3807842e56b97ce677b83bdab09cda48bc2d89ac75aJust continue 3817842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.cmap[safeEval(attrs["code"])] = attrs["name"] 3827842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3837842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3847842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_unknown(CmapSubtable): 3857842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3867842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 3877842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.data = data 3887842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3897842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 3907842e56b97ce677b83bdab09cda48bc2d89ac75aJust return self.data 3917842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3927842e56b97ce677b83bdab09cda48bc2d89ac75aJust 3937842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_classes = { 3947842e56b97ce677b83bdab09cda48bc2d89ac75aJust 0: cmap_format_0, 3957842e56b97ce677b83bdab09cda48bc2d89ac75aJust 2: cmap_format_2, 3967842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4: cmap_format_4, 3977842e56b97ce677b83bdab09cda48bc2d89ac75aJust 6: cmap_format_6, 3987842e56b97ce677b83bdab09cda48bc2d89ac75aJust } 3997842e56b97ce677b83bdab09cda48bc2d89ac75aJust 4007842e56b97ce677b83bdab09cda48bc2d89ac75aJust 401