blob: 0e1e1f7aab7d5a71a012375397082474d49daff2 [file] [log] [blame]
jvrd4d15132002-05-11 00:59:27 +00001from DefaultTable import DefaultTable
2import otData
3import struct
4from types import TupleType
5
jvr823f8cd2006-10-21 14:12:38 +00006class OverflowErrorRecord:
7 def __init__(self, overflowTuple):
8 self.tableType = overflowTuple[0]
9 self.LookupListIndex = overflowTuple[1]
10 self.SubTableIndex = overflowTuple[2]
11 self.itemName = overflowTuple[3]
12 self.itemIndex = overflowTuple[4]
13
14 def __repr__(self):
15 return str((self.tableType, "LookupIndex:", self.LookupListIndex, "SubTableIndex:", self.SubTableIndex, "ItemName:", self.itemName, "ItemIndex:", self.itemIndex))
16
17class OTLOffsetOverflowError(Exception):
18 def __init__(self, overflowErrorRecord):
19 self.value = overflowErrorRecord
20
21 def __str__(self):
22 return repr(self.value)
23
jvrd4d15132002-05-11 00:59:27 +000024
25class BaseTTXConverter(DefaultTable):
26
jvr3a6aa232003-09-02 19:23:13 +000027 """Generic base class for TTX table converters. It functions as an
28 adapter between the TTX (ttLib actually) table model and the model
29 we use for OpenType tables, which is necessarily subtly different.
30 """
jvr64b5c802002-05-11 10:21:36 +000031
jvrd4d15132002-05-11 00:59:27 +000032 def decompile(self, data, font):
33 import otTables
jvrcfadfd02002-07-22 22:13:57 +000034 cachingStats = None
35 reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
jvrd4d15132002-05-11 00:59:27 +000036 tableClass = getattr(otTables, self.tableTag)
37 self.table = tableClass()
38 self.table.decompile(reader, font)
jvrcfadfd02002-07-22 22:13:57 +000039 if 0:
40 stats = [(v, k) for k, v in cachingStats.items()]
41 stats.sort()
42 stats.reverse()
43 print "cachingsstats for ", self.tableTag
44 for v, k in stats:
45 if v < 2:
46 break
47 print v, k
48 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000049
50 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000051 """ Create a top-level OTFWriter for the GPOS/GSUB table.
52 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000053 for each 'converter' record in the table converter list
54 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000055 - For simple items, the write method adds a string to the
56 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000057 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000058 to the writer's self.items, then calls the item's compile method.
59 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
60 each writer representing a table, and the writer.items list containing
61 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000062 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000063 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000064 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
65 Traverse the flat list of tables, calling getDataLength on each to update their position
66 Traverse the flat list of tables again, calling getData each get the data in the table, now that
67 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000068
69 If a lookup subtable overflows an offset, we have to start all over.
70 """
jvrd4d15132002-05-11 00:59:27 +000071 writer = OTTableWriter(self.tableTag)
jvr823f8cd2006-10-21 14:12:38 +000072 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000073 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000074 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000075
jvrd4d15132002-05-11 00:59:27 +000076 def toXML(self, writer, font):
77 self.table.toXML2(writer, font)
78
79 def fromXML(self, (name, attrs, content), font):
80 import otTables
81 if not hasattr(self, "table"):
82 tableClass = getattr(otTables, self.tableTag)
83 self.table = tableClass()
84 self.table.fromXML((name, attrs, content), font)
85
86
87class OTTableReader:
88
jvr64b5c802002-05-11 10:21:36 +000089 """Helper class to retrieve data from an OpenType table."""
90
jvrd4d15132002-05-11 00:59:27 +000091 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
92 self.data = data
93 self.offset = offset
94 self.pos = offset
95 self.tableType = tableType
96 if valueFormat is None:
97 valueFormat = (ValueRecordFactory(), ValueRecordFactory())
98 self.valueFormat = valueFormat
99 self.cachingStats = cachingStats
100
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500101 def getSubReader(self, offset, persistent=False):
jvrd4d15132002-05-11 00:59:27 +0000102 offset = self.offset + offset
103 if self.cachingStats is not None:
104 try:
105 self.cachingStats[offset] = self.cachingStats[offset] + 1
106 except KeyError:
107 self.cachingStats[offset] = 1
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500108 valueFormat = self.valueFormat
109 if persistent:
110 valueFormat = tuple(ValueRecordFactory(v) for v in valueFormat)
111
jvrd4d15132002-05-11 00:59:27 +0000112 subReader = self.__class__(self.data, self.tableType, offset,
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500113 valueFormat, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000114 return subReader
115
116 def readUShort(self):
117 pos = self.pos
118 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000119 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000120 self.pos = newpos
121 return value
122
123 def readShort(self):
124 pos = self.pos
125 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000126 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000127 self.pos = newpos
128 return value
129
130 def readLong(self):
131 pos = self.pos
132 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000133 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000134 self.pos = newpos
135 return value
136
jvr823f8cd2006-10-21 14:12:38 +0000137 def readULong(self):
138 pos = self.pos
139 newpos = pos + 4
140 value, = struct.unpack(">L", self.data[pos:newpos])
141 self.pos = newpos
142 return value
143
jvrd4d15132002-05-11 00:59:27 +0000144 def readTag(self):
145 pos = self.pos
146 newpos = pos + 4
147 value = self.data[pos:newpos]
148 assert len(value) == 4
149 self.pos = newpos
150 return value
151
152 def readStruct(self, format, size=None):
153 if size is None:
154 size = struct.calcsize(format)
155 else:
156 assert size == struct.calcsize(format)
157 pos = self.pos
158 newpos = pos + size
159 values = struct.unpack(format, self.data[pos:newpos])
160 self.pos = newpos
161 return values
162
163 def setValueFormat(self, format, which):
164 self.valueFormat[which].setFormat(format)
165
166 def readValueRecord(self, font, which):
167 return self.valueFormat[which].readValueRecord(self, font)
168
169
170class OTTableWriter:
171
jvr64b5c802002-05-11 10:21:36 +0000172 """Helper class to gather and assemble data for OpenType tables."""
173
jvrd4d15132002-05-11 00:59:27 +0000174 def __init__(self, tableType, valueFormat=None):
175 self.items = []
176 self.tableType = tableType
177 if valueFormat is None:
178 valueFormat = ValueRecordFactory(), ValueRecordFactory()
179 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000180 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000181
jvr4105ca02002-07-23 08:43:03 +0000182 # assembler interface
183
184 def getAllData(self):
185 """Assemble all data, including all subtables."""
186 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000187 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000188 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000189 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000190 # Gather all data in two passes: the absolute positions of all
191 # subtable are needed before the actual data can be assembled.
192 pos = 0
193 for table in tables:
194 table.pos = pos
195 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000196
197 for table in extTables:
198 table.pos = pos
199 pos = pos + table.getDataLength()
200
201
jvr4105ca02002-07-23 08:43:03 +0000202 data = []
203 for table in tables:
204 tableData = table.getData()
205 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000206
207 for table in extTables:
208 tableData = table.getData()
209 data.append(tableData)
210
jvr4105ca02002-07-23 08:43:03 +0000211 return "".join(data)
212
213 def getDataLength(self):
214 """Return the length of this table in bytes, without subtables."""
215 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000216 if hasattr(self, "Extension"):
217 longOffset = 1
218 else:
219 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000220 for item in self.items:
221 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000222 if longOffset:
223 l = l + 4 # sizeof(ULong)
224 else:
225 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000226 else:
227 l = l + len(item)
228 return l
229
230 def getData(self):
231 """Assemble the data for this writer/table, without subtables."""
232 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000233 if hasattr(self,"Extension"):
234 longOffset = 1
235 else:
236 longOffset = 0
237 pos = self.pos
238 numItems = len(items)
239 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000240 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000241
jvr4105ca02002-07-23 08:43:03 +0000242 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000243 if longOffset:
244 items[i] = packULong(item.pos - pos)
245 else:
246 try:
247 items[i] = packUShort(item.pos - pos)
248 except AssertionError:
249 # provide data to fix overflow problem.
250 # If the overflow is to a lookup, or from a lookup to a subtable,
251 # just report the current item.
252 if self.name in [ 'LookupList', 'Lookup']:
253 overflowErrorRecord = self.getOverflowErrorRecord(item)
254 else:
255 # overflow is within a subTable. Life is more complicated.
256 # If we split the sub-table just before the current item, we may still suffer overflow.
257 # This is because duplicate table merging is done only within an Extension subTable tree;
258 # when we split the subtable in two, some items may no longer be duplicates.
259 # Get worst case by adding up all the item lengths, depth first traversal.
260 # and then report the first item that overflows a short.
261 def getDeepItemLength(table):
262 if hasattr(table, "getDataLength"):
263 length = 0
264 for item in table.items:
265 length = length + getDeepItemLength(item)
266 else:
267 length = len(table)
268 return length
269
270 length = self.getDataLength()
271 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
272 # Coverage is first in the item list, but last in the table list,
273 # The original overflow is really in the item list. Skip the Coverage
274 # table in the following test.
275 items = items[i+1:]
276
277 for j in range(len(items)):
278 item = items[j]
279 length = length + getDeepItemLength(item)
280 if length > 65535:
281 break
282 overflowErrorRecord = self.getOverflowErrorRecord(item)
283
284
285 raise OTLOffsetOverflowError, overflowErrorRecord
286
jvr4105ca02002-07-23 08:43:03 +0000287 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000288
jvrcfadfd02002-07-22 22:13:57 +0000289 def __hash__(self):
290 # only works after self._doneWriting() has been called
291 return hash(self.items)
292
293 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100294 if type(self) != type(other): return cmp(type(self), type(other))
295 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400296
297 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000298
jvrcfadfd02002-07-22 22:13:57 +0000299 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000300 # Convert CountData references to data string items
301 # collapse duplicate table references to a unique entry
302 # "tables" are OTTableWriter objects.
303
304 # For Extension Lookup types, we can
305 # eliminate duplicates only within the tree under the Extension Lookup,
306 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000307 if internedTables is None:
308 internedTables = {}
309 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000310 iRange = range(len(items))
311
312 if hasattr(self, "Extension"):
313 newTree = 1
314 else:
315 newTree = 0
316 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000317 item = items[i]
318 if hasattr(item, "getCountData"):
319 items[i] = item.getCountData()
320 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000321 if newTree:
322 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000323 else:
jvr823f8cd2006-10-21 14:12:38 +0000324 item._doneWriting(internedTables)
325 if internedTables.has_key(item):
326 items[i] = item = internedTables[item]
327 else:
328 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000329 self.items = tuple(items)
330
jvr823f8cd2006-10-21 14:12:38 +0000331 def _gatherTables(self, tables=None, extTables=None, done=None):
332 # Convert table references in self.items tree to a flat
333 # list of tables in depth-first traversal order.
334 # "tables" are OTTableWriter objects.
335 # We do the traversal in reverse order at each level, in order to
336 # resolve duplicate references to be the last reference in the list of tables.
337 # For extension lookups, duplicate references can be merged only within the
338 # writer tree under the extension lookup.
339 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000340 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000341 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000342 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000343
344 done[self] = 1
345
346 numItems = len(self.items)
347 iRange = range(numItems)
348 iRange.reverse()
349
350 if hasattr(self, "Extension"):
351 appendExtensions = 1
352 else:
353 appendExtensions = 0
354
355 # add Coverage table if it is sorted last.
356 sortCoverageLast = 0
357 if hasattr(self, "sortCoverageLast"):
358 # Find coverage table
359 for i in range(numItems):
360 item = self.items[i]
361 if hasattr(item, "name") and (item.name == "Coverage"):
362 sortCoverageLast = 1
363 break
364 if not done.has_key(item):
365 item._gatherTables(tables, extTables, done)
366 else:
367 index = max(item.parent.keys())
368 item.parent[index + 1] = self
369
370 saveItem = None
371 for i in iRange:
372 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000373 if not hasattr(item, "getData"):
374 continue
jvr823f8cd2006-10-21 14:12:38 +0000375
376 if sortCoverageLast and (i==1) and item.name == 'Coverage':
377 # we've already 'gathered' it above
378 continue
379
380 if appendExtensions:
381 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
382 newDone = {}
383 item._gatherTables(extTables, None, newDone)
384
385 elif not done.has_key(item):
386 item._gatherTables(tables, extTables, done)
387 else:
388 index = max(item.parent.keys())
389 item.parent[index + 1] = self
390
391
jvrcfadfd02002-07-22 22:13:57 +0000392 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000393 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000394
jvr4105ca02002-07-23 08:43:03 +0000395 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000396
jvr4105ca02002-07-23 08:43:03 +0000397 def getSubWriter(self):
jvr823f8cd2006-10-21 14:12:38 +0000398 subwriter = self.__class__(self.tableType, self.valueFormat)
399 subwriter.parent = {0:self} # because some subtables have idential values, we discard
400 # the duplicates under the getAllData method. Hence some
401 # subtable writers can have more than one parent writer.
402 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000403
404 def writeUShort(self, value):
405 assert 0 <= value < 0x10000
406 self.items.append(struct.pack(">H", value))
407
408 def writeShort(self, value):
409 self.items.append(struct.pack(">h", value))
410
411 def writeLong(self, value):
412 self.items.append(struct.pack(">l", value))
413
jvr823f8cd2006-10-21 14:12:38 +0000414 def writeULong(self, value):
415 self.items.append(struct.pack(">L", value))
416
jvrd4d15132002-05-11 00:59:27 +0000417 def writeTag(self, tag):
418 assert len(tag) == 4
419 self.items.append(tag)
420
421 def writeSubTable(self, subWriter):
422 self.items.append(subWriter)
423
424 def writeCountReference(self, table, name):
425 self.items.append(CountReference(table, name))
426
427 def writeStruct(self, format, values):
428 data = apply(struct.pack, (format,) + values)
429 self.items.append(data)
430
jvr823f8cd2006-10-21 14:12:38 +0000431 def writeData(self, data):
432 self.items.append(data)
433
jvrd4d15132002-05-11 00:59:27 +0000434 def setValueFormat(self, format, which):
435 self.valueFormat[which].setFormat(format)
436
437 def writeValueRecord(self, value, font, which):
438 return self.valueFormat[which].writeValueRecord(self, font, value)
439
jvr823f8cd2006-10-21 14:12:38 +0000440 def getOverflowErrorRecord(self, item):
441 LookupListIndex = SubTableIndex = itemName = itemIndex = None
442 if self.name == 'LookupList':
443 LookupListIndex = item.repeatIndex
444 elif self.name == 'Lookup':
445 LookupListIndex = self.repeatIndex
446 SubTableIndex = item.repeatIndex
447 else:
448 itemName = item.name
449 if hasattr(item, 'repeatIndex'):
450 itemIndex = item.repeatIndex
451 if self.name == 'SubTable':
452 LookupListIndex = self.parent[0].repeatIndex
453 SubTableIndex = self.repeatIndex
454 elif self.name == 'ExtSubTable':
455 LookupListIndex = self.parent[0].parent[0].repeatIndex
456 SubTableIndex = self.parent[0].repeatIndex
457 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
458 itemName = ".".join(self.name, item.name)
459 p1 = self.parent[0]
460 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
461 itemName = ".".join(p1.name, item.name)
462 p1 = p1.parent[0]
463 if p1:
464 if p1.name == 'ExtSubTable':
465 LookupListIndex = self.parent[0].parent[0].repeatIndex
466 SubTableIndex = self.parent[0].repeatIndex
467 else:
468 LookupListIndex = self.parent[0].repeatIndex
469 SubTableIndex = self.repeatIndex
470
471 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
472
jvrd4d15132002-05-11 00:59:27 +0000473
474class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000475 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000476 def __init__(self, table, name):
477 self.table = table
478 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000479 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000480 return packUShort(self.table[self.name])
481
482
jvr64b5c802002-05-11 10:21:36 +0000483def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000484 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000485 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000486
487
jvr823f8cd2006-10-21 14:12:38 +0000488def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000489 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000490 return struct.pack(">L", value)
491
492
jvrd4d15132002-05-11 00:59:27 +0000493
jvr64b5c802002-05-11 10:21:36 +0000494class TableStack:
495 """A stack of table dicts, working as a stack of namespaces so we can
496 retrieve values from (and store values to) tables higher up the stack."""
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500497 def __init__(self, other=None):
498 self.stack = other.stack[:] if other else []
jvr64b5c802002-05-11 10:21:36 +0000499 def push(self, table):
Matt Fontaine65499c12013-08-05 14:35:12 -0700500 self.stack.append(table)
jvr64b5c802002-05-11 10:21:36 +0000501 def pop(self):
Matt Fontaine65499c12013-08-05 14:35:12 -0700502 self.stack.pop()
jvr64b5c802002-05-11 10:21:36 +0000503 def getTop(self):
Matt Fontaine65499c12013-08-05 14:35:12 -0700504 return self.stack[-1]
jvr64b5c802002-05-11 10:21:36 +0000505 def getValue(self, name):
506 return self.__findTable(name)[name]
507 def storeValue(self, name, value):
508 table = self.__findTable(name)
509 if table[name] is None:
510 table[name] = value
511 else:
512 assert table[name] == value, (table[name], value)
513 def __findTable(self, name):
Matt Fontaine65499c12013-08-05 14:35:12 -0700514 for table in reversed(self.stack):
jvr64b5c802002-05-11 10:21:36 +0000515 if table.has_key(name):
516 return table
517 raise KeyError, name
518
519
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500520class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000521 def __init__(self):
522 self.compileStatus = 0 # 0 means table was created
523 # 1 means the table.read() function was called by a table which is subject
524 # to delayed compilation
525 # 2 means that it was subject to delayed compilation, and
526 # has been decompiled
527 # 3 means that the start and end fields have been filled out, and that we
528 # can use the data string rather than compiling from the table data.
529
530 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000531
jvr823f8cd2006-10-21 14:12:38 +0000532 def __getattr__(self, attr):
533 # we get here only when the table does not have the attribute.
534 # This method ovveride exists so that we can try to de-compile
535 # a table which is subject to delayed decompilation, and then try
536 # to get the value again after decompilation.
537 self.recurse +=1
538 if self.recurse > 2:
539 # shouldn't ever get here - we should only get to two levels of recursion.
540 # this guards against self.decompile NOT setting compileStatus to other than 1.
541 raise AttributeError, attr
542 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500543 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000544 val = getattr(self, attr)
545 self.recurse -=1
546 return val
547
548 raise AttributeError, attr
549
550
jvr64b5c802002-05-11 10:21:36 +0000551 """Generic base class for all OpenType (sub)tables."""
552
jvrd4d15132002-05-11 00:59:27 +0000553 def getConverters(self):
554 return self.converters
555
556 def getConverterByName(self, name):
557 return self.convertersByName[name]
558
559 def decompile(self, reader, font, tableStack=None):
jvr823f8cd2006-10-21 14:12:38 +0000560 self.compileStatus = 2 # table has been decompiled.
jvrd4d15132002-05-11 00:59:27 +0000561 if tableStack is None:
562 tableStack = TableStack()
jvrf7ef96c2002-09-10 19:26:38 +0000563 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000564 table = {}
565 self.__rawTable = table # for debugging
566 tableStack.push(table)
567 for conv in self.getConverters():
568 if conv.name == "SubTable":
569 conv = conv.getConverter(reader.tableType,
570 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000571 if conv.name == "ExtSubTable":
572 conv = conv.getConverter(reader.tableType,
573 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000574 if conv.repeat:
575 l = []
576 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
577 l.append(conv.read(reader, font, tableStack))
578 table[conv.name] = l
579 else:
580 table[conv.name] = conv.read(reader, font, tableStack)
581 tableStack.pop()
582 self.postRead(table, font)
583 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000584
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500585 def ensureDecompiled(self):
586 if self.compileStatus != 1:
587 return
588 self.decompile(self.reader, self.font, self.tableStack)
589 del self.reader, self.font, self.tableStack
590
jvr823f8cd2006-10-21 14:12:38 +0000591 def preCompile(self):
592 pass # used only by the LookupList class
593
jvrd4d15132002-05-11 00:59:27 +0000594 def compile(self, writer, font, tableStack=None):
595 if tableStack is None:
596 tableStack = TableStack()
597 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000598
599 if hasattr(self, 'sortCoverageLast'):
600 writer.sortCoverageLast = 1
601
jvrf7ef96c2002-09-10 19:26:38 +0000602 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000603 tableStack.push(table)
604 for conv in self.getConverters():
605 value = table.get(conv.name)
606 if conv.repeat:
607 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000608 value = []
jvrd4d15132002-05-11 00:59:27 +0000609 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000610 for i in range(len(value)):
611 conv.write(writer, font, tableStack, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000612 elif conv.isCount:
613 # Special-case Count values.
614 # Assumption: a Count field will *always* precede
615 # the actual array.
616 # We need a default value, as it may be set later by a nested
617 # table. TableStack.storeValue() will then find it here.
618 table[conv.name] = None
619 # We add a reference: by the time the data is assembled
620 # the Count value will be filled in.
621 writer.writeCountReference(table, conv.name)
622 else:
623 conv.write(writer, font, tableStack, value)
624 tableStack.pop()
625
jvrf7ef96c2002-09-10 19:26:38 +0000626 def readFormat(self, reader):
627 pass
628
629 def writeFormat(self, writer):
630 pass
631
jvrd4d15132002-05-11 00:59:27 +0000632 def postRead(self, table, font):
633 self.__dict__.update(table)
634
635 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500636 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000637 return self.__dict__.copy()
638
639 def toXML(self, xmlWriter, font, attrs=None):
640 tableName = self.__class__.__name__
641 if attrs is None:
642 attrs = []
643 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000644 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000645 xmlWriter.begintag(tableName, attrs)
646 xmlWriter.newline()
647 self.toXML2(xmlWriter, font)
648 xmlWriter.endtag(tableName)
649 xmlWriter.newline()
650
651 def toXML2(self, xmlWriter, font):
652 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
653 # This is because in TTX our parent writes our main tag, and in otBase.py we
654 # do it ourselves. I think I'm getting schizophrenic...
655 for conv in self.getConverters():
656 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000657 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000658 for i in range(len(value)):
659 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000660 conv.xmlWrite(xmlWriter, font, item, conv.name,
661 [("index", i)])
662 else:
663 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000664
665 def fromXML(self, (name, attrs, content), font):
666 try:
667 conv = self.getConverterByName(name)
668 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000669 raise # XXX on KeyError, raise nice error
670 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000671 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000672 seq = getattr(self, conv.name, None)
673 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000674 seq = []
jvr64b5c802002-05-11 10:21:36 +0000675 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000676 seq.append(value)
677 else:
jvr64b5c802002-05-11 10:21:36 +0000678 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000679
680 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100681 if type(self) != type(other): return cmp(type(self), type(other))
682 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400683
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500684 self.ensureDecompiled()
685
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400686 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000687
688
689class FormatSwitchingBaseTable(BaseTable):
690
jvrcfadfd02002-07-22 22:13:57 +0000691 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000692 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
693
jvrd4d15132002-05-11 00:59:27 +0000694 def getConverters(self):
695 return self.converters[self.Format]
696
697 def getConverterByName(self, name):
698 return self.convertersByName[self.Format][name]
699
jvrf7ef96c2002-09-10 19:26:38 +0000700 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000701 self.Format = reader.readUShort()
702 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000703
jvrf7ef96c2002-09-10 19:26:38 +0000704 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000705 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000706
707
jvr64b5c802002-05-11 10:21:36 +0000708#
709# Support for ValueRecords
710#
711# This data type is so different from all other OpenType data types that
712# it requires quite a bit of code for itself. It even has special support
713# in OTTableReader and OTTableWriter...
714#
715
jvrd4d15132002-05-11 00:59:27 +0000716valueRecordFormat = [
717# Mask Name isDevice signed
718 (0x0001, "XPlacement", 0, 1),
719 (0x0002, "YPlacement", 0, 1),
720 (0x0004, "XAdvance", 0, 1),
721 (0x0008, "YAdvance", 0, 1),
722 (0x0010, "XPlaDevice", 1, 0),
723 (0x0020, "YPlaDevice", 1, 0),
724 (0x0040, "XAdvDevice", 1, 0),
725 (0x0080, "YAdvDevice", 1, 0),
726# reserved:
727 (0x0100, "Reserved1", 0, 0),
728 (0x0200, "Reserved2", 0, 0),
729 (0x0400, "Reserved3", 0, 0),
730 (0x0800, "Reserved4", 0, 0),
731 (0x1000, "Reserved5", 0, 0),
732 (0x2000, "Reserved6", 0, 0),
733 (0x4000, "Reserved7", 0, 0),
734 (0x8000, "Reserved8", 0, 0),
735]
736
737def _buildDict():
738 d = {}
739 for mask, name, isDevice, signed in valueRecordFormat:
740 d[name] = mask, isDevice, signed
741 return d
742
743valueRecordFormatDict = _buildDict()
744
745
746class ValueRecordFactory:
747
jvr64b5c802002-05-11 10:21:36 +0000748 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500749
750 def __init__(self, other=None):
751 self.format = other.format if other else None
jvr64b5c802002-05-11 10:21:36 +0000752
jvrd4d15132002-05-11 00:59:27 +0000753 def setFormat(self, valueFormat):
754 format = []
755 for mask, name, isDevice, signed in valueRecordFormat:
756 if valueFormat & mask:
757 format.append((name, isDevice, signed))
758 self.format = format
759
760 def readValueRecord(self, reader, font):
761 format = self.format
762 if not format:
763 return None
764 valueRecord = ValueRecord()
765 for name, isDevice, signed in format:
766 if signed:
767 value = reader.readShort()
768 else:
769 value = reader.readUShort()
770 if isDevice:
771 if value:
772 import otTables
773 subReader = reader.getSubReader(value)
774 value = getattr(otTables, name)()
775 value.decompile(subReader, font)
776 else:
777 value = None
778 setattr(valueRecord, name, value)
779 return valueRecord
780
781 def writeValueRecord(self, writer, font, valueRecord):
782 for name, isDevice, signed in self.format:
783 value = getattr(valueRecord, name, 0)
784 if isDevice:
785 if value:
786 subWriter = writer.getSubWriter()
787 writer.writeSubTable(subWriter)
788 value.compile(subWriter, font)
789 else:
790 writer.writeUShort(0)
791 elif signed:
792 writer.writeShort(value)
793 else:
794 writer.writeUShort(value)
795
796
797class ValueRecord:
798
799 # see ValueRecordFactory
800
801 def getFormat(self):
802 format = 0
803 for name in self.__dict__.keys():
804 format = format | valueRecordFormatDict[name][0]
805 return format
806
807 def toXML(self, xmlWriter, font, valueName, attrs=None):
808 if attrs is None:
809 simpleItems = []
810 else:
811 simpleItems = list(attrs)
812 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
813 if hasattr(self, name):
814 simpleItems.append((name, getattr(self, name)))
815 deviceItems = []
816 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
817 if hasattr(self, name):
818 device = getattr(self, name)
819 if device is not None:
820 deviceItems.append((name, device))
821 if deviceItems:
822 xmlWriter.begintag(valueName, simpleItems)
823 xmlWriter.newline()
824 for name, deviceRecord in deviceItems:
825 if deviceRecord is not None:
826 deviceRecord.toXML(xmlWriter, font)
827 xmlWriter.endtag(valueName)
828 xmlWriter.newline()
829 else:
830 xmlWriter.simpletag(valueName, simpleItems)
831 xmlWriter.newline()
832
833 def fromXML(self, (name, attrs, content), font):
834 import otTables
835 for k, v in attrs.items():
836 setattr(self, k, int(v))
837 for element in content:
838 if type(element) <> TupleType:
839 continue
840 name, attrs, content = element
841 value = getattr(otTables, name)()
842 for elem2 in content:
843 if type(elem2) <> TupleType:
844 continue
845 value.fromXML(elem2, font)
846 setattr(self, name, value)
847
848 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100849 if type(self) != type(other): return cmp(type(self), type(other))
850 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000851
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400852 return cmp(self.__dict__, other.__dict__)