otBase.py revision 821572c9a92d338a7ecbb4261c08ce378eb5434d
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .DefaultTable import DefaultTable
4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct
5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6823f8cd15f16bb9dc3991c2672f16dd90579711bjvrclass OverflowErrorRecord:
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 {}
3579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		class GlobalState:
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		"""
7679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		class GlobalState:
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
140319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod		value = (byteord(self.data[pos]) << 16) | (byteord(self.data[pos+1]) << 8) | byteord(self.data[pos+2])
1419e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		value, = struct.unpack(">H", self.data[pos:newpos])
1429e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		self.pos = newpos
1439e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		return value
1449e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
145823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def readULong(self):
146823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newpos = pos + 4
148823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value, = struct.unpack(">L", self.data[pos:newpos])
149823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.pos = newpos
150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return value
151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readTag(self):
153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
155960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		value = Tag(self.data[pos:newpos])
156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(value) == 4
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
1597981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
16079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
16179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
16279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
16379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
1647981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
16579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
16679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
1677981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1693879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object):
170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
17164b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to gather and assemble data for OpenType tables."""
17264b5c80e80444a124da335e8d4d208bffcf2737bjvr
17379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __init__(self, globalState, localState=None):
174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items = []
175cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.pos = None
17679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.globalState = globalState
17779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = localState
1787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
17979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __setitem__(self, name, value):
18079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state = self.localState.copy() if self.localState else dict()
18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		state[name] = value
18279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.localState = state
1837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod	def __getitem__(self, name):
18579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return self.localState[name]
1867981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod
1874105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# assembler interface
1884105ca0b9515887a8a4cbb873c3de98009cbc633jvr
1894105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getAllData(self):
1904105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble all data, including all subtables."""
1914105ca0b9515887a8a4cbb873c3de98009cbc633jvr		self._doneWriting()
192823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		tables, extTables = self._gatherTables()
1934105ca0b9515887a8a4cbb873c3de98009cbc633jvr		tables.reverse()
194823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extTables.reverse()
1954105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# Gather all data in two passes: the absolute positions of all
1964105ca0b9515887a8a4cbb873c3de98009cbc633jvr		# subtable are needed before the actual data can be assembled.
1974105ca0b9515887a8a4cbb873c3de98009cbc633jvr		pos = 0
1984105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
1994105ca0b9515887a8a4cbb873c3de98009cbc633jvr			table.pos = pos
2004105ca0b9515887a8a4cbb873c3de98009cbc633jvr			pos = pos + table.getDataLength()
201823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
202823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
203823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			table.pos = pos
204823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			pos = pos + table.getDataLength()
205823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
206823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2074105ca0b9515887a8a4cbb873c3de98009cbc633jvr		data = []
2084105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for table in tables:
2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr			tableData = table.getData()
2104105ca0b9515887a8a4cbb873c3de98009cbc633jvr			data.append(tableData)
211823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
212823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for table in extTables:
213823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			tableData = table.getData()
214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			data.append(tableData)
215823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
216821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(data)
2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getDataLength(self):
2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Return the length of this table in bytes, without subtables."""
2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr		l = 0
2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr		for item in self.items:
2224105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData") or hasattr(item, "getCountData"):
223e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 4  # sizeof(ULong)
225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
226823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					l = l + 2  # sizeof(UShort)
2274105ca0b9515887a8a4cbb873c3de98009cbc633jvr			else:
2284105ca0b9515887a8a4cbb873c3de98009cbc633jvr				l = l + len(item)
2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr		return l
2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr
2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getData(self):
2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr		"""Assemble the data for this writer/table, without subtables."""
2334105ca0b9515887a8a4cbb873c3de98009cbc633jvr		items = list(self.items)  # make a shallow copy
234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		pos = self.pos
235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(items)
236823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in range(numItems):
2374105ca0b9515887a8a4cbb873c3de98009cbc633jvr			item = items[i]
238823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
2394105ca0b9515887a8a4cbb873c3de98009cbc633jvr			if hasattr(item, "getData"):
240e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod				if item.longOffset:
241823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					items[i] = packULong(item.pos - pos)
242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
243823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					try:
244823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						items[i] = packUShort(item.pos - pos)
245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					except AssertionError:
246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# provide data to fix overflow problem.
24758acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod						# If the overflow is to a lookup, or from a lookup to a subtable,
248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# just report the current item.
249823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						if self.name in [ 'LookupList', 'Lookup']:
250823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							overflowErrorRecord = self.getOverflowErrorRecord(item)
251823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						else:
252823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# overflow is within a subTable. Life is more complicated.
253823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# If we split the sub-table just before the current item, we may still suffer overflow.
254823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# This is because duplicate table merging is done only within an Extension subTable tree;
255823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# when we split the subtable in two, some items may no longer be duplicates.
256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# Get worst case by adding up all the item lengths, depth first traversal.
257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							# and then report the first item that overflows a short.
258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							def getDeepItemLength(table):
259823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if hasattr(table, "getDataLength"):
260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = 0
261823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									for item in table.items:
262823f8cd15f16bb9dc3991c2672f16dd90579711bjvr										length = length + getDeepItemLength(item)
263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								else:
264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									length = len(table)
265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								return length
266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
267823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							length = self.getDataLength()
268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
269823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# Coverage is first in the item list, but last in the table list,
270823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# The original overflow is really in the item list. Skip the Coverage
271823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								# table in the following test.
272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								items = items[i+1:]
273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							for j in range(len(items)):
275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								item = items[j]
276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								length = length + getDeepItemLength(item)
277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								if length > 65535:
278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									break
279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						overflowErrorRecord = self.getOverflowErrorRecord(item)
280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
281823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
282cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod						raise OTLOffsetOverflowError(overflowErrorRecord)
283823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
284821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		return bytesjoin(items)
285d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
286cfadfd0096d634f4505bca27d6926555d43305d4jvr	def __hash__(self):
287cfadfd0096d634f4505bca27d6926555d43305d4jvr		# only works after self._doneWriting() has been called
288cfadfd0096d634f4505bca27d6926555d43305d4jvr		return hash(self.items)
289cfadfd0096d634f4505bca27d6926555d43305d4jvr
290b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
291b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
292b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
293b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.items == other.items
294cfadfd0096d634f4505bca27d6926555d43305d4jvr
295cfadfd0096d634f4505bca27d6926555d43305d4jvr	def _doneWriting(self, internedTables=None):
296823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert CountData references to data string items
297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# collapse duplicate table references to a unique entry
298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For Extension Lookup types, we can
301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# eliminate duplicates only within the tree under the Extension Lookup,
302823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# as offsets may exceed 64K even between Extension LookupTable subtables.
303cfadfd0096d634f4505bca27d6926555d43305d4jvr		if internedTables is None:
304cfadfd0096d634f4505bca27d6926555d43305d4jvr			internedTables = {}
305cfadfd0096d634f4505bca27d6926555d43305d4jvr		items = self.items
30697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(len(items)))
307823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
308823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
309823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 1
310823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
311823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			newTree = 0
312823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			item = items[i]
314cfadfd0096d634f4505bca27d6926555d43305d4jvr			if hasattr(item, "getCountData"):
315cfadfd0096d634f4505bca27d6926555d43305d4jvr				items[i] = item.getCountData()
316cfadfd0096d634f4505bca27d6926555d43305d4jvr			elif hasattr(item, "getData"):
317823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if newTree:
318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting()
319d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
320823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					item._doneWriting(internedTables)
321bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod					if item in internedTables:
322823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						items[i] = item = internedTables[item]
323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						internedTables[item] = item
325cfadfd0096d634f4505bca27d6926555d43305d4jvr		self.items = tuple(items)
326cfadfd0096d634f4505bca27d6926555d43305d4jvr
327823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def _gatherTables(self, tables=None, extTables=None, done=None):
328823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Convert table references in self.items tree to a flat
329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# list of tables in depth-first traversal order.
330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# "tables" are OTTableWriter objects.
331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We do the traversal in reverse order at each level, in order to
332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# resolve duplicate references to be the last reference in the list of tables.
333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# For extension lookups, duplicate references can be merged only within the
334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# writer tree under the  extension lookup.
335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if tables is None: # init call for first time.
336cfadfd0096d634f4505bca27d6926555d43305d4jvr			tables = []
337823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			extTables = []
338cfadfd0096d634f4505bca27d6926555d43305d4jvr			done = {}
339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		done[self] = 1
341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		numItems = len(self.items)
34397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		iRange = list(range(numItems))
344823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		iRange.reverse()
345823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "Extension"):
347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 1
348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			appendExtensions = 0
350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# add Coverage table if it is sorted last.
352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		sortCoverageLast = 0
353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, "sortCoverageLast"):
354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# Find coverage table
355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			for i in range(numItems):
356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item = self.items[i]
357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if hasattr(item, "name") and (item.name == "Coverage"):
358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					sortCoverageLast = 1
359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					break
360bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if item not in done:
361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		saveItem = None
367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for i in iRange:
368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			item = self.items[i]
369cfadfd0096d634f4505bca27d6926555d43305d4jvr			if not hasattr(item, "getData"):
370cfadfd0096d634f4505bca27d6926555d43305d4jvr				continue
371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if sortCoverageLast and (i==1) and item.name == 'Coverage':
373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# we've already 'gathered' it above
374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				continue
375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if appendExtensions:
377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				newDone = {}
379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(extTables, None, newDone)
380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
381bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			elif item not in done:
382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item._gatherTables(tables, extTables, done)
383823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				index = max(item.parent.keys())
385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				item.parent[index + 1] = self
386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
388cfadfd0096d634f4505bca27d6926555d43305d4jvr		tables.append(self)
389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return tables, extTables
390cfadfd0096d634f4505bca27d6926555d43305d4jvr
3914105ca0b9515887a8a4cbb873c3de98009cbc633jvr	# interface for gathering data, as used by table.compile()
3928e48312f88adc41feb7c730154c6d058a010189djvr
3934105ca0b9515887a8a4cbb873c3de98009cbc633jvr	def getSubWriter(self):
39479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		subwriter = self.__class__(self.globalState, self.localState)
395823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subwriter.parent = {0:self} # because some subtables have idential values, we discard
396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# the duplicates under the getAllData method. Hence some
397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# subtable writers can have more than one parent writer.
398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return subwriter
399d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
400d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeUShort(self, value):
401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert 0 <= value < 0x10000
402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">H", value))
403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeShort(self, value):
405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">h", value))
4069e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
4079e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def writeUInt24(self, value):
4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		assert 0 <= value < 0x1000000
409b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		self.items.append(''.join(bytechr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff)))
410d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
411d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeLong(self, value):
412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">l", value))
413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
414823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeULong(self, value):
415823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(struct.pack(">L", value))
416823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
417d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeTag(self, tag):
418960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		tag = Tag(tag).tobytes()
419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(tag) == 4
420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(tag)
421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeSubTable(self, subWriter):
423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(subWriter)
424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeCountReference(self, table, name):
4267981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		ref = CountReference(table, name)
4277981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		self.items.append(ref)
428ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod		return ref
429d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
430d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeStruct(self, format, values):
43166214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		data = struct.pack(*(format,) + values)
432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(data)
433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
434823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def writeData(self, data):
435823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.items.append(data)
436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def	getOverflowErrorRecord(self, item):
438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		LookupListIndex = SubTableIndex = itemName = itemIndex = None
439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.name == 'LookupList':
440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = item.repeatIndex
441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		elif self.name == 'Lookup':
442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			LookupListIndex = self.repeatIndex
443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			SubTableIndex = item.repeatIndex
444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		else:
445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			itemName = item.name
446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if hasattr(item, 'repeatIndex'):
447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemIndex = item.repeatIndex
448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if self.name == 'SubTable':
449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].repeatIndex
450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.repeatIndex
451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			elif self.name == 'ExtSubTable':
452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				LookupListIndex = self.parent[0].parent[0].repeatIndex
453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTableIndex = self.parent[0].repeatIndex
454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				itemName = ".".join(self.name, item.name)
456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				p1 = self.parent[0]
457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					itemName = ".".join(p1.name, item.name)
459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					p1 = p1.parent[0]
460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				if p1:
461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					if p1.name == 'ExtSubTable':
462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						LookupListIndex = self.parent[0].parent[0].repeatIndex
463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						SubTableIndex = self.parent[0].repeatIndex
464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					else:
465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						LookupListIndex = self.parent[0].repeatIndex
466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						SubTableIndex = self.repeatIndex
467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
46879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
470d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
471d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass CountReference:
472cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""A reference to a Count value, not a count of references."""
473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, table, name):
474d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = table
475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
4767981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod	def setValue(self, value):
4777981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		table = self.table
4787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		name = self.name
4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		if table[name] is None:
4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod			table[name] = value
4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod		else:
4821f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod			assert table[name] == value, (name, table[name], value)
483cfadfd0096d634f4505bca27d6926555d43305d4jvr	def getCountData(self):
484d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return packUShort(self.table[self.name])
485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
48764b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value):
488cfadfd0096d634f4505bca27d6926555d43305d4jvr	assert 0 <= value < 0x10000, value
48964b5c80e80444a124da335e8d4d208bffcf2737bjvr	return struct.pack(">H", value)
490d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
491d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
492823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value):
493ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr	assert 0 <= value < 0x100000000, value
494823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return struct.pack(">L", value)
495823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
496823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
4975988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object):
498823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __init__(self):
499823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.compileStatus = 0 # 0 means table was created
500823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# 1 means the table.read() function was called by a table which is subject
501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# to delayed compilation
502823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# 2 means that it was subject to delayed compilation, and
503823f8cd15f16bb9dc3991c2672f16dd90579711bjvr									# has been decompiled
504823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
505823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.recurse = 0
506823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
507823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	def __getattr__(self, attr):
508823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# we get here only when the table does not have the attribute.
509823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# This method ovveride exists so that we can try to de-compile
510823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# a table which is subject to delayed decompilation, and then try
511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# to get the value again after decompilation.
512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.recurse +=1
513823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.recurse > 2:
514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# shouldn't ever get here - we should only get to two levels of recursion.
515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			# this guards against self.decompile NOT setting compileStatus to other than 1.
516cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if self.compileStatus == 1:
518f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod			self.ensureDecompiled()
519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			val = getattr(self, attr)
520823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			self.recurse -=1
521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			return val
522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
523cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise AttributeError(attr)
524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
525823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
52664b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for all OpenType (sub)tables."""
52764b5c80e80444a124da335e8d4d208bffcf2737bjvr
528d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
529d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters
530d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
531d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
532d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[name]
533d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
534078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def decompile(self, reader, font):
535823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.compileStatus = 2 # table has been decompiled.
536f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.readFormat(reader)
537d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = {}
538d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__rawTable = table  # for debugging
53941caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		converters = self.getConverters()
54041caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod		for conv in converters:
541d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.name == "SubTable":
54279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr						table["LookupType"])
544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if conv.name == "ExtSubTable":
54579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod				conv = conv.getConverter(reader.globalState.tableType,
546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						table["ExtensionLookupType"])
5479e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			if conv.name == "FeatureParams":
5489e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				conv = conv.getConverter(reader["FeatureTag"])
549d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
550d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				l = []
551ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
552ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					countValue = table[conv.repeat]
553ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
554ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
55579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					countValue = reader[conv.repeat]
5566b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				for i in range(countValue + conv.aux):
557078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					l.append(conv.read(reader, font, table))
558d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = l
559d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
5605b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
5615b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
562078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				table[conv.name] = conv.read(reader, font, table)
5639e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
56479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					reader[conv.name] = table[conv.name]
56541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
566d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.postRead(table, font)
56741caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod
568d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		del self.__rawTable  # succeeded, get rid of debugging info
569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
570f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod	def ensureDecompiled(self):
571f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		if self.compileStatus != 1:
572f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod			return
573f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod		self.decompile(self.reader, self.font)
574f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod		del self.reader, self.font
575f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
576078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def compile(self, writer, font):
5773ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		self.ensureDecompiled()
578d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.preWrite(font)
579823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if hasattr(self, 'sortCoverageLast'):
581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			writer.sortCoverageLast = 1
582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
583f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		self.writeFormat(writer)
584d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
585d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = table.get(conv.name)
586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
587d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value is None:
58864b5c80e80444a124da335e8d4d208bffcf2737bjvr					value = []
5896b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod				countValue = len(value) - conv.aux
590ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				if conv.repeat in table:
591ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					ref = table[conv.repeat]
592ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.repeat] = None
593ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					ref.setValue(countValue)
594ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
595ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					# conv.repeat is a propagated count
59679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.repeat].setValue(countValue)
597823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				for i in range(len(value)):
598078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod					conv.write(writer, font, table, value[i], i)
599d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif conv.isCount:
600d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Special-case Count values.
601d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Assumption: a Count field will *always* precede
602ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				# the actual array(s).
603d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We need a default value, as it may be set later by a nested
60441caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod				# table. We will later store it here.
605d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We add a reference: by the time the data is assembled
606d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the Count value will be filled in.
607ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				ref = writer.writeCountReference(table, conv.name)
6089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
609ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.name] = None
61079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod					writer[conv.name] = ref
611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod				else:
612ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod					table[conv.name] = ref
613d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
6145b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, table):
6155b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
616078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod				conv.write(writer, font, table, value)
6179e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				if conv.isPropagated:
6189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod					writer[conv.name] = value
619d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
620f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
621f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
622f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
623f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
624f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr		pass
625f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr
626d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def postRead(self, table, font):
627d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__dict__.update(table)
628d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def preWrite(self, font):
630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__dict__.copy()
631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, attrs=None):
633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableName = self.__class__.__name__
634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			attrs = []
636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if hasattr(self, "Format"):
63764b5c80e80444a124da335e8d4d208bffcf2737bjvr			attrs = attrs + [("Format", self.Format)]
638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.begintag(tableName, attrs)
639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.toXML2(xmlWriter, font)
641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.endtag(tableName)
642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
644d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML2(self, xmlWriter, font):
645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# This is because in TTX our parent writes our main tag, and in otBase.py we
647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# do it ourselves. I think I'm getting schizophrenic...
648d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
64964b5c80e80444a124da335e8d4d208bffcf2737bjvr			if conv.repeat:
6505b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(len(value)):
652d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					item = value[i]
65364b5c80e80444a124da335e8d4d208bffcf2737bjvr					conv.xmlWrite(xmlWriter, font, item, conv.name,
65464b5c80e80444a124da335e8d4d208bffcf2737bjvr							[("index", i)])
65564b5c80e80444a124da335e8d4d208bffcf2737bjvr			else:
6565b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				if conv.aux and not eval(conv.aux, None, vars(self)):
6575b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod					continue
6585b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod				value = getattr(self, conv.name)
65964b5c80e80444a124da335e8d4d208bffcf2737bjvr				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
660d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6613a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		try:
663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv = self.getConverterByName(name)
664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		except KeyError:
665d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			raise    # XXX on KeyError, raise nice error
666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = conv.xmlRead(attrs, content, font)
667d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if conv.repeat:
66852966bb14409b558dc46b52d34953fbde7dc1bcajvr			seq = getattr(self, conv.name, None)
66952966bb14409b558dc46b52d34953fbde7dc1bcajvr			if seq is None:
670d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				seq = []
67164b5c80e80444a124da335e8d4d208bffcf2737bjvr				setattr(self, conv.name, seq)
672d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			seq.append(value)
673d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
67464b5c80e80444a124da335e8d4d208bffcf2737bjvr			setattr(self, conv.name, value)
675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
676b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
677b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
678b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
67996b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
680f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod		self.ensureDecompiled()
681b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		other.ensureDecompiled()
682f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod
683b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
685d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
686d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable):
687d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
688cfadfd0096d634f4505bca27d6926555d43305d4jvr	"""Minor specialization of BaseTable, for tables that have multiple
68964b5c80e80444a124da335e8d4d208bffcf2737bjvr	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
69064b5c80e80444a124da335e8d4d208bffcf2737bjvr
691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
692d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters[self.Format]
693d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
694d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[self.Format][name]
696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
697f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def readFormat(self, reader):
698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.Format = reader.readUShort()
699180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		assert self.Format != 0, (self, reader.pos, len(reader.data))
700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
701f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr	def writeFormat(self, writer):
702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(self.Format)
703d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
70564b5c80e80444a124da335e8d4d208bffcf2737bjvr#
70664b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords
70764b5c80e80444a124da335e8d4d208bffcf2737bjvr#
70864b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that
70964b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support
71064b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter...
71164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
71264b5c80e80444a124da335e8d4d208bffcf2737bjvr
713d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [
714d4d151390d1288f8d2df30f6dfa26a309c7334dajvr#	Mask	 Name            isDevice  signed
715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0001, "XPlacement",   0,        1),
716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0002, "YPlacement",   0,        1),
717d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0004, "XAdvance",     0,        1),
718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0008, "YAdvance",     0,        1),
719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0010, "XPlaDevice",   1,        0),
720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0020, "YPlaDevice",   1,        0),
721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0040, "XAdvDevice",   1,        0),
722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0080, "YAdvDevice",   1,        0),
723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# 	reserved:
724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0100, "Reserved1",    0,        0),
725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0200, "Reserved2",    0,        0),
726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0400, "Reserved3",    0,        0),
727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0800, "Reserved4",    0,        0),
728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x1000, "Reserved5",    0,        0),
729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x2000, "Reserved6",    0,        0),
730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x4000, "Reserved7",    0,        0),
731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x8000, "Reserved8",    0,        0),
732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr]
733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
734d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict():
735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	d = {}
736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for mask, name, isDevice, signed in valueRecordFormat:
737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		d[name] = mask, isDevice, signed
738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return d
739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
740d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict()
741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
743d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecordFactory:
744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
74564b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a format code, this object convert ValueRecords."""
746d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod
747601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod	def __init__(self, valueFormat):
748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = []
749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, signed in valueRecordFormat:
750d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if valueFormat & mask:
751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				format.append((name, isDevice, signed))
752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.format = format
753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, reader, font):
755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = self.format
756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not format:
757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		valueRecord = ValueRecord()
759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in format:
760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if signed:
761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readShort()
762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readUShort()
764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
7662b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod					from . import otTables
767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subReader = reader.getSubReader(value)
768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = getattr(otTables, name)()
769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.decompile(subReader, font)
770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = None
772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(valueRecord, name, value)
773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return valueRecord
774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, writer, font, valueRecord):
776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in self.format:
777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(valueRecord, name, 0)
778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subWriter = writer.getSubWriter()
781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeSubTable(subWriter)
782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.compile(subWriter, font)
783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeUShort(0)
785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif signed:
786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeShort(value)
787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(value)
789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
791d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord:
792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# see ValueRecordFactory
794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getFormat(self):
796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = 0
797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name in self.__dict__.keys():
798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = format | valueRecordFormatDict[name][0]
799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, valueName, attrs=None):
802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = []
804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = list(attrs)
806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[:4]:  # "simple" values
807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				simpleItems.append((name, getattr(self, name)))
809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		deviceItems = []
810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[4:8]:  # device records
811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				device = getattr(self, name)
813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if device is not None:
814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceItems.append((name, device))
815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if deviceItems:
816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.begintag(valueName, simpleItems)
817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for name, deviceRecord in deviceItems:
819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if deviceRecord is not None:
820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceRecord.toXML(xmlWriter, font)
821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.endtag(valueName)
822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.simpletag(valueName, simpleItems)
825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8273a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
8282b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from . import otTables
829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for k, v in attrs.items():
830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, k, int(v))
831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
832b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				continue
834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, attrs, content = element
835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(otTables, name)()
836d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for elem2 in content:
837b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(elem2, tuple):
838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					continue
8393a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				name2, attrs2, content2 = elem2
8403a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				value.fromXML(name2, attrs2, content2, font)
841d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, name, value)
842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
843b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __eq__(self, other):
844b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
845b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
846b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return self.__dict__ == other.__dict__
847