_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