blob: 4fcc411a7f9080492c369dba5568abc7ee796611 [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 readStruct(self, format, size=None):
150 if size is None:
151 size = struct.calcsize(format)
152 else:
153 assert size == struct.calcsize(format)
154 pos = self.pos
155 newpos = pos + size
156 values = struct.unpack(format, self.data[pos:newpos])
157 self.pos = newpos
158 return values
Behdad Esfahbod79817042013-11-24 16:59:42 -0500159
jvrd4d15132002-05-11 00:59:27 +0000160 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500161 self.valueFormat[which] = ValueRecordFactory(format)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500162
jvrd4d15132002-05-11 00:59:27 +0000163 def readValueRecord(self, font, which):
164 return self.valueFormat[which].readValueRecord(self, font)
165
Behdad Esfahbod79817042013-11-24 16:59:42 -0500166 def setCount(self, name, value):
167 self.counts = self.counts.copy() if self.counts else dict()
168 self.counts[name] = value
169
170 def getCount(self, name):
171 return self.counts[name]
172
jvrd4d15132002-05-11 00:59:27 +0000173
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500174class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000175
jvr64b5c802002-05-11 10:21:36 +0000176 """Helper class to gather and assemble data for OpenType tables."""
177
Behdad Esfahbod79817042013-11-24 16:59:42 -0500178 def __init__(self, tableType, valueFormat=None, counts=None):
jvrd4d15132002-05-11 00:59:27 +0000179 self.items = []
180 self.tableType = tableType
181 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500182 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000183 self.valueFormat = valueFormat
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500184 self.counts = counts
jvrcfadfd02002-07-22 22:13:57 +0000185 self.pos = None
Behdad Esfahbod79817042013-11-24 16:59:42 -0500186
187 def setValueFormat(self, format, which):
188 self.valueFormat[which] = ValueRecordFactory(format)
189
190 def setCount(self, name, value):
191 self.counts = self.counts.copy() if self.counts else dict()
192 self.counts[name] = value
193
194 def getCount(self, name):
195 return self.counts[name]
196
jvr4105ca02002-07-23 08:43:03 +0000197 # assembler interface
198
199 def getAllData(self):
200 """Assemble all data, including all subtables."""
201 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000202 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000203 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000204 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000205 # Gather all data in two passes: the absolute positions of all
206 # subtable are needed before the actual data can be assembled.
207 pos = 0
208 for table in tables:
209 table.pos = pos
210 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000211
212 for table in extTables:
213 table.pos = pos
214 pos = pos + table.getDataLength()
215
216
jvr4105ca02002-07-23 08:43:03 +0000217 data = []
218 for table in tables:
219 tableData = table.getData()
220 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000221
222 for table in extTables:
223 tableData = table.getData()
224 data.append(tableData)
225
jvr4105ca02002-07-23 08:43:03 +0000226 return "".join(data)
227
228 def getDataLength(self):
229 """Return the length of this table in bytes, without subtables."""
230 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000231 if hasattr(self, "Extension"):
232 longOffset = 1
233 else:
234 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000235 for item in self.items:
236 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000237 if longOffset:
238 l = l + 4 # sizeof(ULong)
239 else:
240 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000241 else:
242 l = l + len(item)
243 return l
244
245 def getData(self):
246 """Assemble the data for this writer/table, without subtables."""
247 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000248 if hasattr(self,"Extension"):
249 longOffset = 1
250 else:
251 longOffset = 0
252 pos = self.pos
253 numItems = len(items)
254 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000255 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000256
jvr4105ca02002-07-23 08:43:03 +0000257 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000258 if longOffset:
259 items[i] = packULong(item.pos - pos)
260 else:
261 try:
262 items[i] = packUShort(item.pos - pos)
263 except AssertionError:
264 # provide data to fix overflow problem.
265 # If the overflow is to a lookup, or from a lookup to a subtable,
266 # just report the current item.
267 if self.name in [ 'LookupList', 'Lookup']:
268 overflowErrorRecord = self.getOverflowErrorRecord(item)
269 else:
270 # overflow is within a subTable. Life is more complicated.
271 # If we split the sub-table just before the current item, we may still suffer overflow.
272 # This is because duplicate table merging is done only within an Extension subTable tree;
273 # when we split the subtable in two, some items may no longer be duplicates.
274 # Get worst case by adding up all the item lengths, depth first traversal.
275 # and then report the first item that overflows a short.
276 def getDeepItemLength(table):
277 if hasattr(table, "getDataLength"):
278 length = 0
279 for item in table.items:
280 length = length + getDeepItemLength(item)
281 else:
282 length = len(table)
283 return length
284
285 length = self.getDataLength()
286 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
287 # Coverage is first in the item list, but last in the table list,
288 # The original overflow is really in the item list. Skip the Coverage
289 # table in the following test.
290 items = items[i+1:]
291
292 for j in range(len(items)):
293 item = items[j]
294 length = length + getDeepItemLength(item)
295 if length > 65535:
296 break
297 overflowErrorRecord = self.getOverflowErrorRecord(item)
298
299
300 raise OTLOffsetOverflowError, overflowErrorRecord
301
jvr4105ca02002-07-23 08:43:03 +0000302 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000303
jvrcfadfd02002-07-22 22:13:57 +0000304 def __hash__(self):
305 # only works after self._doneWriting() has been called
306 return hash(self.items)
307
308 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100309 if type(self) != type(other): return cmp(type(self), type(other))
310 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400311
312 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000313
jvrcfadfd02002-07-22 22:13:57 +0000314 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000315 # Convert CountData references to data string items
316 # collapse duplicate table references to a unique entry
317 # "tables" are OTTableWriter objects.
318
319 # For Extension Lookup types, we can
320 # eliminate duplicates only within the tree under the Extension Lookup,
321 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000322 if internedTables is None:
323 internedTables = {}
324 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000325 iRange = range(len(items))
326
327 if hasattr(self, "Extension"):
328 newTree = 1
329 else:
330 newTree = 0
331 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000332 item = items[i]
333 if hasattr(item, "getCountData"):
334 items[i] = item.getCountData()
335 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000336 if newTree:
337 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000338 else:
jvr823f8cd2006-10-21 14:12:38 +0000339 item._doneWriting(internedTables)
340 if internedTables.has_key(item):
341 items[i] = item = internedTables[item]
342 else:
343 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000344 self.items = tuple(items)
345
jvr823f8cd2006-10-21 14:12:38 +0000346 def _gatherTables(self, tables=None, extTables=None, done=None):
347 # Convert table references in self.items tree to a flat
348 # list of tables in depth-first traversal order.
349 # "tables" are OTTableWriter objects.
350 # We do the traversal in reverse order at each level, in order to
351 # resolve duplicate references to be the last reference in the list of tables.
352 # For extension lookups, duplicate references can be merged only within the
353 # writer tree under the extension lookup.
354 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000355 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000356 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000357 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000358
359 done[self] = 1
360
361 numItems = len(self.items)
362 iRange = range(numItems)
363 iRange.reverse()
364
365 if hasattr(self, "Extension"):
366 appendExtensions = 1
367 else:
368 appendExtensions = 0
369
370 # add Coverage table if it is sorted last.
371 sortCoverageLast = 0
372 if hasattr(self, "sortCoverageLast"):
373 # Find coverage table
374 for i in range(numItems):
375 item = self.items[i]
376 if hasattr(item, "name") and (item.name == "Coverage"):
377 sortCoverageLast = 1
378 break
379 if not done.has_key(item):
380 item._gatherTables(tables, extTables, done)
381 else:
382 index = max(item.parent.keys())
383 item.parent[index + 1] = self
384
385 saveItem = None
386 for i in iRange:
387 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000388 if not hasattr(item, "getData"):
389 continue
jvr823f8cd2006-10-21 14:12:38 +0000390
391 if sortCoverageLast and (i==1) and item.name == 'Coverage':
392 # we've already 'gathered' it above
393 continue
394
395 if appendExtensions:
396 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
397 newDone = {}
398 item._gatherTables(extTables, None, newDone)
399
400 elif not done.has_key(item):
401 item._gatherTables(tables, extTables, done)
402 else:
403 index = max(item.parent.keys())
404 item.parent[index + 1] = self
405
406
jvrcfadfd02002-07-22 22:13:57 +0000407 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000408 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000409
jvr4105ca02002-07-23 08:43:03 +0000410 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000411
jvr4105ca02002-07-23 08:43:03 +0000412 def getSubWriter(self):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500413 subwriter = self.__class__(self.tableType, self.valueFormat, self.counts)
jvr823f8cd2006-10-21 14:12:38 +0000414 subwriter.parent = {0:self} # because some subtables have idential values, we discard
415 # the duplicates under the getAllData method. Hence some
416 # subtable writers can have more than one parent writer.
417 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000418
419 def writeUShort(self, value):
420 assert 0 <= value < 0x10000
421 self.items.append(struct.pack(">H", value))
422
423 def writeShort(self, value):
424 self.items.append(struct.pack(">h", value))
425
426 def writeLong(self, value):
427 self.items.append(struct.pack(">l", value))
428
jvr823f8cd2006-10-21 14:12:38 +0000429 def writeULong(self, value):
430 self.items.append(struct.pack(">L", value))
431
jvrd4d15132002-05-11 00:59:27 +0000432 def writeTag(self, tag):
433 assert len(tag) == 4
434 self.items.append(tag)
435
436 def writeSubTable(self, subWriter):
437 self.items.append(subWriter)
438
439 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500440 ref = CountReference(table, name)
441 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500442 return ref
jvrd4d15132002-05-11 00:59:27 +0000443
444 def writeStruct(self, format, values):
445 data = apply(struct.pack, (format,) + values)
446 self.items.append(data)
447
jvr823f8cd2006-10-21 14:12:38 +0000448 def writeData(self, data):
449 self.items.append(data)
450
jvrd4d15132002-05-11 00:59:27 +0000451 def writeValueRecord(self, value, font, which):
452 return self.valueFormat[which].writeValueRecord(self, font, value)
453
jvr823f8cd2006-10-21 14:12:38 +0000454 def getOverflowErrorRecord(self, item):
455 LookupListIndex = SubTableIndex = itemName = itemIndex = None
456 if self.name == 'LookupList':
457 LookupListIndex = item.repeatIndex
458 elif self.name == 'Lookup':
459 LookupListIndex = self.repeatIndex
460 SubTableIndex = item.repeatIndex
461 else:
462 itemName = item.name
463 if hasattr(item, 'repeatIndex'):
464 itemIndex = item.repeatIndex
465 if self.name == 'SubTable':
466 LookupListIndex = self.parent[0].repeatIndex
467 SubTableIndex = self.repeatIndex
468 elif self.name == 'ExtSubTable':
469 LookupListIndex = self.parent[0].parent[0].repeatIndex
470 SubTableIndex = self.parent[0].repeatIndex
471 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
472 itemName = ".".join(self.name, item.name)
473 p1 = self.parent[0]
474 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
475 itemName = ".".join(p1.name, item.name)
476 p1 = p1.parent[0]
477 if p1:
478 if p1.name == 'ExtSubTable':
479 LookupListIndex = self.parent[0].parent[0].repeatIndex
480 SubTableIndex = self.parent[0].repeatIndex
481 else:
482 LookupListIndex = self.parent[0].repeatIndex
483 SubTableIndex = self.repeatIndex
484
485 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
486
jvrd4d15132002-05-11 00:59:27 +0000487
488class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000489 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000490 def __init__(self, table, name):
491 self.table = table
492 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500493 def setValue(self, value):
494 table = self.table
495 name = self.name
496 if table[name] is None:
497 table[name] = value
498 else:
499 assert table[name] == value, (table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000500 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000501 return packUShort(self.table[self.name])
502
503
jvr64b5c802002-05-11 10:21:36 +0000504def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000505 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000506 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000507
508
jvr823f8cd2006-10-21 14:12:38 +0000509def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000510 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000511 return struct.pack(">L", value)
512
513
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500514class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000515 def __init__(self):
516 self.compileStatus = 0 # 0 means table was created
517 # 1 means the table.read() function was called by a table which is subject
518 # to delayed compilation
519 # 2 means that it was subject to delayed compilation, and
520 # has been decompiled
521 # 3 means that the start and end fields have been filled out, and that we
522 # can use the data string rather than compiling from the table data.
523
524 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000525
jvr823f8cd2006-10-21 14:12:38 +0000526 def __getattr__(self, attr):
527 # we get here only when the table does not have the attribute.
528 # This method ovveride exists so that we can try to de-compile
529 # a table which is subject to delayed decompilation, and then try
530 # to get the value again after decompilation.
531 self.recurse +=1
532 if self.recurse > 2:
533 # shouldn't ever get here - we should only get to two levels of recursion.
534 # this guards against self.decompile NOT setting compileStatus to other than 1.
535 raise AttributeError, attr
536 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500537 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000538 val = getattr(self, attr)
539 self.recurse -=1
540 return val
541
542 raise AttributeError, attr
543
544
jvr64b5c802002-05-11 10:21:36 +0000545 """Generic base class for all OpenType (sub)tables."""
546
jvrd4d15132002-05-11 00:59:27 +0000547 def getConverters(self):
548 return self.converters
549
550 def getConverterByName(self, name):
551 return self.convertersByName[name]
552
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500553 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000554 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000555 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000556 table = {}
557 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500558 converters = self.getConverters()
559 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000560 if conv.name == "SubTable":
561 conv = conv.getConverter(reader.tableType,
562 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000563 if conv.name == "ExtSubTable":
564 conv = conv.getConverter(reader.tableType,
565 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000566 if conv.repeat:
567 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500568 if conv.repeat in table:
569 countValue = table[conv.repeat]
570 else:
571 # conv.repeat is a propagated count
572 countValue = reader.getCount(conv.repeat)
573 for i in range(countValue + conv.repeatOffset):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500574 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000575 table[conv.name] = l
576 else:
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500577 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500578 if conv.isPropagatedCount:
Behdad Esfahbod79817042013-11-24 16:59:42 -0500579 reader.setCount(conv.name, table[conv.name])
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500580
jvrd4d15132002-05-11 00:59:27 +0000581 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500582
jvrd4d15132002-05-11 00:59:27 +0000583 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000584
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500585 def ensureDecompiled(self):
586 if self.compileStatus != 1:
587 return
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500588 self.decompile(self.reader, self.font)
589 del self.reader, self.font
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500590
jvr823f8cd2006-10-21 14:12:38 +0000591 def preCompile(self):
592 pass # used only by the LookupList class
593
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500594 def compile(self, writer, font):
jvrd4d15132002-05-11 00:59:27 +0000595 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000596
597 if hasattr(self, 'sortCoverageLast'):
598 writer.sortCoverageLast = 1
599
jvrf7ef96c2002-09-10 19:26:38 +0000600 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000601 for conv in self.getConverters():
602 value = table.get(conv.name)
603 if conv.repeat:
604 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000605 value = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500606 countValue = len(value) - conv.repeatOffset
607 if conv.repeat in table:
608 ref = table[conv.repeat]
609 table[conv.repeat] = None
610 ref.setValue(countValue)
611 else:
612 # conv.repeat is a propagated count
613 writer.getCount(conv.repeat).setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000614 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500615 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000616 elif conv.isCount:
617 # Special-case Count values.
618 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500619 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000620 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500621 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000622 # We add a reference: by the time the data is assembled
623 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500624 ref = writer.writeCountReference(table, conv.name)
625 if conv.isPropagatedCount:
626 table[conv.name] = None
627 writer.setCount(conv.name, ref)
628 else:
629 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000630 else:
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500631 conv.write(writer, font, table, value)
jvrd4d15132002-05-11 00:59:27 +0000632
jvrf7ef96c2002-09-10 19:26:38 +0000633 def readFormat(self, reader):
634 pass
635
636 def writeFormat(self, writer):
637 pass
638
jvrd4d15132002-05-11 00:59:27 +0000639 def postRead(self, table, font):
640 self.__dict__.update(table)
641
642 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500643 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000644 return self.__dict__.copy()
645
646 def toXML(self, xmlWriter, font, attrs=None):
647 tableName = self.__class__.__name__
648 if attrs is None:
649 attrs = []
650 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000651 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000652 xmlWriter.begintag(tableName, attrs)
653 xmlWriter.newline()
654 self.toXML2(xmlWriter, font)
655 xmlWriter.endtag(tableName)
656 xmlWriter.newline()
657
658 def toXML2(self, xmlWriter, font):
659 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
660 # This is because in TTX our parent writes our main tag, and in otBase.py we
661 # do it ourselves. I think I'm getting schizophrenic...
662 for conv in self.getConverters():
663 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000664 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000665 for i in range(len(value)):
666 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000667 conv.xmlWrite(xmlWriter, font, item, conv.name,
668 [("index", i)])
669 else:
670 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000671
672 def fromXML(self, (name, attrs, content), font):
673 try:
674 conv = self.getConverterByName(name)
675 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000676 raise # XXX on KeyError, raise nice error
677 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000678 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000679 seq = getattr(self, conv.name, None)
680 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000681 seq = []
jvr64b5c802002-05-11 10:21:36 +0000682 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000683 seq.append(value)
684 else:
jvr64b5c802002-05-11 10:21:36 +0000685 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000686
687 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100688 if type(self) != type(other): return cmp(type(self), type(other))
689 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400690
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500691 self.ensureDecompiled()
692
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400693 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000694
695
696class FormatSwitchingBaseTable(BaseTable):
697
jvrcfadfd02002-07-22 22:13:57 +0000698 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000699 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
700
jvrd4d15132002-05-11 00:59:27 +0000701 def getConverters(self):
702 return self.converters[self.Format]
703
704 def getConverterByName(self, name):
705 return self.convertersByName[self.Format][name]
706
jvrf7ef96c2002-09-10 19:26:38 +0000707 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000708 self.Format = reader.readUShort()
709 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000710
jvrf7ef96c2002-09-10 19:26:38 +0000711 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000712 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000713
714
jvr64b5c802002-05-11 10:21:36 +0000715#
716# Support for ValueRecords
717#
718# This data type is so different from all other OpenType data types that
719# it requires quite a bit of code for itself. It even has special support
720# in OTTableReader and OTTableWriter...
721#
722
jvrd4d15132002-05-11 00:59:27 +0000723valueRecordFormat = [
724# Mask Name isDevice signed
725 (0x0001, "XPlacement", 0, 1),
726 (0x0002, "YPlacement", 0, 1),
727 (0x0004, "XAdvance", 0, 1),
728 (0x0008, "YAdvance", 0, 1),
729 (0x0010, "XPlaDevice", 1, 0),
730 (0x0020, "YPlaDevice", 1, 0),
731 (0x0040, "XAdvDevice", 1, 0),
732 (0x0080, "YAdvDevice", 1, 0),
733# reserved:
734 (0x0100, "Reserved1", 0, 0),
735 (0x0200, "Reserved2", 0, 0),
736 (0x0400, "Reserved3", 0, 0),
737 (0x0800, "Reserved4", 0, 0),
738 (0x1000, "Reserved5", 0, 0),
739 (0x2000, "Reserved6", 0, 0),
740 (0x4000, "Reserved7", 0, 0),
741 (0x8000, "Reserved8", 0, 0),
742]
743
744def _buildDict():
745 d = {}
746 for mask, name, isDevice, signed in valueRecordFormat:
747 d[name] = mask, isDevice, signed
748 return d
749
750valueRecordFormatDict = _buildDict()
751
752
753class ValueRecordFactory:
754
jvr64b5c802002-05-11 10:21:36 +0000755 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500756
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500757 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000758 format = []
759 for mask, name, isDevice, signed in valueRecordFormat:
760 if valueFormat & mask:
761 format.append((name, isDevice, signed))
762 self.format = format
763
764 def readValueRecord(self, reader, font):
765 format = self.format
766 if not format:
767 return None
768 valueRecord = ValueRecord()
769 for name, isDevice, signed in format:
770 if signed:
771 value = reader.readShort()
772 else:
773 value = reader.readUShort()
774 if isDevice:
775 if value:
776 import otTables
777 subReader = reader.getSubReader(value)
778 value = getattr(otTables, name)()
779 value.decompile(subReader, font)
780 else:
781 value = None
782 setattr(valueRecord, name, value)
783 return valueRecord
784
785 def writeValueRecord(self, writer, font, valueRecord):
786 for name, isDevice, signed in self.format:
787 value = getattr(valueRecord, name, 0)
788 if isDevice:
789 if value:
790 subWriter = writer.getSubWriter()
791 writer.writeSubTable(subWriter)
792 value.compile(subWriter, font)
793 else:
794 writer.writeUShort(0)
795 elif signed:
796 writer.writeShort(value)
797 else:
798 writer.writeUShort(value)
799
800
801class ValueRecord:
802
803 # see ValueRecordFactory
804
805 def getFormat(self):
806 format = 0
807 for name in self.__dict__.keys():
808 format = format | valueRecordFormatDict[name][0]
809 return format
810
811 def toXML(self, xmlWriter, font, valueName, attrs=None):
812 if attrs is None:
813 simpleItems = []
814 else:
815 simpleItems = list(attrs)
816 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
817 if hasattr(self, name):
818 simpleItems.append((name, getattr(self, name)))
819 deviceItems = []
820 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
821 if hasattr(self, name):
822 device = getattr(self, name)
823 if device is not None:
824 deviceItems.append((name, device))
825 if deviceItems:
826 xmlWriter.begintag(valueName, simpleItems)
827 xmlWriter.newline()
828 for name, deviceRecord in deviceItems:
829 if deviceRecord is not None:
830 deviceRecord.toXML(xmlWriter, font)
831 xmlWriter.endtag(valueName)
832 xmlWriter.newline()
833 else:
834 xmlWriter.simpletag(valueName, simpleItems)
835 xmlWriter.newline()
836
837 def fromXML(self, (name, attrs, content), font):
838 import otTables
839 for k, v in attrs.items():
840 setattr(self, k, int(v))
841 for element in content:
842 if type(element) <> TupleType:
843 continue
844 name, attrs, content = element
845 value = getattr(otTables, name)()
846 for elem2 in content:
847 if type(elem2) <> TupleType:
848 continue
849 value.fromXML(elem2, font)
850 setattr(self, name, value)
851
852 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100853 if type(self) != type(other): return cmp(type(self), type(other))
854 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000855
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400856 return cmp(self.__dict__, other.__dict__)