blob: b3f880f07a131534e2ab70e494712e3964f1e228 [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 {}
Behdad Esfahbod79f73442013-11-26 17:07:37 -050035 class GlobalState:
36 def __init__(self, tableType, cachingStats):
37 self.tableType = tableType
38 self.cachingStats = cachingStats
39 globalState = GlobalState(tableType=self.tableTag,
40 cachingStats=cachingStats)
41 reader = OTTableReader(data, globalState)
jvrd4d15132002-05-11 00:59:27 +000042 tableClass = getattr(otTables, self.tableTag)
43 self.table = tableClass()
44 self.table.decompile(reader, font)
Behdad Esfahbod0585b642013-11-22 16:20:59 -050045 if cachingStats:
jvrcfadfd02002-07-22 22:13:57 +000046 stats = [(v, k) for k, v in cachingStats.items()]
47 stats.sort()
48 stats.reverse()
49 print "cachingsstats for ", self.tableTag
50 for v, k in stats:
51 if v < 2:
52 break
53 print v, k
54 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000055
56 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000057 """ Create a top-level OTFWriter for the GPOS/GSUB table.
58 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000059 for each 'converter' record in the table converter list
60 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000061 - For simple items, the write method adds a string to the
62 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000063 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000064 to the writer's self.items, then calls the item's compile method.
65 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
66 each writer representing a table, and the writer.items list containing
67 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000068 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000069 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000070 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
71 Traverse the flat list of tables, calling getDataLength on each to update their position
72 Traverse the flat list of tables again, calling getData each get the data in the table, now that
73 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000074
75 If a lookup subtable overflows an offset, we have to start all over.
76 """
Behdad Esfahbod79f73442013-11-26 17:07:37 -050077 class GlobalState:
78 def __init__(self, tableType):
79 self.tableType = tableType
80 globalState = GlobalState(tableType=self.tableTag)
81 writer = OTTableWriter(globalState)
jvr823f8cd2006-10-21 14:12:38 +000082 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000083 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000084 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000085
jvrd4d15132002-05-11 00:59:27 +000086 def toXML(self, writer, font):
87 self.table.toXML2(writer, font)
88
89 def fromXML(self, (name, attrs, content), font):
90 import otTables
91 if not hasattr(self, "table"):
92 tableClass = getattr(otTables, self.tableTag)
93 self.table = tableClass()
94 self.table.fromXML((name, attrs, content), font)
95
96
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050097class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050098
jvr64b5c802002-05-11 10:21:36 +000099 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500100
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500101 __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
Behdad Esfahbod79817042013-11-24 16:59:42 -0500102
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500103 def __init__(self, data, globalState={}, localState=None, offset=0):
jvrd4d15132002-05-11 00:59:27 +0000104 self.data = data
105 self.offset = offset
106 self.pos = offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500107 self.globalState = globalState
108 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500109
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500110 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000111 offset = self.offset + offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500112 cachingStats = self.globalState.cachingStats
113 if cachingStats is not None:
114 cachingStats[offset] = cachingStats.get(offset, 0) + 1
115 return self.__class__(self.data, self.globalState, self.localState, offset)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500116
jvrd4d15132002-05-11 00:59:27 +0000117 def readUShort(self):
118 pos = self.pos
119 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000120 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000121 self.pos = newpos
122 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500123
jvrd4d15132002-05-11 00:59:27 +0000124 def readShort(self):
125 pos = self.pos
126 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000127 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000128 self.pos = newpos
129 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500130
jvrd4d15132002-05-11 00:59:27 +0000131 def readLong(self):
132 pos = self.pos
133 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000134 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000135 self.pos = newpos
136 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500137
jvr823f8cd2006-10-21 14:12:38 +0000138 def readULong(self):
139 pos = self.pos
140 newpos = pos + 4
141 value, = struct.unpack(">L", self.data[pos:newpos])
142 self.pos = newpos
143 return value
144
jvrd4d15132002-05-11 00:59:27 +0000145 def readTag(self):
146 pos = self.pos
147 newpos = pos + 4
148 value = self.data[pos:newpos]
149 assert len(value) == 4
150 self.pos = newpos
151 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500152
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500153 def __setitem__(self, name, value):
154 state = self.localState.copy() if self.localState else dict()
155 state[name] = value
156 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500157
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500158 def __getitem__(self, name):
159 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500160
jvrd4d15132002-05-11 00:59:27 +0000161
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500162class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000163
jvr64b5c802002-05-11 10:21:36 +0000164 """Helper class to gather and assemble data for OpenType tables."""
165
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500166 def __init__(self, globalState, localState=None):
jvrd4d15132002-05-11 00:59:27 +0000167 self.items = []
jvrcfadfd02002-07-22 22:13:57 +0000168 self.pos = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500169 self.globalState = globalState
170 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500171
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500172 def __setitem__(self, name, value):
173 state = self.localState.copy() if self.localState else dict()
174 state[name] = value
175 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500176
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500177 def __getitem__(self, name):
178 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500179
jvr4105ca02002-07-23 08:43:03 +0000180 # assembler interface
181
182 def getAllData(self):
183 """Assemble all data, including all subtables."""
184 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000185 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000186 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000187 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000188 # Gather all data in two passes: the absolute positions of all
189 # subtable are needed before the actual data can be assembled.
190 pos = 0
191 for table in tables:
192 table.pos = pos
193 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000194
195 for table in extTables:
196 table.pos = pos
197 pos = pos + table.getDataLength()
198
199
jvr4105ca02002-07-23 08:43:03 +0000200 data = []
201 for table in tables:
202 tableData = table.getData()
203 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000204
205 for table in extTables:
206 tableData = table.getData()
207 data.append(tableData)
208
jvr4105ca02002-07-23 08:43:03 +0000209 return "".join(data)
210
211 def getDataLength(self):
212 """Return the length of this table in bytes, without subtables."""
213 l = 0
214 for item in self.items:
215 if hasattr(item, "getData") or hasattr(item, "getCountData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500216 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000217 l = l + 4 # sizeof(ULong)
218 else:
219 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000220 else:
221 l = l + len(item)
222 return l
223
224 def getData(self):
225 """Assemble the data for this writer/table, without subtables."""
226 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000227 pos = self.pos
228 numItems = len(items)
229 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000230 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000231
jvr4105ca02002-07-23 08:43:03 +0000232 if hasattr(item, "getData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500233 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000234 items[i] = packULong(item.pos - pos)
235 else:
236 try:
237 items[i] = packUShort(item.pos - pos)
238 except AssertionError:
239 # provide data to fix overflow problem.
Behdad Esfahbod58acba22013-11-24 20:08:05 -0500240 # If the overflow is to a lookup, or from a lookup to a subtable,
jvr823f8cd2006-10-21 14:12:38 +0000241 # just report the current item.
242 if self.name in [ 'LookupList', 'Lookup']:
243 overflowErrorRecord = self.getOverflowErrorRecord(item)
244 else:
245 # overflow is within a subTable. Life is more complicated.
246 # If we split the sub-table just before the current item, we may still suffer overflow.
247 # This is because duplicate table merging is done only within an Extension subTable tree;
248 # when we split the subtable in two, some items may no longer be duplicates.
249 # Get worst case by adding up all the item lengths, depth first traversal.
250 # and then report the first item that overflows a short.
251 def getDeepItemLength(table):
252 if hasattr(table, "getDataLength"):
253 length = 0
254 for item in table.items:
255 length = length + getDeepItemLength(item)
256 else:
257 length = len(table)
258 return length
259
260 length = self.getDataLength()
261 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
262 # Coverage is first in the item list, but last in the table list,
263 # The original overflow is really in the item list. Skip the Coverage
264 # table in the following test.
265 items = items[i+1:]
266
267 for j in range(len(items)):
268 item = items[j]
269 length = length + getDeepItemLength(item)
270 if length > 65535:
271 break
272 overflowErrorRecord = self.getOverflowErrorRecord(item)
273
274
275 raise OTLOffsetOverflowError, overflowErrorRecord
276
jvr4105ca02002-07-23 08:43:03 +0000277 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000278
jvrcfadfd02002-07-22 22:13:57 +0000279 def __hash__(self):
280 # only works after self._doneWriting() has been called
281 return hash(self.items)
282
283 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100284 if type(self) != type(other): return cmp(type(self), type(other))
285 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400286
287 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000288
jvrcfadfd02002-07-22 22:13:57 +0000289 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000290 # Convert CountData references to data string items
291 # collapse duplicate table references to a unique entry
292 # "tables" are OTTableWriter objects.
293
294 # For Extension Lookup types, we can
295 # eliminate duplicates only within the tree under the Extension Lookup,
296 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000297 if internedTables is None:
298 internedTables = {}
299 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000300 iRange = range(len(items))
301
302 if hasattr(self, "Extension"):
303 newTree = 1
304 else:
305 newTree = 0
306 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000307 item = items[i]
308 if hasattr(item, "getCountData"):
309 items[i] = item.getCountData()
310 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000311 if newTree:
312 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000313 else:
jvr823f8cd2006-10-21 14:12:38 +0000314 item._doneWriting(internedTables)
315 if internedTables.has_key(item):
316 items[i] = item = internedTables[item]
317 else:
318 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000319 self.items = tuple(items)
320
jvr823f8cd2006-10-21 14:12:38 +0000321 def _gatherTables(self, tables=None, extTables=None, done=None):
322 # Convert table references in self.items tree to a flat
323 # list of tables in depth-first traversal order.
324 # "tables" are OTTableWriter objects.
325 # We do the traversal in reverse order at each level, in order to
326 # resolve duplicate references to be the last reference in the list of tables.
327 # For extension lookups, duplicate references can be merged only within the
328 # writer tree under the extension lookup.
329 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000330 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000331 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000332 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000333
334 done[self] = 1
335
336 numItems = len(self.items)
337 iRange = range(numItems)
338 iRange.reverse()
339
340 if hasattr(self, "Extension"):
341 appendExtensions = 1
342 else:
343 appendExtensions = 0
344
345 # add Coverage table if it is sorted last.
346 sortCoverageLast = 0
347 if hasattr(self, "sortCoverageLast"):
348 # Find coverage table
349 for i in range(numItems):
350 item = self.items[i]
351 if hasattr(item, "name") and (item.name == "Coverage"):
352 sortCoverageLast = 1
353 break
354 if not done.has_key(item):
355 item._gatherTables(tables, extTables, done)
356 else:
357 index = max(item.parent.keys())
358 item.parent[index + 1] = self
359
360 saveItem = None
361 for i in iRange:
362 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000363 if not hasattr(item, "getData"):
364 continue
jvr823f8cd2006-10-21 14:12:38 +0000365
366 if sortCoverageLast and (i==1) and item.name == 'Coverage':
367 # we've already 'gathered' it above
368 continue
369
370 if appendExtensions:
371 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
372 newDone = {}
373 item._gatherTables(extTables, None, newDone)
374
375 elif not done.has_key(item):
376 item._gatherTables(tables, extTables, done)
377 else:
378 index = max(item.parent.keys())
379 item.parent[index + 1] = self
380
381
jvrcfadfd02002-07-22 22:13:57 +0000382 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000383 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000384
jvr4105ca02002-07-23 08:43:03 +0000385 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000386
jvr4105ca02002-07-23 08:43:03 +0000387 def getSubWriter(self):
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500388 subwriter = self.__class__(self.globalState, self.localState)
jvr823f8cd2006-10-21 14:12:38 +0000389 subwriter.parent = {0:self} # because some subtables have idential values, we discard
390 # the duplicates under the getAllData method. Hence some
391 # subtable writers can have more than one parent writer.
392 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000393
394 def writeUShort(self, value):
395 assert 0 <= value < 0x10000
396 self.items.append(struct.pack(">H", value))
397
398 def writeShort(self, value):
399 self.items.append(struct.pack(">h", value))
400
401 def writeLong(self, value):
402 self.items.append(struct.pack(">l", value))
403
jvr823f8cd2006-10-21 14:12:38 +0000404 def writeULong(self, value):
405 self.items.append(struct.pack(">L", value))
406
jvrd4d15132002-05-11 00:59:27 +0000407 def writeTag(self, tag):
408 assert len(tag) == 4
409 self.items.append(tag)
410
411 def writeSubTable(self, subWriter):
412 self.items.append(subWriter)
413
414 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500415 ref = CountReference(table, name)
416 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500417 return ref
jvrd4d15132002-05-11 00:59:27 +0000418
419 def writeStruct(self, format, values):
420 data = apply(struct.pack, (format,) + values)
421 self.items.append(data)
422
jvr823f8cd2006-10-21 14:12:38 +0000423 def writeData(self, data):
424 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000425
jvr823f8cd2006-10-21 14:12:38 +0000426 def getOverflowErrorRecord(self, item):
427 LookupListIndex = SubTableIndex = itemName = itemIndex = None
428 if self.name == 'LookupList':
429 LookupListIndex = item.repeatIndex
430 elif self.name == 'Lookup':
431 LookupListIndex = self.repeatIndex
432 SubTableIndex = item.repeatIndex
433 else:
434 itemName = item.name
435 if hasattr(item, 'repeatIndex'):
436 itemIndex = item.repeatIndex
437 if self.name == 'SubTable':
438 LookupListIndex = self.parent[0].repeatIndex
439 SubTableIndex = self.repeatIndex
440 elif self.name == 'ExtSubTable':
441 LookupListIndex = self.parent[0].parent[0].repeatIndex
442 SubTableIndex = self.parent[0].repeatIndex
443 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
444 itemName = ".".join(self.name, item.name)
445 p1 = self.parent[0]
446 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
447 itemName = ".".join(p1.name, item.name)
448 p1 = p1.parent[0]
449 if p1:
450 if p1.name == 'ExtSubTable':
451 LookupListIndex = self.parent[0].parent[0].repeatIndex
452 SubTableIndex = self.parent[0].repeatIndex
453 else:
454 LookupListIndex = self.parent[0].repeatIndex
455 SubTableIndex = self.repeatIndex
456
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500457 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000458
jvrd4d15132002-05-11 00:59:27 +0000459
460class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000461 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000462 def __init__(self, table, name):
463 self.table = table
464 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500465 def setValue(self, value):
466 table = self.table
467 name = self.name
468 if table[name] is None:
469 table[name] = value
470 else:
471 assert table[name] == value, (table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000472 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000473 return packUShort(self.table[self.name])
474
475
jvr64b5c802002-05-11 10:21:36 +0000476def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000477 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000478 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000479
480
jvr823f8cd2006-10-21 14:12:38 +0000481def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000482 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000483 return struct.pack(">L", value)
484
485
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500486class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000487 def __init__(self):
488 self.compileStatus = 0 # 0 means table was created
489 # 1 means the table.read() function was called by a table which is subject
490 # to delayed compilation
491 # 2 means that it was subject to delayed compilation, and
492 # has been decompiled
jvr823f8cd2006-10-21 14:12:38 +0000493
494 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000495
jvr823f8cd2006-10-21 14:12:38 +0000496 def __getattr__(self, attr):
497 # we get here only when the table does not have the attribute.
498 # This method ovveride exists so that we can try to de-compile
499 # a table which is subject to delayed decompilation, and then try
500 # to get the value again after decompilation.
501 self.recurse +=1
502 if self.recurse > 2:
503 # shouldn't ever get here - we should only get to two levels of recursion.
504 # this guards against self.decompile NOT setting compileStatus to other than 1.
505 raise AttributeError, attr
506 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500507 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000508 val = getattr(self, attr)
509 self.recurse -=1
510 return val
511
512 raise AttributeError, attr
513
514
jvr64b5c802002-05-11 10:21:36 +0000515 """Generic base class for all OpenType (sub)tables."""
516
jvrd4d15132002-05-11 00:59:27 +0000517 def getConverters(self):
518 return self.converters
519
520 def getConverterByName(self, name):
521 return self.convertersByName[name]
522
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500523 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000524 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000525 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000526 table = {}
527 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500528 converters = self.getConverters()
529 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000530 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500531 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000532 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000533 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500534 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000535 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000536 if conv.repeat:
537 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500538 if conv.repeat in table:
539 countValue = table[conv.repeat]
540 else:
541 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500542 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500543 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500544 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000545 table[conv.name] = l
546 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500547 if conv.aux and not eval(conv.aux, None, table):
548 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500549 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500550 if conv.isPropagatedCount:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500551 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500552
jvrd4d15132002-05-11 00:59:27 +0000553 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500554
jvrd4d15132002-05-11 00:59:27 +0000555 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000556
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500557 def ensureDecompiled(self):
558 if self.compileStatus != 1:
559 return
Behdad Esfahbodf4e61ae2013-11-26 17:37:03 -0500560 self.decompile(self.reader, self.font)
561 del self.reader, self.font
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500562
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500563 def compile(self, writer, font):
jvrd4d15132002-05-11 00:59:27 +0000564 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000565
566 if hasattr(self, 'sortCoverageLast'):
567 writer.sortCoverageLast = 1
568
jvrf7ef96c2002-09-10 19:26:38 +0000569 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000570 for conv in self.getConverters():
571 value = table.get(conv.name)
572 if conv.repeat:
573 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000574 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500575 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500576 if conv.repeat in table:
577 ref = table[conv.repeat]
578 table[conv.repeat] = None
579 ref.setValue(countValue)
580 else:
581 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500582 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000583 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500584 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000585 elif conv.isCount:
586 # Special-case Count values.
587 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500588 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000589 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500590 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000591 # We add a reference: by the time the data is assembled
592 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500593 ref = writer.writeCountReference(table, conv.name)
594 if conv.isPropagatedCount:
595 table[conv.name] = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500596 writer[conv.name] = ref
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500597 else:
598 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000599 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500600 if conv.aux and not eval(conv.aux, None, table):
601 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500602 conv.write(writer, font, table, value)
jvrd4d15132002-05-11 00:59:27 +0000603
jvrf7ef96c2002-09-10 19:26:38 +0000604 def readFormat(self, reader):
605 pass
606
607 def writeFormat(self, writer):
608 pass
609
jvrd4d15132002-05-11 00:59:27 +0000610 def postRead(self, table, font):
611 self.__dict__.update(table)
612
613 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500614 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000615 return self.__dict__.copy()
616
617 def toXML(self, xmlWriter, font, attrs=None):
618 tableName = self.__class__.__name__
619 if attrs is None:
620 attrs = []
621 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000622 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000623 xmlWriter.begintag(tableName, attrs)
624 xmlWriter.newline()
625 self.toXML2(xmlWriter, font)
626 xmlWriter.endtag(tableName)
627 xmlWriter.newline()
628
629 def toXML2(self, xmlWriter, font):
630 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
631 # This is because in TTX our parent writes our main tag, and in otBase.py we
632 # do it ourselves. I think I'm getting schizophrenic...
633 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000634 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500635 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000636 for i in range(len(value)):
637 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000638 conv.xmlWrite(xmlWriter, font, item, conv.name,
639 [("index", i)])
640 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500641 if conv.aux and not eval(conv.aux, None, vars(self)):
642 continue
643 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000644 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000645
646 def fromXML(self, (name, attrs, content), font):
647 try:
648 conv = self.getConverterByName(name)
649 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000650 raise # XXX on KeyError, raise nice error
651 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000652 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000653 seq = getattr(self, conv.name, None)
654 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000655 seq = []
jvr64b5c802002-05-11 10:21:36 +0000656 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000657 seq.append(value)
658 else:
jvr64b5c802002-05-11 10:21:36 +0000659 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000660
661 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100662 if type(self) != type(other): return cmp(type(self), type(other))
663 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400664
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500665 self.ensureDecompiled()
666
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400667 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000668
669
670class FormatSwitchingBaseTable(BaseTable):
671
jvrcfadfd02002-07-22 22:13:57 +0000672 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000673 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
674
jvrd4d15132002-05-11 00:59:27 +0000675 def getConverters(self):
676 return self.converters[self.Format]
677
678 def getConverterByName(self, name):
679 return self.convertersByName[self.Format][name]
680
jvrf7ef96c2002-09-10 19:26:38 +0000681 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000682 self.Format = reader.readUShort()
683 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000684
jvrf7ef96c2002-09-10 19:26:38 +0000685 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000686 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000687
688
jvr64b5c802002-05-11 10:21:36 +0000689#
690# Support for ValueRecords
691#
692# This data type is so different from all other OpenType data types that
693# it requires quite a bit of code for itself. It even has special support
694# in OTTableReader and OTTableWriter...
695#
696
jvrd4d15132002-05-11 00:59:27 +0000697valueRecordFormat = [
698# Mask Name isDevice signed
699 (0x0001, "XPlacement", 0, 1),
700 (0x0002, "YPlacement", 0, 1),
701 (0x0004, "XAdvance", 0, 1),
702 (0x0008, "YAdvance", 0, 1),
703 (0x0010, "XPlaDevice", 1, 0),
704 (0x0020, "YPlaDevice", 1, 0),
705 (0x0040, "XAdvDevice", 1, 0),
706 (0x0080, "YAdvDevice", 1, 0),
707# reserved:
708 (0x0100, "Reserved1", 0, 0),
709 (0x0200, "Reserved2", 0, 0),
710 (0x0400, "Reserved3", 0, 0),
711 (0x0800, "Reserved4", 0, 0),
712 (0x1000, "Reserved5", 0, 0),
713 (0x2000, "Reserved6", 0, 0),
714 (0x4000, "Reserved7", 0, 0),
715 (0x8000, "Reserved8", 0, 0),
716]
717
718def _buildDict():
719 d = {}
720 for mask, name, isDevice, signed in valueRecordFormat:
721 d[name] = mask, isDevice, signed
722 return d
723
724valueRecordFormatDict = _buildDict()
725
726
727class ValueRecordFactory:
728
jvr64b5c802002-05-11 10:21:36 +0000729 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500730
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500731 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000732 format = []
733 for mask, name, isDevice, signed in valueRecordFormat:
734 if valueFormat & mask:
735 format.append((name, isDevice, signed))
736 self.format = format
737
738 def readValueRecord(self, reader, font):
739 format = self.format
740 if not format:
741 return None
742 valueRecord = ValueRecord()
743 for name, isDevice, signed in format:
744 if signed:
745 value = reader.readShort()
746 else:
747 value = reader.readUShort()
748 if isDevice:
749 if value:
750 import otTables
751 subReader = reader.getSubReader(value)
752 value = getattr(otTables, name)()
753 value.decompile(subReader, font)
754 else:
755 value = None
756 setattr(valueRecord, name, value)
757 return valueRecord
758
759 def writeValueRecord(self, writer, font, valueRecord):
760 for name, isDevice, signed in self.format:
761 value = getattr(valueRecord, name, 0)
762 if isDevice:
763 if value:
764 subWriter = writer.getSubWriter()
765 writer.writeSubTable(subWriter)
766 value.compile(subWriter, font)
767 else:
768 writer.writeUShort(0)
769 elif signed:
770 writer.writeShort(value)
771 else:
772 writer.writeUShort(value)
773
774
775class ValueRecord:
776
777 # see ValueRecordFactory
778
779 def getFormat(self):
780 format = 0
781 for name in self.__dict__.keys():
782 format = format | valueRecordFormatDict[name][0]
783 return format
784
785 def toXML(self, xmlWriter, font, valueName, attrs=None):
786 if attrs is None:
787 simpleItems = []
788 else:
789 simpleItems = list(attrs)
790 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
791 if hasattr(self, name):
792 simpleItems.append((name, getattr(self, name)))
793 deviceItems = []
794 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
795 if hasattr(self, name):
796 device = getattr(self, name)
797 if device is not None:
798 deviceItems.append((name, device))
799 if deviceItems:
800 xmlWriter.begintag(valueName, simpleItems)
801 xmlWriter.newline()
802 for name, deviceRecord in deviceItems:
803 if deviceRecord is not None:
804 deviceRecord.toXML(xmlWriter, font)
805 xmlWriter.endtag(valueName)
806 xmlWriter.newline()
807 else:
808 xmlWriter.simpletag(valueName, simpleItems)
809 xmlWriter.newline()
810
811 def fromXML(self, (name, attrs, content), font):
812 import otTables
813 for k, v in attrs.items():
814 setattr(self, k, int(v))
815 for element in content:
816 if type(element) <> TupleType:
817 continue
818 name, attrs, content = element
819 value = getattr(otTables, name)()
820 for elem2 in content:
821 if type(elem2) <> TupleType:
822 continue
823 value.fromXML(elem2, font)
824 setattr(self, name, value)
825
826 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100827 if type(self) != type(other): return cmp(type(self), type(other))
828 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000829
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400830 return cmp(self.__dict__, other.__dict__)