blob: 5f742aea2cbfdf164992d0d18cdf95ad9b1df14c [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
Behdad Esfahbod0585b642013-11-22 16:20:59 -050034 cachingStats = None if True else {}
jvrcfadfd02002-07-22 22:13:57 +000035 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)
Behdad Esfahbod0585b642013-11-22 16:20:59 -050039 if cachingStats:
jvrcfadfd02002-07-22 22:13:57 +000040 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
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500493class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000494 def __init__(self):
495 self.compileStatus = 0 # 0 means table was created
496 # 1 means the table.read() function was called by a table which is subject
497 # to delayed compilation
498 # 2 means that it was subject to delayed compilation, and
499 # has been decompiled
500 # 3 means that the start and end fields have been filled out, and that we
501 # can use the data string rather than compiling from the table data.
502
503 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000504
jvr823f8cd2006-10-21 14:12:38 +0000505 def __getattr__(self, attr):
506 # we get here only when the table does not have the attribute.
507 # This method ovveride exists so that we can try to de-compile
508 # a table which is subject to delayed decompilation, and then try
509 # to get the value again after decompilation.
510 self.recurse +=1
511 if self.recurse > 2:
512 # shouldn't ever get here - we should only get to two levels of recursion.
513 # this guards against self.decompile NOT setting compileStatus to other than 1.
514 raise AttributeError, attr
515 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500516 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000517 val = getattr(self, attr)
518 self.recurse -=1
519 return val
520
521 raise AttributeError, attr
522
523
jvr64b5c802002-05-11 10:21:36 +0000524 """Generic base class for all OpenType (sub)tables."""
525
jvrd4d15132002-05-11 00:59:27 +0000526 def getConverters(self):
527 return self.converters
528
529 def getConverterByName(self, name):
530 return self.convertersByName[name]
531
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500532 def decompile(self, reader, font, countVars=None):
jvr823f8cd2006-10-21 14:12:38 +0000533 self.compileStatus = 2 # table has been decompiled.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500534 if countVars is None:
535 countVars = {}
jvrf7ef96c2002-09-10 19:26:38 +0000536 self.readFormat(reader)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500537 counts = []
jvrd4d15132002-05-11 00:59:27 +0000538 table = {}
539 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500540 converters = self.getConverters()
541 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000542 if conv.name == "SubTable":
543 conv = conv.getConverter(reader.tableType,
544 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000545 if conv.name == "ExtSubTable":
546 conv = conv.getConverter(reader.tableType,
547 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000548 if conv.repeat:
549 l = []
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500550 for i in range(countVars[conv.repeat] + conv.repeatOffset):
551 l.append(conv.read(reader, font, countVars))
jvrd4d15132002-05-11 00:59:27 +0000552 table[conv.name] = l
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500553 if conv.repeat in counts:
554 del countVars[conv.repeat]
555 counts.remove(conv.repeat)
556
jvrd4d15132002-05-11 00:59:27 +0000557 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500558 table[conv.name] = conv.read(reader, font, countVars)
559 if conv.isCount:
560 counts.append(conv.name)
561 countVars[conv.name] = table[conv.name]
562
563 for count in counts:
564 del countVars[count]
565
jvrd4d15132002-05-11 00:59:27 +0000566 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500567
jvrd4d15132002-05-11 00:59:27 +0000568 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000569
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500570 def ensureDecompiled(self):
571 if self.compileStatus != 1:
572 return
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500573 self.decompile(self.reader, self.font, self.countVars)
574 del self.reader, self.font, self.countVars
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500575
jvr823f8cd2006-10-21 14:12:38 +0000576 def preCompile(self):
577 pass # used only by the LookupList class
578
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500579 def compile(self, writer, font, countVars=None):
580 if countVars is None:
581 countVars = {}
582 counts = []
jvrd4d15132002-05-11 00:59:27 +0000583 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000584
585 if hasattr(self, 'sortCoverageLast'):
586 writer.sortCoverageLast = 1
587
jvrf7ef96c2002-09-10 19:26:38 +0000588 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000589 for conv in self.getConverters():
590 value = table.get(conv.name)
591 if conv.repeat:
592 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000593 value = []
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500594 countVars[conv.repeat](len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000595 for i in range(len(value)):
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500596 conv.write(writer, font, countVars, value[i], i)
597 if conv.repeat in counts:
598 del countVars[conv.repeat]
599 counts.remove(conv.repeat)
jvrd4d15132002-05-11 00:59:27 +0000600 elif conv.isCount:
601 # Special-case Count values.
602 # Assumption: a Count field will *always* precede
603 # the actual array.
604 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500605 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000606 table[conv.name] = None
607 # We add a reference: by the time the data is assembled
608 # the Count value will be filled in.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500609 name = conv.name
610 writer.writeCountReference(table, name)
611 counts.append(name)
612 def storeValue(value):
613 if table[name] is None:
614 table[name] = value
615 else:
616 assert table[name] == value, (table[name], value)
617 countVars[name] = storeValue
jvrd4d15132002-05-11 00:59:27 +0000618 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500619 conv.write(writer, font, countVars, value)
620
621 for count in counts:
622 del countVars[count]
jvrd4d15132002-05-11 00:59:27 +0000623
jvrf7ef96c2002-09-10 19:26:38 +0000624 def readFormat(self, reader):
625 pass
626
627 def writeFormat(self, writer):
628 pass
629
jvrd4d15132002-05-11 00:59:27 +0000630 def postRead(self, table, font):
631 self.__dict__.update(table)
632
633 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500634 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000635 return self.__dict__.copy()
636
637 def toXML(self, xmlWriter, font, attrs=None):
638 tableName = self.__class__.__name__
639 if attrs is None:
640 attrs = []
641 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000642 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000643 xmlWriter.begintag(tableName, attrs)
644 xmlWriter.newline()
645 self.toXML2(xmlWriter, font)
646 xmlWriter.endtag(tableName)
647 xmlWriter.newline()
648
649 def toXML2(self, xmlWriter, font):
650 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
651 # This is because in TTX our parent writes our main tag, and in otBase.py we
652 # do it ourselves. I think I'm getting schizophrenic...
653 for conv in self.getConverters():
654 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000655 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000656 for i in range(len(value)):
657 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000658 conv.xmlWrite(xmlWriter, font, item, conv.name,
659 [("index", i)])
660 else:
661 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000662
663 def fromXML(self, (name, attrs, content), font):
664 try:
665 conv = self.getConverterByName(name)
666 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000667 raise # XXX on KeyError, raise nice error
668 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000669 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000670 seq = getattr(self, conv.name, None)
671 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000672 seq = []
jvr64b5c802002-05-11 10:21:36 +0000673 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000674 seq.append(value)
675 else:
jvr64b5c802002-05-11 10:21:36 +0000676 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000677
678 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100679 if type(self) != type(other): return cmp(type(self), type(other))
680 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400681
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500682 self.ensureDecompiled()
683
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400684 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000685
686
687class FormatSwitchingBaseTable(BaseTable):
688
jvrcfadfd02002-07-22 22:13:57 +0000689 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000690 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
691
jvrd4d15132002-05-11 00:59:27 +0000692 def getConverters(self):
693 return self.converters[self.Format]
694
695 def getConverterByName(self, name):
696 return self.convertersByName[self.Format][name]
697
jvrf7ef96c2002-09-10 19:26:38 +0000698 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000699 self.Format = reader.readUShort()
700 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000701
jvrf7ef96c2002-09-10 19:26:38 +0000702 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000703 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000704
705
jvr64b5c802002-05-11 10:21:36 +0000706#
707# Support for ValueRecords
708#
709# This data type is so different from all other OpenType data types that
710# it requires quite a bit of code for itself. It even has special support
711# in OTTableReader and OTTableWriter...
712#
713
jvrd4d15132002-05-11 00:59:27 +0000714valueRecordFormat = [
715# Mask Name isDevice signed
716 (0x0001, "XPlacement", 0, 1),
717 (0x0002, "YPlacement", 0, 1),
718 (0x0004, "XAdvance", 0, 1),
719 (0x0008, "YAdvance", 0, 1),
720 (0x0010, "XPlaDevice", 1, 0),
721 (0x0020, "YPlaDevice", 1, 0),
722 (0x0040, "XAdvDevice", 1, 0),
723 (0x0080, "YAdvDevice", 1, 0),
724# reserved:
725 (0x0100, "Reserved1", 0, 0),
726 (0x0200, "Reserved2", 0, 0),
727 (0x0400, "Reserved3", 0, 0),
728 (0x0800, "Reserved4", 0, 0),
729 (0x1000, "Reserved5", 0, 0),
730 (0x2000, "Reserved6", 0, 0),
731 (0x4000, "Reserved7", 0, 0),
732 (0x8000, "Reserved8", 0, 0),
733]
734
735def _buildDict():
736 d = {}
737 for mask, name, isDevice, signed in valueRecordFormat:
738 d[name] = mask, isDevice, signed
739 return d
740
741valueRecordFormatDict = _buildDict()
742
743
744class ValueRecordFactory:
745
jvr64b5c802002-05-11 10:21:36 +0000746 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500747
748 def __init__(self, other=None):
749 self.format = other.format if other else None
jvr64b5c802002-05-11 10:21:36 +0000750
jvrd4d15132002-05-11 00:59:27 +0000751 def setFormat(self, valueFormat):
752 format = []
753 for mask, name, isDevice, signed in valueRecordFormat:
754 if valueFormat & mask:
755 format.append((name, isDevice, signed))
756 self.format = format
757
758 def readValueRecord(self, reader, font):
759 format = self.format
760 if not format:
761 return None
762 valueRecord = ValueRecord()
763 for name, isDevice, signed in format:
764 if signed:
765 value = reader.readShort()
766 else:
767 value = reader.readUShort()
768 if isDevice:
769 if value:
770 import otTables
771 subReader = reader.getSubReader(value)
772 value = getattr(otTables, name)()
773 value.decompile(subReader, font)
774 else:
775 value = None
776 setattr(valueRecord, name, value)
777 return valueRecord
778
779 def writeValueRecord(self, writer, font, valueRecord):
780 for name, isDevice, signed in self.format:
781 value = getattr(valueRecord, name, 0)
782 if isDevice:
783 if value:
784 subWriter = writer.getSubWriter()
785 writer.writeSubTable(subWriter)
786 value.compile(subWriter, font)
787 else:
788 writer.writeUShort(0)
789 elif signed:
790 writer.writeShort(value)
791 else:
792 writer.writeUShort(value)
793
794
795class ValueRecord:
796
797 # see ValueRecordFactory
798
799 def getFormat(self):
800 format = 0
801 for name in self.__dict__.keys():
802 format = format | valueRecordFormatDict[name][0]
803 return format
804
805 def toXML(self, xmlWriter, font, valueName, attrs=None):
806 if attrs is None:
807 simpleItems = []
808 else:
809 simpleItems = list(attrs)
810 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
811 if hasattr(self, name):
812 simpleItems.append((name, getattr(self, name)))
813 deviceItems = []
814 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
815 if hasattr(self, name):
816 device = getattr(self, name)
817 if device is not None:
818 deviceItems.append((name, device))
819 if deviceItems:
820 xmlWriter.begintag(valueName, simpleItems)
821 xmlWriter.newline()
822 for name, deviceRecord in deviceItems:
823 if deviceRecord is not None:
824 deviceRecord.toXML(xmlWriter, font)
825 xmlWriter.endtag(valueName)
826 xmlWriter.newline()
827 else:
828 xmlWriter.simpletag(valueName, simpleItems)
829 xmlWriter.newline()
830
831 def fromXML(self, (name, attrs, content), font):
832 import otTables
833 for k, v in attrs.items():
834 setattr(self, k, int(v))
835 for element in content:
836 if type(element) <> TupleType:
837 continue
838 name, attrs, content = element
839 value = getattr(otTables, name)()
840 for elem2 in content:
841 if type(elem2) <> TupleType:
842 continue
843 value.fromXML(elem2, font)
844 setattr(self, name, value)
845
846 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100847 if type(self) != type(other): return cmp(type(self), type(other))
848 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000849
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400850 return cmp(self.__dict__, other.__dict__)