11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
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)
8095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod		overflowRecord = None
8195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod
8295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod		while True:
8395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod			try:
8495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				writer = OTTableWriter(globalState)
8595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				self.table.compile(writer, font)
8695f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				return writer.getAllData()
8795f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod
8895f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod			except OTLOffsetOverflowError as e:
8995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod
9095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				if overflowRecord == e.value:
9195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					raise # Oh well...
9295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod
9395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				overflowRecord = e.value
9495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				print("Attempting to fix OTLOffsetOverflowError", e)
9595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				lastItem = overflowRecord
9695f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod
9795f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				ok = 0
9895f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				if overflowRecord.itemName is None:
9995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					from .otTables import fixLookupOverFlows
10095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					ok = fixLookupOverFlows(font, overflowRecord)
10195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				else:
10295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					from .otTables import fixSubTableOverFlows
10395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					ok = fixSubTableOverFlows(font, overflowRecord)
10495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod				if not ok:
10595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod					raise
106823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
107d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, writer, font):
108d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.toXML2(writer, font)
109d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1103a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
1112b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
112d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not hasattr(self, "table"):
113d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tableClass = getattr(otTables, self.tableTag)
114d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			self.table = tableClass()
1153a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		self.table.fromXML(name, attrs, content, font)
116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1183879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableReader(object):
1197981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
12064b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to retrieve data from an OpenType table."""
1213879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbod
12279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	__slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
1237981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
12479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __init__(self, data, globalState={}, localState=None, offset=0):
125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.data = data
126d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.offset = offset
127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = offset
12879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.globalState = globalState
12979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = localState
1307981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1310fac7fe32072e813d36882ba68af1227b6caff9bBehdad Esfahbod	def getSubReader(self, offset):
132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		offset = self.offset + offset
13379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		cachingStats = self.globalState.cachingStats
13479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		if cachingStats is not None:
13579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod			cachingStats[offset] = cachingStats.get(offset, 0) + 1
13679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.__class__(self.data, self.globalState, self.localState, offset)
1377981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readUShort(self):
139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
141e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">H", self.data[pos:newpos])
142d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1447981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readShort(self):
146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
148e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">h", self.data[pos:newpos])
149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1517981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readLong(self):
153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
155e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">l", self.data[pos:newpos])
156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1587981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1599e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def readUInt24(self):
1609e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		pos = self.pos
1619e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		newpos = pos + 3
162c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
1639e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		self.pos = newpos
1649e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		return value
1659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
166823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def readULong(self):
167823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
168823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newpos = pos + 4
169823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value, = struct.unpack(">L", self.data[pos:newpos])
170823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.pos = newpos
171823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return value
172823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readTag(self):
174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
176960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		value = Tag(self.data[pos:newpos])
177d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(value) == 4
178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
18279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
18379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
1857981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
18679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
18779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
1887981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
189d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1903879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object):
191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
19264b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to gather and assemble data for OpenType tables."""
19364b5c80e80444a124da335e8d4d208bffcf2737bjvr
19479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __init__(self, globalState, localState=None):
195d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items = []
196cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.pos = None
19779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.globalState = globalState
19879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = localState
19995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod		self.parent = None
2007981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
20179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
20279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
20379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
20479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
2057981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
20679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
20779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
2087981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# assembler interface
2104105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2114105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getAllData(self):
2124105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble all data, including all subtables."""
2134105ca0b9515887a8a4cbb873c3de98009cbc633jvr		self._doneWriting()
214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		tables, extTables = self._gatherTables()
2154105ca0b9515887a8a4cbb873c3de98009cbc633jvr		tables.reverse()
216823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extTables.reverse()
2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# Gather all data in two passes: the absolute positions of all
2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# subtable are needed before the actual data can be assembled.
2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr		pos = 0
2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr			table.pos = pos
2224105ca0b9515887a8a4cbb873c3de98009cbc633jvr			pos = pos + table.getDataLength()
223823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			table.pos = pos
226823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			pos = pos + table.getDataLength()
227823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
228823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr		data = []
2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr			tableData = table.getData()
2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr			data.append(tableData)
233823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			tableData = table.getData()
236823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			data.append(tableData)
237823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
238821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(data)
2394105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2404105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getDataLength(self):
2414105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Return the length of this table in bytes, without subtables."""
2424105ca0b9515887a8a4cbb873c3de98009cbc633jvr		l = 0
2434105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for item in self.items:
2444105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData") or hasattr(item, "getCountData"):
245e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 4  # sizeof(ULong)
247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 2  # sizeof(UShort)
2494105ca0b9515887a8a4cbb873c3de98009cbc633jvr			else:
2504105ca0b9515887a8a4cbb873c3de98009cbc633jvr				l = l + len(item)
2514105ca0b9515887a8a4cbb873c3de98009cbc633jvr		return l
2524105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2534105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getData(self):
2544105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble the data for this writer/table, without subtables."""
2554105ca0b9515887a8a4cbb873c3de98009cbc633jvr		items = list(self.items)  # make a shallow copy
256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(items)
258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in range(numItems):
2594105ca0b9515887a8a4cbb873c3de98009cbc633jvr			item = items[i]
260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2614105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData"):
262e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					items[i] = packULong(item.pos - pos)
264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					try:
266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						items[i] = packUShort(item.pos - pos)
2679e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod					except struct.error:
268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# provide data to fix overflow problem.
26958acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod						# If the overflow is to a lookup, or from a lookup to a subtable,
270ab0ca1bd6b495ea3b9523c74cc06ab957a10293cBehdad Esfahbod						# just report the current item.  Otherwise...
271ab0ca1bd6b495ea3b9523c74cc06ab957a10293cBehdad Esfahbod						if self.name not in [ 'LookupList', 'Lookup']:
272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# overflow is within a subTable. Life is more complicated.
273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# If we split the sub-table just before the current item, we may still suffer overflow.
274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# This is because duplicate table merging is done only within an Extension subTable tree;
275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# when we split the subtable in two, some items may no longer be duplicates.
276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# Get worst case by adding up all the item lengths, depth first traversal.
277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# and then report the first item that overflows a short.
278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							def getDeepItemLength(table):
279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if hasattr(table, "getDataLength"):
280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = 0
281823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									for item in table.items:
282823f8cd15f16bb9dc3991c2672f16dd90579711bjvr										length = length + getDeepItemLength(item)
283823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								else:
284823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = len(table)
285823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								return length
286823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
287823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							length = self.getDataLength()
288823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
289823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# Coverage is first in the item list, but last in the table list,
290823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# The original overflow is really in the item list. Skip the Coverage
291823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# table in the following test.
292823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								items = items[i+1:]
293823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
294823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							for j in range(len(items)):
295823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								item = items[j]
296823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								length = length + getDeepItemLength(item)
297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if length > 65535:
298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									break
299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						overflowErrorRecord = self.getOverflowErrorRecord(item)
300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
302cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod						raise OTLOffsetOverflowError(overflowErrorRecord)
303823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
304821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(items)
305d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
306cfadfd0096d634f4505bca27d6926555d43305d4jvr	def __hash__(self):
307cfadfd0096d634f4505bca27d6926555d43305d4jvr		# only works after self._doneWriting() has been called
308cfadfd0096d634f4505bca27d6926555d43305d4jvr		return hash(self.items)
309cfadfd0096d634f4505bca27d6926555d43305d4jvr
3108ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
3118ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
312b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
313b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
314273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
315b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.items == other.items
316cfadfd0096d634f4505bca27d6926555d43305d4jvr
317cfadfd0096d634f4505bca27d6926555d43305d4jvr	def _doneWriting(self, internedTables=None):
318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert CountData references to data string items
319823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# collapse duplicate table references to a unique entry
320823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
321823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
322823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For Extension Lookup types, we can
323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# eliminate duplicates only within the tree under the Extension Lookup,
324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# as offsets may exceed 64K even between Extension LookupTable subtables.
325cfadfd0096d634f4505bca27d6926555d43305d4jvr		if internedTables is None:
326cfadfd0096d634f4505bca27d6926555d43305d4jvr			internedTables = {}
327cfadfd0096d634f4505bca27d6926555d43305d4jvr		items = self.items
32897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(len(items)))
329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 1
332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 0
334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
335d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			item = items[i]
336cfadfd0096d634f4505bca27d6926555d43305d4jvr			if hasattr(item, "getCountData"):
337cfadfd0096d634f4505bca27d6926555d43305d4jvr				items[i] = item.getCountData()
338cfadfd0096d634f4505bca27d6926555d43305d4jvr			elif hasattr(item, "getData"):
339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if newTree:
340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting()
341d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting(internedTables)
343dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod					internedItem = internedTables.get(item)
344dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod					if internedItem:
345dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod						items[i] = item = internedItem
346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						internedTables[item] = item
348cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.items = tuple(items)
349cfadfd0096d634f4505bca27d6926555d43305d4jvr
350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def _gatherTables(self, tables=None, extTables=None, done=None):
351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert table references in self.items tree to a flat
352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# list of tables in depth-first traversal order.
353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We do the traversal in reverse order at each level, in order to
355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# resolve duplicate references to be the last reference in the list of tables.
356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For extension lookups, duplicate references can be merged only within the
357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# writer tree under the  extension lookup.
358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if tables is None: # init call for first time.
359cfadfd0096d634f4505bca27d6926555d43305d4jvr			tables = []
360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			extTables = []
361cfadfd0096d634f4505bca27d6926555d43305d4jvr			done = {}
362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		done[self] = 1
364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(self.items)
36697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(numItems))
367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		iRange.reverse()
368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 1
371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 0
373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# add Coverage table if it is sorted last.
375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		sortCoverageLast = 0
376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "sortCoverageLast"):
377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# Find coverage table
378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			for i in range(numItems):
379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item = self.items[i]
380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if hasattr(item, "name") and (item.name == "Coverage"):
381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					sortCoverageLast = 1
382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					break
383bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if item not in done:
384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
386ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				# We're a new parent of item
387ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				pass
388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
390823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			item = self.items[i]
391cfadfd0096d634f4505bca27d6926555d43305d4jvr			if not hasattr(item, "getData"):
392cfadfd0096d634f4505bca27d6926555d43305d4jvr				continue
393823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
394823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if sortCoverageLast and (i==1) and item.name == 'Coverage':
395823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# we've already 'gathered' it above
396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				continue
397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if appendExtensions:
3999e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod				assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
400823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				newDone = {}
401823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(extTables, None, newDone)
402823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
403bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			elif item not in done:
404823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
405823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
406ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				# We're a new parent of item
407ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				pass
408823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
409823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
410cfadfd0096d634f4505bca27d6926555d43305d4jvr		tables.append(self)
411823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return tables, extTables
412cfadfd0096d634f4505bca27d6926555d43305d4jvr
4134105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# interface for gathering data, as used by table.compile()
4148e48312f88adc41feb7c730154c6d058a010189djvr
4154105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getSubWriter(self):
41679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		subwriter = self.__class__(self.globalState, self.localState)
417ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod		subwriter.parent = self # because some subtables have idential values, we discard
418ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod					# the duplicates under the getAllData method. Hence some
419ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod					# subtable writers can have more than one parent writer.
420ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod					# But we just care about first one right now.
421823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return subwriter
422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeUShort(self, value):
424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert 0 <= value < 0x10000
425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">H", value))
426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeShort(self, value):
428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">h", value))
4299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
4309e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def writeUInt24(self, value):
4319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		assert 0 <= value < 0x1000000
432c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		b = struct.pack(">L", value)
433c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod		self.items.append(b[1:])
434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeLong(self, value):
436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">l", value))
437d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeULong(self, value):
439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(struct.pack(">L", value))
440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
441d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeTag(self, tag):
442960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		tag = Tag(tag).tobytes()
443d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(tag) == 4
444d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(tag)
445d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
446d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeSubTable(self, subWriter):
447d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(subWriter)
448d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
449d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeCountReference(self, table, name):
4507981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		ref = CountReference(table, name)
4517981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		self.items.append(ref)
452ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod		return ref
453d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
454d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeStruct(self, format, values):
45566214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		data = struct.pack(*(format,) + values)
456d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(data)
457d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeData(self, data):
459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(data)
460d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def	getOverflowErrorRecord(self, item):
462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		LookupListIndex = SubTableIndex = itemName = itemIndex = None
463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.name == 'LookupList':
464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = item.repeatIndex
465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		elif self.name == 'Lookup':
466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = self.repeatIndex
467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			SubTableIndex = item.repeatIndex
468823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			itemName = item.name
470823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if hasattr(item, 'repeatIndex'):
471823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemIndex = item.repeatIndex
472823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if self.name == 'SubTable':
473ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				LookupListIndex = self.parent.repeatIndex
474823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.repeatIndex
475823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			elif self.name == 'ExtSubTable':
476ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				LookupListIndex = self.parent.parent.repeatIndex
477ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				SubTableIndex = self.parent.repeatIndex
478823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
4799e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod				itemName = ".".join([self.name, item.name])
480ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod				p1 = self.parent
481823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
4829e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod					itemName = ".".join([p1.name, item.name])
483ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod					p1 = p1.parent
484823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if p1:
485823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					if p1.name == 'ExtSubTable':
486ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod						LookupListIndex = p1.parent.parent.repeatIndex
487ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod						SubTableIndex = p1.parent.repeatIndex
488823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
489ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod						LookupListIndex = p1.parent.repeatIndex
4909e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod						SubTableIndex = p1.repeatIndex
491823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
49279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
493823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
494d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
495e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object):
496cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""A reference to a Count value, not a count of references."""
497d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, table, name):
498d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = table
499d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
5007981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod	def setValue(self, value):
5017981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		table = self.table
5027981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		name = self.name
5037981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		if table[name] is None:
5047981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod			table[name] = value
5057981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		else:
5061f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod			assert table[name] == value, (name, table[name], value)
507cfadfd0096d634f4505bca27d6926555d43305d4jvr	def getCountData(self):
508d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return packUShort(self.table[self.name])
509d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
510d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
51164b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value):
51264b5c80e80444a124da335e8d4d208bffcf2737bjvr	return struct.pack(">H", value)
513d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
514d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
515823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value):
516ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr	assert 0 <= value < 0x100000000, value
517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return struct.pack(">L", value)
518823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
5205988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object):
521b4070bd6293998ac4bc6a68c1f774e9e9908c65bBehdad Esfahbod
522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __getattr__(self, attr):
523f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod		reader = self.__dict__.get("reader")
524dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		if reader:
525dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.reader
526dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			font = self.font
527dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.font
528dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			self.decompile(reader, font)
529dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			return getattr(self, attr)
530dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod
531dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		raise AttributeError(attr)
532823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
53364b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for all OpenType (sub)tables."""
53464b5c80e80444a124da335e8d4d208bffcf2737bjvr
535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
536d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters
537d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
538d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
539d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[name]
540d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
541078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def decompile(self, reader, font):
542f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.readFormat(reader)
543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = {}
544d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__rawTable = table  # for debugging
54541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		converters = self.getConverters()
54641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		for conv in converters:
547d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.name == "SubTable":
54879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
549d4d151390d1288f8d2df30f6dfa26a309c7334dajvr						table["LookupType"])
550823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if conv.name == "ExtSubTable":
55179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						table["ExtensionLookupType"])
5539e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			if conv.name == "FeatureParams":
5549e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				conv = conv.getConverter(reader["FeatureTag"])
555d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
556d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				l = []
557ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
558ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					countValue = table[conv.repeat]
559ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
560ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
56179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					countValue = reader[conv.repeat]
5626b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				for i in range(countValue + conv.aux):
563078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					l.append(conv.read(reader, font, table))
564d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = l
565d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
5665b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
5675b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
568078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				table[conv.name] = conv.read(reader, font, table)
5699e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
57079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					reader[conv.name] = table[conv.name]
57141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
572d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.postRead(table, font)
57341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
574d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		del self.__rawTable  # succeeded, get rid of debugging info
575823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
576f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod	def ensureDecompiled(self):
577f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod		reader = self.__dict__.get("reader")
578dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod		if reader:
579dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.reader
580dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			font = self.font
581dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			del self.font
582dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod			self.decompile(reader, font)
583f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
584078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def compile(self, writer, font):
5853ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		self.ensureDecompiled()
586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.preWrite(font)
587823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, 'sortCoverageLast'):
589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			writer.sortCoverageLast = 1
590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
5915fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod		if hasattr(self.__class__, 'LookupType'):
5925fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod			writer['LookupType'].setValue(self.__class__.LookupType)
5935fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod
594f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.writeFormat(writer)
595d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
596d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = table.get(conv.name)
597d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
598d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value is None:
59964b5c80e80444a124da335e8d4d208bffcf2737bjvr					value = []
6006b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				countValue = len(value) - conv.aux
601ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
6026bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod					CountReference(table, conv.repeat).setValue(countValue)
603ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
604ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
60579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.repeat].setValue(countValue)
606823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				for i in range(len(value)):
607078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					conv.write(writer, font, table, value[i], i)
608d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif conv.isCount:
609d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Special-case Count values.
610d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Assumption: a Count field will *always* precede
611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				# the actual array(s).
612d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We need a default value, as it may be set later by a nested
61341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod				# table. We will later store it here.
614d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We add a reference: by the time the data is assembled
615d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the Count value will be filled in.
616ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
6176bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod				table[conv.name] = None
6189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
61979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.name] = ref
6205fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod			elif conv.isLookupType:
6215fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
6225fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				table[conv.name] = None
6235fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod				writer['LookupType'] = ref
624d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
6255b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
6265b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
627078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				conv.write(writer, font, table, value)
6289e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
6299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod					writer[conv.name] = value
630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
631f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
632f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
633f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
634f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
635f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
636f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def postRead(self, table, font):
638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__dict__.update(table)
639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def preWrite(self, font):
641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__dict__.copy()
642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
643d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod	def toXML(self, xmlWriter, font, attrs=None, name=None):
644d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod		tableName = name if name else self.__class__.__name__
645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			attrs = []
647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if hasattr(self, "Format"):
64864b5c80e80444a124da335e8d4d208bffcf2737bjvr			attrs = attrs + [("Format", self.Format)]
649d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.begintag(tableName, attrs)
650d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.toXML2(xmlWriter, font)
652d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.endtag(tableName)
653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
655d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML2(self, xmlWriter, font):
656d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
657d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# This is because in TTX our parent writes our main tag, and in otBase.py we
658d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# do it ourselves. I think I'm getting schizophrenic...
659d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
66064b5c80e80444a124da335e8d4d208bffcf2737bjvr			if conv.repeat:
6615b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(len(value)):
663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					item = value[i]
66464b5c80e80444a124da335e8d4d208bffcf2737bjvr					conv.xmlWrite(xmlWriter, font, item, conv.name,
66564b5c80e80444a124da335e8d4d208bffcf2737bjvr							[("index", i)])
66664b5c80e80444a124da335e8d4d208bffcf2737bjvr			else:
6675b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, vars(self)):
6685b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
6695b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
67064b5c80e80444a124da335e8d4d208bffcf2737bjvr				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
671d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6723a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
673d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		try:
674d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv = self.getConverterByName(name)
675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		except KeyError:
676d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			raise    # XXX on KeyError, raise nice error
677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = conv.xmlRead(attrs, content, font)
678d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if conv.repeat:
67952966bb14409b558dc46b52d34953fbde7dc1bcajvr			seq = getattr(self, conv.name, None)
68052966bb14409b558dc46b52d34953fbde7dc1bcajvr			if seq is None:
681d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				seq = []
68264b5c80e80444a124da335e8d4d208bffcf2737bjvr				setattr(self, conv.name, seq)
683d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			seq.append(value)
684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
68564b5c80e80444a124da335e8d4d208bffcf2737bjvr			setattr(self, conv.name, value)
686d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6878ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
6888ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
689b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
690b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
691273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
69296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
693f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		self.ensureDecompiled()
694b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		other.ensureDecompiled()
695f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
696b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
699d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable):
700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
701cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""Minor specialization of BaseTable, for tables that have multiple
70264b5c80e80444a124da335e8d4d208bffcf2737bjvr	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
70364b5c80e80444a124da335e8d4d208bffcf2737bjvr
704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
705d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters[self.Format]
706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[self.Format][name]
709d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
710f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
711d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.Format = reader.readUShort()
712180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		assert self.Format != 0, (self, reader.pos, len(reader.data))
713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
714f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(self.Format)
716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
717d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod	def toXML(self, xmlWriter, font, attrs=None, name=None):
718d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
719d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod
720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
72164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
72264b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords
72364b5c80e80444a124da335e8d4d208bffcf2737bjvr#
72464b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that
72564b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support
72664b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter...
72764b5c80e80444a124da335e8d4d208bffcf2737bjvr#
72864b5c80e80444a124da335e8d4d208bffcf2737bjvr
729d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [
730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr#	Mask	 Name            isDevice  signed
731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0001, "XPlacement",   0,        1),
732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0002, "YPlacement",   0,        1),
733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0004, "XAdvance",     0,        1),
734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0008, "YAdvance",     0,        1),
735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0010, "XPlaDevice",   1,        0),
736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0020, "YPlaDevice",   1,        0),
737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0040, "XAdvDevice",   1,        0),
738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0080, "YAdvDevice",   1,        0),
739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# 	reserved:
740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0100, "Reserved1",    0,        0),
741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0200, "Reserved2",    0,        0),
742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0400, "Reserved3",    0,        0),
743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0800, "Reserved4",    0,        0),
744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x1000, "Reserved5",    0,        0),
745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x2000, "Reserved6",    0,        0),
746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x4000, "Reserved7",    0,        0),
747d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x8000, "Reserved8",    0,        0),
748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr]
749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
750d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict():
751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	d = {}
752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for mask, name, isDevice, signed in valueRecordFormat:
753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		d[name] = mask, isDevice, signed
754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return d
755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
756d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict()
757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
759e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object):
760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
76164b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a format code, this object convert ValueRecords."""
762d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod
763601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod	def __init__(self, valueFormat):
764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = []
765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, signed in valueRecordFormat:
766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if valueFormat & mask:
767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				format.append((name, isDevice, signed))
768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.format = format
769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, reader, font):
771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = self.format
772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not format:
773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		valueRecord = ValueRecord()
775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in format:
776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if signed:
777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readShort()
778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readUShort()
780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
7822b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod					from . import otTables
783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subReader = reader.getSubReader(value)
784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = getattr(otTables, name)()
785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.decompile(subReader, font)
786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = None
788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(valueRecord, name, value)
789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return valueRecord
790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, writer, font, valueRecord):
792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in self.format:
793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(valueRecord, name, 0)
794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subWriter = writer.getSubWriter()
797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeSubTable(subWriter)
798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.compile(subWriter, font)
799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeUShort(0)
801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif signed:
802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeShort(value)
803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(value)
805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
807e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object):
808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# see ValueRecordFactory
810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getFormat(self):
812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = 0
813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name in self.__dict__.keys():
814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = format | valueRecordFormatDict[name][0]
815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, valueName, attrs=None):
818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = []
820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = list(attrs)
822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[:4]:  # "simple" values
823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				simpleItems.append((name, getattr(self, name)))
825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		deviceItems = []
826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[4:8]:  # device records
827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
828d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				device = getattr(self, name)
829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if device is not None:
830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceItems.append((name, device))
831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if deviceItems:
832d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.begintag(valueName, simpleItems)
833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for name, deviceRecord in deviceItems:
835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if deviceRecord is not None:
836d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceRecord.toXML(xmlWriter, font)
837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.endtag(valueName)
838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
839d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
840d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.simpletag(valueName, simpleItems)
841d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8433a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
8442b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
845d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for k, v in attrs.items():
846d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, k, int(v))
847d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
848b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
849d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				continue
850d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, attrs, content = element
851d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(otTables, name)()
852d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for elem2 in content:
853b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(elem2, tuple):
854d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					continue
8553a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				name2, attrs2, content2 = elem2
8563a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				value.fromXML(name2, attrs2, content2, font)
857d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, name, value)
858d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8598ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod	def __ne__(self, other):
8608ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod		return not self.__eq__(other)
861b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
862b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
863273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
864b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
865