blob: 0f6a4e8a6542c9f39e281fc68f57c45f9e3bd5bf [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
101 def getSubReader(self, offset):
102 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
108
109 subReader = self.__class__(self.data, self.tableType, offset,
110 self.valueFormat, self.cachingStats)
111 return subReader
112
113 def readUShort(self):
114 pos = self.pos
115 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000116 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000117 self.pos = newpos
118 return value
119
120 def readShort(self):
121 pos = self.pos
122 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000123 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000124 self.pos = newpos
125 return value
126
127 def readLong(self):
128 pos = self.pos
129 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000130 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000131 self.pos = newpos
132 return value
133
jvr823f8cd2006-10-21 14:12:38 +0000134 def readULong(self):
135 pos = self.pos
136 newpos = pos + 4
137 value, = struct.unpack(">L", self.data[pos:newpos])
138 self.pos = newpos
139 return value
140
jvrd4d15132002-05-11 00:59:27 +0000141 def readTag(self):
142 pos = self.pos
143 newpos = pos + 4
144 value = self.data[pos:newpos]
145 assert len(value) == 4
146 self.pos = newpos
147 return value
148
149 def readStruct(self, format, size=None):
150 if size is None:
151 size = struct.calcsize(format)
152 else:
153 assert size == struct.calcsize(format)
154 pos = self.pos
155 newpos = pos + size
156 values = struct.unpack(format, self.data[pos:newpos])
157 self.pos = newpos
158 return values
159
160 def setValueFormat(self, format, which):
161 self.valueFormat[which].setFormat(format)
162
163 def readValueRecord(self, font, which):
164 return self.valueFormat[which].readValueRecord(self, font)
165
166
167class OTTableWriter:
168
jvr64b5c802002-05-11 10:21:36 +0000169 """Helper class to gather and assemble data for OpenType tables."""
170
jvrd4d15132002-05-11 00:59:27 +0000171 def __init__(self, tableType, valueFormat=None):
172 self.items = []
173 self.tableType = tableType
174 if valueFormat is None:
175 valueFormat = ValueRecordFactory(), ValueRecordFactory()
176 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000177 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000178
jvr4105ca02002-07-23 08:43:03 +0000179 # assembler interface
180
181 def getAllData(self):
182 """Assemble all data, including all subtables."""
183 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000184 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000185 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000186 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000187 # Gather all data in two passes: the absolute positions of all
188 # subtable are needed before the actual data can be assembled.
189 pos = 0
190 for table in tables:
191 table.pos = pos
192 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000193
194 for table in extTables:
195 table.pos = pos
196 pos = pos + table.getDataLength()
197
198
jvr4105ca02002-07-23 08:43:03 +0000199 data = []
200 for table in tables:
201 tableData = table.getData()
202 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000203
204 for table in extTables:
205 tableData = table.getData()
206 data.append(tableData)
207
jvr4105ca02002-07-23 08:43:03 +0000208 return "".join(data)
209
210 def getDataLength(self):
211 """Return the length of this table in bytes, without subtables."""
212 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000213 if hasattr(self, "Extension"):
214 longOffset = 1
215 else:
216 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000217 for item in self.items:
218 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000219 if longOffset:
220 l = l + 4 # sizeof(ULong)
221 else:
222 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000223 else:
224 l = l + len(item)
225 return l
226
227 def getData(self):
228 """Assemble the data for this writer/table, without subtables."""
229 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000230 if hasattr(self,"Extension"):
231 longOffset = 1
232 else:
233 longOffset = 0
234 pos = self.pos
235 numItems = len(items)
236 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000237 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000238
jvr4105ca02002-07-23 08:43:03 +0000239 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000240 if longOffset:
241 items[i] = packULong(item.pos - pos)
242 else:
243 try:
244 items[i] = packUShort(item.pos - pos)
245 except AssertionError:
246 # provide data to fix overflow problem.
247 # If the overflow is to a lookup, or from a lookup to a subtable,
248 # just report the current item.
249 if self.name in [ 'LookupList', 'Lookup']:
250 overflowErrorRecord = self.getOverflowErrorRecord(item)
251 else:
252 # overflow is within a subTable. Life is more complicated.
253 # If we split the sub-table just before the current item, we may still suffer overflow.
254 # This is because duplicate table merging is done only within an Extension subTable tree;
255 # when we split the subtable in two, some items may no longer be duplicates.
256 # Get worst case by adding up all the item lengths, depth first traversal.
257 # and then report the first item that overflows a short.
258 def getDeepItemLength(table):
259 if hasattr(table, "getDataLength"):
260 length = 0
261 for item in table.items:
262 length = length + getDeepItemLength(item)
263 else:
264 length = len(table)
265 return length
266
267 length = self.getDataLength()
268 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
269 # Coverage is first in the item list, but last in the table list,
270 # The original overflow is really in the item list. Skip the Coverage
271 # table in the following test.
272 items = items[i+1:]
273
274 for j in range(len(items)):
275 item = items[j]
276 length = length + getDeepItemLength(item)
277 if length > 65535:
278 break
279 overflowErrorRecord = self.getOverflowErrorRecord(item)
280
281
282 raise OTLOffsetOverflowError, overflowErrorRecord
283
jvr4105ca02002-07-23 08:43:03 +0000284 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000285
jvrcfadfd02002-07-22 22:13:57 +0000286 def __hash__(self):
287 # only works after self._doneWriting() has been called
288 return hash(self.items)
289
290 def __cmp__(self, other):
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400291 if type(self) != type(other) or \
292 self.__class__ != other.__class__:
jvrcfadfd02002-07-22 22:13:57 +0000293 return cmp(id(self), id(other))
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400294
295 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000296
jvrcfadfd02002-07-22 22:13:57 +0000297 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000298 # Convert CountData references to data string items
299 # collapse duplicate table references to a unique entry
300 # "tables" are OTTableWriter objects.
301
302 # For Extension Lookup types, we can
303 # eliminate duplicates only within the tree under the Extension Lookup,
304 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000305 if internedTables is None:
306 internedTables = {}
307 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000308 iRange = range(len(items))
309
310 if hasattr(self, "Extension"):
311 newTree = 1
312 else:
313 newTree = 0
314 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000315 item = items[i]
316 if hasattr(item, "getCountData"):
317 items[i] = item.getCountData()
318 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000319 if newTree:
320 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000321 else:
jvr823f8cd2006-10-21 14:12:38 +0000322 item._doneWriting(internedTables)
323 if internedTables.has_key(item):
324 items[i] = item = internedTables[item]
325 else:
326 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000327 self.items = tuple(items)
328
jvr823f8cd2006-10-21 14:12:38 +0000329 def _gatherTables(self, tables=None, extTables=None, done=None):
330 # Convert table references in self.items tree to a flat
331 # list of tables in depth-first traversal order.
332 # "tables" are OTTableWriter objects.
333 # We do the traversal in reverse order at each level, in order to
334 # resolve duplicate references to be the last reference in the list of tables.
335 # For extension lookups, duplicate references can be merged only within the
336 # writer tree under the extension lookup.
337 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000338 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000339 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000340 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000341
342 done[self] = 1
343
344 numItems = len(self.items)
345 iRange = range(numItems)
346 iRange.reverse()
347
348 if hasattr(self, "Extension"):
349 appendExtensions = 1
350 else:
351 appendExtensions = 0
352
353 # add Coverage table if it is sorted last.
354 sortCoverageLast = 0
355 if hasattr(self, "sortCoverageLast"):
356 # Find coverage table
357 for i in range(numItems):
358 item = self.items[i]
359 if hasattr(item, "name") and (item.name == "Coverage"):
360 sortCoverageLast = 1
361 break
362 if not done.has_key(item):
363 item._gatherTables(tables, extTables, done)
364 else:
365 index = max(item.parent.keys())
366 item.parent[index + 1] = self
367
368 saveItem = None
369 for i in iRange:
370 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000371 if not hasattr(item, "getData"):
372 continue
jvr823f8cd2006-10-21 14:12:38 +0000373
374 if sortCoverageLast and (i==1) and item.name == 'Coverage':
375 # we've already 'gathered' it above
376 continue
377
378 if appendExtensions:
379 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
380 newDone = {}
381 item._gatherTables(extTables, None, newDone)
382
383 elif not done.has_key(item):
384 item._gatherTables(tables, extTables, done)
385 else:
386 index = max(item.parent.keys())
387 item.parent[index + 1] = self
388
389
jvrcfadfd02002-07-22 22:13:57 +0000390 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000391 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000392
jvr4105ca02002-07-23 08:43:03 +0000393 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000394
jvr4105ca02002-07-23 08:43:03 +0000395 def getSubWriter(self):
jvr823f8cd2006-10-21 14:12:38 +0000396 subwriter = self.__class__(self.tableType, self.valueFormat)
397 subwriter.parent = {0:self} # because some subtables have idential values, we discard
398 # the duplicates under the getAllData method. Hence some
399 # subtable writers can have more than one parent writer.
400 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000401
402 def writeUShort(self, value):
403 assert 0 <= value < 0x10000
404 self.items.append(struct.pack(">H", value))
405
406 def writeShort(self, value):
407 self.items.append(struct.pack(">h", value))
408
409 def writeLong(self, value):
410 self.items.append(struct.pack(">l", value))
411
jvr823f8cd2006-10-21 14:12:38 +0000412 def writeULong(self, value):
413 self.items.append(struct.pack(">L", value))
414
jvrd4d15132002-05-11 00:59:27 +0000415 def writeTag(self, tag):
416 assert len(tag) == 4
417 self.items.append(tag)
418
419 def writeSubTable(self, subWriter):
420 self.items.append(subWriter)
421
422 def writeCountReference(self, table, name):
423 self.items.append(CountReference(table, name))
424
425 def writeStruct(self, format, values):
426 data = apply(struct.pack, (format,) + values)
427 self.items.append(data)
428
jvr823f8cd2006-10-21 14:12:38 +0000429 def writeData(self, data):
430 self.items.append(data)
431
jvrd4d15132002-05-11 00:59:27 +0000432 def setValueFormat(self, format, which):
433 self.valueFormat[which].setFormat(format)
434
435 def writeValueRecord(self, value, font, which):
436 return self.valueFormat[which].writeValueRecord(self, font, value)
437
jvr823f8cd2006-10-21 14:12:38 +0000438 def getOverflowErrorRecord(self, item):
439 LookupListIndex = SubTableIndex = itemName = itemIndex = None
440 if self.name == 'LookupList':
441 LookupListIndex = item.repeatIndex
442 elif self.name == 'Lookup':
443 LookupListIndex = self.repeatIndex
444 SubTableIndex = item.repeatIndex
445 else:
446 itemName = item.name
447 if hasattr(item, 'repeatIndex'):
448 itemIndex = item.repeatIndex
449 if self.name == 'SubTable':
450 LookupListIndex = self.parent[0].repeatIndex
451 SubTableIndex = self.repeatIndex
452 elif self.name == 'ExtSubTable':
453 LookupListIndex = self.parent[0].parent[0].repeatIndex
454 SubTableIndex = self.parent[0].repeatIndex
455 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
456 itemName = ".".join(self.name, item.name)
457 p1 = self.parent[0]
458 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
459 itemName = ".".join(p1.name, item.name)
460 p1 = p1.parent[0]
461 if p1:
462 if p1.name == 'ExtSubTable':
463 LookupListIndex = self.parent[0].parent[0].repeatIndex
464 SubTableIndex = self.parent[0].repeatIndex
465 else:
466 LookupListIndex = self.parent[0].repeatIndex
467 SubTableIndex = self.repeatIndex
468
469 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
470
jvrd4d15132002-05-11 00:59:27 +0000471
472class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000473 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000474 def __init__(self, table, name):
475 self.table = table
476 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000477 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000478 return packUShort(self.table[self.name])
479
480
jvr64b5c802002-05-11 10:21:36 +0000481def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000482 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000483 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000484
485
jvr823f8cd2006-10-21 14:12:38 +0000486def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000487 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000488 return struct.pack(">L", value)
489
490
jvrd4d15132002-05-11 00:59:27 +0000491
jvr64b5c802002-05-11 10:21:36 +0000492class TableStack:
493 """A stack of table dicts, working as a stack of namespaces so we can
494 retrieve values from (and store values to) tables higher up the stack."""
495 def __init__(self):
496 self.stack = []
497 def push(self, table):
Matt Fontaine65499c12013-08-05 14:35:12 -0700498 self.stack.append(table)
jvr64b5c802002-05-11 10:21:36 +0000499 def pop(self):
Matt Fontaine65499c12013-08-05 14:35:12 -0700500 self.stack.pop()
jvr64b5c802002-05-11 10:21:36 +0000501 def getTop(self):
Matt Fontaine65499c12013-08-05 14:35:12 -0700502 return self.stack[-1]
jvr64b5c802002-05-11 10:21:36 +0000503 def getValue(self, name):
504 return self.__findTable(name)[name]
505 def storeValue(self, name, value):
506 table = self.__findTable(name)
507 if table[name] is None:
508 table[name] = value
509 else:
510 assert table[name] == value, (table[name], value)
511 def __findTable(self, name):
Matt Fontaine65499c12013-08-05 14:35:12 -0700512 for table in reversed(self.stack):
jvr64b5c802002-05-11 10:21:36 +0000513 if table.has_key(name):
514 return table
515 raise KeyError, name
516
517
jvrd4d15132002-05-11 00:59:27 +0000518class BaseTable:
jvr823f8cd2006-10-21 14:12:38 +0000519 def __init__(self):
520 self.compileStatus = 0 # 0 means table was created
521 # 1 means the table.read() function was called by a table which is subject
522 # to delayed compilation
523 # 2 means that it was subject to delayed compilation, and
524 # has been decompiled
525 # 3 means that the start and end fields have been filled out, and that we
526 # can use the data string rather than compiling from the table data.
527
528 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000529
jvr823f8cd2006-10-21 14:12:38 +0000530 def __getattr__(self, attr):
531 # we get here only when the table does not have the attribute.
532 # This method ovveride exists so that we can try to de-compile
533 # a table which is subject to delayed decompilation, and then try
534 # to get the value again after decompilation.
535 self.recurse +=1
536 if self.recurse > 2:
537 # shouldn't ever get here - we should only get to two levels of recursion.
538 # this guards against self.decompile NOT setting compileStatus to other than 1.
539 raise AttributeError, attr
540 if self.compileStatus == 1:
541 # table.read() has been called, but table has not yet been decompiled
542 # This happens only for extension tables.
543 self.decompile(self.reader, self.font)
544 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
585 def preCompile(self):
586 pass # used only by the LookupList class
587
jvrd4d15132002-05-11 00:59:27 +0000588 def compile(self, writer, font, tableStack=None):
589 if tableStack is None:
590 tableStack = TableStack()
591 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000592
593 if hasattr(self, 'sortCoverageLast'):
594 writer.sortCoverageLast = 1
595
jvrf7ef96c2002-09-10 19:26:38 +0000596 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000597 tableStack.push(table)
598 for conv in self.getConverters():
599 value = table.get(conv.name)
600 if conv.repeat:
601 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000602 value = []
jvrd4d15132002-05-11 00:59:27 +0000603 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000604 for i in range(len(value)):
605 conv.write(writer, font, tableStack, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000606 elif conv.isCount:
607 # Special-case Count values.
608 # Assumption: a Count field will *always* precede
609 # the actual array.
610 # We need a default value, as it may be set later by a nested
611 # table. TableStack.storeValue() will then find it here.
612 table[conv.name] = None
613 # We add a reference: by the time the data is assembled
614 # the Count value will be filled in.
615 writer.writeCountReference(table, conv.name)
616 else:
617 conv.write(writer, font, tableStack, value)
618 tableStack.pop()
619
jvrf7ef96c2002-09-10 19:26:38 +0000620 def readFormat(self, reader):
621 pass
622
623 def writeFormat(self, writer):
624 pass
625
jvrd4d15132002-05-11 00:59:27 +0000626 def postRead(self, table, font):
627 self.__dict__.update(table)
628
629 def preWrite(self, font):
630 return self.__dict__.copy()
631
632 def toXML(self, xmlWriter, font, attrs=None):
633 tableName = self.__class__.__name__
634 if attrs is None:
635 attrs = []
636 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000637 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000638 xmlWriter.begintag(tableName, attrs)
639 xmlWriter.newline()
640 self.toXML2(xmlWriter, font)
641 xmlWriter.endtag(tableName)
642 xmlWriter.newline()
643
644 def toXML2(self, xmlWriter, font):
645 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
646 # This is because in TTX our parent writes our main tag, and in otBase.py we
647 # do it ourselves. I think I'm getting schizophrenic...
648 for conv in self.getConverters():
649 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000650 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000651 for i in range(len(value)):
652 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000653 conv.xmlWrite(xmlWriter, font, item, conv.name,
654 [("index", i)])
655 else:
656 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000657
658 def fromXML(self, (name, attrs, content), font):
659 try:
660 conv = self.getConverterByName(name)
661 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000662 raise # XXX on KeyError, raise nice error
663 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000664 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000665 seq = getattr(self, conv.name, None)
666 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000667 seq = []
jvr64b5c802002-05-11 10:21:36 +0000668 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000669 seq.append(value)
670 else:
jvr64b5c802002-05-11 10:21:36 +0000671 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000672
673 def __cmp__(self, other):
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400674 if type(self) != type(other) or \
675 self.__class__ != other.__class__:
676 return cmp(id(self), id(other))
677
678 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000679
680
681class FormatSwitchingBaseTable(BaseTable):
682
jvrcfadfd02002-07-22 22:13:57 +0000683 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000684 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
685
jvrd4d15132002-05-11 00:59:27 +0000686 def getConverters(self):
687 return self.converters[self.Format]
688
689 def getConverterByName(self, name):
690 return self.convertersByName[self.Format][name]
691
jvrf7ef96c2002-09-10 19:26:38 +0000692 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000693 self.Format = reader.readUShort()
694 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000695
jvrf7ef96c2002-09-10 19:26:38 +0000696 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000697 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000698
699
jvr64b5c802002-05-11 10:21:36 +0000700#
701# Support for ValueRecords
702#
703# This data type is so different from all other OpenType data types that
704# it requires quite a bit of code for itself. It even has special support
705# in OTTableReader and OTTableWriter...
706#
707
jvrd4d15132002-05-11 00:59:27 +0000708valueRecordFormat = [
709# Mask Name isDevice signed
710 (0x0001, "XPlacement", 0, 1),
711 (0x0002, "YPlacement", 0, 1),
712 (0x0004, "XAdvance", 0, 1),
713 (0x0008, "YAdvance", 0, 1),
714 (0x0010, "XPlaDevice", 1, 0),
715 (0x0020, "YPlaDevice", 1, 0),
716 (0x0040, "XAdvDevice", 1, 0),
717 (0x0080, "YAdvDevice", 1, 0),
718# reserved:
719 (0x0100, "Reserved1", 0, 0),
720 (0x0200, "Reserved2", 0, 0),
721 (0x0400, "Reserved3", 0, 0),
722 (0x0800, "Reserved4", 0, 0),
723 (0x1000, "Reserved5", 0, 0),
724 (0x2000, "Reserved6", 0, 0),
725 (0x4000, "Reserved7", 0, 0),
726 (0x8000, "Reserved8", 0, 0),
727]
728
729def _buildDict():
730 d = {}
731 for mask, name, isDevice, signed in valueRecordFormat:
732 d[name] = mask, isDevice, signed
733 return d
734
735valueRecordFormatDict = _buildDict()
736
737
738class ValueRecordFactory:
739
jvr64b5c802002-05-11 10:21:36 +0000740 """Given a format code, this object convert ValueRecords."""
741
jvrd4d15132002-05-11 00:59:27 +0000742 def setFormat(self, valueFormat):
743 format = []
744 for mask, name, isDevice, signed in valueRecordFormat:
745 if valueFormat & mask:
746 format.append((name, isDevice, signed))
747 self.format = format
748
749 def readValueRecord(self, reader, font):
750 format = self.format
751 if not format:
752 return None
753 valueRecord = ValueRecord()
754 for name, isDevice, signed in format:
755 if signed:
756 value = reader.readShort()
757 else:
758 value = reader.readUShort()
759 if isDevice:
760 if value:
761 import otTables
762 subReader = reader.getSubReader(value)
763 value = getattr(otTables, name)()
764 value.decompile(subReader, font)
765 else:
766 value = None
767 setattr(valueRecord, name, value)
768 return valueRecord
769
770 def writeValueRecord(self, writer, font, valueRecord):
771 for name, isDevice, signed in self.format:
772 value = getattr(valueRecord, name, 0)
773 if isDevice:
774 if value:
775 subWriter = writer.getSubWriter()
776 writer.writeSubTable(subWriter)
777 value.compile(subWriter, font)
778 else:
779 writer.writeUShort(0)
780 elif signed:
781 writer.writeShort(value)
782 else:
783 writer.writeUShort(value)
784
785
786class ValueRecord:
787
788 # see ValueRecordFactory
789
790 def getFormat(self):
791 format = 0
792 for name in self.__dict__.keys():
793 format = format | valueRecordFormatDict[name][0]
794 return format
795
796 def toXML(self, xmlWriter, font, valueName, attrs=None):
797 if attrs is None:
798 simpleItems = []
799 else:
800 simpleItems = list(attrs)
801 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
802 if hasattr(self, name):
803 simpleItems.append((name, getattr(self, name)))
804 deviceItems = []
805 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
806 if hasattr(self, name):
807 device = getattr(self, name)
808 if device is not None:
809 deviceItems.append((name, device))
810 if deviceItems:
811 xmlWriter.begintag(valueName, simpleItems)
812 xmlWriter.newline()
813 for name, deviceRecord in deviceItems:
814 if deviceRecord is not None:
815 deviceRecord.toXML(xmlWriter, font)
816 xmlWriter.endtag(valueName)
817 xmlWriter.newline()
818 else:
819 xmlWriter.simpletag(valueName, simpleItems)
820 xmlWriter.newline()
821
822 def fromXML(self, (name, attrs, content), font):
823 import otTables
824 for k, v in attrs.items():
825 setattr(self, k, int(v))
826 for element in content:
827 if type(element) <> TupleType:
828 continue
829 name, attrs, content = element
830 value = getattr(otTables, name)()
831 for elem2 in content:
832 if type(elem2) <> TupleType:
833 continue
834 value.fromXML(elem2, font)
835 setattr(self, name, value)
836
837 def __cmp__(self, other):
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400838 if type(self) != type(other) or \
839 self.__class__ != other.__class__:
840 return cmp(id(self), id(other))
jvrd4d15132002-05-11 00:59:27 +0000841
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400842 return cmp(self.__dict__, other.__dict__)