blob: 7eadebeb02527c9b10d1d8af48b4e9f8456cf63e [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
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050087class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050088
jvr64b5c802002-05-11 10:21:36 +000089 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050090
Behdad Esfahbod79817042013-11-24 16:59:42 -050091 __slots__ = ('data', 'offset', 'pos', 'tableType', 'valueFormat', 'counts', 'cachingStats')
92
93 def __init__(self, data, tableType, offset=0, valueFormat=None, counts=None, cachingStats=None):
jvrd4d15132002-05-11 00:59:27 +000094 self.data = data
95 self.offset = offset
96 self.pos = offset
97 self.tableType = tableType
98 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -050099 valueFormat = [None, None]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500100 self.counts = counts
jvrd4d15132002-05-11 00:59:27 +0000101 self.valueFormat = valueFormat
102 self.cachingStats = cachingStats
Behdad Esfahbod79817042013-11-24 16:59:42 -0500103
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500104 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000105 offset = self.offset + offset
106 if self.cachingStats is not None:
Behdad Esfahbode84f9fd2013-11-24 18:05:59 -0500107 self.cachingStats[offset] = self.cachingStats.get(offset, 0) + 1
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500108
jvrd4d15132002-05-11 00:59:27 +0000109 subReader = self.__class__(self.data, self.tableType, offset,
Behdad Esfahbod79817042013-11-24 16:59:42 -0500110 self.valueFormat, self.counts, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000111 return subReader
Behdad Esfahbod79817042013-11-24 16:59:42 -0500112
jvrd4d15132002-05-11 00:59:27 +0000113 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500119
jvrd4d15132002-05-11 00:59:27 +0000120 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500126
jvrd4d15132002-05-11 00:59:27 +0000127 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500133
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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500148
jvrd4d15132002-05-11 00:59:27 +0000149 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500150 self.valueFormat[which] = ValueRecordFactory(format)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500151
jvrd4d15132002-05-11 00:59:27 +0000152 def readValueRecord(self, font, which):
153 return self.valueFormat[which].readValueRecord(self, font)
154
Behdad Esfahbod79817042013-11-24 16:59:42 -0500155 def setCount(self, name, value):
156 self.counts = self.counts.copy() if self.counts else dict()
157 self.counts[name] = value
158
159 def getCount(self, name):
160 return self.counts[name]
161
jvrd4d15132002-05-11 00:59:27 +0000162
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500163class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000164
jvr64b5c802002-05-11 10:21:36 +0000165 """Helper class to gather and assemble data for OpenType tables."""
166
Behdad Esfahbod79817042013-11-24 16:59:42 -0500167 def __init__(self, tableType, valueFormat=None, counts=None):
jvrd4d15132002-05-11 00:59:27 +0000168 self.items = []
169 self.tableType = tableType
170 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500171 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000172 self.valueFormat = valueFormat
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500173 self.counts = counts
jvrcfadfd02002-07-22 22:13:57 +0000174 self.pos = None
Behdad Esfahbod79817042013-11-24 16:59:42 -0500175
176 def setValueFormat(self, format, which):
177 self.valueFormat[which] = ValueRecordFactory(format)
178
179 def setCount(self, name, value):
180 self.counts = self.counts.copy() if self.counts else dict()
181 self.counts[name] = value
182
183 def getCount(self, name):
184 return self.counts[name]
185
jvr4105ca02002-07-23 08:43:03 +0000186 # assembler interface
187
188 def getAllData(self):
189 """Assemble all data, including all subtables."""
190 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000191 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000192 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000193 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000194 # Gather all data in two passes: the absolute positions of all
195 # subtable are needed before the actual data can be assembled.
196 pos = 0
197 for table in tables:
198 table.pos = pos
199 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000200
201 for table in extTables:
202 table.pos = pos
203 pos = pos + table.getDataLength()
204
205
jvr4105ca02002-07-23 08:43:03 +0000206 data = []
207 for table in tables:
208 tableData = table.getData()
209 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000210
211 for table in extTables:
212 tableData = table.getData()
213 data.append(tableData)
214
jvr4105ca02002-07-23 08:43:03 +0000215 return "".join(data)
216
217 def getDataLength(self):
218 """Return the length of this table in bytes, without subtables."""
219 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000220 if hasattr(self, "Extension"):
221 longOffset = 1
222 else:
223 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000224 for item in self.items:
225 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000226 if longOffset:
227 l = l + 4 # sizeof(ULong)
228 else:
229 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000230 else:
231 l = l + len(item)
232 return l
233
234 def getData(self):
235 """Assemble the data for this writer/table, without subtables."""
236 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000237 if hasattr(self,"Extension"):
238 longOffset = 1
239 else:
240 longOffset = 0
241 pos = self.pos
242 numItems = len(items)
243 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000244 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000245
jvr4105ca02002-07-23 08:43:03 +0000246 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000247 if longOffset:
248 items[i] = packULong(item.pos - pos)
249 else:
250 try:
251 items[i] = packUShort(item.pos - pos)
252 except AssertionError:
253 # provide data to fix overflow problem.
254 # If the overflow is to a lookup, or from a lookup to a subtable,
255 # just report the current item.
256 if self.name in [ 'LookupList', 'Lookup']:
257 overflowErrorRecord = self.getOverflowErrorRecord(item)
258 else:
259 # overflow is within a subTable. Life is more complicated.
260 # If we split the sub-table just before the current item, we may still suffer overflow.
261 # This is because duplicate table merging is done only within an Extension subTable tree;
262 # when we split the subtable in two, some items may no longer be duplicates.
263 # Get worst case by adding up all the item lengths, depth first traversal.
264 # and then report the first item that overflows a short.
265 def getDeepItemLength(table):
266 if hasattr(table, "getDataLength"):
267 length = 0
268 for item in table.items:
269 length = length + getDeepItemLength(item)
270 else:
271 length = len(table)
272 return length
273
274 length = self.getDataLength()
275 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
276 # Coverage is first in the item list, but last in the table list,
277 # The original overflow is really in the item list. Skip the Coverage
278 # table in the following test.
279 items = items[i+1:]
280
281 for j in range(len(items)):
282 item = items[j]
283 length = length + getDeepItemLength(item)
284 if length > 65535:
285 break
286 overflowErrorRecord = self.getOverflowErrorRecord(item)
287
288
289 raise OTLOffsetOverflowError, overflowErrorRecord
290
jvr4105ca02002-07-23 08:43:03 +0000291 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000292
jvrcfadfd02002-07-22 22:13:57 +0000293 def __hash__(self):
294 # only works after self._doneWriting() has been called
295 return hash(self.items)
296
297 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100298 if type(self) != type(other): return cmp(type(self), type(other))
299 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400300
301 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000302
jvrcfadfd02002-07-22 22:13:57 +0000303 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000304 # Convert CountData references to data string items
305 # collapse duplicate table references to a unique entry
306 # "tables" are OTTableWriter objects.
307
308 # For Extension Lookup types, we can
309 # eliminate duplicates only within the tree under the Extension Lookup,
310 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000311 if internedTables is None:
312 internedTables = {}
313 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000314 iRange = range(len(items))
315
316 if hasattr(self, "Extension"):
317 newTree = 1
318 else:
319 newTree = 0
320 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000321 item = items[i]
322 if hasattr(item, "getCountData"):
323 items[i] = item.getCountData()
324 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000325 if newTree:
326 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000327 else:
jvr823f8cd2006-10-21 14:12:38 +0000328 item._doneWriting(internedTables)
329 if internedTables.has_key(item):
330 items[i] = item = internedTables[item]
331 else:
332 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000333 self.items = tuple(items)
334
jvr823f8cd2006-10-21 14:12:38 +0000335 def _gatherTables(self, tables=None, extTables=None, done=None):
336 # Convert table references in self.items tree to a flat
337 # list of tables in depth-first traversal order.
338 # "tables" are OTTableWriter objects.
339 # We do the traversal in reverse order at each level, in order to
340 # resolve duplicate references to be the last reference in the list of tables.
341 # For extension lookups, duplicate references can be merged only within the
342 # writer tree under the extension lookup.
343 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000344 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000345 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000346 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000347
348 done[self] = 1
349
350 numItems = len(self.items)
351 iRange = range(numItems)
352 iRange.reverse()
353
354 if hasattr(self, "Extension"):
355 appendExtensions = 1
356 else:
357 appendExtensions = 0
358
359 # add Coverage table if it is sorted last.
360 sortCoverageLast = 0
361 if hasattr(self, "sortCoverageLast"):
362 # Find coverage table
363 for i in range(numItems):
364 item = self.items[i]
365 if hasattr(item, "name") and (item.name == "Coverage"):
366 sortCoverageLast = 1
367 break
368 if not done.has_key(item):
369 item._gatherTables(tables, extTables, done)
370 else:
371 index = max(item.parent.keys())
372 item.parent[index + 1] = self
373
374 saveItem = None
375 for i in iRange:
376 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000377 if not hasattr(item, "getData"):
378 continue
jvr823f8cd2006-10-21 14:12:38 +0000379
380 if sortCoverageLast and (i==1) and item.name == 'Coverage':
381 # we've already 'gathered' it above
382 continue
383
384 if appendExtensions:
385 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
386 newDone = {}
387 item._gatherTables(extTables, None, newDone)
388
389 elif not done.has_key(item):
390 item._gatherTables(tables, extTables, done)
391 else:
392 index = max(item.parent.keys())
393 item.parent[index + 1] = self
394
395
jvrcfadfd02002-07-22 22:13:57 +0000396 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000397 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000398
jvr4105ca02002-07-23 08:43:03 +0000399 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000400
jvr4105ca02002-07-23 08:43:03 +0000401 def getSubWriter(self):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500402 subwriter = self.__class__(self.tableType, self.valueFormat, self.counts)
jvr823f8cd2006-10-21 14:12:38 +0000403 subwriter.parent = {0:self} # because some subtables have idential values, we discard
404 # the duplicates under the getAllData method. Hence some
405 # subtable writers can have more than one parent writer.
406 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000407
408 def writeUShort(self, value):
409 assert 0 <= value < 0x10000
410 self.items.append(struct.pack(">H", value))
411
412 def writeShort(self, value):
413 self.items.append(struct.pack(">h", value))
414
415 def writeLong(self, value):
416 self.items.append(struct.pack(">l", value))
417
jvr823f8cd2006-10-21 14:12:38 +0000418 def writeULong(self, value):
419 self.items.append(struct.pack(">L", value))
420
jvrd4d15132002-05-11 00:59:27 +0000421 def writeTag(self, tag):
422 assert len(tag) == 4
423 self.items.append(tag)
424
425 def writeSubTable(self, subWriter):
426 self.items.append(subWriter)
427
428 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500429 ref = CountReference(table, name)
430 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500431 return ref
jvrd4d15132002-05-11 00:59:27 +0000432
433 def writeStruct(self, format, values):
434 data = apply(struct.pack, (format,) + values)
435 self.items.append(data)
436
jvr823f8cd2006-10-21 14:12:38 +0000437 def writeData(self, data):
438 self.items.append(data)
439
jvrd4d15132002-05-11 00:59:27 +0000440 def writeValueRecord(self, value, font, which):
441 return self.valueFormat[which].writeValueRecord(self, font, value)
442
jvr823f8cd2006-10-21 14:12:38 +0000443 def getOverflowErrorRecord(self, item):
444 LookupListIndex = SubTableIndex = itemName = itemIndex = None
445 if self.name == 'LookupList':
446 LookupListIndex = item.repeatIndex
447 elif self.name == 'Lookup':
448 LookupListIndex = self.repeatIndex
449 SubTableIndex = item.repeatIndex
450 else:
451 itemName = item.name
452 if hasattr(item, 'repeatIndex'):
453 itemIndex = item.repeatIndex
454 if self.name == 'SubTable':
455 LookupListIndex = self.parent[0].repeatIndex
456 SubTableIndex = self.repeatIndex
457 elif self.name == 'ExtSubTable':
458 LookupListIndex = self.parent[0].parent[0].repeatIndex
459 SubTableIndex = self.parent[0].repeatIndex
460 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
461 itemName = ".".join(self.name, item.name)
462 p1 = self.parent[0]
463 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
464 itemName = ".".join(p1.name, item.name)
465 p1 = p1.parent[0]
466 if p1:
467 if p1.name == 'ExtSubTable':
468 LookupListIndex = self.parent[0].parent[0].repeatIndex
469 SubTableIndex = self.parent[0].repeatIndex
470 else:
471 LookupListIndex = self.parent[0].repeatIndex
472 SubTableIndex = self.repeatIndex
473
474 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
475
jvrd4d15132002-05-11 00:59:27 +0000476
477class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000478 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000479 def __init__(self, table, name):
480 self.table = table
481 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500482 def setValue(self, value):
483 table = self.table
484 name = self.name
485 if table[name] is None:
486 table[name] = value
487 else:
488 assert table[name] == value, (table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000489 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000490 return packUShort(self.table[self.name])
491
492
jvr64b5c802002-05-11 10:21:36 +0000493def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000494 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000495 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000496
497
jvr823f8cd2006-10-21 14:12:38 +0000498def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000499 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000500 return struct.pack(">L", value)
501
502
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500503class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000504 def __init__(self):
505 self.compileStatus = 0 # 0 means table was created
506 # 1 means the table.read() function was called by a table which is subject
507 # to delayed compilation
508 # 2 means that it was subject to delayed compilation, and
509 # has been decompiled
510 # 3 means that the start and end fields have been filled out, and that we
511 # can use the data string rather than compiling from the table data.
512
513 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000514
jvr823f8cd2006-10-21 14:12:38 +0000515 def __getattr__(self, attr):
516 # we get here only when the table does not have the attribute.
517 # This method ovveride exists so that we can try to de-compile
518 # a table which is subject to delayed decompilation, and then try
519 # to get the value again after decompilation.
520 self.recurse +=1
521 if self.recurse > 2:
522 # shouldn't ever get here - we should only get to two levels of recursion.
523 # this guards against self.decompile NOT setting compileStatus to other than 1.
524 raise AttributeError, attr
525 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500526 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000527 val = getattr(self, attr)
528 self.recurse -=1
529 return val
530
531 raise AttributeError, attr
532
533
jvr64b5c802002-05-11 10:21:36 +0000534 """Generic base class for all OpenType (sub)tables."""
535
jvrd4d15132002-05-11 00:59:27 +0000536 def getConverters(self):
537 return self.converters
538
539 def getConverterByName(self, name):
540 return self.convertersByName[name]
541
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500542 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000543 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000544 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000545 table = {}
546 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500547 converters = self.getConverters()
548 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000549 if conv.name == "SubTable":
550 conv = conv.getConverter(reader.tableType,
551 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000552 if conv.name == "ExtSubTable":
553 conv = conv.getConverter(reader.tableType,
554 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000555 if conv.repeat:
556 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500557 if conv.repeat in table:
558 countValue = table[conv.repeat]
559 else:
560 # conv.repeat is a propagated count
561 countValue = reader.getCount(conv.repeat)
562 for i in range(countValue + conv.repeatOffset):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500563 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000564 table[conv.name] = l
565 else:
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500566 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500567 if conv.isPropagatedCount:
Behdad Esfahbod79817042013-11-24 16:59:42 -0500568 reader.setCount(conv.name, table[conv.name])
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500569
jvrd4d15132002-05-11 00:59:27 +0000570 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500571
jvrd4d15132002-05-11 00:59:27 +0000572 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000573
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500574 def ensureDecompiled(self):
575 if self.compileStatus != 1:
576 return
Behdad Esfahbodc491f452013-11-24 18:28:20 -0500577 subReader = self.reader.getSubReader(self.offset)
578 self.decompile(subReader, self.font)
579 del self.reader, self.font, self.offset
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500580
jvr823f8cd2006-10-21 14:12:38 +0000581 def preCompile(self):
582 pass # used only by the LookupList class
583
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500584 def compile(self, writer, font):
jvrd4d15132002-05-11 00:59:27 +0000585 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000586
587 if hasattr(self, 'sortCoverageLast'):
588 writer.sortCoverageLast = 1
589
jvrf7ef96c2002-09-10 19:26:38 +0000590 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000591 for conv in self.getConverters():
592 value = table.get(conv.name)
593 if conv.repeat:
594 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000595 value = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500596 countValue = len(value) - conv.repeatOffset
597 if conv.repeat in table:
598 ref = table[conv.repeat]
599 table[conv.repeat] = None
600 ref.setValue(countValue)
601 else:
602 # conv.repeat is a propagated count
603 writer.getCount(conv.repeat).setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000604 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500605 conv.write(writer, font, table, 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
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500609 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000610 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500611 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000612 # We add a reference: by the time the data is assembled
613 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500614 ref = writer.writeCountReference(table, conv.name)
615 if conv.isPropagatedCount:
616 table[conv.name] = None
617 writer.setCount(conv.name, ref)
618 else:
619 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000620 else:
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500621 conv.write(writer, font, table, value)
jvrd4d15132002-05-11 00:59:27 +0000622
jvrf7ef96c2002-09-10 19:26:38 +0000623 def readFormat(self, reader):
624 pass
625
626 def writeFormat(self, writer):
627 pass
628
jvrd4d15132002-05-11 00:59:27 +0000629 def postRead(self, table, font):
630 self.__dict__.update(table)
631
632 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500633 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000634 return self.__dict__.copy()
635
636 def toXML(self, xmlWriter, font, attrs=None):
637 tableName = self.__class__.__name__
638 if attrs is None:
639 attrs = []
640 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000641 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000642 xmlWriter.begintag(tableName, attrs)
643 xmlWriter.newline()
644 self.toXML2(xmlWriter, font)
645 xmlWriter.endtag(tableName)
646 xmlWriter.newline()
647
648 def toXML2(self, xmlWriter, font):
649 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
650 # This is because in TTX our parent writes our main tag, and in otBase.py we
651 # do it ourselves. I think I'm getting schizophrenic...
652 for conv in self.getConverters():
653 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000654 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000655 for i in range(len(value)):
656 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000657 conv.xmlWrite(xmlWriter, font, item, conv.name,
658 [("index", i)])
659 else:
660 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000661
662 def fromXML(self, (name, attrs, content), font):
663 try:
664 conv = self.getConverterByName(name)
665 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000666 raise # XXX on KeyError, raise nice error
667 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000668 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000669 seq = getattr(self, conv.name, None)
670 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000671 seq = []
jvr64b5c802002-05-11 10:21:36 +0000672 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000673 seq.append(value)
674 else:
jvr64b5c802002-05-11 10:21:36 +0000675 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000676
677 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100678 if type(self) != type(other): return cmp(type(self), type(other))
679 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400680
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500681 self.ensureDecompiled()
682
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400683 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000684
685
686class FormatSwitchingBaseTable(BaseTable):
687
jvrcfadfd02002-07-22 22:13:57 +0000688 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000689 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
690
jvrd4d15132002-05-11 00:59:27 +0000691 def getConverters(self):
692 return self.converters[self.Format]
693
694 def getConverterByName(self, name):
695 return self.convertersByName[self.Format][name]
696
jvrf7ef96c2002-09-10 19:26:38 +0000697 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000698 self.Format = reader.readUShort()
699 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000700
jvrf7ef96c2002-09-10 19:26:38 +0000701 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000702 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000703
704
jvr64b5c802002-05-11 10:21:36 +0000705#
706# Support for ValueRecords
707#
708# This data type is so different from all other OpenType data types that
709# it requires quite a bit of code for itself. It even has special support
710# in OTTableReader and OTTableWriter...
711#
712
jvrd4d15132002-05-11 00:59:27 +0000713valueRecordFormat = [
714# Mask Name isDevice signed
715 (0x0001, "XPlacement", 0, 1),
716 (0x0002, "YPlacement", 0, 1),
717 (0x0004, "XAdvance", 0, 1),
718 (0x0008, "YAdvance", 0, 1),
719 (0x0010, "XPlaDevice", 1, 0),
720 (0x0020, "YPlaDevice", 1, 0),
721 (0x0040, "XAdvDevice", 1, 0),
722 (0x0080, "YAdvDevice", 1, 0),
723# reserved:
724 (0x0100, "Reserved1", 0, 0),
725 (0x0200, "Reserved2", 0, 0),
726 (0x0400, "Reserved3", 0, 0),
727 (0x0800, "Reserved4", 0, 0),
728 (0x1000, "Reserved5", 0, 0),
729 (0x2000, "Reserved6", 0, 0),
730 (0x4000, "Reserved7", 0, 0),
731 (0x8000, "Reserved8", 0, 0),
732]
733
734def _buildDict():
735 d = {}
736 for mask, name, isDevice, signed in valueRecordFormat:
737 d[name] = mask, isDevice, signed
738 return d
739
740valueRecordFormatDict = _buildDict()
741
742
743class ValueRecordFactory:
744
jvr64b5c802002-05-11 10:21:36 +0000745 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500746
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500747 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000748 format = []
749 for mask, name, isDevice, signed in valueRecordFormat:
750 if valueFormat & mask:
751 format.append((name, isDevice, signed))
752 self.format = format
753
754 def readValueRecord(self, reader, font):
755 format = self.format
756 if not format:
757 return None
758 valueRecord = ValueRecord()
759 for name, isDevice, signed in format:
760 if signed:
761 value = reader.readShort()
762 else:
763 value = reader.readUShort()
764 if isDevice:
765 if value:
766 import otTables
767 subReader = reader.getSubReader(value)
768 value = getattr(otTables, name)()
769 value.decompile(subReader, font)
770 else:
771 value = None
772 setattr(valueRecord, name, value)
773 return valueRecord
774
775 def writeValueRecord(self, writer, font, valueRecord):
776 for name, isDevice, signed in self.format:
777 value = getattr(valueRecord, name, 0)
778 if isDevice:
779 if value:
780 subWriter = writer.getSubWriter()
781 writer.writeSubTable(subWriter)
782 value.compile(subWriter, font)
783 else:
784 writer.writeUShort(0)
785 elif signed:
786 writer.writeShort(value)
787 else:
788 writer.writeUShort(value)
789
790
791class ValueRecord:
792
793 # see ValueRecordFactory
794
795 def getFormat(self):
796 format = 0
797 for name in self.__dict__.keys():
798 format = format | valueRecordFormatDict[name][0]
799 return format
800
801 def toXML(self, xmlWriter, font, valueName, attrs=None):
802 if attrs is None:
803 simpleItems = []
804 else:
805 simpleItems = list(attrs)
806 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
807 if hasattr(self, name):
808 simpleItems.append((name, getattr(self, name)))
809 deviceItems = []
810 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
811 if hasattr(self, name):
812 device = getattr(self, name)
813 if device is not None:
814 deviceItems.append((name, device))
815 if deviceItems:
816 xmlWriter.begintag(valueName, simpleItems)
817 xmlWriter.newline()
818 for name, deviceRecord in deviceItems:
819 if deviceRecord is not None:
820 deviceRecord.toXML(xmlWriter, font)
821 xmlWriter.endtag(valueName)
822 xmlWriter.newline()
823 else:
824 xmlWriter.simpletag(valueName, simpleItems)
825 xmlWriter.newline()
826
827 def fromXML(self, (name, attrs, content), font):
828 import otTables
829 for k, v in attrs.items():
830 setattr(self, k, int(v))
831 for element in content:
832 if type(element) <> TupleType:
833 continue
834 name, attrs, content = element
835 value = getattr(otTables, name)()
836 for elem2 in content:
837 if type(elem2) <> TupleType:
838 continue
839 value.fromXML(elem2, font)
840 setattr(self, name, value)
841
842 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100843 if type(self) != type(other): return cmp(type(self), type(other))
844 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000845
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400846 return cmp(self.__dict__, other.__dict__)