blob: e6fd0ab3956f4af0771bca7017dd3127ff3dc18d [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):
jvrd4d15132002-05-11 00:59:27 +000088
jvr64b5c802002-05-11 10:21:36 +000089 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050090
91 __slots__ = ('data', 'offset', 'pos', 'tableType', 'valueFormat', 'cachingStats')
jvr64b5c802002-05-11 10:21:36 +000092
jvrd4d15132002-05-11 00:59:27 +000093 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
94 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]
jvrd4d15132002-05-11 00:59:27 +0000100 self.valueFormat = valueFormat
101 self.cachingStats = cachingStats
102
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500103 def getSubReader(self, offset, persistent=False):
jvrd4d15132002-05-11 00:59:27 +0000104 offset = self.offset + offset
105 if self.cachingStats is not None:
106 try:
107 self.cachingStats[offset] = self.cachingStats[offset] + 1
108 except KeyError:
109 self.cachingStats[offset] = 1
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500110
jvrd4d15132002-05-11 00:59:27 +0000111 subReader = self.__class__(self.data, self.tableType, offset,
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500112 self.valueFormat, self.cachingStats)
jvrd4d15132002-05-11 00:59:27 +0000113 return subReader
114
115 def readUShort(self):
116 pos = self.pos
117 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000118 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000119 self.pos = newpos
120 return value
121
122 def readShort(self):
123 pos = self.pos
124 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000125 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000126 self.pos = newpos
127 return value
128
129 def readLong(self):
130 pos = self.pos
131 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000132 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000133 self.pos = newpos
134 return value
135
jvr823f8cd2006-10-21 14:12:38 +0000136 def readULong(self):
137 pos = self.pos
138 newpos = pos + 4
139 value, = struct.unpack(">L", self.data[pos:newpos])
140 self.pos = newpos
141 return value
142
jvrd4d15132002-05-11 00:59:27 +0000143 def readTag(self):
144 pos = self.pos
145 newpos = pos + 4
146 value = self.data[pos:newpos]
147 assert len(value) == 4
148 self.pos = newpos
149 return value
150
151 def readStruct(self, format, size=None):
152 if size is None:
153 size = struct.calcsize(format)
154 else:
155 assert size == struct.calcsize(format)
156 pos = self.pos
157 newpos = pos + size
158 values = struct.unpack(format, self.data[pos:newpos])
159 self.pos = newpos
160 return values
161
162 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500163 self.valueFormat[which] = ValueRecordFactory(format)
jvrd4d15132002-05-11 00:59:27 +0000164
165 def readValueRecord(self, font, which):
166 return self.valueFormat[which].readValueRecord(self, font)
167
168
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500169class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000170
jvr64b5c802002-05-11 10:21:36 +0000171 """Helper class to gather and assemble data for OpenType tables."""
172
jvrd4d15132002-05-11 00:59:27 +0000173 def __init__(self, tableType, valueFormat=None):
174 self.items = []
175 self.tableType = tableType
176 if valueFormat is None:
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500177 valueFormat = [None, None]
jvrd4d15132002-05-11 00:59:27 +0000178 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000179 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000180
jvr4105ca02002-07-23 08:43:03 +0000181 # assembler interface
182
183 def getAllData(self):
184 """Assemble all data, including all subtables."""
185 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000186 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000187 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000188 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000189 # Gather all data in two passes: the absolute positions of all
190 # subtable are needed before the actual data can be assembled.
191 pos = 0
192 for table in tables:
193 table.pos = pos
194 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000195
196 for table in extTables:
197 table.pos = pos
198 pos = pos + table.getDataLength()
199
200
jvr4105ca02002-07-23 08:43:03 +0000201 data = []
202 for table in tables:
203 tableData = table.getData()
204 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000205
206 for table in extTables:
207 tableData = table.getData()
208 data.append(tableData)
209
jvr4105ca02002-07-23 08:43:03 +0000210 return "".join(data)
211
212 def getDataLength(self):
213 """Return the length of this table in bytes, without subtables."""
214 l = 0
jvr823f8cd2006-10-21 14:12:38 +0000215 if hasattr(self, "Extension"):
216 longOffset = 1
217 else:
218 longOffset = 0
jvr4105ca02002-07-23 08:43:03 +0000219 for item in self.items:
220 if hasattr(item, "getData") or hasattr(item, "getCountData"):
jvr823f8cd2006-10-21 14:12:38 +0000221 if longOffset:
222 l = l + 4 # sizeof(ULong)
223 else:
224 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000225 else:
226 l = l + len(item)
227 return l
228
229 def getData(self):
230 """Assemble the data for this writer/table, without subtables."""
231 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000232 if hasattr(self,"Extension"):
233 longOffset = 1
234 else:
235 longOffset = 0
236 pos = self.pos
237 numItems = len(items)
238 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000239 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000240
jvr4105ca02002-07-23 08:43:03 +0000241 if hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000242 if longOffset:
243 items[i] = packULong(item.pos - pos)
244 else:
245 try:
246 items[i] = packUShort(item.pos - pos)
247 except AssertionError:
248 # provide data to fix overflow problem.
249 # If the overflow is to a lookup, or from a lookup to a subtable,
250 # just report the current item.
251 if self.name in [ 'LookupList', 'Lookup']:
252 overflowErrorRecord = self.getOverflowErrorRecord(item)
253 else:
254 # overflow is within a subTable. Life is more complicated.
255 # If we split the sub-table just before the current item, we may still suffer overflow.
256 # This is because duplicate table merging is done only within an Extension subTable tree;
257 # when we split the subtable in two, some items may no longer be duplicates.
258 # Get worst case by adding up all the item lengths, depth first traversal.
259 # and then report the first item that overflows a short.
260 def getDeepItemLength(table):
261 if hasattr(table, "getDataLength"):
262 length = 0
263 for item in table.items:
264 length = length + getDeepItemLength(item)
265 else:
266 length = len(table)
267 return length
268
269 length = self.getDataLength()
270 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
271 # Coverage is first in the item list, but last in the table list,
272 # The original overflow is really in the item list. Skip the Coverage
273 # table in the following test.
274 items = items[i+1:]
275
276 for j in range(len(items)):
277 item = items[j]
278 length = length + getDeepItemLength(item)
279 if length > 65535:
280 break
281 overflowErrorRecord = self.getOverflowErrorRecord(item)
282
283
284 raise OTLOffsetOverflowError, overflowErrorRecord
285
jvr4105ca02002-07-23 08:43:03 +0000286 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000287
jvrcfadfd02002-07-22 22:13:57 +0000288 def __hash__(self):
289 # only works after self._doneWriting() has been called
290 return hash(self.items)
291
292 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100293 if type(self) != type(other): return cmp(type(self), type(other))
294 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400295
296 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000297
jvrcfadfd02002-07-22 22:13:57 +0000298 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000299 # Convert CountData references to data string items
300 # collapse duplicate table references to a unique entry
301 # "tables" are OTTableWriter objects.
302
303 # For Extension Lookup types, we can
304 # eliminate duplicates only within the tree under the Extension Lookup,
305 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000306 if internedTables is None:
307 internedTables = {}
308 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000309 iRange = range(len(items))
310
311 if hasattr(self, "Extension"):
312 newTree = 1
313 else:
314 newTree = 0
315 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000316 item = items[i]
317 if hasattr(item, "getCountData"):
318 items[i] = item.getCountData()
319 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000320 if newTree:
321 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000322 else:
jvr823f8cd2006-10-21 14:12:38 +0000323 item._doneWriting(internedTables)
324 if internedTables.has_key(item):
325 items[i] = item = internedTables[item]
326 else:
327 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000328 self.items = tuple(items)
329
jvr823f8cd2006-10-21 14:12:38 +0000330 def _gatherTables(self, tables=None, extTables=None, done=None):
331 # Convert table references in self.items tree to a flat
332 # list of tables in depth-first traversal order.
333 # "tables" are OTTableWriter objects.
334 # We do the traversal in reverse order at each level, in order to
335 # resolve duplicate references to be the last reference in the list of tables.
336 # For extension lookups, duplicate references can be merged only within the
337 # writer tree under the extension lookup.
338 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000339 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000340 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000341 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000342
343 done[self] = 1
344
345 numItems = len(self.items)
346 iRange = range(numItems)
347 iRange.reverse()
348
349 if hasattr(self, "Extension"):
350 appendExtensions = 1
351 else:
352 appendExtensions = 0
353
354 # add Coverage table if it is sorted last.
355 sortCoverageLast = 0
356 if hasattr(self, "sortCoverageLast"):
357 # Find coverage table
358 for i in range(numItems):
359 item = self.items[i]
360 if hasattr(item, "name") and (item.name == "Coverage"):
361 sortCoverageLast = 1
362 break
363 if not done.has_key(item):
364 item._gatherTables(tables, extTables, done)
365 else:
366 index = max(item.parent.keys())
367 item.parent[index + 1] = self
368
369 saveItem = None
370 for i in iRange:
371 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000372 if not hasattr(item, "getData"):
373 continue
jvr823f8cd2006-10-21 14:12:38 +0000374
375 if sortCoverageLast and (i==1) and item.name == 'Coverage':
376 # we've already 'gathered' it above
377 continue
378
379 if appendExtensions:
380 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
381 newDone = {}
382 item._gatherTables(extTables, None, newDone)
383
384 elif not done.has_key(item):
385 item._gatherTables(tables, extTables, done)
386 else:
387 index = max(item.parent.keys())
388 item.parent[index + 1] = self
389
390
jvrcfadfd02002-07-22 22:13:57 +0000391 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000392 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000393
jvr4105ca02002-07-23 08:43:03 +0000394 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000395
jvr4105ca02002-07-23 08:43:03 +0000396 def getSubWriter(self):
jvr823f8cd2006-10-21 14:12:38 +0000397 subwriter = self.__class__(self.tableType, self.valueFormat)
398 subwriter.parent = {0:self} # because some subtables have idential values, we discard
399 # the duplicates under the getAllData method. Hence some
400 # subtable writers can have more than one parent writer.
401 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000402
403 def writeUShort(self, value):
404 assert 0 <= value < 0x10000
405 self.items.append(struct.pack(">H", value))
406
407 def writeShort(self, value):
408 self.items.append(struct.pack(">h", value))
409
410 def writeLong(self, value):
411 self.items.append(struct.pack(">l", value))
412
jvr823f8cd2006-10-21 14:12:38 +0000413 def writeULong(self, value):
414 self.items.append(struct.pack(">L", value))
415
jvrd4d15132002-05-11 00:59:27 +0000416 def writeTag(self, tag):
417 assert len(tag) == 4
418 self.items.append(tag)
419
420 def writeSubTable(self, subWriter):
421 self.items.append(subWriter)
422
423 def writeCountReference(self, table, name):
424 self.items.append(CountReference(table, name))
425
426 def writeStruct(self, format, values):
427 data = apply(struct.pack, (format,) + values)
428 self.items.append(data)
429
jvr823f8cd2006-10-21 14:12:38 +0000430 def writeData(self, data):
431 self.items.append(data)
432
jvrd4d15132002-05-11 00:59:27 +0000433 def setValueFormat(self, format, which):
Behdad Esfahbod6e556aa2013-11-22 19:16:27 -0500434 self.valueFormat[which] = ValueRecordFactory(format)
jvrd4d15132002-05-11 00:59:27 +0000435
436 def writeValueRecord(self, value, font, which):
437 return self.valueFormat[which].writeValueRecord(self, font, value)
438
jvr823f8cd2006-10-21 14:12:38 +0000439 def getOverflowErrorRecord(self, item):
440 LookupListIndex = SubTableIndex = itemName = itemIndex = None
441 if self.name == 'LookupList':
442 LookupListIndex = item.repeatIndex
443 elif self.name == 'Lookup':
444 LookupListIndex = self.repeatIndex
445 SubTableIndex = item.repeatIndex
446 else:
447 itemName = item.name
448 if hasattr(item, 'repeatIndex'):
449 itemIndex = item.repeatIndex
450 if self.name == 'SubTable':
451 LookupListIndex = self.parent[0].repeatIndex
452 SubTableIndex = self.repeatIndex
453 elif self.name == 'ExtSubTable':
454 LookupListIndex = self.parent[0].parent[0].repeatIndex
455 SubTableIndex = self.parent[0].repeatIndex
456 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
457 itemName = ".".join(self.name, item.name)
458 p1 = self.parent[0]
459 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
460 itemName = ".".join(p1.name, item.name)
461 p1 = p1.parent[0]
462 if p1:
463 if p1.name == 'ExtSubTable':
464 LookupListIndex = self.parent[0].parent[0].repeatIndex
465 SubTableIndex = self.parent[0].repeatIndex
466 else:
467 LookupListIndex = self.parent[0].repeatIndex
468 SubTableIndex = self.repeatIndex
469
470 return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
471
jvrd4d15132002-05-11 00:59:27 +0000472
473class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000474 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000475 def __init__(self, table, name):
476 self.table = table
477 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000478 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000479 return packUShort(self.table[self.name])
480
481
jvr64b5c802002-05-11 10:21:36 +0000482def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000483 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000484 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000485
486
jvr823f8cd2006-10-21 14:12:38 +0000487def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000488 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000489 return struct.pack(">L", value)
490
491
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500492class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000493 def __init__(self):
494 self.compileStatus = 0 # 0 means table was created
495 # 1 means the table.read() function was called by a table which is subject
496 # to delayed compilation
497 # 2 means that it was subject to delayed compilation, and
498 # has been decompiled
499 # 3 means that the start and end fields have been filled out, and that we
500 # can use the data string rather than compiling from the table data.
501
502 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000503
jvr823f8cd2006-10-21 14:12:38 +0000504 def __getattr__(self, attr):
505 # we get here only when the table does not have the attribute.
506 # This method ovveride exists so that we can try to de-compile
507 # a table which is subject to delayed decompilation, and then try
508 # to get the value again after decompilation.
509 self.recurse +=1
510 if self.recurse > 2:
511 # shouldn't ever get here - we should only get to two levels of recursion.
512 # this guards against self.decompile NOT setting compileStatus to other than 1.
513 raise AttributeError, attr
514 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500515 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000516 val = getattr(self, attr)
517 self.recurse -=1
518 return val
519
520 raise AttributeError, attr
521
522
jvr64b5c802002-05-11 10:21:36 +0000523 """Generic base class for all OpenType (sub)tables."""
524
jvrd4d15132002-05-11 00:59:27 +0000525 def getConverters(self):
526 return self.converters
527
528 def getConverterByName(self, name):
529 return self.convertersByName[name]
530
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500531 def decompile(self, reader, font, countVars=None):
jvr823f8cd2006-10-21 14:12:38 +0000532 self.compileStatus = 2 # table has been decompiled.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500533 if countVars is None:
534 countVars = {}
jvrf7ef96c2002-09-10 19:26:38 +0000535 self.readFormat(reader)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500536 counts = []
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 Esfahbod41caf2d2013-11-22 19:12:14 -0500549 for i in range(countVars[conv.repeat] + conv.repeatOffset):
550 l.append(conv.read(reader, font, countVars))
jvrd4d15132002-05-11 00:59:27 +0000551 table[conv.name] = l
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500552 if conv.repeat in counts:
553 del countVars[conv.repeat]
554 counts.remove(conv.repeat)
555
jvrd4d15132002-05-11 00:59:27 +0000556 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500557 table[conv.name] = conv.read(reader, font, countVars)
558 if conv.isCount:
559 counts.append(conv.name)
560 countVars[conv.name] = table[conv.name]
561
562 for count in counts:
563 del countVars[count]
564
jvrd4d15132002-05-11 00:59:27 +0000565 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500566
jvrd4d15132002-05-11 00:59:27 +0000567 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000568
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500569 def ensureDecompiled(self):
570 if self.compileStatus != 1:
571 return
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500572 self.decompile(self.reader, self.font, self.countVars)
573 del self.reader, self.font, self.countVars
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500574
jvr823f8cd2006-10-21 14:12:38 +0000575 def preCompile(self):
576 pass # used only by the LookupList class
577
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500578 def compile(self, writer, font, countVars=None):
579 if countVars is None:
580 countVars = {}
581 counts = []
jvrd4d15132002-05-11 00:59:27 +0000582 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000583
584 if hasattr(self, 'sortCoverageLast'):
585 writer.sortCoverageLast = 1
586
jvrf7ef96c2002-09-10 19:26:38 +0000587 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000588 for conv in self.getConverters():
589 value = table.get(conv.name)
590 if conv.repeat:
591 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000592 value = []
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500593 countVars[conv.repeat](len(value) - conv.repeatOffset)
jvr823f8cd2006-10-21 14:12:38 +0000594 for i in range(len(value)):
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500595 conv.write(writer, font, countVars, value[i], i)
596 if conv.repeat in counts:
597 del countVars[conv.repeat]
598 counts.remove(conv.repeat)
jvrd4d15132002-05-11 00:59:27 +0000599 elif conv.isCount:
600 # Special-case Count values.
601 # Assumption: a Count field will *always* precede
602 # the actual array.
603 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500604 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000605 table[conv.name] = None
606 # We add a reference: by the time the data is assembled
607 # the Count value will be filled in.
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500608 name = conv.name
609 writer.writeCountReference(table, name)
610 counts.append(name)
Behdad Esfahbod24f978c2013-11-24 15:08:06 -0500611 def storeValueFactory(table, name):
612 def storeValue(value):
613 if table[name] is None:
614 table[name] = value
615 else:
616 assert table[name] == value, (table[name], value)
617 return storeValue
618 countVars[name] = storeValueFactory(table, name)
jvrd4d15132002-05-11 00:59:27 +0000619 else:
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500620 conv.write(writer, font, countVars, value)
621
622 for count in counts:
623 del countVars[count]
jvrd4d15132002-05-11 00:59:27 +0000624
jvrf7ef96c2002-09-10 19:26:38 +0000625 def readFormat(self, reader):
626 pass
627
628 def writeFormat(self, writer):
629 pass
630
jvrd4d15132002-05-11 00:59:27 +0000631 def postRead(self, table, font):
632 self.__dict__.update(table)
633
634 def preWrite(self, font):
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500635 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000636 return self.__dict__.copy()
637
638 def toXML(self, xmlWriter, font, attrs=None):
639 tableName = self.__class__.__name__
640 if attrs is None:
641 attrs = []
642 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000643 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000644 xmlWriter.begintag(tableName, attrs)
645 xmlWriter.newline()
646 self.toXML2(xmlWriter, font)
647 xmlWriter.endtag(tableName)
648 xmlWriter.newline()
649
650 def toXML2(self, xmlWriter, font):
651 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
652 # This is because in TTX our parent writes our main tag, and in otBase.py we
653 # do it ourselves. I think I'm getting schizophrenic...
654 for conv in self.getConverters():
655 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000656 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000657 for i in range(len(value)):
658 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000659 conv.xmlWrite(xmlWriter, font, item, conv.name,
660 [("index", i)])
661 else:
662 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000663
664 def fromXML(self, (name, attrs, content), font):
665 try:
666 conv = self.getConverterByName(name)
667 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000668 raise # XXX on KeyError, raise nice error
669 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000670 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000671 seq = getattr(self, conv.name, None)
672 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000673 seq = []
jvr64b5c802002-05-11 10:21:36 +0000674 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000675 seq.append(value)
676 else:
jvr64b5c802002-05-11 10:21:36 +0000677 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000678
679 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100680 if type(self) != type(other): return cmp(type(self), type(other))
681 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400682
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500683 self.ensureDecompiled()
684
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400685 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000686
687
688class FormatSwitchingBaseTable(BaseTable):
689
jvrcfadfd02002-07-22 22:13:57 +0000690 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000691 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
692
jvrd4d15132002-05-11 00:59:27 +0000693 def getConverters(self):
694 return self.converters[self.Format]
695
696 def getConverterByName(self, name):
697 return self.convertersByName[self.Format][name]
698
jvrf7ef96c2002-09-10 19:26:38 +0000699 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000700 self.Format = reader.readUShort()
701 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000702
jvrf7ef96c2002-09-10 19:26:38 +0000703 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000704 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000705
706
jvr64b5c802002-05-11 10:21:36 +0000707#
708# Support for ValueRecords
709#
710# This data type is so different from all other OpenType data types that
711# it requires quite a bit of code for itself. It even has special support
712# in OTTableReader and OTTableWriter...
713#
714
jvrd4d15132002-05-11 00:59:27 +0000715valueRecordFormat = [
716# Mask Name isDevice signed
717 (0x0001, "XPlacement", 0, 1),
718 (0x0002, "YPlacement", 0, 1),
719 (0x0004, "XAdvance", 0, 1),
720 (0x0008, "YAdvance", 0, 1),
721 (0x0010, "XPlaDevice", 1, 0),
722 (0x0020, "YPlaDevice", 1, 0),
723 (0x0040, "XAdvDevice", 1, 0),
724 (0x0080, "YAdvDevice", 1, 0),
725# reserved:
726 (0x0100, "Reserved1", 0, 0),
727 (0x0200, "Reserved2", 0, 0),
728 (0x0400, "Reserved3", 0, 0),
729 (0x0800, "Reserved4", 0, 0),
730 (0x1000, "Reserved5", 0, 0),
731 (0x2000, "Reserved6", 0, 0),
732 (0x4000, "Reserved7", 0, 0),
733 (0x8000, "Reserved8", 0, 0),
734]
735
736def _buildDict():
737 d = {}
738 for mask, name, isDevice, signed in valueRecordFormat:
739 d[name] = mask, isDevice, signed
740 return d
741
742valueRecordFormatDict = _buildDict()
743
744
745class ValueRecordFactory:
746
jvr64b5c802002-05-11 10:21:36 +0000747 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500748
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500749 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000750 format = []
751 for mask, name, isDevice, signed in valueRecordFormat:
752 if valueFormat & mask:
753 format.append((name, isDevice, signed))
754 self.format = format
755
756 def readValueRecord(self, reader, font):
757 format = self.format
758 if not format:
759 return None
760 valueRecord = ValueRecord()
761 for name, isDevice, signed in format:
762 if signed:
763 value = reader.readShort()
764 else:
765 value = reader.readUShort()
766 if isDevice:
767 if value:
768 import otTables
769 subReader = reader.getSubReader(value)
770 value = getattr(otTables, name)()
771 value.decompile(subReader, font)
772 else:
773 value = None
774 setattr(valueRecord, name, value)
775 return valueRecord
776
777 def writeValueRecord(self, writer, font, valueRecord):
778 for name, isDevice, signed in self.format:
779 value = getattr(valueRecord, name, 0)
780 if isDevice:
781 if value:
782 subWriter = writer.getSubWriter()
783 writer.writeSubTable(subWriter)
784 value.compile(subWriter, font)
785 else:
786 writer.writeUShort(0)
787 elif signed:
788 writer.writeShort(value)
789 else:
790 writer.writeUShort(value)
791
792
793class ValueRecord:
794
795 # see ValueRecordFactory
796
797 def getFormat(self):
798 format = 0
799 for name in self.__dict__.keys():
800 format = format | valueRecordFormatDict[name][0]
801 return format
802
803 def toXML(self, xmlWriter, font, valueName, attrs=None):
804 if attrs is None:
805 simpleItems = []
806 else:
807 simpleItems = list(attrs)
808 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
809 if hasattr(self, name):
810 simpleItems.append((name, getattr(self, name)))
811 deviceItems = []
812 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
813 if hasattr(self, name):
814 device = getattr(self, name)
815 if device is not None:
816 deviceItems.append((name, device))
817 if deviceItems:
818 xmlWriter.begintag(valueName, simpleItems)
819 xmlWriter.newline()
820 for name, deviceRecord in deviceItems:
821 if deviceRecord is not None:
822 deviceRecord.toXML(xmlWriter, font)
823 xmlWriter.endtag(valueName)
824 xmlWriter.newline()
825 else:
826 xmlWriter.simpletag(valueName, simpleItems)
827 xmlWriter.newline()
828
829 def fromXML(self, (name, attrs, content), font):
830 import otTables
831 for k, v in attrs.items():
832 setattr(self, k, int(v))
833 for element in content:
834 if type(element) <> TupleType:
835 continue
836 name, attrs, content = element
837 value = getattr(otTables, name)()
838 for elem2 in content:
839 if type(elem2) <> TupleType:
840 continue
841 value.fromXML(elem2, font)
842 setattr(self, name, value)
843
844 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100845 if type(self) != type(other): return cmp(type(self), type(other))
846 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000847
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400848 return cmp(self.__dict__, other.__dict__)