blob: 4eb74acec347eb5438714b2b4c160371ac83b870 [file] [log] [blame]
jvrd4d15132002-05-11 00:59:27 +00001from DefaultTable import DefaultTable
2import otData
3import struct
4from types import TupleType
5
jvr823f8cd2006-10-21 14:12:38 +00006class OverflowErrorRecord:
7 def __init__(self, overflowTuple):
8 self.tableType = overflowTuple[0]
9 self.LookupListIndex = overflowTuple[1]
10 self.SubTableIndex = overflowTuple[2]
11 self.itemName = overflowTuple[3]
12 self.itemIndex = overflowTuple[4]
13
14 def __repr__(self):
15 return str((self.tableType, "LookupIndex:", self.LookupListIndex, "SubTableIndex:", self.SubTableIndex, "ItemName:", self.itemName, "ItemIndex:", self.itemIndex))
16
17class OTLOffsetOverflowError(Exception):
18 def __init__(self, overflowErrorRecord):
19 self.value = overflowErrorRecord
20
21 def __str__(self):
22 return repr(self.value)
23
jvrd4d15132002-05-11 00:59:27 +000024
25class BaseTTXConverter(DefaultTable):
26
jvr3a6aa232003-09-02 19:23:13 +000027 """Generic base class for TTX table converters. It functions as an
28 adapter between the TTX (ttLib actually) table model and the model
29 we use for OpenType tables, which is necessarily subtly different.
30 """
jvr64b5c802002-05-11 10:21:36 +000031
jvrd4d15132002-05-11 00:59:27 +000032 def decompile(self, data, font):
33 import otTables
Behdad Esfahbod0585b642013-11-22 16:20:59 -050034 cachingStats = None if True else {}
jvrcfadfd02002-07-22 22:13:57 +000035 reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
jvrd4d15132002-05-11 00:59:27 +000036 tableClass = getattr(otTables, self.tableTag)
37 self.table = tableClass()
38 self.table.decompile(reader, font)
Behdad Esfahbod0585b642013-11-22 16:20:59 -050039 if cachingStats:
jvrcfadfd02002-07-22 22:13:57 +000040 stats = [(v, k) for k, v in cachingStats.items()]
41 stats.sort()
42 stats.reverse()
43 print "cachingsstats for ", self.tableTag
44 for v, k in stats:
45 if v < 2:
46 break
47 print v, k
48 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000049
50 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000051 """ Create a top-level OTFWriter for the GPOS/GSUB table.
52 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000053 for each 'converter' record in the table converter list
54 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000055 - For simple items, the write method adds a string to the
56 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000057 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000058 to the writer's self.items, then calls the item's compile method.
59 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
60 each writer representing a table, and the writer.items list containing
61 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000062 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000063 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000064 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
65 Traverse the flat list of tables, calling getDataLength on each to update their position
66 Traverse the flat list of tables again, calling getData each get the data in the table, now that
67 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000068
69 If a lookup subtable overflows an offset, we have to start all over.
70 """
jvrd4d15132002-05-11 00:59:27 +000071 writer = OTTableWriter(self.tableTag)
jvr823f8cd2006-10-21 14:12:38 +000072 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000073 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000074 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000075
jvrd4d15132002-05-11 00:59:27 +000076 def toXML(self, writer, font):
77 self.table.toXML2(writer, font)
78
79 def fromXML(self, (name, attrs, content), font):
80 import otTables
81 if not hasattr(self, "table"):
82 tableClass = getattr(otTables, self.tableTag)
83 self.table = tableClass()
84 self.table.fromXML((name, attrs, content), font)
85
86
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050087class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050088
jvr64b5c802002-05-11 10:21:36 +000089 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050090
Behdad Esfahbod79817042013-11-24 16:59:42 -050091 __slots__ = ('data', 'offset', 'pos', 'tableType', 'valueFormat', 'counts', 'cachingStats')
92
93 def __init__(self, data, tableType, offset=0, valueFormat=None, counts=None, cachingStats=None):
jvrd4d15132002-05-11 00:59:27 +000094 self.data = data
95 self.offset = offset
96 self.pos = offset
97 self.tableType = tableType
98 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -050099 valueFormat = [None, None]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500100 self.counts = counts
jvrd4d15132002-05-11 00:59:27 +0000101 self.valueFormat = valueFormat
102 self.cachingStats = cachingStats
Behdad Esfahbod79817042013-11-24 16:59:42 -0500103
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500104 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000105 offset = self.offset + offset
106 if self.cachingStats is not None:
Behdad Esfahbode84f9fd2013-11-24 18:05:59 -0500107 self.cachingStats[offset] = self.cachingStats.get(offset, 0) + 1
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500108
jvrd4d15132002-05-11 00:59:27 +0000109 subReader = self.__class__(self.data, self.tableType, offset,
Behdad Esfahbod79817042013-11-24 16:59:42 -0500110 self.valueFormat, self.counts, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000111 return subReader
Behdad Esfahbod79817042013-11-24 16:59:42 -0500112
jvrd4d15132002-05-11 00:59:27 +0000113 def readUShort(self):
114 pos = self.pos
115 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000116 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000117 self.pos = newpos
118 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500119
jvrd4d15132002-05-11 00:59:27 +0000120 def readShort(self):
121 pos = self.pos
122 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000123 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000124 self.pos = newpos
125 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500126
jvrd4d15132002-05-11 00:59:27 +0000127 def readLong(self):
128 pos = self.pos
129 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000130 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000131 self.pos = newpos
132 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500133
jvr823f8cd2006-10-21 14:12:38 +0000134 def readULong(self):
135 pos = self.pos
136 newpos = pos + 4
137 value, = struct.unpack(">L", self.data[pos:newpos])
138 self.pos = newpos
139 return value
140
jvrd4d15132002-05-11 00:59:27 +0000141 def readTag(self):
142 pos = self.pos
143 newpos = pos + 4
144 value = self.data[pos:newpos]
145 assert len(value) == 4
146 self.pos = newpos
147 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500148
jvrd4d15132002-05-11 00:59:27 +0000149 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500150 self.valueFormat[which] = ValueRecordFactory(format)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500151
jvrd4d15132002-05-11 00:59:27 +0000152 def readValueRecord(self, font, which):
153 return self.valueFormat[which].readValueRecord(self, font)
154
Behdad Esfahbod79817042013-11-24 16:59:42 -0500155 def setCount(self, name, value):
156 self.counts = self.counts.copy() if self.counts else dict()
157 self.counts[name] = value
158
159 def getCount(self, name):
160 return self.counts[name]
161
jvrd4d15132002-05-11 00:59:27 +0000162
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500163class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000164
jvr64b5c802002-05-11 10:21:36 +0000165 """Helper class to gather and assemble data for OpenType tables."""
166
Behdad Esfahbod79817042013-11-24 16:59:42 -0500167 def __init__(self, tableType, valueFormat=None, counts=None):
jvrd4d15132002-05-11 00:59:27 +0000168 self.items = []
169 self.tableType = tableType
170 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500171 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000172 self.valueFormat = valueFormat
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500173 self.counts = counts
jvrcfadfd02002-07-22 22:13:57 +0000174 self.pos = None
Behdad Esfahbod79817042013-11-24 16:59:42 -0500175
176 def setValueFormat(self, format, which):
177 self.valueFormat[which] = ValueRecordFactory(format)
178
179 def setCount(self, name, value):
180 self.counts = self.counts.copy() if self.counts else dict()
181 self.counts[name] = value
182
183 def getCount(self, name):
184 return self.counts[name]
185
jvr4105ca02002-07-23 08:43:03 +0000186 # assembler interface
187
188 def getAllData(self):
189 """Assemble all data, including all subtables."""
190 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000191 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000192 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000193 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000194 # Gather all data in two passes: the absolute positions of all
195 # subtable are needed before the actual data can be assembled.
196 pos = 0
197 for table in tables:
198 table.pos = pos
199 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000200
201 for table in extTables:
202 table.pos = pos
203 pos = pos + table.getDataLength()
204
205
jvr4105ca02002-07-23 08:43:03 +0000206 data = []
207 for table in tables:
208 tableData = table.getData()
209 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000210
211 for table in extTables:
212 tableData = table.getData()
213 data.append(tableData)
214
jvr4105ca02002-07-23 08:43:03 +0000215 return "".join(data)
216
217 def getDataLength(self):
218 """Return the length of this table in bytes, without subtables."""
219 l = 0
220 for item in self.items:
221 if hasattr(item, "getData") or hasattr(item, "getCountData"):
Behdad Esfahbodd58c38d2013-11-24 21:58:53 -0500222 if self.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000223 l = l + 4 # sizeof(ULong)
224 else:
225 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000226 else:
227 l = l + len(item)
228 return l
229
230 def getData(self):
231 """Assemble the data for this writer/table, without subtables."""
232 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000233 pos = self.pos
234 numItems = len(items)
235 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000236 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000237
jvr4105ca02002-07-23 08:43:03 +0000238 if hasattr(item, "getData"):
Behdad Esfahbodd58c38d2013-11-24 21:58:53 -0500239 if self.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000240 items[i] = packULong(item.pos - pos)
241 else:
242 try:
243 items[i] = packUShort(item.pos - pos)
244 except AssertionError:
245 # provide data to fix overflow problem.
Behdad Esfahbod58acba22013-11-24 20:08:05 -0500246 # If the overflow is to a lookup, or from a lookup to a subtable,
jvr823f8cd2006-10-21 14:12:38 +0000247 # just report the current item.
248 if self.name in [ 'LookupList', 'Lookup']:
249 overflowErrorRecord = self.getOverflowErrorRecord(item)
250 else:
251 # overflow is within a subTable. Life is more complicated.
252 # If we split the sub-table just before the current item, we may still suffer overflow.
253 # This is because duplicate table merging is done only within an Extension subTable tree;
254 # when we split the subtable in two, some items may no longer be duplicates.
255 # Get worst case by adding up all the item lengths, depth first traversal.
256 # and then report the first item that overflows a short.
257 def getDeepItemLength(table):
258 if hasattr(table, "getDataLength"):
259 length = 0
260 for item in table.items:
261 length = length + getDeepItemLength(item)
262 else:
263 length = len(table)
264 return length
265
266 length = self.getDataLength()
267 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
268 # Coverage is first in the item list, but last in the table list,
269 # The original overflow is really in the item list. Skip the Coverage
270 # table in the following test.
271 items = items[i+1:]
272
273 for j in range(len(items)):
274 item = items[j]
275 length = length + getDeepItemLength(item)
276 if length > 65535:
277 break
278 overflowErrorRecord = self.getOverflowErrorRecord(item)
279
280
281 raise OTLOffsetOverflowError, overflowErrorRecord
282
jvr4105ca02002-07-23 08:43:03 +0000283 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000284
jvrcfadfd02002-07-22 22:13:57 +0000285 def __hash__(self):
286 # only works after self._doneWriting() has been called
287 return hash(self.items)
288
289 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100290 if type(self) != type(other): return cmp(type(self), type(other))
291 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400292
293 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000294
jvrcfadfd02002-07-22 22:13:57 +0000295 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000296 # Convert CountData references to data string items
297 # collapse duplicate table references to a unique entry
298 # "tables" are OTTableWriter objects.
299
300 # For Extension Lookup types, we can
301 # eliminate duplicates only within the tree under the Extension Lookup,
302 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000303 if internedTables is None:
304 internedTables = {}
305 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000306 iRange = range(len(items))
307
308 if hasattr(self, "Extension"):
309 newTree = 1
310 else:
311 newTree = 0
312 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000313 item = items[i]
314 if hasattr(item, "getCountData"):
315 items[i] = item.getCountData()
316 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000317 if newTree:
318 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000319 else:
jvr823f8cd2006-10-21 14:12:38 +0000320 item._doneWriting(internedTables)
321 if internedTables.has_key(item):
322 items[i] = item = internedTables[item]
323 else:
324 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000325 self.items = tuple(items)
326
jvr823f8cd2006-10-21 14:12:38 +0000327 def _gatherTables(self, tables=None, extTables=None, done=None):
328 # Convert table references in self.items tree to a flat
329 # list of tables in depth-first traversal order.
330 # "tables" are OTTableWriter objects.
331 # We do the traversal in reverse order at each level, in order to
332 # resolve duplicate references to be the last reference in the list of tables.
333 # For extension lookups, duplicate references can be merged only within the
334 # writer tree under the extension lookup.
335 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000336 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000337 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000338 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000339
340 done[self] = 1
341
342 numItems = len(self.items)
343 iRange = range(numItems)
344 iRange.reverse()
345
346 if hasattr(self, "Extension"):
347 appendExtensions = 1
348 else:
349 appendExtensions = 0
350
351 # add Coverage table if it is sorted last.
352 sortCoverageLast = 0
353 if hasattr(self, "sortCoverageLast"):
354 # Find coverage table
355 for i in range(numItems):
356 item = self.items[i]
357 if hasattr(item, "name") and (item.name == "Coverage"):
358 sortCoverageLast = 1
359 break
360 if not done.has_key(item):
361 item._gatherTables(tables, extTables, done)
362 else:
363 index = max(item.parent.keys())
364 item.parent[index + 1] = self
365
366 saveItem = None
367 for i in iRange:
368 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000369 if not hasattr(item, "getData"):
370 continue
jvr823f8cd2006-10-21 14:12:38 +0000371
372 if sortCoverageLast and (i==1) and item.name == 'Coverage':
373 # we've already 'gathered' it above
374 continue
375
376 if appendExtensions:
377 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
378 newDone = {}
379 item._gatherTables(extTables, None, newDone)
380
381 elif not done.has_key(item):
382 item._gatherTables(tables, extTables, done)
383 else:
384 index = max(item.parent.keys())
385 item.parent[index + 1] = self
386
387
jvrcfadfd02002-07-22 22:13:57 +0000388 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000389 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000390
jvr4105ca02002-07-23 08:43:03 +0000391 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000392
jvr4105ca02002-07-23 08:43:03 +0000393 def getSubWriter(self):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500394 subwriter = self.__class__(self.tableType, self.valueFormat, self.counts)
jvr823f8cd2006-10-21 14:12:38 +0000395 subwriter.parent = {0:self} # because some subtables have idential values, we discard
396 # the duplicates under the getAllData method. Hence some
397 # subtable writers can have more than one parent writer.
398 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000399
400 def writeUShort(self, value):
401 assert 0 <= value < 0x10000
402 self.items.append(struct.pack(">H", value))
403
404 def writeShort(self, value):
405 self.items.append(struct.pack(">h", value))
406
407 def writeLong(self, value):
408 self.items.append(struct.pack(">l", value))
409
jvr823f8cd2006-10-21 14:12:38 +0000410 def writeULong(self, value):
411 self.items.append(struct.pack(">L", value))
412
jvrd4d15132002-05-11 00:59:27 +0000413 def writeTag(self, tag):
414 assert len(tag) == 4
415 self.items.append(tag)
416
417 def writeSubTable(self, subWriter):
418 self.items.append(subWriter)
419
420 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500421 ref = CountReference(table, name)
422 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500423 return ref
jvrd4d15132002-05-11 00:59:27 +0000424
425 def writeStruct(self, format, values):
426 data = apply(struct.pack, (format,) + values)
427 self.items.append(data)
428
jvr823f8cd2006-10-21 14:12:38 +0000429 def writeData(self, data):
430 self.items.append(data)
431
jvrd4d15132002-05-11 00:59:27 +0000432 def writeValueRecord(self, value, font, which):
433 return self.valueFormat[which].writeValueRecord(self, font, value)
434
jvr823f8cd2006-10-21 14:12:38 +0000435 def getOverflowErrorRecord(self, item):
436 LookupListIndex = SubTableIndex = itemName = itemIndex = None
437 if self.name == 'LookupList':
438 LookupListIndex = item.repeatIndex
439 elif self.name == 'Lookup':
440 LookupListIndex = self.repeatIndex
441 SubTableIndex = item.repeatIndex
442 else:
443 itemName = item.name
444 if hasattr(item, 'repeatIndex'):
445 itemIndex = item.repeatIndex
446 if self.name == 'SubTable':
447 LookupListIndex = self.parent[0].repeatIndex
448 SubTableIndex = self.repeatIndex
449 elif self.name == 'ExtSubTable':
450 LookupListIndex = self.parent[0].parent[0].repeatIndex
451 SubTableIndex = self.parent[0].repeatIndex
452 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
453 itemName = ".".join(self.name, item.name)
454 p1 = self.parent[0]
455 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
456 itemName = ".".join(p1.name, item.name)
457 p1 = p1.parent[0]
458 if p1:
459 if p1.name == 'ExtSubTable':
460 LookupListIndex = self.parent[0].parent[0].repeatIndex
461 SubTableIndex = self.parent[0].repeatIndex
462 else:
463 LookupListIndex = self.parent[0].repeatIndex
464 SubTableIndex = self.repeatIndex
465
466 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
467
jvrd4d15132002-05-11 00:59:27 +0000468
469class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000470 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000471 def __init__(self, table, name):
472 self.table = table
473 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500474 def setValue(self, value):
475 table = self.table
476 name = self.name
477 if table[name] is None:
478 table[name] = value
479 else:
480 assert table[name] == value, (table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000481 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000482 return packUShort(self.table[self.name])
483
484
jvr64b5c802002-05-11 10:21:36 +0000485def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000486 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000487 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000488
489
jvr823f8cd2006-10-21 14:12:38 +0000490def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000491 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000492 return struct.pack(">L", value)
493
494
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500495class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000496 def __init__(self):
497 self.compileStatus = 0 # 0 means table was created
498 # 1 means the table.read() function was called by a table which is subject
499 # to delayed compilation
500 # 2 means that it was subject to delayed compilation, and
501 # has been decompiled
502 # 3 means that the start and end fields have been filled out, and that we
503 # can use the data string rather than compiling from the table data.
504
505 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000506
jvr823f8cd2006-10-21 14:12:38 +0000507 def __getattr__(self, attr):
508 # we get here only when the table does not have the attribute.
509 # This method ovveride exists so that we can try to de-compile
510 # a table which is subject to delayed decompilation, and then try
511 # to get the value again after decompilation.
512 self.recurse +=1
513 if self.recurse > 2:
514 # shouldn't ever get here - we should only get to two levels of recursion.
515 # this guards against self.decompile NOT setting compileStatus to other than 1.
516 raise AttributeError, attr
517 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500518 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000519 val = getattr(self, attr)
520 self.recurse -=1
521 return val
522
523 raise AttributeError, attr
524
525
jvr64b5c802002-05-11 10:21:36 +0000526 """Generic base class for all OpenType (sub)tables."""
527
jvrd4d15132002-05-11 00:59:27 +0000528 def getConverters(self):
529 return self.converters
530
531 def getConverterByName(self, name):
532 return self.convertersByName[name]
533
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500534 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000535 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000536 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000537 table = {}
538 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500539 converters = self.getConverters()
540 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000541 if conv.name == "SubTable":
542 conv = conv.getConverter(reader.tableType,
543 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000544 if conv.name == "ExtSubTable":
545 conv = conv.getConverter(reader.tableType,
546 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000547 if conv.repeat:
548 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500549 if conv.repeat in table:
550 countValue = table[conv.repeat]
551 else:
552 # conv.repeat is a propagated count
553 countValue = reader.getCount(conv.repeat)
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500554 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500555 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000556 table[conv.name] = l
557 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500558 if conv.aux and not eval(conv.aux, None, table):
559 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500560 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500561 if conv.isPropagatedCount:
Behdad Esfahbod79817042013-11-24 16:59:42 -0500562 reader.setCount(conv.name, table[conv.name])
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500563
jvrd4d15132002-05-11 00:59:27 +0000564 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500565
jvrd4d15132002-05-11 00:59:27 +0000566 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000567
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500568 def ensureDecompiled(self):
569 if self.compileStatus != 1:
570 return
Behdad Esfahbodc491f452013-11-24 18:28:20 -0500571 subReader = self.reader.getSubReader(self.offset)
572 self.decompile(subReader, self.font)
573 del self.reader, self.font, self.offset
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500574
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500575 def compile(self, writer, font):
jvrd4d15132002-05-11 00:59:27 +0000576 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000577
578 if hasattr(self, 'sortCoverageLast'):
579 writer.sortCoverageLast = 1
580
jvrf7ef96c2002-09-10 19:26:38 +0000581 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000582 for conv in self.getConverters():
583 value = table.get(conv.name)
584 if conv.repeat:
585 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000586 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500587 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500588 if conv.repeat in table:
589 ref = table[conv.repeat]
590 table[conv.repeat] = None
591 ref.setValue(countValue)
592 else:
593 # conv.repeat is a propagated count
594 writer.getCount(conv.repeat).setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000595 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500596 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000597 elif conv.isCount:
598 # Special-case Count values.
599 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500600 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000601 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500602 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000603 # We add a reference: by the time the data is assembled
604 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500605 ref = writer.writeCountReference(table, conv.name)
606 if conv.isPropagatedCount:
607 table[conv.name] = None
608 writer.setCount(conv.name, ref)
609 else:
610 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000611 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500612 if conv.aux and not eval(conv.aux, None, table):
613 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500614 conv.write(writer, font, table, value)
jvrd4d15132002-05-11 00:59:27 +0000615
jvrf7ef96c2002-09-10 19:26:38 +0000616 def readFormat(self, reader):
617 pass
618
619 def writeFormat(self, writer):
620 pass
621
jvrd4d15132002-05-11 00:59:27 +0000622 def postRead(self, table, font):
623 self.__dict__.update(table)
624
625 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500626 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000627 return self.__dict__.copy()
628
629 def toXML(self, xmlWriter, font, attrs=None):
630 tableName = self.__class__.__name__
631 if attrs is None:
632 attrs = []
633 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000634 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000635 xmlWriter.begintag(tableName, attrs)
636 xmlWriter.newline()
637 self.toXML2(xmlWriter, font)
638 xmlWriter.endtag(tableName)
639 xmlWriter.newline()
640
641 def toXML2(self, xmlWriter, font):
642 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
643 # This is because in TTX our parent writes our main tag, and in otBase.py we
644 # do it ourselves. I think I'm getting schizophrenic...
645 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000646 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500647 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000648 for i in range(len(value)):
649 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000650 conv.xmlWrite(xmlWriter, font, item, conv.name,
651 [("index", i)])
652 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500653 if conv.aux and not eval(conv.aux, None, vars(self)):
654 continue
655 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000656 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000657
658 def fromXML(self, (name, attrs, content), font):
659 try:
660 conv = self.getConverterByName(name)
661 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000662 raise # XXX on KeyError, raise nice error
663 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000664 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000665 seq = getattr(self, conv.name, None)
666 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000667 seq = []
jvr64b5c802002-05-11 10:21:36 +0000668 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000669 seq.append(value)
670 else:
jvr64b5c802002-05-11 10:21:36 +0000671 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000672
673 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100674 if type(self) != type(other): return cmp(type(self), type(other))
675 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400676
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500677 self.ensureDecompiled()
678
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400679 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000680
681
682class FormatSwitchingBaseTable(BaseTable):
683
jvrcfadfd02002-07-22 22:13:57 +0000684 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000685 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
686
jvrd4d15132002-05-11 00:59:27 +0000687 def getConverters(self):
688 return self.converters[self.Format]
689
690 def getConverterByName(self, name):
691 return self.convertersByName[self.Format][name]
692
jvrf7ef96c2002-09-10 19:26:38 +0000693 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000694 self.Format = reader.readUShort()
695 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000696
jvrf7ef96c2002-09-10 19:26:38 +0000697 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000698 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000699
700
jvr64b5c802002-05-11 10:21:36 +0000701#
702# Support for ValueRecords
703#
704# This data type is so different from all other OpenType data types that
705# it requires quite a bit of code for itself. It even has special support
706# in OTTableReader and OTTableWriter...
707#
708
jvrd4d15132002-05-11 00:59:27 +0000709valueRecordFormat = [
710# Mask Name isDevice signed
711 (0x0001, "XPlacement", 0, 1),
712 (0x0002, "YPlacement", 0, 1),
713 (0x0004, "XAdvance", 0, 1),
714 (0x0008, "YAdvance", 0, 1),
715 (0x0010, "XPlaDevice", 1, 0),
716 (0x0020, "YPlaDevice", 1, 0),
717 (0x0040, "XAdvDevice", 1, 0),
718 (0x0080, "YAdvDevice", 1, 0),
719# reserved:
720 (0x0100, "Reserved1", 0, 0),
721 (0x0200, "Reserved2", 0, 0),
722 (0x0400, "Reserved3", 0, 0),
723 (0x0800, "Reserved4", 0, 0),
724 (0x1000, "Reserved5", 0, 0),
725 (0x2000, "Reserved6", 0, 0),
726 (0x4000, "Reserved7", 0, 0),
727 (0x8000, "Reserved8", 0, 0),
728]
729
730def _buildDict():
731 d = {}
732 for mask, name, isDevice, signed in valueRecordFormat:
733 d[name] = mask, isDevice, signed
734 return d
735
736valueRecordFormatDict = _buildDict()
737
738
739class ValueRecordFactory:
740
jvr64b5c802002-05-11 10:21:36 +0000741 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500742
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500743 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000744 format = []
745 for mask, name, isDevice, signed in valueRecordFormat:
746 if valueFormat & mask:
747 format.append((name, isDevice, signed))
748 self.format = format
749
750 def readValueRecord(self, reader, font):
751 format = self.format
752 if not format:
753 return None
754 valueRecord = ValueRecord()
755 for name, isDevice, signed in format:
756 if signed:
757 value = reader.readShort()
758 else:
759 value = reader.readUShort()
760 if isDevice:
761 if value:
762 import otTables
763 subReader = reader.getSubReader(value)
764 value = getattr(otTables, name)()
765 value.decompile(subReader, font)
766 else:
767 value = None
768 setattr(valueRecord, name, value)
769 return valueRecord
770
771 def writeValueRecord(self, writer, font, valueRecord):
772 for name, isDevice, signed in self.format:
773 value = getattr(valueRecord, name, 0)
774 if isDevice:
775 if value:
776 subWriter = writer.getSubWriter()
777 writer.writeSubTable(subWriter)
778 value.compile(subWriter, font)
779 else:
780 writer.writeUShort(0)
781 elif signed:
782 writer.writeShort(value)
783 else:
784 writer.writeUShort(value)
785
786
787class ValueRecord:
788
789 # see ValueRecordFactory
790
791 def getFormat(self):
792 format = 0
793 for name in self.__dict__.keys():
794 format = format | valueRecordFormatDict[name][0]
795 return format
796
797 def toXML(self, xmlWriter, font, valueName, attrs=None):
798 if attrs is None:
799 simpleItems = []
800 else:
801 simpleItems = list(attrs)
802 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
803 if hasattr(self, name):
804 simpleItems.append((name, getattr(self, name)))
805 deviceItems = []
806 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
807 if hasattr(self, name):
808 device = getattr(self, name)
809 if device is not None:
810 deviceItems.append((name, device))
811 if deviceItems:
812 xmlWriter.begintag(valueName, simpleItems)
813 xmlWriter.newline()
814 for name, deviceRecord in deviceItems:
815 if deviceRecord is not None:
816 deviceRecord.toXML(xmlWriter, font)
817 xmlWriter.endtag(valueName)
818 xmlWriter.newline()
819 else:
820 xmlWriter.simpletag(valueName, simpleItems)
821 xmlWriter.newline()
822
823 def fromXML(self, (name, attrs, content), font):
824 import otTables
825 for k, v in attrs.items():
826 setattr(self, k, int(v))
827 for element in content:
828 if type(element) <> TupleType:
829 continue
830 name, attrs, content = element
831 value = getattr(otTables, name)()
832 for elem2 in content:
833 if type(elem2) <> TupleType:
834 continue
835 value.fromXML(elem2, font)
836 setattr(self, name, value)
837
838 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100839 if type(self) != type(other): return cmp(type(self), type(other))
840 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000841
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400842 return cmp(self.__dict__, other.__dict__)