otConverters.py revision 2b06aaa2a6bcd363c25fb0c43f6bb906906594bd
1d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom types import TupleType
2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom fontTools.misc.textTools import safeEval
32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .otBase import ValueRecordFactory
4d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
664b5c80e80444a124da335e8d4d208bffcf2737bjvrdef buildConverters(tableSpec, tableNamespace):
764b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a table spec from otData.py, build a converter object for each
864b5c80e80444a124da335e8d4d208bffcf2737bjvr	field of the table. This is called for each table in otData.py, and
964b5c80e80444a124da335e8d4d208bffcf2737bjvr	the results are assigned to the corresponding class in otTables.py."""
10d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	converters = []
11d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	convertersByName = {}
126b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod	for tp, name, repeat, aux, descr in tableSpec:
13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if name.startswith("ValueFormat"):
14d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			assert tp == "uint16"
15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			converterClass = ValueFormat
16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		elif name.endswith("Count"):
17d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			assert tp == "uint16"
18d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			converterClass = Count
19d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		elif name == "SubTable":
20d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			converterClass = SubTable
21823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		elif name == "ExtSubTable":
22823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			converterClass = ExtSubTable
239e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		elif name == "FeatureParams":
249e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			converterClass = FeatureParams
25d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			converterClass = converterMapping[tp]
27d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableClass = tableNamespace.get(name)
286b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod		conv = converterClass(name, repeat, aux, tableClass)
29823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if name in ["SubTable", "ExtSubTable"]:
30d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv.lookupTypes = tableNamespace['lookupTypes']
31d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# also create reverse mapping
32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for t in conv.lookupTypes.values():
33d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for cls in t.values():
346b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod					convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
359e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		if name == "FeatureParams":
369e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			conv.featureParamTypes = tableNamespace['featureParamTypes']
379e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			conv.defaultFeatureParams = tableNamespace['FeatureParams']
389e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod			for cls in conv.featureParamTypes.values():
399e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod				convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
40d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		converters.append(conv)
41bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		assert name not in convertersByName
42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		convertersByName[name] = conv
43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return converters, convertersByName
44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
45d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
46d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass BaseConverter(object):
47d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
4864b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Base class for converter objects. Apart from the constructor, this
4964b5c80e80444a124da335e8d4d208bffcf2737bjvr	is an abstract class."""
5064b5c80e80444a124da335e8d4d208bffcf2737bjvr
516b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod	def __init__(self, name, repeat, aux, tableClass):
52d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
53d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.repeat = repeat
546b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod		self.aux = aux
55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.tableClass = tableClass
56d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.isCount = name.endswith("Count")
579e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag"]
58d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
59078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
6064b5c80e80444a124da335e8d4d208bffcf2737bjvr		"""Read a value from the reader."""
61d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		raise NotImplementedError, self
62d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
63078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
6464b5c80e80444a124da335e8d4d208bffcf2737bjvr		"""Write a value to the writer."""
65d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		raise NotImplementedError, self
66d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6764b5c80e80444a124da335e8d4d208bffcf2737bjvr	def xmlRead(self, attrs, content, font):
6864b5c80e80444a124da335e8d4d208bffcf2737bjvr		"""Read a value from XML."""
69d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		raise NotImplementedError, self
70d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
7164b5c80e80444a124da335e8d4d208bffcf2737bjvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
7264b5c80e80444a124da335e8d4d208bffcf2737bjvr		"""Write a value to XML."""
73d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		raise NotImplementedError, self
74d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
75d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
76d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass SimpleValue(BaseConverter):
77d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
78d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.simpletag(name, attrs + [("value", value)])
79d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
80d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlRead(self, attrs, content, font):
81d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return attrs["value"]
82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
83d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass IntValue(SimpleValue):
84d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlRead(self, attrs, content, font):
85ffc3cfeed12e67b383ed5f540d05cab01b33bb77Behdad Esfahbod		return int(attrs["value"], 0)
86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
87d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Long(IntValue):
88078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
89d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return reader.readLong()
90078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeLong(value)
92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
93a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbodclass Version(BaseConverter):
94078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
95a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		value = reader.readLong()
96a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
97a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		return float(value) / 0x10000
98078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
99a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		if value < 0x10000:
100a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod			value *= 0x10000
101a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		value = int(round(value))
102a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
103a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		writer.writeLong(value)
1047007a0854548f744c44bb9c35380efcba7894bfbjvr	def xmlRead(self, attrs, content, font):
105a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		value = attrs["value"]
106a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		value = float(int(value, 0)) if value.startswith("0") else float(value)
107a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		if value >= 0x10000:
108a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod			value = float(value) / 0x10000
109a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		return value
110a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod	def xmlWrite(self, xmlWriter, font, value, name, attrs):
111a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		if value >= 0x10000:
112a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod			value = float(value) / 0x10000
113a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		if value % 1 != 0:
114a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod			# Write as hex
115a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod			value = "0x%08x" % (int(round(value * 0x10000)))
116a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		xmlWriter.simpletag(name, attrs + [("value", value)])
117a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod		xmlWriter.newline()
118b776a882ee55fff8c39543f352c528bb5d338177jvr
119d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Short(IntValue):
120078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return reader.readShort()
122078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeShort(value)
124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
125d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass UShort(IntValue):
126078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return reader.readUShort()
128078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(value)
130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass UInt24(IntValue):
1329e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def read(self, reader, font, tableDict):
1339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		return reader.readUInt24()
1349e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
1359e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		writer.writeUInt24(value)
1369e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
137d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Count(Short):
138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.comment("%s=%s" % (name, value))
140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
142d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Tag(SimpleValue):
143078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return reader.readTag()
145078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeTag(value)
147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
148d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass GlyphID(SimpleValue):
149078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value = reader.readUShort()
151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value =  font.getGlyphName(value)
152823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return value
153823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
154078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
155823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		value =  font.getGlyphID(value)
156823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		writer.writeUShort(value)
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
1583ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbodclass FloatValue(SimpleValue):
1593ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod	def xmlRead(self, attrs, content, font):
1603ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		return float(attrs["value"])
1613ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod
1623ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbodclass DeciPoints(FloatValue):
1633ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod	def read(self, reader, font, tableDict):
1643ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		value = reader.readUShort()
1653ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		return value / 10.
1663ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod
1673ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
1683ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod		writer.writeUShort(int(round(value * 10)))
169b776a882ee55fff8c39543f352c528bb5d338177jvr
170d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Struct(BaseConverter):
171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
172078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.tableClass()
174078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod		table.decompile(reader, font)
175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return table
176d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
177078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
178078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod		value.compile(writer, font)
179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
180d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if value is None:
182cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod			if attrs:
183cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				# If there are attributes (probably index), then
184cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				# don't drop this even if it's NULL.  It will mess
185cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				# up the array indices of the containing element.
186cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				xmlWriter.simpletag(name, attrs + [("empty", True)])
187cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				xmlWriter.newline()
188cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod			else:
189cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod				pass # NULL table, ignore
190d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value.toXML(xmlWriter, font, attrs)
192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlRead(self, attrs, content, font):
194d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.tableClass()
195cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod		if attrs.get("empty"):
196cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod			return None
197d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		Format = attrs.get("Format")
198d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if Format is not None:
199d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			table.Format = int(Format)
200d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
20152966bb14409b558dc46b52d34953fbde7dc1bcajvr			if type(element) == TupleType:
20252966bb14409b558dc46b52d34953fbde7dc1bcajvr				name, attrs, content = element
20352966bb14409b558dc46b52d34953fbde7dc1bcajvr				table.fromXML((name, attrs, content), font)
20452966bb14409b558dc46b52d34953fbde7dc1bcajvr			else:
20552966bb14409b558dc46b52d34953fbde7dc1bcajvr				pass
206d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return table
207d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
208d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
209d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Table(Struct):
210fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod
211d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod	longOffset = False
212d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod
213fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod	def readOffset(self, reader):
214fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod		return reader.readUShort()
215fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod
216fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod	def writeNullOffset(self, writer):
217e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod		if self.longOffset:
218fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod			writer.writeULong(0)
219fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod		else:
220fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod			writer.writeUShort(0)
221d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
2227ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod	def read(self, reader, font, tableDict):
223fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod		offset = self.readOffset(reader)
224d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if offset == 0:
225d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
226d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if offset <= 3:
227d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# XXX hack to work around buggy pala.ttf
228d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			print "*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
229d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					% (offset, self.tableClass.__name__)
230d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
231d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.tableClass()
232f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod		table.reader = reader.getSubReader(offset)
233c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod		table.font = font
234c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod		table.compileStatus = 1
2357ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod		if not font.lazy:
236c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod			table.ensureDecompiled()
237d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return table
238d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
239078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
240d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if value is None:
241fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod			self.writeNullOffset(writer)
242d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
243d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			subWriter = writer.getSubWriter()
244e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod			subWriter.longOffset = self.longOffset
245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			subWriter.name = self.name
246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if repeatIndex is not None:
247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				subWriter.repeatIndex = repeatIndex
248d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			writer.writeSubTable(subWriter)
249078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod			value.compile(subWriter, font)
250d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
251d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass LTable(Table):
252d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod
253d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod	longOffset = True
254d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod
255d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod	def readOffset(self, reader):
256d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod		return reader.readULong()
257d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod
258d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod
259d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass SubTable(Table):
260d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverter(self, tableType, lookupType):
261fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod		tableClass = self.lookupTypes[tableType][lookupType]
2626b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod		return self.__class__(self.name, self.repeat, self.aux, tableClass)
263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
265d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass ExtSubTable(LTable, SubTable):
266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
267078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer.
269fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod		Table.write(self, writer, font, tableDict, value, repeatIndex)
270d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
2719e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParams(Table):
2729e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def getConverter(self, featureTag):
2739e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
2749e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		return self.__class__(self.name, self.repeat, self.aux, tableClass)
2759e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
276d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
277d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueFormat(IntValue):
2786b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod	def __init__(self, name, repeat, aux, tableClass):
2796b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod		BaseConverter.__init__(self, name, repeat, aux, tableClass)
28079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
281078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
282d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = reader.readUShort()
28379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		reader[self.which] = ValueRecordFactory(format)
284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
285078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, format, repeatIndex=None):
286d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(format)
28779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		writer[self.which] = ValueRecordFactory(format)
288d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
289b776a882ee55fff8c39543f352c528bb5d338177jvr
290d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord(ValueFormat):
291078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
29279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		return reader[self.which].readValueRecord(reader, font)
293078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
29479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod		writer[self.which].writeValueRecord(writer, font, value)
295d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
296d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if value is None:
297d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			pass  # NULL table, ignore
298d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
299d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value.toXML(xmlWriter, font, self.name, attrs)
300d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlRead(self, attrs, content, font):
3012b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from .otBase import ValueRecord
302d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = ValueRecord()
303d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value.fromXML((None, attrs, content), font)
304d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
305d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
306d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
307d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass DeltaValue(BaseConverter):
308d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
309078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def read(self, reader, font, tableDict):
3102edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		StartSize = tableDict["StartSize"]
3112edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		EndSize = tableDict["EndSize"]
3122edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		DeltaFormat = tableDict["DeltaFormat"]
313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
314d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		nItems = EndSize - StartSize + 1
315d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		nBits = 1 << DeltaFormat
316d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		minusOffset = 1 << nBits
317d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		mask = (1 << nBits) - 1
318d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		signMask = 1 << (nBits - 1)
319d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		DeltaValue = []
321d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tmp, shift = 0, 0
322d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for i in range(nItems):
323d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if shift == 0:
324d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				tmp, shift = reader.readUShort(), 16
325d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			shift = shift - nBits
326d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = (tmp >> shift) & mask
327d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if value & signMask:
328d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = value - minusOffset
329d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			DeltaValue.append(value)
330d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return DeltaValue
331d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
332078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod	def write(self, writer, font, tableDict, value, repeatIndex=None):
3332edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		StartSize = tableDict["StartSize"]
3342edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		EndSize = tableDict["EndSize"]
3352edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod		DeltaFormat = tableDict["DeltaFormat"]
3366f9b64f2966d18cccad3fcf6e03f44f208dbc2a4Behdad Esfahbod		DeltaValue = value
337d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
338d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		nItems = EndSize - StartSize + 1
339d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		nBits = 1 << DeltaFormat
340d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(DeltaValue) == nItems
341d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		mask = (1 << nBits) - 1
342d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
343d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tmp, shift = 0, 16
344d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for value in DeltaValue:
345d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			shift = shift - nBits
346d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tmp = tmp | ((value & mask) << shift)
347d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if shift == 0:
348d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(tmp)
349d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				tmp, shift = 0, 16
350d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if shift <> 16:
351d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			writer.writeUShort(tmp)
352d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
353d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlWrite(self, xmlWriter, font, value, name, attrs):
354d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.simpletag(name, attrs + [("value", value)])
355d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
356d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
357d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def xmlRead(self, attrs, content, font):
358d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return safeEval(attrs["value"])
359d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
360d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
361d4d151390d1288f8d2df30f6dfa26a309c7334dajvrconverterMapping = {
362d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# type         class
363d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"int16":       Short,
364d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"uint16":      UShort,
3659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	"uint24":      UInt24,
366a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod	"Version":     Version,
367d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"Tag":         Tag,
368d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"GlyphID":     GlyphID,
3693ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod	"DeciPoints":  DeciPoints,
370d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"struct":      Struct,
371d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"Offset":      Table,
372d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod	"LOffset":     LTable,
373d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"ValueRecord": ValueRecord,
3747d130307e6e6094e40a841a3c41fc74b203b7e8eBehdad Esfahbod	"DeltaValue":  DeltaValue,
375d4d151390d1288f8d2df30f6dfa26a309c7334dajvr}
376d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
377