otBase.py revision e69caf8771a0dd675ca3c4490625dac963a6b65c
1d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom DefaultTable import DefaultTable
2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport otData
3d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct
4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom types import TupleType
5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
6d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
7d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTTXConverter(DefaultTable):
8d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
964b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for TTX table converters. Functions as an adapter
1064b5c80e80444a124da335e8d4d208bffcf2737bjvr	between the TTX (ttLib actually) table model and the model we use for
1164b5c80e80444a124da335e8d4d208bffcf2737bjvr	OpenType tables, which is neccesarily subtly different."""
1264b5c80e80444a124da335e8d4d208bffcf2737bjvr
13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def decompile(self, data, font):
14d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		import otTables
15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		reader = OTTableReader(data, self.tableTag)
16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableClass = getattr(otTables, self.tableTag)
17d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = tableClass()
18d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.decompile(reader, font)
19d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
20d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def compile(self, font):
21d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer = OTTableWriter(self.tableTag)
22d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.compile(writer, font)
23d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return writer.getData()
24d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
25d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, writer, font):
26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.toXML2(writer, font)
27d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
28d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def fromXML(self, (name, attrs, content), font):
29d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		import otTables
30d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not hasattr(self, "table"):
31d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tableClass = getattr(otTables, self.tableTag)
32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			self.table = tableClass()
33d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table.fromXML((name, attrs, content), font)
34d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
35d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
36d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass OTTableReader:
37d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
3864b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to retrieve data from an OpenType table."""
3964b5c80e80444a124da335e8d4d208bffcf2737bjvr
40d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
41d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.data = data
42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.offset = offset
43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = offset
44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.tableType = tableType
45d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if valueFormat is None:
46d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			valueFormat = (ValueRecordFactory(), ValueRecordFactory())
47d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.valueFormat = valueFormat
48d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.cachingStats = cachingStats
49d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
50d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getSubReader(self, offset):
51d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		offset = self.offset + offset
52d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if self.cachingStats is not None:
53d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			try:
54d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				self.cachingStats[offset] = self.cachingStats[offset] + 1
55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			except KeyError:
56d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				self.cachingStats[offset] = 1
57d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
58d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		subReader = self.__class__(self.data, self.tableType, offset,
59d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			self.valueFormat, self.cachingStats)
60d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return subReader
61d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
62d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readUShort(self):
63d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
64d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
65e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">H", self.data[pos:newpos])
66d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
67d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
68d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
69d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readShort(self):
70d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
71d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 2
72e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">h", self.data[pos:newpos])
73d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
74d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
75d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
76d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readLong(self):
77d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
78d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
79e69caf8771a0dd675ca3c4490625dac963a6b65cjvr		value, = struct.unpack(">l", self.data[pos:newpos])
80d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
81d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
83d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readTag(self):
84d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
85d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + 4
86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = self.data[pos:newpos]
87d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(value) == 4
88d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
89d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return value
90d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readStruct(self, format, size=None):
92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if size is None:
93d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			size = struct.calcsize(format)
94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
95d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			assert size == struct.calcsize(format)
96d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		pos = self.pos
97d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		newpos = pos + size
98d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		values = struct.unpack(format, self.data[pos:newpos])
99d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.pos = newpos
100d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return values
101d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
102d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def setValueFormat(self, format, which):
103d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.valueFormat[which].setFormat(format)
104d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
105d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, font, which):
106d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.valueFormat[which].readValueRecord(self, font)
107d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
108d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
109d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass OTTableWriter:
110d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
11164b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Helper class to gather and assemble data for OpenType tables."""
11264b5c80e80444a124da335e8d4d208bffcf2737bjvr
113d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, tableType, valueFormat=None):
114d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items = []
115d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.tableType = tableType
116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if valueFormat is None:
117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			valueFormat = ValueRecordFactory(), ValueRecordFactory()
118d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.valueFormat = valueFormat
119d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
120d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getSubWriter(self):
121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__class__(self.tableType, self.valueFormat)
122d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getData(self):
124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		items = list(self.items)
125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		offset = 0
126d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for item in items:
127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(item, "getData") or hasattr(item, "getCount"):
128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				offset = offset + 2  # sizeof(UShort)
129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				offset = offset + len(item)
131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		subTables = []
132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		cache = {}
133d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for i in range(len(items)):
134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			item = items[i]
135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(item, "getData"):
136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				subTableData = item.getData()
137d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if cache.has_key(subTableData):
138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					items[i] = packUShort(cache[subTableData])
139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					items[i] = packUShort(offset)
141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subTables.append(subTableData)
142d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					cache[subTableData] = offset
143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					offset = offset + len(subTableData)
144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif hasattr(item, "getCount"):
145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				items[i] = item.getCount()
146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return "".join(items + subTables)
147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
148d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeUShort(self, value):
149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert 0 <= value < 0x10000
150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">H", value))
151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeShort(self, value):
153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">h", value))
154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeLong(self, value):
156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(struct.pack(">l", value))
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeTag(self, tag):
159d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert len(tag) == 4
160d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(tag)
161d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
162d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeSubTable(self, subWriter):
163d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(subWriter)
164d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
165d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeCountReference(self, table, name):
166d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(CountReference(table, name))
167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeStruct(self, format, values):
169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		data = apply(struct.pack, (format,) + values)
170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.items.append(data)
171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
172d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def setValueFormat(self, format, which):
173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.valueFormat[which].setFormat(format)
174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, value, font, which):
176d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.valueFormat[which].writeValueRecord(self, font, value)
177d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
179d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass CountReference:
18064b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""A reference to a Count value, not a count of a reference."""
181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __init__(self, table, name):
182d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.table = table
183d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.name = name
184d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getCount(self):
185d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return packUShort(self.table[self.name])
186d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
187d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
18864b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value):
18964b5c80e80444a124da335e8d4d208bffcf2737bjvr	assert 0 <= value < 0x10000
19064b5c80e80444a124da335e8d4d208bffcf2737bjvr	return struct.pack(">H", value)
191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
19464b5c80e80444a124da335e8d4d208bffcf2737bjvrclass TableStack:
19564b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""A stack of table dicts, working as a stack of namespaces so we can
19664b5c80e80444a124da335e8d4d208bffcf2737bjvr	retrieve values from (and store values to) tables higher up the stack."""
19764b5c80e80444a124da335e8d4d208bffcf2737bjvr	def __init__(self):
19864b5c80e80444a124da335e8d4d208bffcf2737bjvr		self.stack = []
19964b5c80e80444a124da335e8d4d208bffcf2737bjvr	def push(self, table):
20064b5c80e80444a124da335e8d4d208bffcf2737bjvr		self.stack.insert(0, table)
20164b5c80e80444a124da335e8d4d208bffcf2737bjvr	def pop(self):
20264b5c80e80444a124da335e8d4d208bffcf2737bjvr		self.stack.pop(0)
20364b5c80e80444a124da335e8d4d208bffcf2737bjvr	def getTop(self):
20464b5c80e80444a124da335e8d4d208bffcf2737bjvr		return self.stack[0]
20564b5c80e80444a124da335e8d4d208bffcf2737bjvr	def getValue(self, name):
20664b5c80e80444a124da335e8d4d208bffcf2737bjvr		return self.__findTable(name)[name]
20764b5c80e80444a124da335e8d4d208bffcf2737bjvr	def storeValue(self, name, value):
20864b5c80e80444a124da335e8d4d208bffcf2737bjvr		table = self.__findTable(name)
20964b5c80e80444a124da335e8d4d208bffcf2737bjvr		if table[name] is None:
21064b5c80e80444a124da335e8d4d208bffcf2737bjvr			table[name] = value
21164b5c80e80444a124da335e8d4d208bffcf2737bjvr		else:
21264b5c80e80444a124da335e8d4d208bffcf2737bjvr			assert table[name] == value, (table[name], value)
21364b5c80e80444a124da335e8d4d208bffcf2737bjvr	def __findTable(self, name):
21464b5c80e80444a124da335e8d4d208bffcf2737bjvr		for table in self.stack:
21564b5c80e80444a124da335e8d4d208bffcf2737bjvr			if table.has_key(name):
21664b5c80e80444a124da335e8d4d208bffcf2737bjvr				return table
21764b5c80e80444a124da335e8d4d208bffcf2737bjvr		raise KeyError, name
21864b5c80e80444a124da335e8d4d208bffcf2737bjvr
21964b5c80e80444a124da335e8d4d208bffcf2737bjvr
220d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTable:
221d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
22264b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Generic base class for all OpenType (sub)tables."""
22364b5c80e80444a124da335e8d4d208bffcf2737bjvr
224d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
225d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters
226d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
227d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
228d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[name]
229d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
230d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def decompile(self, reader, font, tableStack=None):
231d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if tableStack is None:
232d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tableStack = TableStack()
233d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = {}
234d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__rawTable = table  # for debugging
235d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableStack.push(table)
236d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
237d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.name == "SubTable":
238d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				conv = conv.getConverter(reader.tableType,
239d4d151390d1288f8d2df30f6dfa26a309c7334dajvr						table["LookupType"])
240d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
241d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				l = []
242d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
243d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					l.append(conv.read(reader, font, tableStack))
244d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = l
245d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
246d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = conv.read(reader, font, tableStack)
247d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableStack.pop()
248d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.postRead(table, font)
249d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		del self.__rawTable  # succeeded, get rid of debugging info
250d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
251d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def compile(self, writer, font, tableStack=None):
252d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if tableStack is None:
253d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			tableStack = TableStack()
254d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		table = self.preWrite(font)
255d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableStack.push(table)
256d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
257d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = table.get(conv.name)
258d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if conv.repeat:
259d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value is None:
26064b5c80e80444a124da335e8d4d208bffcf2737bjvr					value = []
261d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
262d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for item in value:
263d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					conv.write(writer, font, tableStack, item)
264d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif conv.isCount:
265d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Special-case Count values.
266d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# Assumption: a Count field will *always* precede
267d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the actual array.
268d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We need a default value, as it may be set later by a nested
269d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# table. TableStack.storeValue() will then find it here.
270d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				table[conv.name] = None
271d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# We add a reference: by the time the data is assembled
272d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				# the Count value will be filled in.
273d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeCountReference(table, conv.name)
274d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
275d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				conv.write(writer, font, tableStack, value)
276d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableStack.pop()
277d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
278d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def postRead(self, table, font):
279d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.__dict__.update(table)
280d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
281d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def preWrite(self, font):
282d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.__dict__.copy()
283d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, attrs=None):
285d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		tableName = self.__class__.__name__
286d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
287d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			attrs = []
288d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if hasattr(self, "Format"):
28964b5c80e80444a124da335e8d4d208bffcf2737bjvr			attrs = attrs + [("Format", self.Format)]
290d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.begintag(tableName, attrs)
291d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
292d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.toXML2(xmlWriter, font)
293d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.endtag(tableName)
294d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		xmlWriter.newline()
295d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
296d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML2(self, xmlWriter, font):
297d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
298d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# This is because in TTX our parent writes our main tag, and in otBase.py we
299d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# do it ourselves. I think I'm getting schizophrenic...
300d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for conv in self.getConverters():
301d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(self, conv.name)
30264b5c80e80444a124da335e8d4d208bffcf2737bjvr			if conv.repeat:
303d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				for i in range(len(value)):
304d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					item = value[i]
30564b5c80e80444a124da335e8d4d208bffcf2737bjvr					conv.xmlWrite(xmlWriter, font, item, conv.name,
30664b5c80e80444a124da335e8d4d208bffcf2737bjvr							[("index", i)])
30764b5c80e80444a124da335e8d4d208bffcf2737bjvr			else:
30864b5c80e80444a124da335e8d4d208bffcf2737bjvr				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
309d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
310d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def fromXML(self, (name, attrs, content), font):
311d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		try:
312d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			conv = self.getConverterByName(name)
313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		except KeyError:
31464b5c80e80444a124da335e8d4d208bffcf2737bjvr##			print self, name, attrs, content
315d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			raise    # XXX on KeyError, raise nice error
316d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		value = conv.xmlRead(attrs, content, font)
317d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if conv.repeat:
318d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			try:
31964b5c80e80444a124da335e8d4d208bffcf2737bjvr				seq = getattr(self, conv.name)
320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			except AttributeError:
321d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				seq = []
32264b5c80e80444a124da335e8d4d208bffcf2737bjvr				setattr(self, conv.name, seq)
323d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			seq.append(value)
324d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
32564b5c80e80444a124da335e8d4d208bffcf2737bjvr			setattr(self, conv.name, value)
326d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
327d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __cmp__(self, other):
328d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# this is only for debugging, so it's ok to barf
329d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# when 'other' has no __dict__ or __class__
330d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		rv = cmp(self.__class__, other.__class__)
331d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not rv:
332d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			rv = cmp(self.__dict__, other.__dict__)
333d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return rv
334d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
335d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return rv
336d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
337d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
338d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable):
339d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
34064b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Small specialization of BaseTable, for tables that have multiple
34164b5c80e80444a124da335e8d4d208bffcf2737bjvr	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
34264b5c80e80444a124da335e8d4d208bffcf2737bjvr
343d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverters(self):
344d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.converters[self.Format]
345d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
346d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getConverterByName(self, name):
347d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return self.convertersByName[self.Format][name]
348d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
349d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def decompile(self, reader, font, tableStack=None):
350d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.Format = reader.readUShort()
351d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		assert self.Format <> 0, (self, reader.pos, len(reader.data))
352d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		BaseTable.decompile(self, reader, font, tableStack)
353d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
354d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def compile(self, writer, font, tableStack=None):
355d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		writer.writeUShort(self.Format)
356d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		BaseTable.compile(self, writer, font, tableStack)
357d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
358d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
35964b5c80e80444a124da335e8d4d208bffcf2737bjvr#
36064b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords
36164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
36264b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that
36364b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support
36464b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter...
36564b5c80e80444a124da335e8d4d208bffcf2737bjvr#
36664b5c80e80444a124da335e8d4d208bffcf2737bjvr
367d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [
368d4d151390d1288f8d2df30f6dfa26a309c7334dajvr#	Mask	 Name            isDevice  signed
369d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0001, "XPlacement",   0,        1),
370d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0002, "YPlacement",   0,        1),
371d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0004, "XAdvance",     0,        1),
372d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0008, "YAdvance",     0,        1),
373d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0010, "XPlaDevice",   1,        0),
374d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0020, "YPlaDevice",   1,        0),
375d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0040, "XAdvDevice",   1,        0),
376d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0080, "YAdvDevice",   1,        0),
377d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# 	reserved:
378d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0100, "Reserved1",    0,        0),
379d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0200, "Reserved2",    0,        0),
380d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0400, "Reserved3",    0,        0),
381d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x0800, "Reserved4",    0,        0),
382d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x1000, "Reserved5",    0,        0),
383d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x2000, "Reserved6",    0,        0),
384d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x4000, "Reserved7",    0,        0),
385d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	(0x8000, "Reserved8",    0,        0),
386d4d151390d1288f8d2df30f6dfa26a309c7334dajvr]
387d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
388d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict():
389d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	d = {}
390d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for mask, name, isDevice, signed in valueRecordFormat:
391d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		d[name] = mask, isDevice, signed
392d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	return d
393d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
394d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict()
395d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
396d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
397d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecordFactory:
398d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
39964b5c80e80444a124da335e8d4d208bffcf2737bjvr	"""Given a format code, this object convert ValueRecords."""
40064b5c80e80444a124da335e8d4d208bffcf2737bjvr
401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def setFormat(self, valueFormat):
402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = []
403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, signed in valueRecordFormat:
404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if valueFormat & mask:
405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				format.append((name, isDevice, signed))
406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		self.format = format
407d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
408d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def readValueRecord(self, reader, font):
409d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = self.format
410d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not format:
411d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return None
412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		valueRecord = ValueRecord()
413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in format:
414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if signed:
415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readShort()
416d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
417d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value = reader.readUShort()
418d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					import otTables
421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subReader = reader.getSubReader(value)
422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = getattr(otTables, name)()
423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.decompile(subReader, font)
424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value = None
426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(valueRecord, name, value)
427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return valueRecord
428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
429d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def writeValueRecord(self, writer, font, valueRecord):
430d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name, isDevice, signed in self.format:
431d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(valueRecord, name, 0)
432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if isDevice:
433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if value:
434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					subWriter = writer.getSubWriter()
435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeSubTable(subWriter)
436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					value.compile(subWriter, font)
437d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				else:
438d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					writer.writeUShort(0)
439d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			elif signed:
440d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeShort(value)
441d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			else:
442d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				writer.writeUShort(value)
443d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
444d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
445d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord:
446d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
447d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# see ValueRecordFactory
448d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
449d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def getFormat(self):
450d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		format = 0
451d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for name in self.__dict__.keys():
452d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = format | valueRecordFormatDict[name][0]
453d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		return format
454d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
455d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def toXML(self, xmlWriter, font, valueName, attrs=None):
456d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if attrs is None:
457d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = []
458d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
459d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			simpleItems = list(attrs)
460d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[:4]:  # "simple" values
461d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
462d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				simpleItems.append((name, getattr(self, name)))
463d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		deviceItems = []
464d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for mask, name, isDevice, format in valueRecordFormat[4:8]:  # device records
465d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if hasattr(self, name):
466d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				device = getattr(self, name)
467d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if device is not None:
468d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceItems.append((name, device))
469d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if deviceItems:
470d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.begintag(valueName, simpleItems)
471d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
472d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for name, deviceRecord in deviceItems:
473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if deviceRecord is not None:
474d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					deviceRecord.toXML(xmlWriter, font)
475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.endtag(valueName)
476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
478d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.simpletag(valueName, simpleItems)
479d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			xmlWriter.newline()
480d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
481d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def fromXML(self, (name, attrs, content), font):
482d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		import otTables
483d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for k, v in attrs.items():
484d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, k, int(v))
485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for element in content:
486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if type(element) <> TupleType:
487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				continue
488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, attrs, content = element
489d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			value = getattr(otTables, name)()
490d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			for elem2 in content:
491d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				if type(elem2) <> TupleType:
492d4d151390d1288f8d2df30f6dfa26a309c7334dajvr					continue
493d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				value.fromXML(elem2, font)
494d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			setattr(self, name, value)
495d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
496d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	def __cmp__(self, other):
497d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# this is only for debugging, so it's ok to barf
498d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		# when 'other' has no __dict__ or __class__
499d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		rv = cmp(self.__class__, other.__class__)
500d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not rv:
501d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			rv = cmp(self.__dict__, other.__dict__)
502d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return rv
503d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
504d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			return rv
505d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
506