blob: 961cebb899f42a0e0fb2a80fd7a671266ecd2c48 [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
87class OTTableReader:
88
jvr64b5c802002-05-11 10:21:36 +000089 """Helper class to retrieve data from an OpenType table."""
90
jvrd4d15132002-05-11 00:59:27 +000091 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
92 self.data = data
93 self.offset = offset
94 self.pos = offset
95 self.tableType = tableType
96 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -050097 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +000098 self.valueFormat = valueFormat
99 self.cachingStats = cachingStats
100
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500101 def getSubReader(self, offset, persistent=False):
jvrd4d15132002-05-11 00:59:27 +0000102 offset = self.offset + offset
103 if self.cachingStats is not None:
104 try:
105 self.cachingStats[offset] = self.cachingStats[offset] + 1
106 except KeyError:
107 self.cachingStats[offset] = 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 Esfahbod6e556aa2013-11-22 19:16:27 -0500110 self.valueFormat, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000111 return subReader
112
113 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
119
120 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
126
127 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
133
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
148
149 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
159
160 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500161 self.valueFormat[which] = ValueRecordFactory(format)
jvrd4d15132002-05-11 00:59:27 +0000162
163 def readValueRecord(self, font, which):
164 return self.valueFormat[which].readValueRecord(self, font)
165
166
167class OTTableWriter:
168
jvr64b5c802002-05-11 10:21:36 +0000169 """Helper class to gather and assemble data for OpenType tables."""
170
jvrd4d15132002-05-11 00:59:27 +0000171 def __init__(self, tableType, valueFormat=None):
172 self.items = []
173 self.tableType = tableType
174 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500175 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000176 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000177 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000178
jvr4105ca02002-07-23 08:43:03 +0000179 # assembler interface
180
181 def getAllData(self):
182 """Assemble all data, including all subtables."""
183 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000184 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000185 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000186 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000187 # Gather all data in two passes: the absolute positions of all
188 # subtable are needed before the actual data can be assembled.
189 pos = 0
190 for table in tables:
191 table.pos = pos
192 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000193
194 for table in extTables:
195 table.pos = pos
196 pos = pos + table.getDataLength()
197
198
jvr4105ca02002-07-23 08:43:03 +0000199 data = []
200 for table in tables:
201 tableData = table.getData()
202 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000203
204 for table in extTables:
205 tableData = table.getData()
206 data.append(tableData)
207
jvr4105ca02002-07-23 08:43:03 +0000208 return "".join(data)
209
210 def getDataLength(self):
211 """Return the length of this table in bytes, without subtables."""
212 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000213 if hasattr(self, "Extension"):
214 longOffset = 1
215 else:
216 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000217 for item in self.items:
218 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000219 if longOffset:
220 l = l + 4 # sizeof(ULong)
221 else:
222 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000223 else:
224 l = l + len(item)
225 return l
226
227 def getData(self):
228 """Assemble the data for this writer/table, without subtables."""
229 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000230 if hasattr(self,"Extension"):
231 longOffset = 1
232 else:
233 longOffset = 0
234 pos = self.pos
235 numItems = len(items)
236 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000237 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000238
jvr4105ca02002-07-23 08:43:03 +0000239 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000240 if longOffset:
241 items[i] = packULong(item.pos - pos)
242 else:
243 try:
244 items[i] = packUShort(item.pos - pos)
245 except AssertionError:
246 # provide data to fix overflow problem.
247 # If the overflow is to a lookup, or from a lookup to a subtable,
248 # just report the current item.
249 if self.name in [ 'LookupList', 'Lookup']:
250 overflowErrorRecord = self.getOverflowErrorRecord(item)
251 else:
252 # overflow is within a subTable. Life is more complicated.
253 # If we split the sub-table just before the current item, we may still suffer overflow.
254 # This is because duplicate table merging is done only within an Extension subTable tree;
255 # when we split the subtable in two, some items may no longer be duplicates.
256 # Get worst case by adding up all the item lengths, depth first traversal.
257 # and then report the first item that overflows a short.
258 def getDeepItemLength(table):
259 if hasattr(table, "getDataLength"):
260 length = 0
261 for item in table.items:
262 length = length + getDeepItemLength(item)
263 else:
264 length = len(table)
265 return length
266
267 length = self.getDataLength()
268 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
269 # Coverage is first in the item list, but last in the table list,
270 # The original overflow is really in the item list. Skip the Coverage
271 # table in the following test.
272 items = items[i+1:]
273
274 for j in range(len(items)):
275 item = items[j]
276 length = length + getDeepItemLength(item)
277 if length > 65535:
278 break
279 overflowErrorRecord = self.getOverflowErrorRecord(item)
280
281
282 raise OTLOffsetOverflowError, overflowErrorRecord
283
jvr4105ca02002-07-23 08:43:03 +0000284 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000285
jvrcfadfd02002-07-22 22:13:57 +0000286 def __hash__(self):
287 # only works after self._doneWriting() has been called
288 return hash(self.items)
289
290 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100291 if type(self) != type(other): return cmp(type(self), type(other))
292 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400293
294 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000295
jvrcfadfd02002-07-22 22:13:57 +0000296 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000297 # Convert CountData references to data string items
298 # collapse duplicate table references to a unique entry
299 # "tables" are OTTableWriter objects.
300
301 # For Extension Lookup types, we can
302 # eliminate duplicates only within the tree under the Extension Lookup,
303 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000304 if internedTables is None:
305 internedTables = {}
306 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000307 iRange = range(len(items))
308
309 if hasattr(self, "Extension"):
310 newTree = 1
311 else:
312 newTree = 0
313 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000314 item = items[i]
315 if hasattr(item, "getCountData"):
316 items[i] = item.getCountData()
317 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000318 if newTree:
319 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000320 else:
jvr823f8cd2006-10-21 14:12:38 +0000321 item._doneWriting(internedTables)
322 if internedTables.has_key(item):
323 items[i] = item = internedTables[item]
324 else:
325 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000326 self.items = tuple(items)
327
jvr823f8cd2006-10-21 14:12:38 +0000328 def _gatherTables(self, tables=None, extTables=None, done=None):
329 # Convert table references in self.items tree to a flat
330 # list of tables in depth-first traversal order.
331 # "tables" are OTTableWriter objects.
332 # We do the traversal in reverse order at each level, in order to
333 # resolve duplicate references to be the last reference in the list of tables.
334 # For extension lookups, duplicate references can be merged only within the
335 # writer tree under the extension lookup.
336 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000337 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000338 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000339 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000340
341 done[self] = 1
342
343 numItems = len(self.items)
344 iRange = range(numItems)
345 iRange.reverse()
346
347 if hasattr(self, "Extension"):
348 appendExtensions = 1
349 else:
350 appendExtensions = 0
351
352 # add Coverage table if it is sorted last.
353 sortCoverageLast = 0
354 if hasattr(self, "sortCoverageLast"):
355 # Find coverage table
356 for i in range(numItems):
357 item = self.items[i]
358 if hasattr(item, "name") and (item.name == "Coverage"):
359 sortCoverageLast = 1
360 break
361 if not done.has_key(item):
362 item._gatherTables(tables, extTables, done)
363 else:
364 index = max(item.parent.keys())
365 item.parent[index + 1] = self
366
367 saveItem = None
368 for i in iRange:
369 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000370 if not hasattr(item, "getData"):
371 continue
jvr823f8cd2006-10-21 14:12:38 +0000372
373 if sortCoverageLast and (i==1) and item.name == 'Coverage':
374 # we've already 'gathered' it above
375 continue
376
377 if appendExtensions:
378 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
379 newDone = {}
380 item._gatherTables(extTables, None, newDone)
381
382 elif not done.has_key(item):
383 item._gatherTables(tables, extTables, done)
384 else:
385 index = max(item.parent.keys())
386 item.parent[index + 1] = self
387
388
jvrcfadfd02002-07-22 22:13:57 +0000389 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000390 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000391
jvr4105ca02002-07-23 08:43:03 +0000392 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000393
jvr4105ca02002-07-23 08:43:03 +0000394 def getSubWriter(self):
jvr823f8cd2006-10-21 14:12:38 +0000395 subwriter = self.__class__(self.tableType, self.valueFormat)
396 subwriter.parent = {0:self} # because some subtables have idential values, we discard
397 # the duplicates under the getAllData method. Hence some
398 # subtable writers can have more than one parent writer.
399 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000400
401 def writeUShort(self, value):
402 assert 0 <= value < 0x10000
403 self.items.append(struct.pack(">H", value))
404
405 def writeShort(self, value):
406 self.items.append(struct.pack(">h", value))
407
408 def writeLong(self, value):
409 self.items.append(struct.pack(">l", value))
410
jvr823f8cd2006-10-21 14:12:38 +0000411 def writeULong(self, value):
412 self.items.append(struct.pack(">L", value))
413
jvrd4d15132002-05-11 00:59:27 +0000414 def writeTag(self, tag):
415 assert len(tag) == 4
416 self.items.append(tag)
417
418 def writeSubTable(self, subWriter):
419 self.items.append(subWriter)
420
421 def writeCountReference(self, table, name):
422 self.items.append(CountReference(table, name))
423
424 def writeStruct(self, format, values):
425 data = apply(struct.pack, (format,) + values)
426 self.items.append(data)
427
jvr823f8cd2006-10-21 14:12:38 +0000428 def writeData(self, data):
429 self.items.append(data)
430
jvrd4d15132002-05-11 00:59:27 +0000431 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500432 self.valueFormat[which] = ValueRecordFactory(format)
jvrd4d15132002-05-11 00:59:27 +0000433
434 def writeValueRecord(self, value, font, which):
435 return self.valueFormat[which].writeValueRecord(self, font, value)
436
jvr823f8cd2006-10-21 14:12:38 +0000437 def getOverflowErrorRecord(self, item):
438 LookupListIndex = SubTableIndex = itemName = itemIndex = None
439 if self.name == 'LookupList':
440 LookupListIndex = item.repeatIndex
441 elif self.name == 'Lookup':
442 LookupListIndex = self.repeatIndex
443 SubTableIndex = item.repeatIndex
444 else:
445 itemName = item.name
446 if hasattr(item, 'repeatIndex'):
447 itemIndex = item.repeatIndex
448 if self.name == 'SubTable':
449 LookupListIndex = self.parent[0].repeatIndex
450 SubTableIndex = self.repeatIndex
451 elif self.name == 'ExtSubTable':
452 LookupListIndex = self.parent[0].parent[0].repeatIndex
453 SubTableIndex = self.parent[0].repeatIndex
454 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
455 itemName = ".".join(self.name, item.name)
456 p1 = self.parent[0]
457 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
458 itemName = ".".join(p1.name, item.name)
459 p1 = p1.parent[0]
460 if p1:
461 if p1.name == 'ExtSubTable':
462 LookupListIndex = self.parent[0].parent[0].repeatIndex
463 SubTableIndex = self.parent[0].repeatIndex
464 else:
465 LookupListIndex = self.parent[0].repeatIndex
466 SubTableIndex = self.repeatIndex
467
468 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
469
jvrd4d15132002-05-11 00:59:27 +0000470
471class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000472 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000473 def __init__(self, table, name):
474 self.table = table
475 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000476 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000477 return packUShort(self.table[self.name])
478
479
jvr64b5c802002-05-11 10:21:36 +0000480def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000481 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000482 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000483
484
jvr823f8cd2006-10-21 14:12:38 +0000485def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000486 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000487 return struct.pack(">L", value)
488
489
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500490class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000491 def __init__(self):
492 self.compileStatus = 0 # 0 means table was created
493 # 1 means the table.read() function was called by a table which is subject
494 # to delayed compilation
495 # 2 means that it was subject to delayed compilation, and
496 # has been decompiled
497 # 3 means that the start and end fields have been filled out, and that we
498 # can use the data string rather than compiling from the table data.
499
500 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000501
jvr823f8cd2006-10-21 14:12:38 +0000502 def __getattr__(self, attr):
503 # we get here only when the table does not have the attribute.
504 # This method ovveride exists so that we can try to de-compile
505 # a table which is subject to delayed decompilation, and then try
506 # to get the value again after decompilation.
507 self.recurse +=1
508 if self.recurse > 2:
509 # shouldn't ever get here - we should only get to two levels of recursion.
510 # this guards against self.decompile NOT setting compileStatus to other than 1.
511 raise AttributeError, attr
512 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500513 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000514 val = getattr(self, attr)
515 self.recurse -=1
516 return val
517
518 raise AttributeError, attr
519
520
jvr64b5c802002-05-11 10:21:36 +0000521 """Generic base class for all OpenType (sub)tables."""
522
jvrd4d15132002-05-11 00:59:27 +0000523 def getConverters(self):
524 return self.converters
525
526 def getConverterByName(self, name):
527 return self.convertersByName[name]
528
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500529 def decompile(self, reader, font, countVars=None):
jvr823f8cd2006-10-21 14:12:38 +0000530 self.compileStatus = 2 # table has been decompiled.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500531 if countVars is None:
532 countVars = {}
jvrf7ef96c2002-09-10 19:26:38 +0000533 self.readFormat(reader)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500534 counts = []
jvrd4d15132002-05-11 00:59:27 +0000535 table = {}
536 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500537 converters = self.getConverters()
538 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000539 if conv.name == "SubTable":
540 conv = conv.getConverter(reader.tableType,
541 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000542 if conv.name == "ExtSubTable":
543 conv = conv.getConverter(reader.tableType,
544 table["ExtensionLookupType"])
jvrd4d15132002-05-11 00:59:27 +0000545 if conv.repeat:
546 l = []
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500547 for i in range(countVars[conv.repeat] + conv.repeatOffset):
548 l.append(conv.read(reader, font, countVars))
jvrd4d15132002-05-11 00:59:27 +0000549 table[conv.name] = l
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500550 if conv.repeat in counts:
551 del countVars[conv.repeat]
552 counts.remove(conv.repeat)
553
jvrd4d15132002-05-11 00:59:27 +0000554 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500555 table[conv.name] = conv.read(reader, font, countVars)
556 if conv.isCount:
557 counts.append(conv.name)
558 countVars[conv.name] = table[conv.name]
559
560 for count in counts:
561 del countVars[count]
562
jvrd4d15132002-05-11 00:59:27 +0000563 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500564
jvrd4d15132002-05-11 00:59:27 +0000565 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000566
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500567 def ensureDecompiled(self):
568 if self.compileStatus != 1:
569 return
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500570 self.decompile(self.reader, self.font, self.countVars)
571 del self.reader, self.font, self.countVars
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500572
jvr823f8cd2006-10-21 14:12:38 +0000573 def preCompile(self):
574 pass # used only by the LookupList class
575
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500576 def compile(self, writer, font, countVars=None):
577 if countVars is None:
578 countVars = {}
579 counts = []
jvrd4d15132002-05-11 00:59:27 +0000580 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000581
582 if hasattr(self, 'sortCoverageLast'):
583 writer.sortCoverageLast = 1
584
jvrf7ef96c2002-09-10 19:26:38 +0000585 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000586 for conv in self.getConverters():
587 value = table.get(conv.name)
588 if conv.repeat:
589 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000590 value = []
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500591 countVars[conv.repeat](len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000592 for i in range(len(value)):
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500593 conv.write(writer, font, countVars, value[i], i)
594 if conv.repeat in counts:
595 del countVars[conv.repeat]
596 counts.remove(conv.repeat)
jvrd4d15132002-05-11 00:59:27 +0000597 elif conv.isCount:
598 # Special-case Count values.
599 # Assumption: a Count field will *always* precede
600 # the actual array.
601 # 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 table[conv.name] = None
604 # We add a reference: by the time the data is assembled
605 # the Count value will be filled in.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500606 name = conv.name
607 writer.writeCountReference(table, name)
608 counts.append(name)
609 def storeValue(value):
610 if table[name] is None:
611 table[name] = value
612 else:
613 assert table[name] == value, (table[name], value)
614 countVars[name] = storeValue
jvrd4d15132002-05-11 00:59:27 +0000615 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500616 conv.write(writer, font, countVars, value)
617
618 for count in counts:
619 del countVars[count]
jvrd4d15132002-05-11 00:59:27 +0000620
jvrf7ef96c2002-09-10 19:26:38 +0000621 def readFormat(self, reader):
622 pass
623
624 def writeFormat(self, writer):
625 pass
626
jvrd4d15132002-05-11 00:59:27 +0000627 def postRead(self, table, font):
628 self.__dict__.update(table)
629
630 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500631 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000632 return self.__dict__.copy()
633
634 def toXML(self, xmlWriter, font, attrs=None):
635 tableName = self.__class__.__name__
636 if attrs is None:
637 attrs = []
638 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000639 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000640 xmlWriter.begintag(tableName, attrs)
641 xmlWriter.newline()
642 self.toXML2(xmlWriter, font)
643 xmlWriter.endtag(tableName)
644 xmlWriter.newline()
645
646 def toXML2(self, xmlWriter, font):
647 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
648 # This is because in TTX our parent writes our main tag, and in otBase.py we
649 # do it ourselves. I think I'm getting schizophrenic...
650 for conv in self.getConverters():
651 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000652 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000653 for i in range(len(value)):
654 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000655 conv.xmlWrite(xmlWriter, font, item, conv.name,
656 [("index", i)])
657 else:
658 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000659
660 def fromXML(self, (name, attrs, content), font):
661 try:
662 conv = self.getConverterByName(name)
663 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000664 raise # XXX on KeyError, raise nice error
665 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000666 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000667 seq = getattr(self, conv.name, None)
668 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000669 seq = []
jvr64b5c802002-05-11 10:21:36 +0000670 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000671 seq.append(value)
672 else:
jvr64b5c802002-05-11 10:21:36 +0000673 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000674
675 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100676 if type(self) != type(other): return cmp(type(self), type(other))
677 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400678
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500679 self.ensureDecompiled()
680
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400681 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000682
683
684class FormatSwitchingBaseTable(BaseTable):
685
jvrcfadfd02002-07-22 22:13:57 +0000686 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000687 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
688
jvrd4d15132002-05-11 00:59:27 +0000689 def getConverters(self):
690 return self.converters[self.Format]
691
692 def getConverterByName(self, name):
693 return self.convertersByName[self.Format][name]
694
jvrf7ef96c2002-09-10 19:26:38 +0000695 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000696 self.Format = reader.readUShort()
697 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000698
jvrf7ef96c2002-09-10 19:26:38 +0000699 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000700 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000701
702
jvr64b5c802002-05-11 10:21:36 +0000703#
704# Support for ValueRecords
705#
706# This data type is so different from all other OpenType data types that
707# it requires quite a bit of code for itself. It even has special support
708# in OTTableReader and OTTableWriter...
709#
710
jvrd4d15132002-05-11 00:59:27 +0000711valueRecordFormat = [
712# Mask Name isDevice signed
713 (0x0001, "XPlacement", 0, 1),
714 (0x0002, "YPlacement", 0, 1),
715 (0x0004, "XAdvance", 0, 1),
716 (0x0008, "YAdvance", 0, 1),
717 (0x0010, "XPlaDevice", 1, 0),
718 (0x0020, "YPlaDevice", 1, 0),
719 (0x0040, "XAdvDevice", 1, 0),
720 (0x0080, "YAdvDevice", 1, 0),
721# reserved:
722 (0x0100, "Reserved1", 0, 0),
723 (0x0200, "Reserved2", 0, 0),
724 (0x0400, "Reserved3", 0, 0),
725 (0x0800, "Reserved4", 0, 0),
726 (0x1000, "Reserved5", 0, 0),
727 (0x2000, "Reserved6", 0, 0),
728 (0x4000, "Reserved7", 0, 0),
729 (0x8000, "Reserved8", 0, 0),
730]
731
732def _buildDict():
733 d = {}
734 for mask, name, isDevice, signed in valueRecordFormat:
735 d[name] = mask, isDevice, signed
736 return d
737
738valueRecordFormatDict = _buildDict()
739
740
741class ValueRecordFactory:
742
jvr64b5c802002-05-11 10:21:36 +0000743 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500744
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500745 def __init__(self, valueFormat=0):
jvrd4d15132002-05-11 00:59:27 +0000746 format = []
747 for mask, name, isDevice, signed in valueRecordFormat:
748 if valueFormat & mask:
749 format.append((name, isDevice, signed))
750 self.format = format
751
752 def readValueRecord(self, reader, font):
753 format = self.format
754 if not format:
755 return None
756 valueRecord = ValueRecord()
757 for name, isDevice, signed in format:
758 if signed:
759 value = reader.readShort()
760 else:
761 value = reader.readUShort()
762 if isDevice:
763 if value:
764 import otTables
765 subReader = reader.getSubReader(value)
766 value = getattr(otTables, name)()
767 value.decompile(subReader, font)
768 else:
769 value = None
770 setattr(valueRecord, name, value)
771 return valueRecord
772
773 def writeValueRecord(self, writer, font, valueRecord):
774 for name, isDevice, signed in self.format:
775 value = getattr(valueRecord, name, 0)
776 if isDevice:
777 if value:
778 subWriter = writer.getSubWriter()
779 writer.writeSubTable(subWriter)
780 value.compile(subWriter, font)
781 else:
782 writer.writeUShort(0)
783 elif signed:
784 writer.writeShort(value)
785 else:
786 writer.writeUShort(value)
787
788
789class ValueRecord:
790
791 # see ValueRecordFactory
792
793 def getFormat(self):
794 format = 0
795 for name in self.__dict__.keys():
796 format = format | valueRecordFormatDict[name][0]
797 return format
798
799 def toXML(self, xmlWriter, font, valueName, attrs=None):
800 if attrs is None:
801 simpleItems = []
802 else:
803 simpleItems = list(attrs)
804 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
805 if hasattr(self, name):
806 simpleItems.append((name, getattr(self, name)))
807 deviceItems = []
808 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
809 if hasattr(self, name):
810 device = getattr(self, name)
811 if device is not None:
812 deviceItems.append((name, device))
813 if deviceItems:
814 xmlWriter.begintag(valueName, simpleItems)
815 xmlWriter.newline()
816 for name, deviceRecord in deviceItems:
817 if deviceRecord is not None:
818 deviceRecord.toXML(xmlWriter, font)
819 xmlWriter.endtag(valueName)
820 xmlWriter.newline()
821 else:
822 xmlWriter.simpletag(valueName, simpleItems)
823 xmlWriter.newline()
824
825 def fromXML(self, (name, attrs, content), font):
826 import otTables
827 for k, v in attrs.items():
828 setattr(self, k, int(v))
829 for element in content:
830 if type(element) <> TupleType:
831 continue
832 name, attrs, content = element
833 value = getattr(otTables, name)()
834 for elem2 in content:
835 if type(elem2) <> TupleType:
836 continue
837 value.fromXML(elem2, font)
838 setattr(self, name, value)
839
840 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100841 if type(self) != type(other): return cmp(type(self), type(other))
842 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000843
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400844 return cmp(self.__dict__, other.__dict__)