otBase.py revision dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4
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)
2449e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod					except struct.error:
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):
293273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
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)
322dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod					internedItem = internedTables.get(item)
323dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod					if internedItem:
324dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod						items[i] = item = internedItem
325823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
326823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						internedTables[item] = item
327cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.items = tuple(items)
328cfadfd0096d634f4505bca27d6926555d43305d4jvr
329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def _gatherTables(self, tables=None, extTables=None, done=None):
330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert table references in self.items tree to a flat
331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# list of tables in depth-first traversal order.
332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We do the traversal in reverse order at each level, in order to
334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# resolve duplicate references to be the last reference in the list of tables.
335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For extension lookups, duplicate references can be merged only within the
336823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# writer tree under the  extension lookup.
337823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if tables is None: # init call for first time.
338cfadfd0096d634f4505bca27d6926555d43305d4jvr			tables = []
339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			extTables = []
340cfadfd0096d634f4505bca27d6926555d43305d4jvr			done = {}
341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		done[self] = 1
343823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
344823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(self.items)
34597dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(numItems))
346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		iRange.reverse()
347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 1
350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 0
352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# add Coverage table if it is sorted last.
354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		sortCoverageLast = 0
355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "sortCoverageLast"):
356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# Find coverage table
357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			for i in range(numItems):
358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item = self.items[i]
359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if hasattr(item, "name") and (item.name == "Coverage"):
360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					sortCoverageLast = 1
361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					break
362bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if item not in done:
363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		saveItem = None
369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			item = self.items[i]
371cfadfd0096d634f4505bca27d6926555d43305d4jvr			if not hasattr(item, "getData"):
372cfadfd0096d634f4505bca27d6926555d43305d4jvr				continue
373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if sortCoverageLast and (i==1) and item.name == 'Coverage':
375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# we've already 'gathered' it above
376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				continue
377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if appendExtensions:
3799e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod				assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				newDone = {}
381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(extTables, None, newDone)
382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
383bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			elif item not in done:
384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
390cfadfd0096d634f4505bca27d6926555d43305d4jvr		tables.append(self)
391823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return tables, extTables
392cfadfd0096d634f4505bca27d6926555d43305d4jvr
3934105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# interface for gathering data, as used by table.compile()
3948e48312f88adc41feb7c730154c6d058a010189djvr
3954105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getSubWriter(self):
39679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		subwriter = self.__class__(self.globalState, self.localState)
397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subwriter.parent = {0:self} # because some subtables have idential values, we discard
398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# the duplicates under the getAllData method. Hence some
399823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# subtable writers can have more than one parent writer.
400823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return subwriter
401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeUShort(self, value):
403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert 0 <= value < 0x10000
404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">H", value))
405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeShort(self, value):
407d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">h", value))
4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
4099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def writeUInt24(self, value):
4109e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		assert 0 <= value < 0x1000000
411c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		b = struct.pack(">L", value)
412c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		self.items.append(b[1:])
413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeLong(self, value):
415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">l", value))
416d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
417823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeULong(self, value):
418823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(struct.pack(">L", value))
419823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeTag(self, tag):
421960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		tag = Tag(tag).tobytes()
422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(tag) == 4
423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(tag)
424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeSubTable(self, subWriter):
426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(subWriter)
427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeCountReference(self, table, name):
4297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		ref = CountReference(table, name)
4307981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		self.items.append(ref)
431ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod		return ref
432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeStruct(self, format, values):
43466214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		data = struct.pack(*(format,) + values)
435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(data)
436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeData(self, data):
438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(data)
439d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def	getOverflowErrorRecord(self, item):
441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		LookupListIndex = SubTableIndex = itemName = itemIndex = None
442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.name == 'LookupList':
443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = item.repeatIndex
444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		elif self.name == 'Lookup':
445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = self.repeatIndex
446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			SubTableIndex = item.repeatIndex
447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			itemName = item.name
449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if hasattr(item, 'repeatIndex'):
450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemIndex = item.repeatIndex
451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if self.name == 'SubTable':
452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].repeatIndex
453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.repeatIndex
454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			elif self.name == 'ExtSubTable':
455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].parent[0].repeatIndex
456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.parent[0].repeatIndex
457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
4589e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod				itemName = ".".join([self.name, item.name])
459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				p1 = self.parent[0]
460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
4619e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod					itemName = ".".join([p1.name, item.name])
462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					p1 = p1.parent[0]
463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if p1:
464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					if p1.name == 'ExtSubTable':
4659e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod						LookupListIndex = p1.parent[0].parent[0].repeatIndex
4669e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod						SubTableIndex = p1.parent[0].repeatIndex
467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
4689e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod						LookupListIndex = p1.parent[0].repeatIndex
4699e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod						SubTableIndex = p1.repeatIndex
470823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
47179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
472823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
474e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object):
475cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""A reference to a Count value, not a count of references."""
476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, table, name):
477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = table
478d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod	def setValue(self, value):
4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		table = self.table
4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		name = self.name
4827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		if table[name] is None:
4837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod			table[name] = value
4847981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		else:
4851f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod			assert table[name] == value, (name, table[name], value)
486cfadfd0096d634f4505bca27d6926555d43305d4jvr	def getCountData(self):
487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return packUShort(self.table[self.name])
488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
489d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
49064b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(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):
500b4070bd6293998ac4bc6a68c1f774e9e9908c65bBehdad Esfahbod
501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __getattr__(self, attr):
502f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod		reader = self.__dict__.get("reader")
503dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		if reader:
504dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.reader
505dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			font = self.font
506dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.font
507dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			self.decompile(reader, font)
508dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			return getattr(self, attr)
509dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod
510dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		raise AttributeError(attr)
511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
51364b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for all OpenType (sub)tables."""
51464b5c80e80444a124da335e8d4d208bffcf2737bjvr
515d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
516d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters
517d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
518d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
519d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[name]
520d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
521078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def decompile(self, reader, font):
522f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.readFormat(reader)
523d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = {}
524d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__rawTable = table  # for debugging
52541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		converters = self.getConverters()
52641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		for conv in converters:
527d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.name == "SubTable":
52879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
529d4d151390d1288f8d2df30f6dfa26a309c7334dajvr						table["LookupType"])
530823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if conv.name == "ExtSubTable":
53179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
532823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						table["ExtensionLookupType"])
5339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			if conv.name == "FeatureParams":
5349e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				conv = conv.getConverter(reader["FeatureTag"])
535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
536d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				l = []
537ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
538ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					countValue = table[conv.repeat]
539ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
540ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
54179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					countValue = reader[conv.repeat]
5426b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				for i in range(countValue + conv.aux):
543078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					l.append(conv.read(reader, font, table))
544d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = l
545d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
5465b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
5475b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
548078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				table[conv.name] = conv.read(reader, font, table)
5499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
55079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					reader[conv.name] = table[conv.name]
55141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
552d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.postRead(table, font)
55341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
554d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		del self.__rawTable  # succeeded, get rid of debugging info
555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
556f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod	def ensureDecompiled(self):
557f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod		reader = self.__dict__.get("reader")
558dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		if reader:
559dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.reader
560dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			font = self.font
561dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.font
562dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			self.decompile(reader, font)
563f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
564078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def compile(self, writer, font):
5653ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		self.ensureDecompiled()
566d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.preWrite(font)
567823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
568823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, 'sortCoverageLast'):
569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			writer.sortCoverageLast = 1
570823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
5715fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod		if hasattr(self.__class__, 'LookupType'):
5725fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod			writer['LookupType'].setValue(self.__class__.LookupType)
5735fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod
574f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.writeFormat(writer)
575d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
576d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = table.get(conv.name)
577d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
578d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value is None:
57964b5c80e80444a124da335e8d4d208bffcf2737bjvr					value = []
5806b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				countValue = len(value) - conv.aux
581ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
5826bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod					CountReference(table, conv.repeat).setValue(countValue)
583ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
584ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
58579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.repeat].setValue(countValue)
586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				for i in range(len(value)):
587078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					conv.write(writer, font, table, value[i], i)
588d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif conv.isCount:
589d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Special-case Count values.
590d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Assumption: a Count field will *always* precede
591ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				# the actual array(s).
592d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We need a default value, as it may be set later by a nested
59341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod				# table. We will later store it here.
594d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We add a reference: by the time the data is assembled
595d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the Count value will be filled in.
596ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
5976bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod				table[conv.name] = None
5989e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
59979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.name] = ref
6005fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod			elif conv.isLookupType:
6015fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
6025fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				table[conv.name] = None
6035fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				writer['LookupType'] = ref
604d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
6055b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
6065b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
607078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				conv.write(writer, font, table, value)
6089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
6099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod					writer[conv.name] = value
610d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
611f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
612f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
613f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
614f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
615f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
616f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
617d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def postRead(self, table, font):
618d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__dict__.update(table)
619d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
620d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def preWrite(self, font):
621d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__dict__.copy()
622d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
623d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod	def toXML(self, xmlWriter, font, attrs=None, name=None):
624d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod		tableName = name if name else self.__class__.__name__
625d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
626d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			attrs = []
627d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if hasattr(self, "Format"):
62864b5c80e80444a124da335e8d4d208bffcf2737bjvr			attrs = attrs + [("Format", self.Format)]
629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.begintag(tableName, attrs)
630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.toXML2(xmlWriter, font)
632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.endtag(tableName)
633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML2(self, xmlWriter, font):
636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# This is because in TTX our parent writes our main tag, and in otBase.py we
638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# do it ourselves. I think I'm getting schizophrenic...
639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
64064b5c80e80444a124da335e8d4d208bffcf2737bjvr			if conv.repeat:
6415b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(len(value)):
643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					item = value[i]
64464b5c80e80444a124da335e8d4d208bffcf2737bjvr					conv.xmlWrite(xmlWriter, font, item, conv.name,
64564b5c80e80444a124da335e8d4d208bffcf2737bjvr							[("index", i)])
64664b5c80e80444a124da335e8d4d208bffcf2737bjvr			else:
6475b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, vars(self)):
6485b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
6495b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
65064b5c80e80444a124da335e8d4d208bffcf2737bjvr				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6523a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		try:
654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv = self.getConverterByName(name)
655d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		except KeyError:
656d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			raise    # XXX on KeyError, raise nice error
657d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = conv.xmlRead(attrs, content, font)
658d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if conv.repeat:
65952966bb14409b558dc46b52d34953fbde7dc1bcajvr			seq = getattr(self, conv.name, None)
66052966bb14409b558dc46b52d34953fbde7dc1bcajvr			if seq is None:
661d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				seq = []
66264b5c80e80444a124da335e8d4d208bffcf2737bjvr				setattr(self, conv.name, seq)
663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			seq.append(value)
664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
66564b5c80e80444a124da335e8d4d208bffcf2737bjvr			setattr(self, conv.name, value)
666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6678ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
6688ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
669b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
670b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
671273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
67296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
673f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		self.ensureDecompiled()
674b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		other.ensureDecompiled()
675f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
676b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
678d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
679d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable):
680d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
681cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""Minor specialization of BaseTable, for tables that have multiple
68264b5c80e80444a124da335e8d4d208bffcf2737bjvr	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
68364b5c80e80444a124da335e8d4d208bffcf2737bjvr
684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
685d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters[self.Format]
686d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
687d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
688d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[self.Format][name]
689d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
690f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.Format = reader.readUShort()
692180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		assert self.Format != 0, (self, reader.pos, len(reader.data))
693d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
694f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(self.Format)
696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
697d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod	def toXML(self, xmlWriter, font, attrs=None, name=None):
698d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
699d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod
700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
70164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
70264b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords
70364b5c80e80444a124da335e8d4d208bffcf2737bjvr#
70464b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that
70564b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support
70664b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter...
70764b5c80e80444a124da335e8d4d208bffcf2737bjvr#
70864b5c80e80444a124da335e8d4d208bffcf2737bjvr
709d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [
710d4d151390d1288f8d2df30f6dfa26a309c7334dajvr#	Mask	 Name            isDevice  signed
711d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0001, "XPlacement",   0,        1),
712d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0002, "YPlacement",   0,        1),
713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0004, "XAdvance",     0,        1),
714d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0008, "YAdvance",     0,        1),
715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0010, "XPlaDevice",   1,        0),
716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0020, "YPlaDevice",   1,        0),
717d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0040, "XAdvDevice",   1,        0),
718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0080, "YAdvDevice",   1,        0),
719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# 	reserved:
720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0100, "Reserved1",    0,        0),
721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0200, "Reserved2",    0,        0),
722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0400, "Reserved3",    0,        0),
723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0800, "Reserved4",    0,        0),
724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x1000, "Reserved5",    0,        0),
725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x2000, "Reserved6",    0,        0),
726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x4000, "Reserved7",    0,        0),
727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x8000, "Reserved8",    0,        0),
728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr]
729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
730d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict():
731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	d = {}
732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for mask, name, isDevice, signed in valueRecordFormat:
733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		d[name] = mask, isDevice, signed
734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return d
735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
736d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict()
737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
739e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object):
740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
74164b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a format code, this object convert ValueRecords."""
742d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod
743601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod	def __init__(self, valueFormat):
744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = []
745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, signed in valueRecordFormat:
746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if valueFormat & mask:
747d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				format.append((name, isDevice, signed))
748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.format = format
749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
750d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, reader, font):
751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = self.format
752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not format:
753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		valueRecord = ValueRecord()
755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in format:
756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if signed:
757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readShort()
758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readUShort()
760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
7622b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod					from . import otTables
763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subReader = reader.getSubReader(value)
764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = getattr(otTables, name)()
765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.decompile(subReader, font)
766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = None
768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(valueRecord, name, value)
769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return valueRecord
770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, writer, font, valueRecord):
772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in self.format:
773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(valueRecord, name, 0)
774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subWriter = writer.getSubWriter()
777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeSubTable(subWriter)
778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.compile(subWriter, font)
779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeUShort(0)
781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif signed:
782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeShort(value)
783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(value)
785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
787e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object):
788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# see ValueRecordFactory
790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getFormat(self):
792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = 0
793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name in self.__dict__.keys():
794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = format | valueRecordFormatDict[name][0]
795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, valueName, attrs=None):
798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = []
800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = list(attrs)
802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[:4]:  # "simple" values
803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				simpleItems.append((name, getattr(self, name)))
805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		deviceItems = []
806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[4:8]:  # device records
807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				device = getattr(self, name)
809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if device is not None:
810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceItems.append((name, device))
811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if deviceItems:
812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.begintag(valueName, simpleItems)
813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for name, deviceRecord in deviceItems:
815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if deviceRecord is not None:
816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceRecord.toXML(xmlWriter, font)
817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.endtag(valueName)
818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.simpletag(valueName, simpleItems)
821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8233a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
8242b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for k, v in attrs.items():
826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, k, int(v))
827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
828b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				continue
830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, attrs, content = element
831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(otTables, name)()
832d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for elem2 in content:
833b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(elem2, tuple):
834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					continue
8353a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				name2, attrs2, content2 = elem2
8363a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				value.fromXML(name2, attrs2, content2, font)
837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, name, value)
838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8398ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
8408ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
841b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
842b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
843273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
844b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
845