blob: 0044603383bcbffbc05e8f6b089e7b7d049a1fe6 [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 Esfahbodd01c44a2013-11-22 15:21:41 -0500104 def getSubReader(self, offset, persistent=False):
jvrd4d15132002-05-11 00:59:27 +0000105 offset = self.offset + offset
106 if self.cachingStats is not None:
107 try:
108 self.cachingStats[offset] = self.cachingStats[offset] + 1
109 except KeyError:
110 self.cachingStats[offset] = 1
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500111
jvrd4d15132002-05-11 00:59:27 +0000112 subReader = self.__class__(self.data, self.tableType, offset,
Behdad Esfahbod79817042013-11-24 16:59:42 -0500113 self.valueFormat, self.counts, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000114 return subReader
Behdad Esfahbod79817042013-11-24 16:59:42 -0500115
jvrd4d15132002-05-11 00:59:27 +0000116 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500122
jvrd4d15132002-05-11 00:59:27 +0000123 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500129
jvrd4d15132002-05-11 00:59:27 +0000130 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500136
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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500151
jvrd4d15132002-05-11 00:59:27 +0000152 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500162
jvrd4d15132002-05-11 00:59:27 +0000163 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500164 self.valueFormat[which] = ValueRecordFactory(format)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500165
jvrd4d15132002-05-11 00:59:27 +0000166 def readValueRecord(self, font, which):
167 return self.valueFormat[which].readValueRecord(self, font)
168
Behdad Esfahbod79817042013-11-24 16:59:42 -0500169 def setCount(self, name, value):
170 self.counts = self.counts.copy() if self.counts else dict()
171 self.counts[name] = value
172
173 def getCount(self, name):
174 return self.counts[name]
175
jvrd4d15132002-05-11 00:59:27 +0000176
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500177class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000178
jvr64b5c802002-05-11 10:21:36 +0000179 """Helper class to gather and assemble data for OpenType tables."""
180
Behdad Esfahbod79817042013-11-24 16:59:42 -0500181 def __init__(self, tableType, valueFormat=None, counts=None):
jvrd4d15132002-05-11 00:59:27 +0000182 self.items = []
183 self.tableType = tableType
184 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500185 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000186 self.valueFormat = valueFormat
Behdad Esfahbod79817042013-11-24 16:59:42 -0500187 self.counts = None
jvrcfadfd02002-07-22 22:13:57 +0000188 self.pos = None
Behdad Esfahbod79817042013-11-24 16:59:42 -0500189
190 def setValueFormat(self, format, which):
191 self.valueFormat[which] = ValueRecordFactory(format)
192
193 def setCount(self, name, value):
194 self.counts = self.counts.copy() if self.counts else dict()
195 self.counts[name] = value
196
197 def getCount(self, name):
198 return self.counts[name]
199
jvr4105ca02002-07-23 08:43:03 +0000200 # assembler interface
201
202 def getAllData(self):
203 """Assemble all data, including all subtables."""
204 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000205 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000206 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000207 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000208 # Gather all data in two passes: the absolute positions of all
209 # subtable are needed before the actual data can be assembled.
210 pos = 0
211 for table in tables:
212 table.pos = pos
213 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000214
215 for table in extTables:
216 table.pos = pos
217 pos = pos + table.getDataLength()
218
219
jvr4105ca02002-07-23 08:43:03 +0000220 data = []
221 for table in tables:
222 tableData = table.getData()
223 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000224
225 for table in extTables:
226 tableData = table.getData()
227 data.append(tableData)
228
jvr4105ca02002-07-23 08:43:03 +0000229 return "".join(data)
230
231 def getDataLength(self):
232 """Return the length of this table in bytes, without subtables."""
233 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000234 if hasattr(self, "Extension"):
235 longOffset = 1
236 else:
237 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000238 for item in self.items:
239 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000240 if longOffset:
241 l = l + 4 # sizeof(ULong)
242 else:
243 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000244 else:
245 l = l + len(item)
246 return l
247
248 def getData(self):
249 """Assemble the data for this writer/table, without subtables."""
250 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000251 if hasattr(self,"Extension"):
252 longOffset = 1
253 else:
254 longOffset = 0
255 pos = self.pos
256 numItems = len(items)
257 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000258 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000259
jvr4105ca02002-07-23 08:43:03 +0000260 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000261 if longOffset:
262 items[i] = packULong(item.pos - pos)
263 else:
264 try:
265 items[i] = packUShort(item.pos - pos)
266 except AssertionError:
267 # provide data to fix overflow problem.
268 # If the overflow is to a lookup, or from a lookup to a subtable,
269 # just report the current item.
270 if self.name in [ 'LookupList', 'Lookup']:
271 overflowErrorRecord = self.getOverflowErrorRecord(item)
272 else:
273 # overflow is within a subTable. Life is more complicated.
274 # If we split the sub-table just before the current item, we may still suffer overflow.
275 # This is because duplicate table merging is done only within an Extension subTable tree;
276 # when we split the subtable in two, some items may no longer be duplicates.
277 # Get worst case by adding up all the item lengths, depth first traversal.
278 # and then report the first item that overflows a short.
279 def getDeepItemLength(table):
280 if hasattr(table, "getDataLength"):
281 length = 0
282 for item in table.items:
283 length = length + getDeepItemLength(item)
284 else:
285 length = len(table)
286 return length
287
288 length = self.getDataLength()
289 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
290 # Coverage is first in the item list, but last in the table list,
291 # The original overflow is really in the item list. Skip the Coverage
292 # table in the following test.
293 items = items[i+1:]
294
295 for j in range(len(items)):
296 item = items[j]
297 length = length + getDeepItemLength(item)
298 if length > 65535:
299 break
300 overflowErrorRecord = self.getOverflowErrorRecord(item)
301
302
303 raise OTLOffsetOverflowError, overflowErrorRecord
304
jvr4105ca02002-07-23 08:43:03 +0000305 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000306
jvrcfadfd02002-07-22 22:13:57 +0000307 def __hash__(self):
308 # only works after self._doneWriting() has been called
309 return hash(self.items)
310
311 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100312 if type(self) != type(other): return cmp(type(self), type(other))
313 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400314
315 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000316
jvrcfadfd02002-07-22 22:13:57 +0000317 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000318 # Convert CountData references to data string items
319 # collapse duplicate table references to a unique entry
320 # "tables" are OTTableWriter objects.
321
322 # For Extension Lookup types, we can
323 # eliminate duplicates only within the tree under the Extension Lookup,
324 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000325 if internedTables is None:
326 internedTables = {}
327 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000328 iRange = range(len(items))
329
330 if hasattr(self, "Extension"):
331 newTree = 1
332 else:
333 newTree = 0
334 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000335 item = items[i]
336 if hasattr(item, "getCountData"):
337 items[i] = item.getCountData()
338 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000339 if newTree:
340 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000341 else:
jvr823f8cd2006-10-21 14:12:38 +0000342 item._doneWriting(internedTables)
343 if internedTables.has_key(item):
344 items[i] = item = internedTables[item]
345 else:
346 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000347 self.items = tuple(items)
348
jvr823f8cd2006-10-21 14:12:38 +0000349 def _gatherTables(self, tables=None, extTables=None, done=None):
350 # Convert table references in self.items tree to a flat
351 # list of tables in depth-first traversal order.
352 # "tables" are OTTableWriter objects.
353 # We do the traversal in reverse order at each level, in order to
354 # resolve duplicate references to be the last reference in the list of tables.
355 # For extension lookups, duplicate references can be merged only within the
356 # writer tree under the extension lookup.
357 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000358 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000359 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000360 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000361
362 done[self] = 1
363
364 numItems = len(self.items)
365 iRange = range(numItems)
366 iRange.reverse()
367
368 if hasattr(self, "Extension"):
369 appendExtensions = 1
370 else:
371 appendExtensions = 0
372
373 # add Coverage table if it is sorted last.
374 sortCoverageLast = 0
375 if hasattr(self, "sortCoverageLast"):
376 # Find coverage table
377 for i in range(numItems):
378 item = self.items[i]
379 if hasattr(item, "name") and (item.name == "Coverage"):
380 sortCoverageLast = 1
381 break
382 if not done.has_key(item):
383 item._gatherTables(tables, extTables, done)
384 else:
385 index = max(item.parent.keys())
386 item.parent[index + 1] = self
387
388 saveItem = None
389 for i in iRange:
390 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000391 if not hasattr(item, "getData"):
392 continue
jvr823f8cd2006-10-21 14:12:38 +0000393
394 if sortCoverageLast and (i==1) and item.name == 'Coverage':
395 # we've already 'gathered' it above
396 continue
397
398 if appendExtensions:
399 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
400 newDone = {}
401 item._gatherTables(extTables, None, newDone)
402
403 elif not done.has_key(item):
404 item._gatherTables(tables, extTables, done)
405 else:
406 index = max(item.parent.keys())
407 item.parent[index + 1] = self
408
409
jvrcfadfd02002-07-22 22:13:57 +0000410 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000411 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000412
jvr4105ca02002-07-23 08:43:03 +0000413 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000414
jvr4105ca02002-07-23 08:43:03 +0000415 def getSubWriter(self):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500416 subwriter = self.__class__(self.tableType, self.valueFormat, self.counts)
jvr823f8cd2006-10-21 14:12:38 +0000417 subwriter.parent = {0:self} # because some subtables have idential values, we discard
418 # the duplicates under the getAllData method. Hence some
419 # subtable writers can have more than one parent writer.
420 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000421
422 def writeUShort(self, value):
423 assert 0 <= value < 0x10000
424 self.items.append(struct.pack(">H", value))
425
426 def writeShort(self, value):
427 self.items.append(struct.pack(">h", value))
428
429 def writeLong(self, value):
430 self.items.append(struct.pack(">l", value))
431
jvr823f8cd2006-10-21 14:12:38 +0000432 def writeULong(self, value):
433 self.items.append(struct.pack(">L", value))
434
jvrd4d15132002-05-11 00:59:27 +0000435 def writeTag(self, tag):
436 assert len(tag) == 4
437 self.items.append(tag)
438
439 def writeSubTable(self, subWriter):
440 self.items.append(subWriter)
441
442 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500443 ref = CountReference(table, name)
444 self.items.append(ref)
445 self.setCount(name, ref)
jvrd4d15132002-05-11 00:59:27 +0000446
447 def writeStruct(self, format, values):
448 data = apply(struct.pack, (format,) + values)
449 self.items.append(data)
450
jvr823f8cd2006-10-21 14:12:38 +0000451 def writeData(self, data):
452 self.items.append(data)
453
jvrd4d15132002-05-11 00:59:27 +0000454 def writeValueRecord(self, value, font, which):
455 return self.valueFormat[which].writeValueRecord(self, font, value)
456
jvr823f8cd2006-10-21 14:12:38 +0000457 def getOverflowErrorRecord(self, item):
458 LookupListIndex = SubTableIndex = itemName = itemIndex = None
459 if self.name == 'LookupList':
460 LookupListIndex = item.repeatIndex
461 elif self.name == 'Lookup':
462 LookupListIndex = self.repeatIndex
463 SubTableIndex = item.repeatIndex
464 else:
465 itemName = item.name
466 if hasattr(item, 'repeatIndex'):
467 itemIndex = item.repeatIndex
468 if self.name == 'SubTable':
469 LookupListIndex = self.parent[0].repeatIndex
470 SubTableIndex = self.repeatIndex
471 elif self.name == 'ExtSubTable':
472 LookupListIndex = self.parent[0].parent[0].repeatIndex
473 SubTableIndex = self.parent[0].repeatIndex
474 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
475 itemName = ".".join(self.name, item.name)
476 p1 = self.parent[0]
477 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
478 itemName = ".".join(p1.name, item.name)
479 p1 = p1.parent[0]
480 if p1:
481 if p1.name == 'ExtSubTable':
482 LookupListIndex = self.parent[0].parent[0].repeatIndex
483 SubTableIndex = self.parent[0].repeatIndex
484 else:
485 LookupListIndex = self.parent[0].repeatIndex
486 SubTableIndex = self.repeatIndex
487
488 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
489
jvrd4d15132002-05-11 00:59:27 +0000490
491class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000492 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000493 def __init__(self, table, name):
494 self.table = table
495 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500496 def setValue(self, value):
497 table = self.table
498 name = self.name
499 if table[name] is None:
500 table[name] = value
501 else:
502 assert table[name] == value, (table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000503 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000504 return packUShort(self.table[self.name])
505
506
jvr64b5c802002-05-11 10:21:36 +0000507def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000508 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000509 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000510
511
jvr823f8cd2006-10-21 14:12:38 +0000512def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000513 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000514 return struct.pack(">L", value)
515
516
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500517class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000518 def __init__(self):
519 self.compileStatus = 0 # 0 means table was created
520 # 1 means the table.read() function was called by a table which is subject
521 # to delayed compilation
522 # 2 means that it was subject to delayed compilation, and
523 # has been decompiled
524 # 3 means that the start and end fields have been filled out, and that we
525 # can use the data string rather than compiling from the table data.
526
527 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000528
jvr823f8cd2006-10-21 14:12:38 +0000529 def __getattr__(self, attr):
530 # we get here only when the table does not have the attribute.
531 # This method ovveride exists so that we can try to de-compile
532 # a table which is subject to delayed decompilation, and then try
533 # to get the value again after decompilation.
534 self.recurse +=1
535 if self.recurse > 2:
536 # shouldn't ever get here - we should only get to two levels of recursion.
537 # this guards against self.decompile NOT setting compileStatus to other than 1.
538 raise AttributeError, attr
539 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500540 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000541 val = getattr(self, attr)
542 self.recurse -=1
543 return val
544
545 raise AttributeError, attr
546
547
jvr64b5c802002-05-11 10:21:36 +0000548 """Generic base class for all OpenType (sub)tables."""
549
jvrd4d15132002-05-11 00:59:27 +0000550 def getConverters(self):
551 return self.converters
552
553 def getConverterByName(self, name):
554 return self.convertersByName[name]
555
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500556 def decompile(self, reader, font, countVars=None):
jvr823f8cd2006-10-21 14:12:38 +0000557 self.compileStatus = 2 # table has been decompiled.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500558 if countVars is None:
559 countVars = {}
jvrf7ef96c2002-09-10 19:26:38 +0000560 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000561 table = {}
562 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500563 converters = self.getConverters()
564 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000565 if conv.name == "SubTable":
566 conv = conv.getConverter(reader.tableType,
567 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000568 if conv.name == "ExtSubTable":
569 conv = conv.getConverter(reader.tableType,
570 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000571 if conv.repeat:
572 l = []
Behdad Esfahbod79817042013-11-24 16:59:42 -0500573 for i in range(reader.getCount(conv.repeat) + conv.repeatOffset):
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500574 l.append(conv.read(reader, font, countVars))
jvrd4d15132002-05-11 00:59:27 +0000575 table[conv.name] = l
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500576
jvrd4d15132002-05-11 00:59:27 +0000577 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500578 table[conv.name] = conv.read(reader, font, countVars)
Behdad Esfahbod6f9b64f2013-11-24 16:09:57 -0500579 if conv.isCount or conv.isSize:
Behdad Esfahbod79817042013-11-24 16:59:42 -0500580 reader.setCount(conv.name, table[conv.name])
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500581
jvrd4d15132002-05-11 00:59:27 +0000582 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500583
jvrd4d15132002-05-11 00:59:27 +0000584 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000585
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500586 def ensureDecompiled(self):
587 if self.compileStatus != 1:
588 return
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500589 self.decompile(self.reader, self.font, self.countVars)
590 del self.reader, self.font, self.countVars
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500591
jvr823f8cd2006-10-21 14:12:38 +0000592 def preCompile(self):
593 pass # used only by the LookupList class
594
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500595 def compile(self, writer, font, countVars=None):
596 if countVars is None:
597 countVars = {}
jvrd4d15132002-05-11 00:59:27 +0000598 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000599
600 if hasattr(self, 'sortCoverageLast'):
601 writer.sortCoverageLast = 1
602
jvrf7ef96c2002-09-10 19:26:38 +0000603 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000604 for conv in self.getConverters():
605 value = table.get(conv.name)
606 if conv.repeat:
607 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000608 value = []
Behdad Esfahbod79817042013-11-24 16:59:42 -0500609 writer.getCount(conv.repeat).setValue(len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000610 for i in range(len(value)):
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500611 conv.write(writer, font, countVars, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000612 elif conv.isCount:
613 # Special-case Count values.
614 # Assumption: a Count field will *always* precede
615 # the actual array.
616 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500617 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000618 table[conv.name] = None
619 # We add a reference: by the time the data is assembled
620 # the Count value will be filled in.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500621 name = conv.name
622 writer.writeCountReference(table, name)
jvrd4d15132002-05-11 00:59:27 +0000623 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500624 conv.write(writer, font, countVars, value)
Behdad Esfahbod6f9b64f2013-11-24 16:09:57 -0500625 if conv.isSize:
Behdad Esfahbod79817042013-11-24 16:59:42 -0500626 writer.setCount(conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000627
jvrf7ef96c2002-09-10 19:26:38 +0000628 def readFormat(self, reader):
629 pass
630
631 def writeFormat(self, writer):
632 pass
633
jvrd4d15132002-05-11 00:59:27 +0000634 def postRead(self, table, font):
635 self.__dict__.update(table)
636
637 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500638 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000639 return self.__dict__.copy()
640
641 def toXML(self, xmlWriter, font, attrs=None):
642 tableName = self.__class__.__name__
643 if attrs is None:
644 attrs = []
645 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000646 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000647 xmlWriter.begintag(tableName, attrs)
648 xmlWriter.newline()
649 self.toXML2(xmlWriter, font)
650 xmlWriter.endtag(tableName)
651 xmlWriter.newline()
652
653 def toXML2(self, xmlWriter, font):
654 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
655 # This is because in TTX our parent writes our main tag, and in otBase.py we
656 # do it ourselves. I think I'm getting schizophrenic...
657 for conv in self.getConverters():
658 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000659 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000660 for i in range(len(value)):
661 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000662 conv.xmlWrite(xmlWriter, font, item, conv.name,
663 [("index", i)])
664 else:
665 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000666
667 def fromXML(self, (name, attrs, content), font):
668 try:
669 conv = self.getConverterByName(name)
670 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000671 raise # XXX on KeyError, raise nice error
672 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000673 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000674 seq = getattr(self, conv.name, None)
675 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000676 seq = []
jvr64b5c802002-05-11 10:21:36 +0000677 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000678 seq.append(value)
679 else:
jvr64b5c802002-05-11 10:21:36 +0000680 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000681
682 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100683 if type(self) != type(other): return cmp(type(self), type(other))
684 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400685
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500686 self.ensureDecompiled()
687
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400688 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000689
690
691class FormatSwitchingBaseTable(BaseTable):
692
jvrcfadfd02002-07-22 22:13:57 +0000693 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000694 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
695
jvrd4d15132002-05-11 00:59:27 +0000696 def getConverters(self):
697 return self.converters[self.Format]
698
699 def getConverterByName(self, name):
700 return self.convertersByName[self.Format][name]
701
jvrf7ef96c2002-09-10 19:26:38 +0000702 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000703 self.Format = reader.readUShort()
704 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000705
jvrf7ef96c2002-09-10 19:26:38 +0000706 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000707 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000708
709
jvr64b5c802002-05-11 10:21:36 +0000710#
711# Support for ValueRecords
712#
713# This data type is so different from all other OpenType data types that
714# it requires quite a bit of code for itself. It even has special support
715# in OTTableReader and OTTableWriter...
716#
717
jvrd4d15132002-05-11 00:59:27 +0000718valueRecordFormat = [
719# Mask Name isDevice signed
720 (0x0001, "XPlacement", 0, 1),
721 (0x0002, "YPlacement", 0, 1),
722 (0x0004, "XAdvance", 0, 1),
723 (0x0008, "YAdvance", 0, 1),
724 (0x0010, "XPlaDevice", 1, 0),
725 (0x0020, "YPlaDevice", 1, 0),
726 (0x0040, "XAdvDevice", 1, 0),
727 (0x0080, "YAdvDevice", 1, 0),
728# reserved:
729 (0x0100, "Reserved1", 0, 0),
730 (0x0200, "Reserved2", 0, 0),
731 (0x0400, "Reserved3", 0, 0),
732 (0x0800, "Reserved4", 0, 0),
733 (0x1000, "Reserved5", 0, 0),
734 (0x2000, "Reserved6", 0, 0),
735 (0x4000, "Reserved7", 0, 0),
736 (0x8000, "Reserved8", 0, 0),
737]
738
739def _buildDict():
740 d = {}
741 for mask, name, isDevice, signed in valueRecordFormat:
742 d[name] = mask, isDevice, signed
743 return d
744
745valueRecordFormatDict = _buildDict()
746
747
748class ValueRecordFactory:
749
jvr64b5c802002-05-11 10:21:36 +0000750 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500751
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500752 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000753 format = []
754 for mask, name, isDevice, signed in valueRecordFormat:
755 if valueFormat & mask:
756 format.append((name, isDevice, signed))
757 self.format = format
758
759 def readValueRecord(self, reader, font):
760 format = self.format
761 if not format:
762 return None
763 valueRecord = ValueRecord()
764 for name, isDevice, signed in format:
765 if signed:
766 value = reader.readShort()
767 else:
768 value = reader.readUShort()
769 if isDevice:
770 if value:
771 import otTables
772 subReader = reader.getSubReader(value)
773 value = getattr(otTables, name)()
774 value.decompile(subReader, font)
775 else:
776 value = None
777 setattr(valueRecord, name, value)
778 return valueRecord
779
780 def writeValueRecord(self, writer, font, valueRecord):
781 for name, isDevice, signed in self.format:
782 value = getattr(valueRecord, name, 0)
783 if isDevice:
784 if value:
785 subWriter = writer.getSubWriter()
786 writer.writeSubTable(subWriter)
787 value.compile(subWriter, font)
788 else:
789 writer.writeUShort(0)
790 elif signed:
791 writer.writeShort(value)
792 else:
793 writer.writeUShort(value)
794
795
796class ValueRecord:
797
798 # see ValueRecordFactory
799
800 def getFormat(self):
801 format = 0
802 for name in self.__dict__.keys():
803 format = format | valueRecordFormatDict[name][0]
804 return format
805
806 def toXML(self, xmlWriter, font, valueName, attrs=None):
807 if attrs is None:
808 simpleItems = []
809 else:
810 simpleItems = list(attrs)
811 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
812 if hasattr(self, name):
813 simpleItems.append((name, getattr(self, name)))
814 deviceItems = []
815 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
816 if hasattr(self, name):
817 device = getattr(self, name)
818 if device is not None:
819 deviceItems.append((name, device))
820 if deviceItems:
821 xmlWriter.begintag(valueName, simpleItems)
822 xmlWriter.newline()
823 for name, deviceRecord in deviceItems:
824 if deviceRecord is not None:
825 deviceRecord.toXML(xmlWriter, font)
826 xmlWriter.endtag(valueName)
827 xmlWriter.newline()
828 else:
829 xmlWriter.simpletag(valueName, simpleItems)
830 xmlWriter.newline()
831
832 def fromXML(self, (name, attrs, content), font):
833 import otTables
834 for k, v in attrs.items():
835 setattr(self, k, int(v))
836 for element in content:
837 if type(element) <> TupleType:
838 continue
839 name, attrs, content = element
840 value = getattr(otTables, name)()
841 for elem2 in content:
842 if type(elem2) <> TupleType:
843 continue
844 value.fromXML(elem2, font)
845 setattr(self, name, value)
846
847 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100848 if type(self) != type(other): return cmp(type(self), type(other))
849 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000850
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400851 return cmp(self.__dict__, other.__dict__)