blob: f3876f4c518da69d6bff8489d64e10b8a48a38c5 [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 Esfahbodc491f452013-11-24 18:28:20 -0500588 subReader = self.reader.getSubReader(self.offset)
589 self.decompile(subReader, self.font)
590 del self.reader, self.font, self.offset
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 Esfahbod078b3632013-11-24 17:08:06 -0500595 def compile(self, writer, font):
jvrd4d15132002-05-11 00:59:27 +0000596 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000597
598 if hasattr(self, 'sortCoverageLast'):
599 writer.sortCoverageLast = 1
600
jvrf7ef96c2002-09-10 19:26:38 +0000601 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000602 for conv in self.getConverters():
603 value = table.get(conv.name)
604 if conv.repeat:
605 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000606 value = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500607 countValue = len(value) - conv.repeatOffset
608 if conv.repeat in table:
609 ref = table[conv.repeat]
610 table[conv.repeat] = None
611 ref.setValue(countValue)
612 else:
613 # conv.repeat is a propagated count
614 writer.getCount(conv.repeat).setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000615 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500616 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000617 elif conv.isCount:
618 # Special-case Count values.
619 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500620 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000621 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500622 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000623 # We add a reference: by the time the data is assembled
624 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500625 ref = writer.writeCountReference(table, conv.name)
626 if conv.isPropagatedCount:
627 table[conv.name] = None
628 writer.setCount(conv.name, ref)
629 else:
630 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000631 else:
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500632 conv.write(writer, font, table, value)
jvrd4d15132002-05-11 00:59:27 +0000633
jvrf7ef96c2002-09-10 19:26:38 +0000634 def readFormat(self, reader):
635 pass
636
637 def writeFormat(self, writer):
638 pass
639
jvrd4d15132002-05-11 00:59:27 +0000640 def postRead(self, table, font):
641 self.__dict__.update(table)
642
643 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500644 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000645 return self.__dict__.copy()
646
647 def toXML(self, xmlWriter, font, attrs=None):
648 tableName = self.__class__.__name__
649 if attrs is None:
650 attrs = []
651 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000652 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000653 xmlWriter.begintag(tableName, attrs)
654 xmlWriter.newline()
655 self.toXML2(xmlWriter, font)
656 xmlWriter.endtag(tableName)
657 xmlWriter.newline()
658
659 def toXML2(self, xmlWriter, font):
660 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
661 # This is because in TTX our parent writes our main tag, and in otBase.py we
662 # do it ourselves. I think I'm getting schizophrenic...
663 for conv in self.getConverters():
664 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000665 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000666 for i in range(len(value)):
667 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000668 conv.xmlWrite(xmlWriter, font, item, conv.name,
669 [("index", i)])
670 else:
671 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000672
673 def fromXML(self, (name, attrs, content), font):
674 try:
675 conv = self.getConverterByName(name)
676 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000677 raise # XXX on KeyError, raise nice error
678 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000679 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000680 seq = getattr(self, conv.name, None)
681 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000682 seq = []
jvr64b5c802002-05-11 10:21:36 +0000683 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000684 seq.append(value)
685 else:
jvr64b5c802002-05-11 10:21:36 +0000686 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000687
688 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100689 if type(self) != type(other): return cmp(type(self), type(other))
690 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400691
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500692 self.ensureDecompiled()
693
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400694 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000695
696
697class FormatSwitchingBaseTable(BaseTable):
698
jvrcfadfd02002-07-22 22:13:57 +0000699 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000700 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
701
jvrd4d15132002-05-11 00:59:27 +0000702 def getConverters(self):
703 return self.converters[self.Format]
704
705 def getConverterByName(self, name):
706 return self.convertersByName[self.Format][name]
707
jvrf7ef96c2002-09-10 19:26:38 +0000708 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000709 self.Format = reader.readUShort()
710 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000711
jvrf7ef96c2002-09-10 19:26:38 +0000712 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000713 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000714
715
jvr64b5c802002-05-11 10:21:36 +0000716#
717# Support for ValueRecords
718#
719# This data type is so different from all other OpenType data types that
720# it requires quite a bit of code for itself. It even has special support
721# in OTTableReader and OTTableWriter...
722#
723
jvrd4d15132002-05-11 00:59:27 +0000724valueRecordFormat = [
725# Mask Name isDevice signed
726 (0x0001, "XPlacement", 0, 1),
727 (0x0002, "YPlacement", 0, 1),
728 (0x0004, "XAdvance", 0, 1),
729 (0x0008, "YAdvance", 0, 1),
730 (0x0010, "XPlaDevice", 1, 0),
731 (0x0020, "YPlaDevice", 1, 0),
732 (0x0040, "XAdvDevice", 1, 0),
733 (0x0080, "YAdvDevice", 1, 0),
734# reserved:
735 (0x0100, "Reserved1", 0, 0),
736 (0x0200, "Reserved2", 0, 0),
737 (0x0400, "Reserved3", 0, 0),
738 (0x0800, "Reserved4", 0, 0),
739 (0x1000, "Reserved5", 0, 0),
740 (0x2000, "Reserved6", 0, 0),
741 (0x4000, "Reserved7", 0, 0),
742 (0x8000, "Reserved8", 0, 0),
743]
744
745def _buildDict():
746 d = {}
747 for mask, name, isDevice, signed in valueRecordFormat:
748 d[name] = mask, isDevice, signed
749 return d
750
751valueRecordFormatDict = _buildDict()
752
753
754class ValueRecordFactory:
755
jvr64b5c802002-05-11 10:21:36 +0000756 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500757
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500758 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000759 format = []
760 for mask, name, isDevice, signed in valueRecordFormat:
761 if valueFormat & mask:
762 format.append((name, isDevice, signed))
763 self.format = format
764
765 def readValueRecord(self, reader, font):
766 format = self.format
767 if not format:
768 return None
769 valueRecord = ValueRecord()
770 for name, isDevice, signed in format:
771 if signed:
772 value = reader.readShort()
773 else:
774 value = reader.readUShort()
775 if isDevice:
776 if value:
777 import otTables
778 subReader = reader.getSubReader(value)
779 value = getattr(otTables, name)()
780 value.decompile(subReader, font)
781 else:
782 value = None
783 setattr(valueRecord, name, value)
784 return valueRecord
785
786 def writeValueRecord(self, writer, font, valueRecord):
787 for name, isDevice, signed in self.format:
788 value = getattr(valueRecord, name, 0)
789 if isDevice:
790 if value:
791 subWriter = writer.getSubWriter()
792 writer.writeSubTable(subWriter)
793 value.compile(subWriter, font)
794 else:
795 writer.writeUShort(0)
796 elif signed:
797 writer.writeShort(value)
798 else:
799 writer.writeUShort(value)
800
801
802class ValueRecord:
803
804 # see ValueRecordFactory
805
806 def getFormat(self):
807 format = 0
808 for name in self.__dict__.keys():
809 format = format | valueRecordFormatDict[name][0]
810 return format
811
812 def toXML(self, xmlWriter, font, valueName, attrs=None):
813 if attrs is None:
814 simpleItems = []
815 else:
816 simpleItems = list(attrs)
817 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
818 if hasattr(self, name):
819 simpleItems.append((name, getattr(self, name)))
820 deviceItems = []
821 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
822 if hasattr(self, name):
823 device = getattr(self, name)
824 if device is not None:
825 deviceItems.append((name, device))
826 if deviceItems:
827 xmlWriter.begintag(valueName, simpleItems)
828 xmlWriter.newline()
829 for name, deviceRecord in deviceItems:
830 if deviceRecord is not None:
831 deviceRecord.toXML(xmlWriter, font)
832 xmlWriter.endtag(valueName)
833 xmlWriter.newline()
834 else:
835 xmlWriter.simpletag(valueName, simpleItems)
836 xmlWriter.newline()
837
838 def fromXML(self, (name, attrs, content), font):
839 import otTables
840 for k, v in attrs.items():
841 setattr(self, k, int(v))
842 for element in content:
843 if type(element) <> TupleType:
844 continue
845 name, attrs, content = element
846 value = getattr(otTables, name)()
847 for elem2 in content:
848 if type(elem2) <> TupleType:
849 continue
850 value.fromXML(elem2, font)
851 setattr(self, name, value)
852
853 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100854 if type(self) != type(other): return cmp(type(self), type(other))
855 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000856
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400857 return cmp(self.__dict__, other.__dict__)