otBase.py revision 8ea6439d3b66c5acc246261d761d4375bcb7cfab
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .DefaultTable import DefaultTable
4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct
5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass OverflowErrorRecord(object):
7823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __init__(self, overflowTuple):
8823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.tableType = overflowTuple[0]
9823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.LookupListIndex = overflowTuple[1]
10823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.SubTableIndex = overflowTuple[2]
11823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.itemName = overflowTuple[3]
12823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.itemIndex = overflowTuple[4]
13823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
14823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __repr__(self):
15823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return str((self.tableType, "LookupIndex:", self.LookupListIndex, "SubTableIndex:", self.SubTableIndex, "ItemName:", self.itemName, "ItemIndex:", self.itemIndex))
16823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
17823f8cd15f16bb9dc3991c2672f16dd90579711bjvrclass OTLOffsetOverflowError(Exception):
18823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __init__(self, overflowErrorRecord):
19823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.value = overflowErrorRecord
20823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
21823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __str__(self):
22823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return repr(self.value)
23823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
24d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
25d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTTXConverter(DefaultTable):
26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
273a6aa2359e179779c281e6d319cb9a1a851e6df1jvr	"""Generic base class for TTX table converters. It functions as an
283a6aa2359e179779c281e6d319cb9a1a851e6df1jvr	adapter between the TTX (ttLib actually) table model and the model
293a6aa2359e179779c281e6d319cb9a1a851e6df1jvr	we use for OpenType tables, which is necessarily subtly different.
303a6aa2359e179779c281e6d319cb9a1a851e6df1jvr	"""
3164b5c80e80444a124da335e8d4d208bffcf2737bjvr
32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def decompile(self, data, font):
332b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
340585b64f70cca15aab3483f529c8faf1b7b825fdBehdad Esfahbod		cachingStats = None if True else {}
35e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod		class GlobalState(object):
3679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod			def __init__(self, tableType, cachingStats):
3779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				self.tableType = tableType
3879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				self.cachingStats = cachingStats
3979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		globalState = GlobalState(tableType=self.tableTag,
4079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					  cachingStats=cachingStats)
4179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		reader = OTTableReader(data, globalState)
42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableClass = getattr(otTables, self.tableTag)
43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = tableClass()
44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.decompile(reader, font)
450585b64f70cca15aab3483f529c8faf1b7b825fdBehdad Esfahbod		if cachingStats:
46ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod			stats = sorted([(v, k) for k, v in cachingStats.items()])
47cfadfd0096d634f4505bca27d6926555d43305d4jvr			stats.reverse()
483ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("cachingsstats for ", self.tableTag)
49cfadfd0096d634f4505bca27d6926555d43305d4jvr			for v, k in stats:
50cfadfd0096d634f4505bca27d6926555d43305d4jvr				if v < 2:
51cfadfd0096d634f4505bca27d6926555d43305d4jvr					break
523ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print(v, k)
533ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("---", len(stats))
54d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def compile(self, font):
56823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		""" Create a top-level OTFWriter for the GPOS/GSUB table.
57823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			Call the compile method for the the table
581c734526926cad812f4f0e688eb2f68a33eda2f8jvr				for each 'converter' record in the table converter list
591c734526926cad812f4f0e688eb2f68a33eda2f8jvr					call converter's write method for each item in the value.
60823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						- For simple items, the write method adds a string to the
61823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						writer's self.items list.
621c734526926cad812f4f0e688eb2f68a33eda2f8jvr						- For Struct/Table/Subtable items, it add first adds new writer to the
63823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						to the writer's self.items, then calls the item's compile method.
64823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						This creates a tree of writers, rooted at the GUSB/GPOS writer, with
65823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						each writer representing a table, and the writer.items list containing
66823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						the child data strings and writers.
671c734526926cad812f4f0e688eb2f68a33eda2f8jvr			call the getAllData method
68823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				call _doneWriting, which removes duplicates
691c734526926cad812f4f0e688eb2f68a33eda2f8jvr				call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
701c734526926cad812f4f0e688eb2f68a33eda2f8jvr				Traverse the flat list of tables, calling getDataLength on each to update their position
711c734526926cad812f4f0e688eb2f68a33eda2f8jvr				Traverse the flat list of tables again, calling getData each get the data in the table, now that
721c734526926cad812f4f0e688eb2f68a33eda2f8jvr				pos's and offset are known.
73823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
74823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				If a lookup subtable overflows an offset, we have to start all over.
75823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		"""
76e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod		class GlobalState(object):
7779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod			def __init__(self, tableType):
7879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				self.tableType = tableType
7979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		globalState = GlobalState(tableType=self.tableTag)
8079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		writer = OTTableWriter(globalState)
81823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		writer.parent = None
82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.compile(writer, font)
83cfadfd0096d634f4505bca27d6926555d43305d4jvr		return writer.getAllData()
84823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
85d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, writer, font):
86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.toXML2(writer, font)
87d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
883a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
892b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
90d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not hasattr(self, "table"):
91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tableClass = getattr(otTables, self.tableTag)
92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			self.table = tableClass()
933a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		self.table.fromXML(name, attrs, content, font)
94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
95d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
963879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableReader(object):
977981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
9864b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to retrieve data from an OpenType table."""
993879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbod
10079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	__slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
1017981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
10279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __init__(self, data, globalState={}, localState=None, offset=0):
103d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.data = data
104d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.offset = offset
105d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = offset
10679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.globalState = globalState
10779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = localState
1087981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1090fac7fe32072e813d36882ba68af1227b6caff9bBehdad Esfahbod	def getSubReader(self, offset):
110d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		offset = self.offset + offset
11179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		cachingStats = self.globalState.cachingStats
11279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		if cachingStats is not None:
11379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod			cachingStats[offset] = cachingStats.get(offset, 0) + 1
11479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.__class__(self.data, self.globalState, self.localState, offset)
1157981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readUShort(self):
117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
118d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
119e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">H", self.data[pos:newpos])
120d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1227981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readShort(self):
124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
126e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">h", self.data[pos:newpos])
127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readLong(self):
131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
133e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">l", self.data[pos:newpos])
134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1367981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1379e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def readUInt24(self):
1389e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		pos = self.pos
1399e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		newpos = pos + 3
140c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
1419e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		self.pos = newpos
1429e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		return value
1439e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
144823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def readULong(self):
145823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
146823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newpos = pos + 4
147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value, = struct.unpack(">L", self.data[pos:newpos])
148823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.pos = newpos
149823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return value
150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readTag(self):
152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
154960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		value = Tag(self.data[pos:newpos])
155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(value) == 4
156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1587981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
15979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
16079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
16179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
16279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
1637981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
16479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
16579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
1667981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1683879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object):
169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
17064b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to gather and assemble data for OpenType tables."""
17164b5c80e80444a124da335e8d4d208bffcf2737bjvr
17279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __init__(self, globalState, localState=None):
173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items = []
174cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.pos = None
17579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.globalState = globalState
17679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = localState
1777981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
17879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
17979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
18079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
1827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
18379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
1857981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1864105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# assembler interface
1874105ca0b9515887a8a4cbb873c3de98009cbc633jvr
1884105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getAllData(self):
1894105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble all data, including all subtables."""
1904105ca0b9515887a8a4cbb873c3de98009cbc633jvr		self._doneWriting()
191823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		tables, extTables = self._gatherTables()
1924105ca0b9515887a8a4cbb873c3de98009cbc633jvr		tables.reverse()
193823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extTables.reverse()
1944105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# Gather all data in two passes: the absolute positions of all
1954105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# subtable are needed before the actual data can be assembled.
1964105ca0b9515887a8a4cbb873c3de98009cbc633jvr		pos = 0
1974105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
1984105ca0b9515887a8a4cbb873c3de98009cbc633jvr			table.pos = pos
1994105ca0b9515887a8a4cbb873c3de98009cbc633jvr			pos = pos + table.getDataLength()
200823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
201823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
202823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			table.pos = pos
203823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			pos = pos + table.getDataLength()
204823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
205823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2064105ca0b9515887a8a4cbb873c3de98009cbc633jvr		data = []
2074105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
2084105ca0b9515887a8a4cbb873c3de98009cbc633jvr			tableData = table.getData()
2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr			data.append(tableData)
210823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
211823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
212823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			tableData = table.getData()
213823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			data.append(tableData)
214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
215821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(data)
2164105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getDataLength(self):
2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Return the length of this table in bytes, without subtables."""
2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr		l = 0
2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for item in self.items:
2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData") or hasattr(item, "getCountData"):
222e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
223823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 4  # sizeof(ULong)
224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 2  # sizeof(UShort)
2264105ca0b9515887a8a4cbb873c3de98009cbc633jvr			else:
2274105ca0b9515887a8a4cbb873c3de98009cbc633jvr				l = l + len(item)
2284105ca0b9515887a8a4cbb873c3de98009cbc633jvr		return l
2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getData(self):
2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble the data for this writer/table, without subtables."""
2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr		items = list(self.items)  # make a shallow copy
233823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(items)
235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in range(numItems):
2364105ca0b9515887a8a4cbb873c3de98009cbc633jvr			item = items[i]
237823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2384105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData"):
239e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
240823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					items[i] = packULong(item.pos - pos)
241823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					try:
243823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						items[i] = packUShort(item.pos - pos)
244823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					except AssertionError:
245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# provide data to fix overflow problem.
24658acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod						# If the overflow is to a lookup, or from a lookup to a subtable,
247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# just report the current item.
248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						if self.name in [ 'LookupList', 'Lookup']:
249823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							overflowErrorRecord = self.getOverflowErrorRecord(item)
250823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						else:
251823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# overflow is within a subTable. Life is more complicated.
252823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# If we split the sub-table just before the current item, we may still suffer overflow.
253823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# This is because duplicate table merging is done only within an Extension subTable tree;
254823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# when we split the subtable in two, some items may no longer be duplicates.
255823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# Get worst case by adding up all the item lengths, depth first traversal.
256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# and then report the first item that overflows a short.
257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							def getDeepItemLength(table):
258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if hasattr(table, "getDataLength"):
259823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = 0
260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									for item in table.items:
261823f8cd15f16bb9dc3991c2672f16dd90579711bjvr										length = length + getDeepItemLength(item)
262823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								else:
263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = len(table)
264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								return length
265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							length = self.getDataLength()
267823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# Coverage is first in the item list, but last in the table list,
269823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# The original overflow is really in the item list. Skip the Coverage
270823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# table in the following test.
271823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								items = items[i+1:]
272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							for j in range(len(items)):
274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								item = items[j]
275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								length = length + getDeepItemLength(item)
276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if length > 65535:
277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									break
278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						overflowErrorRecord = self.getOverflowErrorRecord(item)
279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
281cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod						raise OTLOffsetOverflowError(overflowErrorRecord)
282823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
283821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(items)
284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
285cfadfd0096d634f4505bca27d6926555d43305d4jvr	def __hash__(self):
286cfadfd0096d634f4505bca27d6926555d43305d4jvr		# only works after self._doneWriting() has been called
287cfadfd0096d634f4505bca27d6926555d43305d4jvr		return hash(self.items)
288cfadfd0096d634f4505bca27d6926555d43305d4jvr
2898ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
2908ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
291b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
292b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
293b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
294b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.items == other.items
295cfadfd0096d634f4505bca27d6926555d43305d4jvr
296cfadfd0096d634f4505bca27d6926555d43305d4jvr	def _doneWriting(self, internedTables=None):
297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert CountData references to data string items
298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# collapse duplicate table references to a unique entry
299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For Extension Lookup types, we can
302823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# eliminate duplicates only within the tree under the Extension Lookup,
303823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# as offsets may exceed 64K even between Extension LookupTable subtables.
304cfadfd0096d634f4505bca27d6926555d43305d4jvr		if internedTables is None:
305cfadfd0096d634f4505bca27d6926555d43305d4jvr			internedTables = {}
306cfadfd0096d634f4505bca27d6926555d43305d4jvr		items = self.items
30797dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(len(items)))
308823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
309823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
310823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 1
311823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
312823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 0
313823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
314d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			item = items[i]
315cfadfd0096d634f4505bca27d6926555d43305d4jvr			if hasattr(item, "getCountData"):
316cfadfd0096d634f4505bca27d6926555d43305d4jvr				items[i] = item.getCountData()
317cfadfd0096d634f4505bca27d6926555d43305d4jvr			elif hasattr(item, "getData"):
318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if newTree:
319823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting()
320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
321823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting(internedTables)
322bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod					if item in internedTables:
323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						items[i] = item = internedTables[item]
324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
325823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						internedTables[item] = item
326cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.items = tuple(items)
327cfadfd0096d634f4505bca27d6926555d43305d4jvr
328823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def _gatherTables(self, tables=None, extTables=None, done=None):
329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert table references in self.items tree to a flat
330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# list of tables in depth-first traversal order.
331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We do the traversal in reverse order at each level, in order to
333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# resolve duplicate references to be the last reference in the list of tables.
334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For extension lookups, duplicate references can be merged only within the
335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# writer tree under the  extension lookup.
336823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if tables is None: # init call for first time.
337cfadfd0096d634f4505bca27d6926555d43305d4jvr			tables = []
338823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			extTables = []
339cfadfd0096d634f4505bca27d6926555d43305d4jvr			done = {}
340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		done[self] = 1
342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
343823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(self.items)
34497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(numItems))
345823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		iRange.reverse()
346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 1
349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 0
351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# add Coverage table if it is sorted last.
353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		sortCoverageLast = 0
354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "sortCoverageLast"):
355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# Find coverage table
356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			for i in range(numItems):
357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item = self.items[i]
358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if hasattr(item, "name") and (item.name == "Coverage"):
359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					sortCoverageLast = 1
360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					break
361bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if item not in done:
362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		saveItem = None
368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			item = self.items[i]
370cfadfd0096d634f4505bca27d6926555d43305d4jvr			if not hasattr(item, "getData"):
371cfadfd0096d634f4505bca27d6926555d43305d4jvr				continue
372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if sortCoverageLast and (i==1) and item.name == 'Coverage':
374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# we've already 'gathered' it above
375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				continue
376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if appendExtensions:
3789e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod				assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				newDone = {}
380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(extTables, None, newDone)
381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
382bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			elif item not in done:
383823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
389cfadfd0096d634f4505bca27d6926555d43305d4jvr		tables.append(self)
390823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return tables, extTables
391cfadfd0096d634f4505bca27d6926555d43305d4jvr
3924105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# interface for gathering data, as used by table.compile()
3938e48312f88adc41feb7c730154c6d058a010189djvr
3944105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getSubWriter(self):
39579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		subwriter = self.__class__(self.globalState, self.localState)
396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subwriter.parent = {0:self} # because some subtables have idential values, we discard
397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# the duplicates under the getAllData method. Hence some
398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# subtable writers can have more than one parent writer.
399823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return subwriter
400d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeUShort(self, value):
402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert 0 <= value < 0x10000
403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">H", value))
404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeShort(self, value):
406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">h", value))
4079e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def writeUInt24(self, value):
4099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		assert 0 <= value < 0x1000000
410c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		b = struct.pack(">L", value)
411c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		self.items.append(b[1:])
412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeLong(self, value):
414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">l", value))
415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
416823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeULong(self, value):
417823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(struct.pack(">L", value))
418823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeTag(self, tag):
420960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		tag = Tag(tag).tobytes()
421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(tag) == 4
422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(tag)
423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeSubTable(self, subWriter):
425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(subWriter)
426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeCountReference(self, table, name):
4287981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		ref = CountReference(table, name)
4297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		self.items.append(ref)
430ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod		return ref
431d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeStruct(self, format, values):
43366214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		data = struct.pack(*(format,) + values)
434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(data)
435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
436823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeData(self, data):
437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(data)
438d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def	getOverflowErrorRecord(self, item):
440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		LookupListIndex = SubTableIndex = itemName = itemIndex = None
441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.name == 'LookupList':
442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = item.repeatIndex
443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		elif self.name == 'Lookup':
444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = self.repeatIndex
445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			SubTableIndex = item.repeatIndex
446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			itemName = item.name
448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if hasattr(item, 'repeatIndex'):
449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemIndex = item.repeatIndex
450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if self.name == 'SubTable':
451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].repeatIndex
452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.repeatIndex
453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			elif self.name == 'ExtSubTable':
454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].parent[0].repeatIndex
455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.parent[0].repeatIndex
456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemName = ".".join(self.name, item.name)
458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				p1 = self.parent[0]
459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					itemName = ".".join(p1.name, item.name)
461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					p1 = p1.parent[0]
462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if p1:
463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					if p1.name == 'ExtSubTable':
464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						LookupListIndex = self.parent[0].parent[0].repeatIndex
465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						SubTableIndex = self.parent[0].repeatIndex
466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						LookupListIndex = self.parent[0].repeatIndex
468823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						SubTableIndex = self.repeatIndex
469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
47079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
471823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
472d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
473e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object):
474cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""A reference to a Count value, not a count of references."""
475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, table, name):
476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = table
477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
4787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod	def setValue(self, value):
4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		table = self.table
4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		name = self.name
4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		if table[name] is None:
4827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod			table[name] = value
4837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		else:
4841f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod			assert table[name] == value, (name, table[name], value)
485cfadfd0096d634f4505bca27d6926555d43305d4jvr	def getCountData(self):
486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return packUShort(self.table[self.name])
487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
48964b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value):
490cfadfd0096d634f4505bca27d6926555d43305d4jvr	assert 0 <= value < 0x10000, value
49164b5c80e80444a124da335e8d4d208bffcf2737bjvr	return struct.pack(">H", value)
492d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
493d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
494823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value):
495ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr	assert 0 <= value < 0x100000000, value
496823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return struct.pack(">L", value)
497823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
498823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
4995988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object):
500823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __init__(self):
501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.compileStatus = 0 # 0 means table was created
502823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# 1 means the table.read() function was called by a table which is subject
503823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# to delayed compilation
504823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# 2 means that it was subject to delayed compilation, and
505823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# has been decompiled
506823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
507823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.recurse = 0
508823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
509823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __getattr__(self, attr):
510823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# we get here only when the table does not have the attribute.
511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# This method ovveride exists so that we can try to de-compile
512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# a table which is subject to delayed decompilation, and then try
513823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# to get the value again after decompilation.
514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.recurse +=1
515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.recurse > 2:
516823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# shouldn't ever get here - we should only get to two levels of recursion.
517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# this guards against self.decompile NOT setting compileStatus to other than 1.
518cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.compileStatus == 1:
520f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod			self.ensureDecompiled()
521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			val = getattr(self, attr)
522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			self.recurse -=1
523823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			return val
524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
525cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise AttributeError(attr)
526823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
527823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
52864b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for all OpenType (sub)tables."""
52964b5c80e80444a124da335e8d4d208bffcf2737bjvr
530d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
531d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters
532d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
533d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
534d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[name]
535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
536078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def decompile(self, reader, font):
537823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.compileStatus = 2 # table has been decompiled.
538f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.readFormat(reader)
539d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = {}
540d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__rawTable = table  # for debugging
54141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		converters = self.getConverters()
54241caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		for conv in converters:
543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.name == "SubTable":
54479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
545d4d151390d1288f8d2df30f6dfa26a309c7334dajvr						table["LookupType"])
546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if conv.name == "ExtSubTable":
54779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						table["ExtensionLookupType"])
5499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			if conv.name == "FeatureParams":
5509e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				conv = conv.getConverter(reader["FeatureTag"])
551d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
552d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				l = []
553ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
554ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					countValue = table[conv.repeat]
555ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
556ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
55779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					countValue = reader[conv.repeat]
5586b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				for i in range(countValue + conv.aux):
559078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					l.append(conv.read(reader, font, table))
560d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = l
561d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
5625b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
5635b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
564078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				table[conv.name] = conv.read(reader, font, table)
5659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
56679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					reader[conv.name] = table[conv.name]
56741caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
568d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.postRead(table, font)
56941caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
570d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		del self.__rawTable  # succeeded, get rid of debugging info
571823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
572f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod	def ensureDecompiled(self):
573f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		if self.compileStatus != 1:
574f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod			return
575f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod		self.decompile(self.reader, self.font)
576f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod		del self.reader, self.font
577f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
578078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def compile(self, writer, font):
5793ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		self.ensureDecompiled()
580d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.preWrite(font)
581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, 'sortCoverageLast'):
583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			writer.sortCoverageLast = 1
584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
585f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.writeFormat(writer)
586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
587d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = table.get(conv.name)
588d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
589d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value is None:
59064b5c80e80444a124da335e8d4d208bffcf2737bjvr					value = []
5916b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				countValue = len(value) - conv.aux
592ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
593ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					ref = table[conv.repeat]
594ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.repeat] = None
595ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					ref.setValue(countValue)
596ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
597ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
59879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.repeat].setValue(countValue)
599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				for i in range(len(value)):
600078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					conv.write(writer, font, table, value[i], i)
601d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif conv.isCount:
602d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Special-case Count values.
603d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Assumption: a Count field will *always* precede
604ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				# the actual array(s).
605d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We need a default value, as it may be set later by a nested
60641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod				# table. We will later store it here.
607d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We add a reference: by the time the data is assembled
608d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the Count value will be filled in.
609ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
6109e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.name] = None
61279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.name] = ref
613ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
614ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.name] = ref
615d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
6165b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
6175b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
618078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				conv.write(writer, font, table, value)
6199e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
6209e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod					writer[conv.name] = value
621d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
622f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
623f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
624f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
625f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
626f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
627f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
628d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def postRead(self, table, font):
629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__dict__.update(table)
630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def preWrite(self, font):
632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__dict__.copy()
633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, attrs=None):
635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableName = self.__class__.__name__
636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			attrs = []
638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if hasattr(self, "Format"):
63964b5c80e80444a124da335e8d4d208bffcf2737bjvr			attrs = attrs + [("Format", self.Format)]
640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.begintag(tableName, attrs)
641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.toXML2(xmlWriter, font)
643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.endtag(tableName)
644d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML2(self, xmlWriter, font):
647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
648d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# This is because in TTX our parent writes our main tag, and in otBase.py we
649d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# do it ourselves. I think I'm getting schizophrenic...
650d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
65164b5c80e80444a124da335e8d4d208bffcf2737bjvr			if conv.repeat:
6525b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(len(value)):
654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					item = value[i]
65564b5c80e80444a124da335e8d4d208bffcf2737bjvr					conv.xmlWrite(xmlWriter, font, item, conv.name,
65664b5c80e80444a124da335e8d4d208bffcf2737bjvr							[("index", i)])
65764b5c80e80444a124da335e8d4d208bffcf2737bjvr			else:
6585b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, vars(self)):
6595b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
6605b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
66164b5c80e80444a124da335e8d4d208bffcf2737bjvr				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6633a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		try:
665d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv = self.getConverterByName(name)
666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		except KeyError:
667d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			raise    # XXX on KeyError, raise nice error
668d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = conv.xmlRead(attrs, content, font)
669d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if conv.repeat:
67052966bb14409b558dc46b52d34953fbde7dc1bcajvr			seq = getattr(self, conv.name, None)
67152966bb14409b558dc46b52d34953fbde7dc1bcajvr			if seq is None:
672d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				seq = []
67364b5c80e80444a124da335e8d4d208bffcf2737bjvr				setattr(self, conv.name, seq)
674d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			seq.append(value)
675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
67664b5c80e80444a124da335e8d4d208bffcf2737bjvr			setattr(self, conv.name, value)
677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6788ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
6798ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
680b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
681b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
682b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
68396b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
684f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		self.ensureDecompiled()
685b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		other.ensureDecompiled()
686f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
687b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
688d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
689d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
690d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable):
691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
692cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""Minor specialization of BaseTable, for tables that have multiple
69364b5c80e80444a124da335e8d4d208bffcf2737bjvr	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
69464b5c80e80444a124da335e8d4d208bffcf2737bjvr
695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters[self.Format]
697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
699d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[self.Format][name]
700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
701f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.Format = reader.readUShort()
703180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		assert self.Format != 0, (self, reader.pos, len(reader.data))
704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
705f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(self.Format)
707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
70964b5c80e80444a124da335e8d4d208bffcf2737bjvr#
71064b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords
71164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
71264b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that
71364b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support
71464b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter...
71564b5c80e80444a124da335e8d4d208bffcf2737bjvr#
71664b5c80e80444a124da335e8d4d208bffcf2737bjvr
717d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [
718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr#	Mask	 Name            isDevice  signed
719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0001, "XPlacement",   0,        1),
720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0002, "YPlacement",   0,        1),
721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0004, "XAdvance",     0,        1),
722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0008, "YAdvance",     0,        1),
723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0010, "XPlaDevice",   1,        0),
724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0020, "YPlaDevice",   1,        0),
725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0040, "XAdvDevice",   1,        0),
726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0080, "YAdvDevice",   1,        0),
727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# 	reserved:
728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0100, "Reserved1",    0,        0),
729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0200, "Reserved2",    0,        0),
730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0400, "Reserved3",    0,        0),
731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0800, "Reserved4",    0,        0),
732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x1000, "Reserved5",    0,        0),
733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x2000, "Reserved6",    0,        0),
734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x4000, "Reserved7",    0,        0),
735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x8000, "Reserved8",    0,        0),
736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr]
737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
738d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict():
739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	d = {}
740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for mask, name, isDevice, signed in valueRecordFormat:
741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		d[name] = mask, isDevice, signed
742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return d
743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
744d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict()
745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
747e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object):
748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
74964b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a format code, this object convert ValueRecords."""
750d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod
751601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod	def __init__(self, valueFormat):
752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = []
753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, signed in valueRecordFormat:
754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if valueFormat & mask:
755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				format.append((name, isDevice, signed))
756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.format = format
757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, reader, font):
759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = self.format
760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not format:
761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		valueRecord = ValueRecord()
763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in format:
764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if signed:
765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readShort()
766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readUShort()
768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
7702b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod					from . import otTables
771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subReader = reader.getSubReader(value)
772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = getattr(otTables, name)()
773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.decompile(subReader, font)
774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = None
776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(valueRecord, name, value)
777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return valueRecord
778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, writer, font, valueRecord):
780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in self.format:
781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(valueRecord, name, 0)
782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subWriter = writer.getSubWriter()
785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeSubTable(subWriter)
786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.compile(subWriter, font)
787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeUShort(0)
789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif signed:
790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeShort(value)
791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(value)
793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
795e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object):
796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# see ValueRecordFactory
798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getFormat(self):
800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = 0
801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name in self.__dict__.keys():
802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = format | valueRecordFormatDict[name][0]
803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, valueName, attrs=None):
806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = []
808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = list(attrs)
810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[:4]:  # "simple" values
811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				simpleItems.append((name, getattr(self, name)))
813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		deviceItems = []
814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[4:8]:  # device records
815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				device = getattr(self, name)
817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if device is not None:
818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceItems.append((name, device))
819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if deviceItems:
820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.begintag(valueName, simpleItems)
821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for name, deviceRecord in deviceItems:
823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if deviceRecord is not None:
824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceRecord.toXML(xmlWriter, font)
825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.endtag(valueName)
826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
828d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.simpletag(valueName, simpleItems)
829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8313a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
8322b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for k, v in attrs.items():
834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, k, int(v))
835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
836b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				continue
838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, attrs, content = element
839d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(otTables, name)()
840d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for elem2 in content:
841b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(elem2, tuple):
842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					continue
8433a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				name2, attrs2, content2 = elem2
8443a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				value.fromXML(name2, attrs2, content2, font)
845d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, name, value)
846d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8478ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
8488ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
849b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
850b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
851b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
852b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
853