_c_m_a_p.py revision 32c10eecffb4923e0721c395e4b80fb732543f18
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
330e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.textTools import safeEval, readHex
42b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom . import DefaultTable
530e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport sys
67842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport struct
77842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport array
8d299b55d14fa77411140c0cc1c2524583b4ffa58jvrimport operator
97842e56b97ce677b83bdab09cda48bc2d89ac75aJust
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
117842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass table__c_m_a_p(DefaultTable.DefaultTable):
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def getcmap(self, platformID, platEncID):
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for subtable in self.tables:
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if (subtable.platformID == platformID and
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust					subtable.platEncID == platEncID):
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return subtable
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return None # not found
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust
207842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tableVersion, numSubTables = struct.unpack(">HH", data[:4])
227842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tableVersion = int(tableVersion)
237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables = tables = []
24d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		seenOffsets = {}
257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(numSubTables):
267842e56b97ce677b83bdab09cda48bc2d89ac75aJust			platformID, platEncID, offset = struct.unpack(
277842e56b97ce677b83bdab09cda48bc2d89ac75aJust					">HHl", data[4+i*8:4+(i+1)*8])
287842e56b97ce677b83bdab09cda48bc2d89ac75aJust			platformID, platEncID = int(platformID), int(platEncID)
297842e56b97ce677b83bdab09cda48bc2d89ac75aJust			format, length = struct.unpack(">HH", data[offset:offset+4])
3051a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader			if format in [8,10,12,13]:
31924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				format, reserved, length = struct.unpack(">HHL", data[offset:offset+8])
320cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			elif format in [14]:
330cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				format, length = struct.unpack(">HL", data[offset:offset+6])
340cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
352db352c748a933d85264deb102036137f06b840fjvr			if not length:
363ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s,  format %s offset %s. Skipping table." % (platformID, platEncID,format, offset))
372db352c748a933d85264deb102036137f06b840fjvr				continue
38bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if format not in cmap_classes:
397842e56b97ce677b83bdab09cda48bc2d89ac75aJust				table = cmap_format_unknown(format)
407842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
417842e56b97ce677b83bdab09cda48bc2d89ac75aJust				table = cmap_classes[format](format)
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust			table.platformID = platformID
437842e56b97ce677b83bdab09cda48bc2d89ac75aJust			table.platEncID = platEncID
44d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# Note that by default we decompile only the subtable header info;
45d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# any other data gets decompiled only when an attribute of the
46d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# subtable is referenced.
47d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			table.decompileHeader(data[offset:offset+int(length)], ttFont)
48bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if offset in seenOffsets:
49d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				table.cmap = tables[seenOffsets[offset]].cmap
50d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			else:
51d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				seenOffsets[offset] = i
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust			tables.append(table)
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables.sort()    # sort according to the spec; see CmapSubtable.__cmp__()
567842e56b97ce677b83bdab09cda48bc2d89ac75aJust		numSubTables = len(self.tables)
577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		totalOffset = 4 + 8 * numSubTables
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = struct.pack(">HH", self.tableVersion, numSubTables)
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tableData = ""
60d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		seen = {}  # Some tables are the same object reference. Don't compile them twice.
61d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		done = {}  # Some tables are different objects, but compile to the same data chunk
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for table in self.tables:
63d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			try:
64d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				offset = seen[id(table.cmap)]
65d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			except KeyError:
66d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				chunk = table.compile(ttFont)
67bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod				if chunk in done:
68d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					offset = done[chunk]
69d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				else:
70d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					offset = seen[id(table.cmap)] = done[chunk] = totalOffset + len(tableData)
71d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					tableData = tableData + chunk
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
737842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data + tableData
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.simpletag("tableVersion", version=self.tableVersion)
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for table in self.tables:
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust			table.toXML(writer, ttFont)
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust
813a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if name == "tableVersion":
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.tableVersion = safeEval(attrs["version"])
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return
85180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if name[:12] != "cmap_format_":
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not hasattr(self, "tables"):
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.tables = []
890cd79a5642101821d66392e3bd0e3c445e97f09bjvr		format = safeEval(name[12:])
90bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if format not in cmap_classes:
917842e56b97ce677b83bdab09cda48bc2d89ac75aJust			table = cmap_format_unknown(format)
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust			table = cmap_classes[format](format)
947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		table.platformID = safeEval(attrs["platformID"])
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust		table.platEncID = safeEval(attrs["platEncID"])
963a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		table.fromXML(name, attrs, content, ttFont)
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables.append(table)
987842e56b97ce677b83bdab09cda48bc2d89ac75aJust
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass CmapSubtable:
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, format):
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.format = format
104d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = None
105d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.ttFont = None
106d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
107d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def __getattr__(self, attr):
108d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# allow lazy decompilation of subtables.
109d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if attr[:2] == '__': # don't handle requests for member functions like '__lt__'
110cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
111d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data == None:
112cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
113d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.decompile(None, None) # use saved data.
114d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = None # Once this table has been decompiled, make sure we don't
115d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						# just return the original data. Also avoids recursion when
116d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						# called with an attribute that the cmap subtable doesn't have.
117d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		return getattr(self, attr)
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
119d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def decompileHeader(self, data, ttFont):
120d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		format, length, language = struct.unpack(">HHH", data[:6])
121d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		assert len(data) == length, "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
122d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.format = int(format)
123d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.length = int(length)
124d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.language = int(language)
125d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = data[6:]
126d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.ttFont = ttFont
127d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.begintag(self.__class__.__name__, [
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platformID", self.platformID),
1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platEncID", self.platEncID),
132a84b28d934fb697755823c62799f4b65e2b92237jvr				("language", self.language),
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust				])
1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
135ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		codes = sorted(self.cmap.items())
136a84b28d934fb697755823c62799f4b65e2b92237jvr		self._writeCodes(codes, writer)
1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.endtag(self.__class__.__name__)
1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
139a84b28d934fb697755823c62799f4b65e2b92237jvr
140a84b28d934fb697755823c62799f4b65e2b92237jvr	def _writeCodes(self, codes, writer):
141d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if (self.platformID, self.platEncID) == (3, 1) or (self.platformID, self.platEncID) == (3, 10) or self.platformID == 0:
142a84b28d934fb697755823c62799f4b65e2b92237jvr			from fontTools.unicode import Unicode
143a84b28d934fb697755823c62799f4b65e2b92237jvr			isUnicode = 1
144a84b28d934fb697755823c62799f4b65e2b92237jvr		else:
145a84b28d934fb697755823c62799f4b65e2b92237jvr			isUnicode = 0
146a84b28d934fb697755823c62799f4b65e2b92237jvr		for code, name in codes:
147a84b28d934fb697755823c62799f4b65e2b92237jvr			writer.simpletag("map", code=hex(code), name=name)
148a84b28d934fb697755823c62799f4b65e2b92237jvr			if isUnicode:
149a84b28d934fb697755823c62799f4b65e2b92237jvr				writer.comment(Unicode[code])
150a84b28d934fb697755823c62799f4b65e2b92237jvr			writer.newline()
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __cmp__(self, other):
153ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		if not isinstance(self, type(other)): return cmp(type(self), type(other))
15496b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# implemented so that list.sort() sorts according to the cmap spec.
1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust		selfTuple = (
15794118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platformID", None),
15894118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platEncID", None),
15994118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "language", None),
16094118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			self.__dict__)
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		otherTuple = (
16294118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platformID", None),
16394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platEncID", None),
16494118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "language", None),
16594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			other.__dict__)
1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return cmp(selfTuple, otherTuple)
1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1697842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_0(CmapSubtable):
1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
172d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
173d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
174d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
175d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(data[offset:offset+int(length)], ttFont)
176d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
17717012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
178d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = self.data # decompileHeader assigns the data after the header to self.data
179d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphIdArray = array.array("B")
181d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		glyphIdArray.fromstring(self.data)
1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.cmap = cmap = {}
183d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenArray = len(glyphIdArray)
18497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		charCodes = list(range(lenArray))
185d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		names = map(self.ttFont.getGlyphName, glyphIdArray)
186e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
187d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
190d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
191d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return struct.pack(">HHH", 0, 262, self.language) + self.data
192d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
193ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		charCodeList = sorted(self.cmap.items())
194d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		charCodes = [entry[0] for entry in charCodeList]
195d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		valueList = [entry[1] for entry in charCodeList]
19697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		assert charCodes == list(range(256))
197d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		valueList = map(ttFont.getGlyphID, valueList)
198d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
1998da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod		glyphIdArray = array.array("B", valueList)
2000cd79a5642101821d66392e3bd0e3c445e97f09bjvr		data = struct.pack(">HHH", 0, 262, self.language) + glyphIdArray.tostring()
2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		assert len(data) == 262
2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data
2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2043a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
2050cd79a5642101821d66392e3bd0e3c445e97f09bjvr		self.language = safeEval(attrs["language"])
206d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if not hasattr(self, "cmap"):
207d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.cmap = {}
208d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for element in content:
210b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name, attrs, content = element
213180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "map":
2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
215d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap[safeEval(attrs["code"])] = attrs["name"]
2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust
218bafa66e665afa581b58391585f1792578a4d3d2djvrsubHeaderFormat = ">HHhH"
219bafa66e665afa581b58391585f1792578a4d3d2djvrclass SubHeader:
220bafa66e665afa581b58391585f1792578a4d3d2djvr	def __init__(self):
221bafa66e665afa581b58391585f1792578a4d3d2djvr		self.firstCode = None
222bafa66e665afa581b58391585f1792578a4d3d2djvr		self.entryCount = None
223bafa66e665afa581b58391585f1792578a4d3d2djvr		self.idDelta = None
224bafa66e665afa581b58391585f1792578a4d3d2djvr		self.idRangeOffset = None
225bafa66e665afa581b58391585f1792578a4d3d2djvr		self.glyphIndexArray = []
226bafa66e665afa581b58391585f1792578a4d3d2djvr
2277842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_2(CmapSubtable):
2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust
229d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def setIDDelta(self, subHeader):
230d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		subHeader.idDelta = 0
231d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# find the minGI which is not zero.
232d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		minGI = subHeader.glyphIndexArray[0]
233d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		for gid in subHeader.glyphIndexArray:
234d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if (gid != 0) and (gid < minGI):
235d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				minGI = gid
236d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
237d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K.
238d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# We would like to pick an idDelta such that the first glyphArray GID is 1,
239d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# so that we are more likely to be able to combine glypharray GID subranges.
240d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# This means that we have a problem when minGI is > 32K
241d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Since the final gi is reconstructed from the glyphArray GID by:
242d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		#    (short)finalGID = (gid +  idDelta) % 0x10000),
243d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
244d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# negative number to an unsigned short.
245d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
246d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if  (minGI > 1):
247d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if  minGI > 0x7FFF:
248d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.idDelta = -(0x10000 - minGI) -1
249d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			else:
250d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.idDelta =  minGI -1
251d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			idDelta = subHeader.idDelta
252d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			for i in range(subHeader.entryCount):
253d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				gid = subHeader.glyphIndexArray[i]
254d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				if gid > 0:
255d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					subHeader.glyphIndexArray[i] = gid - idDelta
256d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
257d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
259d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
260d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
261d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
262d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(data[offset:offset+int(length)], ttFont)
263d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
26417012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
265d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
266d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = self.data # decompileHeader assigns the data after the header to self.data
267bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeaderKeys = []
268bafa66e665afa581b58391585f1792578a4d3d2djvr		maxSubHeaderindex = 0
269bafa66e665afa581b58391585f1792578a4d3d2djvr		# get the key array, and determine the number of subHeaders.
270d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		allKeys = array.array("H")
271d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		allKeys.fromstring(data[:512])
272d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = data[512:]
273180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if sys.byteorder != "big":
274d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			allKeys.byteswap()
27532c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		subHeaderKeys = [ key//8 for key in allKeys]
276d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		maxSubHeaderindex = max(subHeaderKeys)
2777842e56b97ce677b83bdab09cda48bc2d89ac75aJust
278bafa66e665afa581b58391585f1792578a4d3d2djvr		#Load subHeaders
279bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeaderList = []
280d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		pos = 0
281bafa66e665afa581b58391585f1792578a4d3d2djvr		for i in range(maxSubHeaderindex + 1):
282bafa66e665afa581b58391585f1792578a4d3d2djvr			subHeader = SubHeader()
283bafa66e665afa581b58391585f1792578a4d3d2djvr			(subHeader.firstCode, subHeader.entryCount, subHeader.idDelta, \
284d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.idRangeOffset) = struct.unpack(subHeaderFormat, data[pos:pos + 8])
285d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			pos += 8
286d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			giDataPos = pos + subHeader.idRangeOffset-2
287d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			giList = array.array("H")
288d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			giList.fromstring(data[giDataPos:giDataPos + subHeader.entryCount*2])
289180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if sys.byteorder != "big":
290d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				giList.byteswap()
291d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.glyphIndexArray = giList
292bafa66e665afa581b58391585f1792578a4d3d2djvr			subHeaderList.append(subHeader)
293bafa66e665afa581b58391585f1792578a4d3d2djvr		# How this gets processed.
294bafa66e665afa581b58391585f1792578a4d3d2djvr		# Charcodes may be one or two bytes.
295bafa66e665afa581b58391585f1792578a4d3d2djvr		# The first byte of a charcode is mapped through the  subHeaderKeys, to select
296bafa66e665afa581b58391585f1792578a4d3d2djvr		# a subHeader. For any subheader but 0, the next byte is then mapped through the
297bafa66e665afa581b58391585f1792578a4d3d2djvr		# selected subheader. If subheader Index 0 is selected, then the byte itself is
298bafa66e665afa581b58391585f1792578a4d3d2djvr		# mapped through the subheader, and there is no second byte.
299bafa66e665afa581b58391585f1792578a4d3d2djvr		# Then assume that the subsequent byte is the first byte of the next charcode,and repeat.
300bafa66e665afa581b58391585f1792578a4d3d2djvr		#
301bafa66e665afa581b58391585f1792578a4d3d2djvr		# Each subheader references a range in the glyphIndexArray whose length is entryCount.
302bafa66e665afa581b58391585f1792578a4d3d2djvr		# The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray
303bafa66e665afa581b58391585f1792578a4d3d2djvr		# referenced by another subheader.
304bafa66e665afa581b58391585f1792578a4d3d2djvr		# The only subheader that will be referenced by more than one first-byte value is the subheader
305bafa66e665afa581b58391585f1792578a4d3d2djvr		# that maps the entire range of glyphID values to glyphIndex 0, e.g notdef:
306bafa66e665afa581b58391585f1792578a4d3d2djvr		#	 {firstChar 0, EntryCount 0,idDelta 0,idRangeOffset xx}
307bafa66e665afa581b58391585f1792578a4d3d2djvr		# A byte being mapped though a subheader is treated as in index into a mapping of array index to font glyphIndex.
308bafa66e665afa581b58391585f1792578a4d3d2djvr		# A subheader specifies a subrange within (0...256) by the
309bafa66e665afa581b58391585f1792578a4d3d2djvr		# firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero
310bafa66e665afa581b58391585f1792578a4d3d2djvr		# (e.g. glyph not in font).
311bafa66e665afa581b58391585f1792578a4d3d2djvr		# If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar).
312bafa66e665afa581b58391585f1792578a4d3d2djvr		# The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by
313bafa66e665afa581b58391585f1792578a4d3d2djvr		# counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the
314bafa66e665afa581b58391585f1792578a4d3d2djvr		# glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex.
315bafa66e665afa581b58391585f1792578a4d3d2djvr		# Example for Logocut-Medium
316bafa66e665afa581b58391585f1792578a4d3d2djvr		# first byte of charcode = 129; selects subheader 1.
317bafa66e665afa581b58391585f1792578a4d3d2djvr		# subheader 1 = {firstChar 64, EntryCount 108,idDelta 42,idRangeOffset 0252}
318bafa66e665afa581b58391585f1792578a4d3d2djvr		# second byte of charCode = 66
319bafa66e665afa581b58391585f1792578a4d3d2djvr		# the index offset = 66-64 = 2.
320bafa66e665afa581b58391585f1792578a4d3d2djvr		# The subrange of the glyphIndexArray starting at 0x0252 bytes from the idRangeOffset word is:
321bafa66e665afa581b58391585f1792578a4d3d2djvr		# [glyphIndexArray index], [subrange array index] = glyphIndex
322bafa66e665afa581b58391585f1792578a4d3d2djvr		# [256], [0]=1 	from charcode [129, 64]
323bafa66e665afa581b58391585f1792578a4d3d2djvr		# [257], [1]=2  	from charcode [129, 65]
324bafa66e665afa581b58391585f1792578a4d3d2djvr		# [258], [2]=3  	from charcode [129, 66]
325bafa66e665afa581b58391585f1792578a4d3d2djvr		# [259], [3]=4  	from charcode [129, 67]
326d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero,
327d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# add it to the glyphID to get the final glyphIndex
328bafa66e665afa581b58391585f1792578a4d3d2djvr		# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
329bafa66e665afa581b58391585f1792578a4d3d2djvr
330bafa66e665afa581b58391585f1792578a4d3d2djvr		self.data = ""
331d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.cmap = cmap = {}
332d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		notdefGI = 0
333bafa66e665afa581b58391585f1792578a4d3d2djvr		for firstByte in range(256):
334bafa66e665afa581b58391585f1792578a4d3d2djvr			subHeadindex = subHeaderKeys[firstByte]
335bafa66e665afa581b58391585f1792578a4d3d2djvr			subHeader = subHeaderList[subHeadindex]
336bafa66e665afa581b58391585f1792578a4d3d2djvr			if subHeadindex == 0:
337bafa66e665afa581b58391585f1792578a4d3d2djvr				if (firstByte < subHeader.firstCode) or (firstByte >= subHeader.firstCode + subHeader.entryCount):
338d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					continue # gi is notdef.
339bafa66e665afa581b58391585f1792578a4d3d2djvr				else:
340bafa66e665afa581b58391585f1792578a4d3d2djvr					charCode = firstByte
341bafa66e665afa581b58391585f1792578a4d3d2djvr					offsetIndex = firstByte - subHeader.firstCode
342bafa66e665afa581b58391585f1792578a4d3d2djvr					gi = subHeader.glyphIndexArray[offsetIndex]
343bafa66e665afa581b58391585f1792578a4d3d2djvr					if gi != 0:
344d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						gi = (gi + subHeader.idDelta) % 0x10000
345d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					else:
346d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						continue # gi is notdef.
347d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				cmap[charCode] = gi
348bafa66e665afa581b58391585f1792578a4d3d2djvr			else:
349bafa66e665afa581b58391585f1792578a4d3d2djvr				if subHeader.entryCount:
350d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					charCodeOffset = firstByte * 256 + subHeader.firstCode
351bafa66e665afa581b58391585f1792578a4d3d2djvr					for offsetIndex in range(subHeader.entryCount):
352d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						charCode = charCodeOffset + offsetIndex
353bafa66e665afa581b58391585f1792578a4d3d2djvr						gi = subHeader.glyphIndexArray[offsetIndex]
354bafa66e665afa581b58391585f1792578a4d3d2djvr						if gi != 0:
355d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							gi = (gi + subHeader.idDelta) % 0x10000
356d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						else:
357d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							continue
358d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						cmap[charCode] = gi
359d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# If not subHeader.entryCount, then all char codes with this first byte are
360d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the
361d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# same as mapping it to .notdef.
362d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# cmap values are GID's.
363d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		glyphOrder = self.ttFont.getGlyphOrder()
364c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		gids = list(cmap.values())
365c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		charCodes = list(cmap.keys())
366d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCmap = len(gids)
367d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
368e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
369d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except IndexError:
370d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			getGlyphName = self.ttFont.getGlyphName
371e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(getGlyphName, gids ))
372e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
373d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
374bafa66e665afa581b58391585f1792578a4d3d2djvr
3757842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
376d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
377d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
378bafa66e665afa581b58391585f1792578a4d3d2djvr		kEmptyTwoCharCodeRange = -1
379d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		notdefGI = 0
380d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
381ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		items = sorted(self.cmap.items())
382d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		charCodes = [item[0] for item in items]
383d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		names = [item[1] for item in items]
384d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		nameMap = ttFont.getReverseGlyphMap()
385d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCharCodes = len(charCodes)
386d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
387e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
388d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except KeyError:
389d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
390d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			try:
391e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
392d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			except KeyError:
393d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# allow virtual GIDs in format 2 tables
394d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				gids = []
395d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				for name in names:
396d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					try:
397d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						gid = nameMap[name]
398d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					except KeyError:
399d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						try:
400d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							if (name[:3] == 'gid'):
401d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								gid = eval(name[3:])
402d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							else:
403d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								gid = ttFont.getGlyphID(name)
404d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						except:
405d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							raise KeyError(name)
406d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
407d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					gids.append(gid)
408d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
409d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Process the (char code to gid) item list  in char code order.
410d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# By definition, all one byte char codes map to subheader 0.
411d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
412d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# which defines all char codes in its range to map to notdef) unless proven otherwise.
413d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Note that since the char code items are processed in char code order, all the char codes with the
414d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# same first byte are in sequential order.
415bafa66e665afa581b58391585f1792578a4d3d2djvr
416d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		subHeaderKeys = [ kEmptyTwoCharCodeRange for x in  range(256)] # list of indices into subHeaderList.
417bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeaderList = []
418d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
419d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# We force this subheader entry 0  to exist in the subHeaderList in the case where some one comes up
420d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# with a cmap where all the one byte char codes map to notdef,
421d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# with the result that the subhead 0 would not get created just by processing the item list.
422d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		charCode = charCodes[0]
423d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if charCode > 255:
424d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader = SubHeader()
425d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.firstCode = 0
426d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.entryCount = 0
427d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.idDelta = 0
428d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.idRangeOffset = 0
429d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeaderList.append(subHeader)
430d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
431bafa66e665afa581b58391585f1792578a4d3d2djvr
432bafa66e665afa581b58391585f1792578a4d3d2djvr		lastFirstByte = -1
433d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		items = zip(charCodes, gids)
434d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		for charCode, gid in items:
435d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if gid == 0:
436d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				continue
437bafa66e665afa581b58391585f1792578a4d3d2djvr			firstbyte = charCode >> 8
438bafa66e665afa581b58391585f1792578a4d3d2djvr			secondByte = charCode & 0x00FF
439d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
440d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if firstbyte != lastFirstByte: # Need to update the current subhead, and start a new one.
441bafa66e665afa581b58391585f1792578a4d3d2djvr				if lastFirstByte > -1:
442d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					# fix GI's and iDelta of current subheader.
443d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					self.setIDDelta(subHeader)
444d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
445d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					# If it was sunheader 0 for one-byte charCodes, then we need to set the subHeaderKeys value to zero
446d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					# for the indices matching the char codes.
447d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					if lastFirstByte == 0:
448d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						for index in range(subHeader.entryCount):
449d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							charCode = subHeader.firstCode + index
450d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							subHeaderKeys[charCode] = 0
451d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
452bafa66e665afa581b58391585f1792578a4d3d2djvr					assert (subHeader.entryCount == len(subHeader.glyphIndexArray)), "Error - subhead entry count does not match len of glyphID subrange."
453bafa66e665afa581b58391585f1792578a4d3d2djvr				# init new subheader
454bafa66e665afa581b58391585f1792578a4d3d2djvr				subHeader = SubHeader()
455bafa66e665afa581b58391585f1792578a4d3d2djvr				subHeader.firstCode = secondByte
456d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.entryCount = 1
457d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.glyphIndexArray.append(gid)
458d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeaderList.append(subHeader)
459d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeaderKeys[firstbyte] = len(subHeaderList) -1
460bafa66e665afa581b58391585f1792578a4d3d2djvr				lastFirstByte = firstbyte
461bafa66e665afa581b58391585f1792578a4d3d2djvr			else:
462d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# need to fill in with notdefs all the code points between the last charCode and the current charCode.
463bafa66e665afa581b58391585f1792578a4d3d2djvr				codeDiff = secondByte - (subHeader.firstCode + subHeader.entryCount)
464bafa66e665afa581b58391585f1792578a4d3d2djvr				for i in range(codeDiff):
465d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					subHeader.glyphIndexArray.append(notdefGI)
466d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.glyphIndexArray.append(gid)
467bafa66e665afa581b58391585f1792578a4d3d2djvr				subHeader.entryCount = subHeader.entryCount + codeDiff + 1
468d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
469d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# fix GI's and iDelta of last subheader that we we added to the subheader array.
470d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.setIDDelta(subHeader)
471d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
472d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Now we add a final subheader for the subHeaderKeys which maps to empty two byte charcode ranges.
473bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeader = SubHeader()
474bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeader.firstCode = 0
475bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeader.entryCount = 0
476bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeader.idDelta = 0
477bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeader.idRangeOffset = 2
478bafa66e665afa581b58391585f1792578a4d3d2djvr		subHeaderList.append(subHeader)
479bafa66e665afa581b58391585f1792578a4d3d2djvr		emptySubheadIndex = len(subHeaderList) - 1
480bafa66e665afa581b58391585f1792578a4d3d2djvr		for index in range(256):
481d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if subHeaderKeys[index] == kEmptyTwoCharCodeRange:
482bafa66e665afa581b58391585f1792578a4d3d2djvr				subHeaderKeys[index] = emptySubheadIndex
483bafa66e665afa581b58391585f1792578a4d3d2djvr		# Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the
484d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray,
485bafa66e665afa581b58391585f1792578a4d3d2djvr		# since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with
486bafa66e665afa581b58391585f1792578a4d3d2djvr		# charcode 0 and GID 0.
487bafa66e665afa581b58391585f1792578a4d3d2djvr
488bafa66e665afa581b58391585f1792578a4d3d2djvr		idRangeOffset = (len(subHeaderList)-1)*8  + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
489d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		subheadRangeLen = len(subHeaderList) -1 # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2.
490d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		for index in range(subheadRangeLen):
491d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader = subHeaderList[index]
492d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			subHeader.idRangeOffset = 0
493d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			for j  in range(index):
494d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				prevSubhead = subHeaderList[j]
495d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				if prevSubhead.glyphIndexArray == subHeader.glyphIndexArray: # use the glyphIndexArray subarray
496d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8
497d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					subHeader.glyphIndexArray = []
498d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					break
499d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			if subHeader.idRangeOffset == 0: # didn't find one.
500d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				subHeader.idRangeOffset = idRangeOffset
501d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				idRangeOffset = (idRangeOffset - 8) + subHeader.entryCount*2 # one less subheader, one more subArray.
502d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			else:
503d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				idRangeOffset = idRangeOffset - 8  # one less subheader
504d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
505bafa66e665afa581b58391585f1792578a4d3d2djvr		# Now we can write out the data!
506bafa66e665afa581b58391585f1792578a4d3d2djvr		length = 6 + 512 + 8*len(subHeaderList) # header, 256 subHeaderKeys, and subheader array.
507bafa66e665afa581b58391585f1792578a4d3d2djvr		for subhead in 	subHeaderList[:-1]:
508d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			length = length + len(subhead.glyphIndexArray)*2  # We can't use subhead.entryCount, as some of the subhead may share subArrays.
509d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		dataList = [struct.pack(">HHH", 2, length, self.language)]
510bafa66e665afa581b58391585f1792578a4d3d2djvr		for index in subHeaderKeys:
511d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			dataList.append(struct.pack(">H", index*8))
512bafa66e665afa581b58391585f1792578a4d3d2djvr		for subhead in 	subHeaderList:
513d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			dataList.append(struct.pack(subHeaderFormat, subhead.firstCode, subhead.entryCount, subhead.idDelta, subhead.idRangeOffset))
514bafa66e665afa581b58391585f1792578a4d3d2djvr		for subhead in 	subHeaderList[:-1]:
515bafa66e665afa581b58391585f1792578a4d3d2djvr			for gi in subhead.glyphIndexArray:
516d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				dataList.append(struct.pack(">H", gi))
517d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = "".join(dataList)
518bafa66e665afa581b58391585f1792578a4d3d2djvr		assert (len(data) == length), "Error: cmap format 2 is not same length as calculated! actual: " + str(len(data))+ " calc : " + str(length)
519bafa66e665afa581b58391585f1792578a4d3d2djvr		return data
520d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
521d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
5223a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
5230cd79a5642101821d66392e3bd0e3c445e97f09bjvr		self.language = safeEval(attrs["language"])
524d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if not hasattr(self, "cmap"):
525d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.cmap = {}
526d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
527d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
528bafa66e665afa581b58391585f1792578a4d3d2djvr		for element in content:
529b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
530bafa66e665afa581b58391585f1792578a4d3d2djvr				continue
531bafa66e665afa581b58391585f1792578a4d3d2djvr			name, attrs, content = element
532180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "map":
533bafa66e665afa581b58391585f1792578a4d3d2djvr				continue
534d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap[safeEval(attrs["code"])] = attrs["name"]
5357842e56b97ce677b83bdab09cda48bc2d89ac75aJust
5367842e56b97ce677b83bdab09cda48bc2d89ac75aJust
5377842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_format_4_format = ">7H"
5387842e56b97ce677b83bdab09cda48bc2d89ac75aJust
5391f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  endCode[segCount]          # Ending character code for each segment, last = 0xFFFF.
5401f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  reservedPad                # This value should be zero
5411f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  startCode[segCount]        # Starting character code for each segment
5421f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  idDelta[segCount]          # Delta for all character codes in segment
5431f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  idRangeOffset[segCount]    # Offset in bytes to glyph indexArray, or 0
5441f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr#uint16  glyphIndexArray[variable]  # Glyph index array
5457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
546542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvrdef splitRange(startCode, endCode, cmap):
5471f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# Try to split a range of character codes into subranges with consecutive
5481f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# glyph IDs in such a way that the cmap4 subtable can be stored "most"
5491f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# efficiently. I can't prove I've got the optimal solution, but it seems
5501f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# to do well with the fonts I tested: none became bigger, many became smaller.
551542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	if startCode == endCode:
552542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		return [], [endCode]
553542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
554542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	lastID = cmap[startCode]
555542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	lastCode = startCode
556542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	inOrder = None
557542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	orderedBegin = None
5581f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	subRanges = []
559542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
5601f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# Gather subranges in which the glyph IDs are consecutive.
561542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	for code in range(startCode + 1, endCode + 1):
562542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		glyphID = cmap[code]
563542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
564542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		if glyphID - 1 == lastID:
565542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			if inOrder is None or not inOrder:
566542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				inOrder = 1
567542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				orderedBegin = lastCode
568542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		else:
569542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			if inOrder:
570542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				inOrder = 0
5711f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr				subRanges.append((orderedBegin, lastCode))
572542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				orderedBegin = None
573542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
574542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		lastID = glyphID
575542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		lastCode = code
576542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
577542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	if inOrder:
5781f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr		subRanges.append((orderedBegin, lastCode))
579542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	assert lastCode == endCode
580542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
5811f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# Now filter out those new subranges that would only make the data bigger.
5821f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# A new segment cost 8 bytes, not using a new segment costs 2 bytes per
5831f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# character.
5841f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	newRanges = []
5851f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	for b, e in subRanges:
586542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		if b == startCode and e == endCode:
587542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			break  # the whole range, we're fine
588542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		if b == startCode or e == endCode:
589542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			threshold = 4  # split costs one more segment
590542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		else:
591542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			threshold = 8  # split costs two more segments
592542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		if (e - b + 1) > threshold:
5931f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr			newRanges.append((b, e))
5941f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	subRanges = newRanges
595542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
5961f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	if not subRanges:
597542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		return [], [endCode]
598542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
5991f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	if subRanges[0][0] != startCode:
6001f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr		subRanges.insert(0, (startCode, subRanges[0][0] - 1))
6011f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	if subRanges[-1][1] != endCode:
6021f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr		subRanges.append((subRanges[-1][1] + 1, endCode))
6031f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr
6041f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# Fill the "holes" in the segments list -- those are the segments in which
6051f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# the glyph IDs are _not_ consecutive.
606542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	i = 1
6071f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	while i < len(subRanges):
6081f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr		if subRanges[i-1][1] + 1 != subRanges[i][0]:
6091f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr			subRanges.insert(i, (subRanges[i-1][1] + 1, subRanges[i][0] - 1))
610542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			i = i + 1
611542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		i = i + 1
612542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
6131f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	# Transform the ranges into startCode/endCode lists.
614542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	start = []
615542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	end = []
6161f8a4bb02ac37fb92f3dbe9eca5d66ecee6755f3jvr	for b, e in subRanges:
617542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		start.append(b)
618542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		end.append(e)
619542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	start.pop(0)
620542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
621542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	assert len(start) + 1 == len(end)
622542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr	return start, end
623542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
624542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr
6257842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_4(CmapSubtable):
6267842e56b97ce677b83bdab09cda48bc2d89ac75aJust
6277842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
628d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
629d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
630d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
631d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(self.data[offset:offset+int(length)], ttFont)
632d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
63317012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
634d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
635d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = self.data # decompileHeader assigns the data after the header to self.data
636d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		(segCountX2, searchRange, entrySelector, rangeShift) = \
637d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					struct.unpack(">4H", data[:8])
638d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = data[8:]
63932c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		segCount = segCountX2 // 2
6407842e56b97ce677b83bdab09cda48bc2d89ac75aJust
641542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		allCodes = array.array("H")
642d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		allCodes.fromstring(data)
643d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = data = None
644d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
645180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if sys.byteorder != "big":
646542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			allCodes.byteswap()
6477842e56b97ce677b83bdab09cda48bc2d89ac75aJust
6487842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# divide the data
649542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		endCode = allCodes[:segCount]
650542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		allCodes = allCodes[segCount+1:]  # the +1 is skipping the reservedPad field
651542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		startCode = allCodes[:segCount]
652542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		allCodes = allCodes[segCount:]
653542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		idDelta = allCodes[:segCount]
654542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		allCodes = allCodes[segCount:]
655542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		idRangeOffset = allCodes[:segCount]
656542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		glyphIndexArray = allCodes[segCount:]
657d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenGIArray = len(glyphIndexArray)
658d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
6597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# build 2-byte character mapping
660d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		charCodes = []
661d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		gids = []
6627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(len(startCode) - 1):	# don't do 0xffff!
66397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			rangeCharCodes = list(range(startCode[i], endCode[i] + 1))
664d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			charCodes = charCodes + rangeCharCodes
665d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			for charCode in rangeCharCodes:
6667842e56b97ce677b83bdab09cda48bc2d89ac75aJust				rangeOffset = idRangeOffset[i]
6677842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if rangeOffset == 0:
6687842e56b97ce677b83bdab09cda48bc2d89ac75aJust					glyphID = charCode + idDelta[i]
6697842e56b97ce677b83bdab09cda48bc2d89ac75aJust				else:
6707842e56b97ce677b83bdab09cda48bc2d89ac75aJust					# *someone* needs to get killed.
67132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod					index = idRangeOffset[i] // 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
672d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array  is not less than the length of the array (%d) !" % (i, index, lenGIArray)
673180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod					if glyphIndexArray[index] != 0:  # if not missing glyph
6747842e56b97ce677b83bdab09cda48bc2d89ac75aJust						glyphID = glyphIndexArray[index] + idDelta[i]
6757842e56b97ce677b83bdab09cda48bc2d89ac75aJust					else:
6767842e56b97ce677b83bdab09cda48bc2d89ac75aJust						glyphID = 0  # missing glyph
677d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				gids.append(glyphID % 0x10000)
678d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
679d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.cmap = cmap = {}
680d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCmap = len(gids)
681d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		glyphOrder = self.ttFont.getGlyphOrder()
682d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
683e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
684d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except IndexError:
685d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			getGlyphName = self.ttFont.getGlyphName
686e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(getGlyphName, gids ))
687e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
688d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
689d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
690d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
691d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def setIDDelta(self, idDelta):
692d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
693d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# idDelta is a short, and must be between -32K and 32K
694d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# startCode can be between 0 and 64K-1, and the first glyph index can be between 1 and 64K-1
695d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# This means that we have a problem because we can need to assign to idDelta values
696d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# between -(64K-2) and 64K -1.
697d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Since the final gi is reconstructed from the glyphArray GID by:
698d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		#    (short)finalGID = (gid +  idDelta) % 0x10000),
699d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we can get from a startCode of 0 to a final GID of 64 -1K by subtracting 1, and casting the
700d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# negative number to an unsigned short.
701d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# Similarly , we can get from a startCode of 64K-1 to a final GID of 1 by adding 2, because of
702d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# the modulo arithmetic.
703d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
704d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if idDelta > 0x7FFF:
705d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			idDelta = idDelta - 0x10000
706d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		elif idDelta <  -0x7FFF:
707d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			idDelta = idDelta + 0x10000
708d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
709d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		return idDelta
710d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
711d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
7127842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
713d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
714d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
715d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
716ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		from fontTools.ttLib.sfnt import maxPowerOfTwo
7177842e56b97ce677b83bdab09cda48bc2d89ac75aJust
718c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		charCodes = list(self.cmap.keys())
719d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCharCodes = len(charCodes)
720d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if lenCharCodes == 0:
721d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			startCode = [0xffff]
722d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			endCode = [0xffff]
723d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
7242db352c748a933d85264deb102036137f06b840fjvr			charCodes.sort()
725e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
726d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			nameMap = ttFont.getReverseGlyphMap()
727d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			try:
728e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
729d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			except KeyError:
730d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				nameMap = ttFont.getReverseGlyphMap(rebuild=1)
731d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				try:
732e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod					gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
733d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				except KeyError:
734d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					# allow virtual GIDs in format 4 tables
735d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					gids = []
736d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					for name in names:
737d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						try:
738d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							gid = nameMap[name]
739d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						except KeyError:
740d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							try:
741d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								if (name[:3] == 'gid'):
742d299b55d14fa77411140c0cc1c2524583b4ffa58jvr									gid = eval(name[3:])
743d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								else:
744d299b55d14fa77411140c0cc1c2524583b4ffa58jvr									gid = ttFont.getGlyphID(name)
745d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							except:
746d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								raise KeyError(name)
747d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
748d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						gids.append(gid)
749d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap = {}  # code:glyphID mapping
750e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
7517842e56b97ce677b83bdab09cda48bc2d89ac75aJust
752d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# Build startCode and endCode lists.
753d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# Split the char codes in ranges of consecutive char codes, then split
754d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# each range in more ranges of consecutive/not consecutive glyph IDs.
755d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			# See splitRange().
756d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			lastCode = charCodes[0]
757d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			endCode = []
758d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			startCode = [lastCode]
759d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			for charCode in charCodes[1:]:  # skip the first code, it's the first start code
760d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				if charCode == lastCode + 1:
761d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					lastCode = charCode
762d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					continue
763d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				start, end = splitRange(startCode[-1], lastCode, cmap)
764d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				startCode.extend(start)
765d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				endCode.extend(end)
766d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				startCode.append(charCode)
767542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				lastCode = charCode
768d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			endCode.append(lastCode)
769d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			startCode.append(0xffff)
770d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			endCode.append(0xffff)
7717842e56b97ce677b83bdab09cda48bc2d89ac75aJust
772542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		# build up rest of cruft
7737842e56b97ce677b83bdab09cda48bc2d89ac75aJust		idDelta = []
7747842e56b97ce677b83bdab09cda48bc2d89ac75aJust		idRangeOffset = []
7757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphIndexArray = []
7767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(len(endCode)-1):  # skip the closing codes (0xffff)
7777842e56b97ce677b83bdab09cda48bc2d89ac75aJust			indices = []
778542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr			for charCode in range(startCode[i], endCode[i] + 1):
779542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				indices.append(cmap[charCode])
78097dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			if  (indices == list(range(indices[0], indices[0] + len(indices)))):
781d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				idDeltaTemp = self.setIDDelta(indices[0] - startCode[i])
782d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				idDelta.append( idDeltaTemp)
7837842e56b97ce677b83bdab09cda48bc2d89ac75aJust				idRangeOffset.append(0)
7847842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
7857842e56b97ce677b83bdab09cda48bc2d89ac75aJust				# someone *definitely* needs to get killed.
7867842e56b97ce677b83bdab09cda48bc2d89ac75aJust				idDelta.append(0)
7877842e56b97ce677b83bdab09cda48bc2d89ac75aJust				idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i))
788542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr				glyphIndexArray.extend(indices)
7897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		idDelta.append(1)  # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
7907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		idRangeOffset.append(0)
7917842e56b97ce677b83bdab09cda48bc2d89ac75aJust
7927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# Insane.
7937842e56b97ce677b83bdab09cda48bc2d89ac75aJust		segCount = len(endCode)
7947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		segCountX2 = segCount * 2
795542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		maxExponent = maxPowerOfTwo(segCount)
796542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		searchRange = 2 * (2 ** maxExponent)
797542b9510e6a8909e35e99a5279b7c2ec57c78e3cjvr		entrySelector = maxExponent
7987842e56b97ce677b83bdab09cda48bc2d89ac75aJust		rangeShift = 2 * segCount - searchRange
7997842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8008da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod		charCodeArray = array.array("H", endCode + [0] + startCode)
8018da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod		idDeltaeArray = array.array("h", idDelta)
8028da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod		restArray = array.array("H", idRangeOffset + glyphIndexArray)
803180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if sys.byteorder != "big":
8048da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod			charCodeArray.byteswap()
8058da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod			idDeltaeArray.byteswap()
8068da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod			restArray.byteswap()
807d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = charCodeArray.tostring() + idDeltaeArray.tostring() + restArray.tostring()
808d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
8097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		length = struct.calcsize(cmap_format_4_format) + len(data)
8100cd79a5642101821d66392e3bd0e3c445e97f09bjvr		header = struct.pack(cmap_format_4_format, self.format, length, self.language,
8117842e56b97ce677b83bdab09cda48bc2d89ac75aJust				segCountX2, searchRange, entrySelector, rangeShift)
812d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		return header + data
8137842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8143a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
8150cd79a5642101821d66392e3bd0e3c445e97f09bjvr		self.language = safeEval(attrs["language"])
816d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if not hasattr(self, "cmap"):
817d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.cmap = {}
818d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
819d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
8207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for element in content:
821b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
8227842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
823d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			nameMap, attrsMap, dummyContent = element
824180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if nameMap != "map":
825d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				assert 0, "Unrecognized keyword in cmap subtable"
826d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap[safeEval(attrsMap["code"])] = attrsMap["name"]
8277842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8287842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8297842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_6(CmapSubtable):
8307842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8317842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
832d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
833d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
834d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
835d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(data[offset:offset+int(length)], ttFont)
836d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
83717012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
838d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
839d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = self.data # decompileHeader assigns the data after the header to self.data
840d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		firstCode, entryCount = struct.unpack(">HH", data[:4])
8417842e56b97ce677b83bdab09cda48bc2d89ac75aJust		firstCode = int(firstCode)
842d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = data[4:]
843f6b1563e0dc4e396264d62598cac856b0959c0f7Just		#assert len(data) == 2 * entryCount  # XXX not true in Apple's Helvetica!!!
8447842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphIndexArray = array.array("H")
84543fa4be9483ec7cfc2f3c183be8bed746862b7f3Just		glyphIndexArray.fromstring(data[:2 * int(entryCount)])
846180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if sys.byteorder != "big":
8477842e56b97ce677b83bdab09cda48bc2d89ac75aJust			glyphIndexArray.byteswap()
848d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = data = None
849d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
8507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.cmap = cmap = {}
851d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
852d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenArray = len(glyphIndexArray)
85397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		charCodes = list(range(firstCode, firstCode + lenArray))
854d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		glyphOrder = self.ttFont.getGlyphOrder()
855d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
856e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray ))
857d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except IndexError:
858d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			getGlyphName = self.ttFont.getGlyphName
859e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(getGlyphName, glyphIndexArray ))
860e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
8617842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8627842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
863d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
864d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
865d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
866c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		codes = list(cmap.keys())
867d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if codes: # yes, there are empty cmap tables.
86897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			codes = list(range(codes[0], codes[-1] + 1))
869d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			firstCode = codes[0]
87013a08d0c3a59402459875155b7dbd194787fb229Behdad Esfahbod			valueList = [cmap.get(code, ".notdef") for code in codes]
871d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			valueList = map(ttFont.getGlyphID, valueList)
8728da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod			glyphIndexArray = array.array("H", valueList)
873180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if sys.byteorder != "big":
8748da8242d614d8e57f5c1d1730436d455777449b5Behdad Esfahbod				glyphIndexArray.byteswap()
875d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			data = glyphIndexArray.tostring()
876d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
877d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			data = ""
878d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			firstCode = 0
8797842e56b97ce677b83bdab09cda48bc2d89ac75aJust		header = struct.pack(">HHHHH",
880d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				6, len(data) + 10, self.language, firstCode, len(codes))
8817842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return header + data
8827842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8833a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
8840cd79a5642101821d66392e3bd0e3c445e97f09bjvr		self.language = safeEval(attrs["language"])
885d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if not hasattr(self, "cmap"):
886d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.cmap = {}
887d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
888d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
8897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for element in content:
890b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
8917842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
8927842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name, attrs, content = element
893180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "map":
8947842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
895d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap[safeEval(attrs["code"])] = attrs["name"]
8967842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8977842e56b97ce677b83bdab09cda48bc2d89ac75aJust
89851a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournaderclass cmap_format_12_or_13(CmapSubtable):
899924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
900d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def __init__(self, format):
901d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.format = format
902d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.reserved = 0
903d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = None
904d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.ttFont = None
905d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
906d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def decompileHeader(self, data, ttFont):
907924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		format, reserved, length, language, nGroups = struct.unpack(">HHLLL", data[:16])
90851a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
909924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.format = format
910924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.reserved = reserved
911924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.length = length
912924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.language = language
913924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.nGroups = nGroups
914d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = data[16:]
915d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.ttFont = ttFont
916d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
917d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def decompile(self, data, ttFont):
918d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
919d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
920d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
921d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(data[offset:offset+int(length)], ttFont)
922d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
92317012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
924d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
925d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = self.data # decompileHeader assigns the data after the header to self.data
926d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		charCodes = []
927d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		gids = []
928d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		pos = 0
929d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		for i in range(self.nGroups):
930d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			startCharCode, endCharCode, glyphID = struct.unpack(">LLL",data[pos:pos+12] )
931d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			pos += 12
932d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			lenGroup = 1 + endCharCode - startCharCode
93397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			charCodes += list(range(startCharCode, endCharCode +1))
93451a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader			gids += self._computeGIDs(glyphID, lenGroup)
935d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = data = None
936d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.cmap = cmap = {}
937d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCmap = len(gids)
938d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		glyphOrder = self.ttFont.getGlyphOrder()
939d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
940e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
941d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except IndexError:
942d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			getGlyphName = self.ttFont.getGlyphName
943e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			names = list(map(getGlyphName, gids ))
944e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
945924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
946924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr	def compile(self, ttFont):
947d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
94851a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader			return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
949c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		charCodes = list(self.cmap.keys())
950d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lenCharCodes = len(charCodes)
951c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		names = list(self.cmap.values())
952d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		nameMap = ttFont.getReverseGlyphMap()
953d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		try:
954e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
955d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		except KeyError:
956d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
957d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			try:
958e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
959d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			except KeyError:
960d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				# allow virtual GIDs in format 12 tables
961d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				gids = []
962d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				for name in names:
963d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					try:
964d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						gid = nameMap[name]
965d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					except KeyError:
966d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						try:
967d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							if (name[:3] == 'gid'):
968d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								gid = eval(name[3:])
969d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							else:
970d299b55d14fa77411140c0cc1c2524583b4ffa58jvr								gid = ttFont.getGlyphID(name)
971d299b55d14fa77411140c0cc1c2524583b4ffa58jvr						except:
972d299b55d14fa77411140c0cc1c2524583b4ffa58jvr							raise KeyError(name)
973d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
974d299b55d14fa77411140c0cc1c2524583b4ffa58jvr					gids.append(gid)
975d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
976924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		cmap = {}  # code:glyphID mapping
977e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
978924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
979924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		charCodes.sort()
980d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		index = 0
981924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		startCharCode = charCodes[0]
982924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		startGlyphID = cmap[startCharCode]
98351a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		lastGlyphID = startGlyphID - self._format_step
984d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lastCharCode = startCharCode - 1
9850cd79a5642101821d66392e3bd0e3c445e97f09bjvr		nGroups = 0
986d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		dataList =  []
987d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		maxIndex = len(charCodes)
988d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		for index in range(maxIndex):
989d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			charCode = charCodes[index]
990924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr			glyphID = cmap[charCode]
99151a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader			if not self._IsInSameRun(glyphID, lastGlyphID, charCode, lastCharCode):
992d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
993924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				startCharCode = charCode
994d299b55d14fa77411140c0cc1c2524583b4ffa58jvr				startGlyphID = glyphID
995924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				nGroups = nGroups + 1
996d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			lastGlyphID = glyphID
997d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			lastCharCode = charCode
998d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
9990cd79a5642101821d66392e3bd0e3c445e97f09bjvr		nGroups = nGroups + 1
1000d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		data = "".join(dataList)
1001d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		lengthSubtable = len(data) +16
1002d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		assert len(data) == (nGroups*12) == (lengthSubtable-16)
1003d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		return struct.pack(">HHLLL", self.format, self.reserved , lengthSubtable, self.language, nGroups) + data
1004924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
1005924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr	def toXML(self, writer, ttFont):
1006924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		writer.begintag(self.__class__.__name__, [
1007924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("platformID", self.platformID),
1008924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("platEncID", self.platEncID),
1009924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("format", self.format),
1010924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("reserved", self.reserved),
1011924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("length", self.length),
1012924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("language", self.language),
1013924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				("nGroups", self.nGroups),
1014924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				])
1015924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		writer.newline()
1016ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		codes = sorted(self.cmap.items())
1017a84b28d934fb697755823c62799f4b65e2b92237jvr		self._writeCodes(codes, writer)
1018924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		writer.endtag(self.__class__.__name__)
1019924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		writer.newline()
1020924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
10213a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
1022d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.format = safeEval(attrs["format"])
1023d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.reserved = safeEval(attrs["reserved"])
1024d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.length = safeEval(attrs["length"])
1025924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		self.language = safeEval(attrs["language"])
1026d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.nGroups = safeEval(attrs["nGroups"])
1027d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if not hasattr(self, "cmap"):
1028d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.cmap = {}
1029d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmap = self.cmap
1030d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
1031924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		for element in content:
1032b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
1033924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				continue
1034924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr			name, attrs, content = element
1035180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "map":
1036924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr				continue
1037d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			cmap[safeEval(attrs["code"])] = attrs["name"]
1038924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
1039924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr
104051a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournaderclass cmap_format_12(cmap_format_12_or_13):
104151a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def __init__(self, format):
104251a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		cmap_format_12_or_13.__init__(self, format)
104351a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		self._format_step = 1
104451a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
104551a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
104697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		return list(range(startingGlyph, startingGlyph + numberOfGlyphs))
104751a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
104851a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
104951a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
105051a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
105151a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
105251a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournaderclass cmap_format_13(cmap_format_12_or_13):
105351a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def __init__(self, format):
105451a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		cmap_format_12_or_13.__init__(self, format)
105551a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		self._format_step = 0
105651a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
105751a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
105851a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		return [startingGlyph] * numberOfGlyphs
105951a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
106051a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader	def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
106151a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
106251a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
106351a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader
10640cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvrdef  cvtToUVS(threeByteString):
1065180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod	if sys.byteorder != "big":
10660cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		data = "\0" +threeByteString
10670cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	else:
10680cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		data = threeByteString + "\0"
10690cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	val, = struct.unpack(">L", data)
10700cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	return val
10710cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10720cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvrdef  cvtFromUVS(val):
1073180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod	if sys.byteorder != "big":
10740cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		threeByteString = struct.pack(">L", val)[1:]
10750cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	else:
10760cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		threeByteString = struct.pack(">L", val)[:3]
10770cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	return threeByteString
10780cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10790cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvrdef cmpUVSListEntry(first, second):
10800cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	uv1, glyphName1 = first
10810cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	uv2, glyphName2 = second
10820cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10830cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	if (glyphName1 == None) and (glyphName2 != None):
10840cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		return -1
10850cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	elif (glyphName2 == None) and (glyphName1 != None):
10860cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		return 1
10870cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10880cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	ret = cmp(uv1, uv2)
10890cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	if ret:
10900cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		return ret
10910cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	return cmp(glyphName1, glyphName2)
10920cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10930cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10940cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvrclass cmap_format_14(CmapSubtable):
10950cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
10960cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	def decompileHeader(self, data, ttFont):
10970cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		format, length, numVarSelectorRecords = struct.unpack(">HLL", data[:10])
10980cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.data = data[10:]
10990cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.length = length
11000cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.numVarSelectorRecords = numVarSelectorRecords
11010cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.ttFont = ttFont
11020cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.language = 0xFF # has no language.
11030cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11040cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	def decompile(self, data, ttFont):
11057ab0cb0b09f67f69990c47944100010435cc3b6aBehdad Esfahbod		if data != None and ttFont != None and ttFont.lazy:
11060cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			self.decompileHeader(data, ttFont)
11070cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		else:
110817012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
11090cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		data = self.data
11100cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11110cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
11120cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		uvsDict = {}
11130cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		recOffset = 0
11140cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		for n in range(self.numVarSelectorRecords):
11150cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uvs, defOVSOffset, nonDefUVSOffset =  struct.unpack(">3sLL", data[recOffset:recOffset +11])
11160cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			recOffset += 11
11170cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			varUVS = cvtToUVS(uvs)
11180cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			if defOVSOffset:
11190cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				startOffset = defOVSOffset  - 10
11200cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				numValues, = struct.unpack(">L", data[startOffset:startOffset+4])
11210cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				startOffset +=4
11220cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				for r in range(numValues):
11230cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					uv, addtlCnt = struct.unpack(">3sB", data[startOffset:startOffset+4])
11240cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					startOffset += 4
11250cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					firstBaseUV = cvtToUVS(uv)
11260cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					cnt = addtlCnt+1
112797dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod					baseUVList = list(range(firstBaseUV, firstBaseUV+cnt))
11280cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					glyphList = [None]*cnt
11290cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					localUVList = zip(baseUVList, glyphList)
11300cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					try:
11310cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr						uvsDict[varUVS].extend(localUVList)
11320cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					except KeyError:
1133fa5f2e85ab49c349468f5ae08f15163daa256a04Behdad Esfahbod						uvsDict[varUVS] = list(localUVList)
11340cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11350cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			if nonDefUVSOffset:
11360cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				startOffset = nonDefUVSOffset  - 10
11370cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				numRecs, = struct.unpack(">L", data[startOffset:startOffset+4])
11380cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				startOffset +=4
11390cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				localUVList = []
11400cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				for r in range(numRecs):
11410cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					uv, gid = struct.unpack(">3sH", data[startOffset:startOffset+5])
11420cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					startOffset += 5
11430cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					uv = cvtToUVS(uv)
11440cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					glyphName = self.ttFont.getGlyphName(gid)
11450cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					localUVList.append( [uv, glyphName] )
11460cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				try:
11470cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					uvsDict[varUVS].extend(localUVList)
11480cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				except KeyError:
11490cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					uvsDict[varUVS] = localUVList
11500cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11510cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.uvsDict = uvsDict
11520cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11530cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	def toXML(self, writer, ttFont):
11540cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		writer.begintag(self.__class__.__name__, [
11550cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				("platformID", self.platformID),
11560cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				("platEncID", self.platEncID),
11570cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				("format", self.format),
11580cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				("length", self.length),
11590cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				("numVarSelectorRecords", self.numVarSelectorRecords),
11600cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				])
11610cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		writer.newline()
11620cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		uvsDict = self.uvsDict
1163ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		uvsList = sorted(uvsDict.keys())
11640cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		for uvs in uvsList:
11650cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uvList = uvsDict[uvs]
11660cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uvList.sort(cmpUVSListEntry)
11670cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			for uv, gname in uvList:
11680cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				if gname == None:
11690cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					gname = "None"
11700cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				# I use the arg rather than th keyword syntax in order to preserve the attribute order.
11710cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				writer.simpletag("map", [ ("uvs",hex(uvs)), ("uv",hex(uv)), ("name", gname)]  )
11720cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				writer.newline()
11730cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		writer.endtag(self.__class__.__name__)
11740cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		writer.newline()
11750cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11763a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
11770cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.format = safeEval(attrs["format"])
11780cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.length = safeEval(attrs["length"])
11790cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.numVarSelectorRecords = safeEval(attrs["numVarSelectorRecords"])
11800cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.language = 0xFF # provide a value so that  CmapSubtable.__cmp__() won't fail
11810cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		if not hasattr(self, "cmap"):
11820cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
11830cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		if not hasattr(self, "uvsDict"):
11840cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			self.uvsDict  = {}
11850cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uvsDict = self.uvsDict
11860cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
11870cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		for element in content:
1188b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
11890cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				continue
11900cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			name, attrs, content = element
1191180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "map":
11920cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				continue
11930cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uvs = safeEval(attrs["uvs"])
11940cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			uv = safeEval(attrs["uv"])
11950cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			gname = attrs["name"]
11960cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			if gname == "None":
11970cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				gname = None
11980cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			try:
11990cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				uvsDict[uvs].append( [uv, gname])
12000cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			except KeyError:
12010cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				uvsDict[uvs] = [ [uv, gname] ]
12020cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12030cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12040cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr	def compile(self, ttFont):
12050cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		if self.data:
12060cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			return struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords) + self.data
12070cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12080cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		uvsDict = self.uvsDict
1209ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		uvsList = sorted(uvsDict.keys())
12100cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.numVarSelectorRecords = len(uvsList)
12110cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		offset = 10 + self.numVarSelectorRecords*11 # current value is end of VarSelectorRecords block.
12120cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		data = []
12130cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		varSelectorRecords =[]
12140cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		for uvs in uvsList:
12150cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			entryList = uvsDict[uvs]
12160cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
121728aeabb08b2656cb240063865c37f192532badf5Behdad Esfahbod			defList = [entry for entry in entryList if entry[1] == None]
12180cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			if defList:
1219e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod				defList = [entry[0] for entry in defList]
12200cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				defOVSOffset = offset
12210cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				defList.sort()
12220cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12230cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				lastUV = defList[0]
12240cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				cnt = -1
12250cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				defRecs = []
12260cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				for defEntry in defList:
12270cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					cnt +=1
12280cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					if (lastUV+cnt) != defEntry:
12290cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr						rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt-1)
12300cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr						lastUV = defEntry
12310cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr						defRecs.append(rec)
12320cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr						cnt = 0
12330cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12340cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt)
12350cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				defRecs.append(rec)
12360cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12370cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				numDefRecs = len(defRecs)
12380cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				data.append(struct.pack(">L", numDefRecs))
12390cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				data.extend(defRecs)
12400cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				offset += 4 + numDefRecs*4
12410cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			else:
12420cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				defOVSOffset = 0
12430cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
124428aeabb08b2656cb240063865c37f192532badf5Behdad Esfahbod			ndefList = [entry for entry in entryList if entry[1] != None]
12450cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			if ndefList:
12460cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				nonDefUVSOffset = offset
12470cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				ndefList.sort()
12480cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				numNonDefRecs = len(ndefList)
12490cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				data.append(struct.pack(">L", numNonDefRecs))
12500cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				offset += 4 + numNonDefRecs*5
12510cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12520cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				for uv, gname in ndefList:
12530cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					gid = ttFont.getGlyphID(gname)
12540cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					ndrec = struct.pack(">3sH", cvtFromUVS(uv), gid)
12550cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr					data.append(ndrec)
12560cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			else:
12570cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr				nonDefUVSOffset = 0
12580cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12590cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
12600cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr			varSelectorRecords.append(vrec)
12610cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12620cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		data = "".join(varSelectorRecords) + "".join(data)
12630cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.length = 10 + len(data)
12640cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		headerdata = struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords)
12650cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		self.data = headerdata + data
12660cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12670cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		return self.data
12680cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12690cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr
12707842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass cmap_format_unknown(CmapSubtable):
12717842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1272a84b28d934fb697755823c62799f4b65e2b92237jvr	def toXML(self, writer, ttFont):
1273d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		cmapName = self.__class__.__name__[:12] + str(self.format)
1274d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		writer.begintag(cmapName, [
1275a84b28d934fb697755823c62799f4b65e2b92237jvr				("platformID", self.platformID),
1276a84b28d934fb697755823c62799f4b65e2b92237jvr				("platEncID", self.platEncID),
1277a84b28d934fb697755823c62799f4b65e2b92237jvr				])
1278a84b28d934fb697755823c62799f4b65e2b92237jvr		writer.newline()
1279d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		writer.dumphex(self.data)
1280d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		writer.endtag(cmapName)
1281a84b28d934fb697755823c62799f4b65e2b92237jvr		writer.newline()
1282a84b28d934fb697755823c62799f4b65e2b92237jvr
12833a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
1284d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.data = readHex(content)
1285d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		self.cmap = {}
1286d299b55d14fa77411140c0cc1c2524583b4ffa58jvr
1287d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def decompileHeader(self, data, ttFont):
1288427f9802bccf942f51567170e72a71ac14443c71jvr		self.language = 0  # dummy value
12897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.data = data
12907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1291d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def decompile(self, data, ttFont):
1292d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
1293d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
1294d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if data != None and ttFont != None:
1295d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			self.decompileHeader(data[offset:offset+int(length)], ttFont)
1296d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
129717012aabbbc595da0e0c809fff09408bef6b758epabs			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
12987842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1299d299b55d14fa77411140c0cc1c2524583b4ffa58jvr	def compile(self, ttFont):
1300d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		if self.data:
1301d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return self.data
1302d299b55d14fa77411140c0cc1c2524583b4ffa58jvr		else:
1303d299b55d14fa77411140c0cc1c2524583b4ffa58jvr			return None
13047842e56b97ce677b83bdab09cda48bc2d89ac75aJust
13057842e56b97ce677b83bdab09cda48bc2d89ac75aJustcmap_classes = {
13067842e56b97ce677b83bdab09cda48bc2d89ac75aJust		0: cmap_format_0,
13077842e56b97ce677b83bdab09cda48bc2d89ac75aJust		2: cmap_format_2,
13087842e56b97ce677b83bdab09cda48bc2d89ac75aJust		4: cmap_format_4,
13097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		6: cmap_format_6,
1310924e4e274e18508a70b2a8b150f6e9be033d3a3ejvr		12: cmap_format_12,
131151a17826be4fb43d1f6ad5ada94207d8e18fc458Roozbeh Pournader		13: cmap_format_13,
13120cb8a08d89ecd3126aac5a528997bfd0d41e8b10jvr		14: cmap_format_14,
13137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		}
1314