blob: d556e63a1c7287e8a8faad6479c01f4e4a29a696 [file] [log] [blame]
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -05001from .DefaultTable import DefaultTable
2from . import otData
jvrd4d15132002-05-11 00:59:27 +00003import 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):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -050033 from . import otTables
Behdad Esfahbod0585b642013-11-22 16:20:59 -050034 cachingStats = None if True else {}
Behdad Esfahbod79f73442013-11-26 17:07:37 -050035 class GlobalState:
36 def __init__(self, tableType, cachingStats):
37 self.tableType = tableType
38 self.cachingStats = cachingStats
39 globalState = GlobalState(tableType=self.tableTag,
40 cachingStats=cachingStats)
41 reader = OTTableReader(data, globalState)
jvrd4d15132002-05-11 00:59:27 +000042 tableClass = getattr(otTables, self.tableTag)
43 self.table = tableClass()
44 self.table.decompile(reader, font)
Behdad Esfahbod0585b642013-11-22 16:20:59 -050045 if cachingStats:
jvrcfadfd02002-07-22 22:13:57 +000046 stats = [(v, k) for k, v in cachingStats.items()]
47 stats.sort()
48 stats.reverse()
49 print "cachingsstats for ", self.tableTag
50 for v, k in stats:
51 if v < 2:
52 break
53 print v, k
54 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000055
56 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000057 """ Create a top-level OTFWriter for the GPOS/GSUB table.
58 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000059 for each 'converter' record in the table converter list
60 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000061 - For simple items, the write method adds a string to the
62 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000063 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000064 to the writer's self.items, then calls the item's compile method.
65 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
66 each writer representing a table, and the writer.items list containing
67 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000068 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000069 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000070 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
71 Traverse the flat list of tables, calling getDataLength on each to update their position
72 Traverse the flat list of tables again, calling getData each get the data in the table, now that
73 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000074
75 If a lookup subtable overflows an offset, we have to start all over.
76 """
Behdad Esfahbod79f73442013-11-26 17:07:37 -050077 class GlobalState:
78 def __init__(self, tableType):
79 self.tableType = tableType
80 globalState = GlobalState(tableType=self.tableTag)
81 writer = OTTableWriter(globalState)
jvr823f8cd2006-10-21 14:12:38 +000082 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000083 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000084 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000085
jvrd4d15132002-05-11 00:59:27 +000086 def toXML(self, writer, font):
87 self.table.toXML2(writer, font)
88
89 def fromXML(self, (name, attrs, content), font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -050090 from . import otTables
jvrd4d15132002-05-11 00:59:27 +000091 if not hasattr(self, "table"):
92 tableClass = getattr(otTables, self.tableTag)
93 self.table = tableClass()
94 self.table.fromXML((name, attrs, content), font)
95
96
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050097class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050098
jvr64b5c802002-05-11 10:21:36 +000099 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500100
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500101 __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
Behdad Esfahbod79817042013-11-24 16:59:42 -0500102
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500103 def __init__(self, data, globalState={}, localState=None, offset=0):
jvrd4d15132002-05-11 00:59:27 +0000104 self.data = data
105 self.offset = offset
106 self.pos = offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500107 self.globalState = globalState
108 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500109
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500110 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000111 offset = self.offset + offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500112 cachingStats = self.globalState.cachingStats
113 if cachingStats is not None:
114 cachingStats[offset] = cachingStats.get(offset, 0) + 1
115 return self.__class__(self.data, self.globalState, self.localState, offset)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500116
jvrd4d15132002-05-11 00:59:27 +0000117 def readUShort(self):
118 pos = self.pos
119 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000120 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000121 self.pos = newpos
122 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500123
jvrd4d15132002-05-11 00:59:27 +0000124 def readShort(self):
125 pos = self.pos
126 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000127 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000128 self.pos = newpos
129 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500130
jvrd4d15132002-05-11 00:59:27 +0000131 def readLong(self):
132 pos = self.pos
133 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000134 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000135 self.pos = newpos
136 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500137
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500138 def readUInt24(self):
139 pos = self.pos
140 newpos = pos + 3
141 value = (ord(self.data[pos]) << 16) | (ord(self.data[pos+1]) << 8) | ord(self.data[pos+2])
142 value, = struct.unpack(">H", self.data[pos:newpos])
143 self.pos = newpos
144 return value
145
jvr823f8cd2006-10-21 14:12:38 +0000146 def readULong(self):
147 pos = self.pos
148 newpos = pos + 4
149 value, = struct.unpack(">L", self.data[pos:newpos])
150 self.pos = newpos
151 return value
152
jvrd4d15132002-05-11 00:59:27 +0000153 def readTag(self):
154 pos = self.pos
155 newpos = pos + 4
156 value = self.data[pos:newpos]
157 assert len(value) == 4
158 self.pos = newpos
159 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500160
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500161 def __setitem__(self, name, value):
162 state = self.localState.copy() if self.localState else dict()
163 state[name] = value
164 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500165
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500166 def __getitem__(self, name):
167 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500168
jvrd4d15132002-05-11 00:59:27 +0000169
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500170class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000171
jvr64b5c802002-05-11 10:21:36 +0000172 """Helper class to gather and assemble data for OpenType tables."""
173
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500174 def __init__(self, globalState, localState=None):
jvrd4d15132002-05-11 00:59:27 +0000175 self.items = []
jvrcfadfd02002-07-22 22:13:57 +0000176 self.pos = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500177 self.globalState = globalState
178 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500179
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500180 def __setitem__(self, name, value):
181 state = self.localState.copy() if self.localState else dict()
182 state[name] = value
183 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500184
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500185 def __getitem__(self, name):
186 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500187
jvr4105ca02002-07-23 08:43:03 +0000188 # assembler interface
189
190 def getAllData(self):
191 """Assemble all data, including all subtables."""
192 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000193 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000194 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000195 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000196 # Gather all data in two passes: the absolute positions of all
197 # subtable are needed before the actual data can be assembled.
198 pos = 0
199 for table in tables:
200 table.pos = pos
201 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000202
203 for table in extTables:
204 table.pos = pos
205 pos = pos + table.getDataLength()
206
207
jvr4105ca02002-07-23 08:43:03 +0000208 data = []
209 for table in tables:
210 tableData = table.getData()
211 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000212
213 for table in extTables:
214 tableData = table.getData()
215 data.append(tableData)
216
jvr4105ca02002-07-23 08:43:03 +0000217 return "".join(data)
218
219 def getDataLength(self):
220 """Return the length of this table in bytes, without subtables."""
221 l = 0
222 for item in self.items:
223 if hasattr(item, "getData") or hasattr(item, "getCountData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500224 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000225 l = l + 4 # sizeof(ULong)
226 else:
227 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000228 else:
229 l = l + len(item)
230 return l
231
232 def getData(self):
233 """Assemble the data for this writer/table, without subtables."""
234 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000235 pos = self.pos
236 numItems = len(items)
237 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000238 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000239
jvr4105ca02002-07-23 08:43:03 +0000240 if hasattr(item, "getData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500241 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000242 items[i] = packULong(item.pos - pos)
243 else:
244 try:
245 items[i] = packUShort(item.pos - pos)
246 except AssertionError:
247 # provide data to fix overflow problem.
Behdad Esfahbod58acba22013-11-24 20:08:05 -0500248 # If the overflow is to a lookup, or from a lookup to a subtable,
jvr823f8cd2006-10-21 14:12:38 +0000249 # just report the current item.
250 if self.name in [ 'LookupList', 'Lookup']:
251 overflowErrorRecord = self.getOverflowErrorRecord(item)
252 else:
253 # overflow is within a subTable. Life is more complicated.
254 # If we split the sub-table just before the current item, we may still suffer overflow.
255 # This is because duplicate table merging is done only within an Extension subTable tree;
256 # when we split the subtable in two, some items may no longer be duplicates.
257 # Get worst case by adding up all the item lengths, depth first traversal.
258 # and then report the first item that overflows a short.
259 def getDeepItemLength(table):
260 if hasattr(table, "getDataLength"):
261 length = 0
262 for item in table.items:
263 length = length + getDeepItemLength(item)
264 else:
265 length = len(table)
266 return length
267
268 length = self.getDataLength()
269 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
270 # Coverage is first in the item list, but last in the table list,
271 # The original overflow is really in the item list. Skip the Coverage
272 # table in the following test.
273 items = items[i+1:]
274
275 for j in range(len(items)):
276 item = items[j]
277 length = length + getDeepItemLength(item)
278 if length > 65535:
279 break
280 overflowErrorRecord = self.getOverflowErrorRecord(item)
281
282
283 raise OTLOffsetOverflowError, overflowErrorRecord
284
jvr4105ca02002-07-23 08:43:03 +0000285 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000286
jvrcfadfd02002-07-22 22:13:57 +0000287 def __hash__(self):
288 # only works after self._doneWriting() has been called
289 return hash(self.items)
290
291 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100292 if type(self) != type(other): return cmp(type(self), type(other))
293 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400294
295 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000296
jvrcfadfd02002-07-22 22:13:57 +0000297 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000298 # Convert CountData references to data string items
299 # collapse duplicate table references to a unique entry
300 # "tables" are OTTableWriter objects.
301
302 # For Extension Lookup types, we can
303 # eliminate duplicates only within the tree under the Extension Lookup,
304 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000305 if internedTables is None:
306 internedTables = {}
307 items = self.items
jvr823f8cd2006-10-21 14:12:38 +0000308 iRange = range(len(items))
309
310 if hasattr(self, "Extension"):
311 newTree = 1
312 else:
313 newTree = 0
314 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000315 item = items[i]
316 if hasattr(item, "getCountData"):
317 items[i] = item.getCountData()
318 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000319 if newTree:
320 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000321 else:
jvr823f8cd2006-10-21 14:12:38 +0000322 item._doneWriting(internedTables)
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500323 if item in internedTables:
jvr823f8cd2006-10-21 14:12:38 +0000324 items[i] = item = internedTables[item]
325 else:
326 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000327 self.items = tuple(items)
328
jvr823f8cd2006-10-21 14:12:38 +0000329 def _gatherTables(self, tables=None, extTables=None, done=None):
330 # Convert table references in self.items tree to a flat
331 # list of tables in depth-first traversal order.
332 # "tables" are OTTableWriter objects.
333 # We do the traversal in reverse order at each level, in order to
334 # resolve duplicate references to be the last reference in the list of tables.
335 # For extension lookups, duplicate references can be merged only within the
336 # writer tree under the extension lookup.
337 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000338 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000339 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000340 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000341
342 done[self] = 1
343
344 numItems = len(self.items)
345 iRange = range(numItems)
346 iRange.reverse()
347
348 if hasattr(self, "Extension"):
349 appendExtensions = 1
350 else:
351 appendExtensions = 0
352
353 # add Coverage table if it is sorted last.
354 sortCoverageLast = 0
355 if hasattr(self, "sortCoverageLast"):
356 # Find coverage table
357 for i in range(numItems):
358 item = self.items[i]
359 if hasattr(item, "name") and (item.name == "Coverage"):
360 sortCoverageLast = 1
361 break
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500362 if item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000363 item._gatherTables(tables, extTables, done)
364 else:
365 index = max(item.parent.keys())
366 item.parent[index + 1] = self
367
368 saveItem = None
369 for i in iRange:
370 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000371 if not hasattr(item, "getData"):
372 continue
jvr823f8cd2006-10-21 14:12:38 +0000373
374 if sortCoverageLast and (i==1) and item.name == 'Coverage':
375 # we've already 'gathered' it above
376 continue
377
378 if appendExtensions:
379 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
380 newDone = {}
381 item._gatherTables(extTables, None, newDone)
382
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500383 elif item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000384 item._gatherTables(tables, extTables, done)
385 else:
386 index = max(item.parent.keys())
387 item.parent[index + 1] = self
388
389
jvrcfadfd02002-07-22 22:13:57 +0000390 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000391 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000392
jvr4105ca02002-07-23 08:43:03 +0000393 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000394
jvr4105ca02002-07-23 08:43:03 +0000395 def getSubWriter(self):
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500396 subwriter = self.__class__(self.globalState, self.localState)
jvr823f8cd2006-10-21 14:12:38 +0000397 subwriter.parent = {0:self} # because some subtables have idential values, we discard
398 # the duplicates under the getAllData method. Hence some
399 # subtable writers can have more than one parent writer.
400 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000401
402 def writeUShort(self, value):
403 assert 0 <= value < 0x10000
404 self.items.append(struct.pack(">H", value))
405
406 def writeShort(self, value):
407 self.items.append(struct.pack(">h", value))
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500408
409 def writeUInt24(self, value):
410 assert 0 <= value < 0x1000000
411 self.items.append(''.join(chr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff)))
jvrd4d15132002-05-11 00:59:27 +0000412
413 def writeLong(self, value):
414 self.items.append(struct.pack(">l", value))
415
jvr823f8cd2006-10-21 14:12:38 +0000416 def writeULong(self, value):
417 self.items.append(struct.pack(">L", value))
418
jvrd4d15132002-05-11 00:59:27 +0000419 def writeTag(self, tag):
420 assert len(tag) == 4
421 self.items.append(tag)
422
423 def writeSubTable(self, subWriter):
424 self.items.append(subWriter)
425
426 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500427 ref = CountReference(table, name)
428 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500429 return ref
jvrd4d15132002-05-11 00:59:27 +0000430
431 def writeStruct(self, format, values):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -0500432 data = struct.pack(*(format,) + values)
jvrd4d15132002-05-11 00:59:27 +0000433 self.items.append(data)
434
jvr823f8cd2006-10-21 14:12:38 +0000435 def writeData(self, data):
436 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000437
jvr823f8cd2006-10-21 14:12:38 +0000438 def getOverflowErrorRecord(self, item):
439 LookupListIndex = SubTableIndex = itemName = itemIndex = None
440 if self.name == 'LookupList':
441 LookupListIndex = item.repeatIndex
442 elif self.name == 'Lookup':
443 LookupListIndex = self.repeatIndex
444 SubTableIndex = item.repeatIndex
445 else:
446 itemName = item.name
447 if hasattr(item, 'repeatIndex'):
448 itemIndex = item.repeatIndex
449 if self.name == 'SubTable':
450 LookupListIndex = self.parent[0].repeatIndex
451 SubTableIndex = self.repeatIndex
452 elif self.name == 'ExtSubTable':
453 LookupListIndex = self.parent[0].parent[0].repeatIndex
454 SubTableIndex = self.parent[0].repeatIndex
455 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
456 itemName = ".".join(self.name, item.name)
457 p1 = self.parent[0]
458 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
459 itemName = ".".join(p1.name, item.name)
460 p1 = p1.parent[0]
461 if p1:
462 if p1.name == 'ExtSubTable':
463 LookupListIndex = self.parent[0].parent[0].repeatIndex
464 SubTableIndex = self.parent[0].repeatIndex
465 else:
466 LookupListIndex = self.parent[0].repeatIndex
467 SubTableIndex = self.repeatIndex
468
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500469 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000470
jvrd4d15132002-05-11 00:59:27 +0000471
472class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000473 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000474 def __init__(self, table, name):
475 self.table = table
476 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500477 def setValue(self, value):
478 table = self.table
479 name = self.name
480 if table[name] is None:
481 table[name] = value
482 else:
Behdad Esfahbod1f0eed82013-11-26 18:41:53 -0500483 assert table[name] == value, (name, table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000484 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000485 return packUShort(self.table[self.name])
486
487
jvr64b5c802002-05-11 10:21:36 +0000488def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000489 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000490 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000491
492
jvr823f8cd2006-10-21 14:12:38 +0000493def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000494 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000495 return struct.pack(">L", value)
496
497
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500498class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000499 def __init__(self):
500 self.compileStatus = 0 # 0 means table was created
501 # 1 means the table.read() function was called by a table which is subject
502 # to delayed compilation
503 # 2 means that it was subject to delayed compilation, and
504 # has been decompiled
jvr823f8cd2006-10-21 14:12:38 +0000505
506 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000507
jvr823f8cd2006-10-21 14:12:38 +0000508 def __getattr__(self, attr):
509 # we get here only when the table does not have the attribute.
510 # This method ovveride exists so that we can try to de-compile
511 # a table which is subject to delayed decompilation, and then try
512 # to get the value again after decompilation.
513 self.recurse +=1
514 if self.recurse > 2:
515 # shouldn't ever get here - we should only get to two levels of recursion.
516 # this guards against self.decompile NOT setting compileStatus to other than 1.
517 raise AttributeError, attr
518 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500519 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000520 val = getattr(self, attr)
521 self.recurse -=1
522 return val
523
524 raise AttributeError, attr
525
526
jvr64b5c802002-05-11 10:21:36 +0000527 """Generic base class for all OpenType (sub)tables."""
528
jvrd4d15132002-05-11 00:59:27 +0000529 def getConverters(self):
530 return self.converters
531
532 def getConverterByName(self, name):
533 return self.convertersByName[name]
534
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500535 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000536 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000537 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000538 table = {}
539 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500540 converters = self.getConverters()
541 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000542 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500543 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000544 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000545 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500546 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000547 table["ExtensionLookupType"])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500548 if conv.name == "FeatureParams":
549 conv = conv.getConverter(reader["FeatureTag"])
jvrd4d15132002-05-11 00:59:27 +0000550 if conv.repeat:
551 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500552 if conv.repeat in table:
553 countValue = table[conv.repeat]
554 else:
555 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500556 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500557 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500558 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000559 table[conv.name] = l
560 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500561 if conv.aux and not eval(conv.aux, None, table):
562 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500563 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500564 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500565 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500566
jvrd4d15132002-05-11 00:59:27 +0000567 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500568
jvrd4d15132002-05-11 00:59:27 +0000569 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000570
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500571 def ensureDecompiled(self):
572 if self.compileStatus != 1:
573 return
Behdad Esfahbodf4e61ae2013-11-26 17:37:03 -0500574 self.decompile(self.reader, self.font)
575 del self.reader, self.font
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500576
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500577 def compile(self, writer, font):
Behdad Esfahbod3ac9e632013-11-26 19:42:55 -0500578 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000579 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000580
581 if hasattr(self, 'sortCoverageLast'):
582 writer.sortCoverageLast = 1
583
jvrf7ef96c2002-09-10 19:26:38 +0000584 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000585 for conv in self.getConverters():
586 value = table.get(conv.name)
587 if conv.repeat:
588 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000589 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500590 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500591 if conv.repeat in table:
592 ref = table[conv.repeat]
593 table[conv.repeat] = None
594 ref.setValue(countValue)
595 else:
596 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500597 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000598 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500599 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000600 elif conv.isCount:
601 # Special-case Count values.
602 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500603 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000604 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500605 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000606 # We add a reference: by the time the data is assembled
607 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500608 ref = writer.writeCountReference(table, conv.name)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500609 if conv.isPropagated:
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500610 table[conv.name] = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500611 writer[conv.name] = ref
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500612 else:
613 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000614 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500615 if conv.aux and not eval(conv.aux, None, table):
616 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500617 conv.write(writer, font, table, value)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500618 if conv.isPropagated:
619 writer[conv.name] = value
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):
631 return self.__dict__.copy()
632
633 def toXML(self, xmlWriter, font, attrs=None):
634 tableName = self.__class__.__name__
635 if attrs is None:
636 attrs = []
637 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000638 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000639 xmlWriter.begintag(tableName, attrs)
640 xmlWriter.newline()
641 self.toXML2(xmlWriter, font)
642 xmlWriter.endtag(tableName)
643 xmlWriter.newline()
644
645 def toXML2(self, xmlWriter, font):
646 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
647 # This is because in TTX our parent writes our main tag, and in otBase.py we
648 # do it ourselves. I think I'm getting schizophrenic...
649 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000650 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500651 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000652 for i in range(len(value)):
653 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000654 conv.xmlWrite(xmlWriter, font, item, conv.name,
655 [("index", i)])
656 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500657 if conv.aux and not eval(conv.aux, None, vars(self)):
658 continue
659 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000660 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000661
662 def fromXML(self, (name, attrs, content), font):
663 try:
664 conv = self.getConverterByName(name)
665 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000666 raise # XXX on KeyError, raise nice error
667 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000668 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000669 seq = getattr(self, conv.name, None)
670 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000671 seq = []
jvr64b5c802002-05-11 10:21:36 +0000672 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000673 seq.append(value)
674 else:
jvr64b5c802002-05-11 10:21:36 +0000675 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000676
677 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100678 if type(self) != type(other): return cmp(type(self), type(other))
679 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400680
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500681 self.ensureDecompiled()
682
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400683 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000684
685
686class FormatSwitchingBaseTable(BaseTable):
687
jvrcfadfd02002-07-22 22:13:57 +0000688 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000689 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
690
jvrd4d15132002-05-11 00:59:27 +0000691 def getConverters(self):
692 return self.converters[self.Format]
693
694 def getConverterByName(self, name):
695 return self.convertersByName[self.Format][name]
696
jvrf7ef96c2002-09-10 19:26:38 +0000697 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000698 self.Format = reader.readUShort()
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500699 assert self.Format != 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000700
jvrf7ef96c2002-09-10 19:26:38 +0000701 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000702 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000703
704
jvr64b5c802002-05-11 10:21:36 +0000705#
706# Support for ValueRecords
707#
708# This data type is so different from all other OpenType data types that
709# it requires quite a bit of code for itself. It even has special support
710# in OTTableReader and OTTableWriter...
711#
712
jvrd4d15132002-05-11 00:59:27 +0000713valueRecordFormat = [
714# Mask Name isDevice signed
715 (0x0001, "XPlacement", 0, 1),
716 (0x0002, "YPlacement", 0, 1),
717 (0x0004, "XAdvance", 0, 1),
718 (0x0008, "YAdvance", 0, 1),
719 (0x0010, "XPlaDevice", 1, 0),
720 (0x0020, "YPlaDevice", 1, 0),
721 (0x0040, "XAdvDevice", 1, 0),
722 (0x0080, "YAdvDevice", 1, 0),
723# reserved:
724 (0x0100, "Reserved1", 0, 0),
725 (0x0200, "Reserved2", 0, 0),
726 (0x0400, "Reserved3", 0, 0),
727 (0x0800, "Reserved4", 0, 0),
728 (0x1000, "Reserved5", 0, 0),
729 (0x2000, "Reserved6", 0, 0),
730 (0x4000, "Reserved7", 0, 0),
731 (0x8000, "Reserved8", 0, 0),
732]
733
734def _buildDict():
735 d = {}
736 for mask, name, isDevice, signed in valueRecordFormat:
737 d[name] = mask, isDevice, signed
738 return d
739
740valueRecordFormatDict = _buildDict()
741
742
743class ValueRecordFactory:
744
jvr64b5c802002-05-11 10:21:36 +0000745 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500746
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500747 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000748 format = []
749 for mask, name, isDevice, signed in valueRecordFormat:
750 if valueFormat & mask:
751 format.append((name, isDevice, signed))
752 self.format = format
753
754 def readValueRecord(self, reader, font):
755 format = self.format
756 if not format:
757 return None
758 valueRecord = ValueRecord()
759 for name, isDevice, signed in format:
760 if signed:
761 value = reader.readShort()
762 else:
763 value = reader.readUShort()
764 if isDevice:
765 if value:
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500766 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000767 subReader = reader.getSubReader(value)
768 value = getattr(otTables, name)()
769 value.decompile(subReader, font)
770 else:
771 value = None
772 setattr(valueRecord, name, value)
773 return valueRecord
774
775 def writeValueRecord(self, writer, font, valueRecord):
776 for name, isDevice, signed in self.format:
777 value = getattr(valueRecord, name, 0)
778 if isDevice:
779 if value:
780 subWriter = writer.getSubWriter()
781 writer.writeSubTable(subWriter)
782 value.compile(subWriter, font)
783 else:
784 writer.writeUShort(0)
785 elif signed:
786 writer.writeShort(value)
787 else:
788 writer.writeUShort(value)
789
790
791class ValueRecord:
792
793 # see ValueRecordFactory
794
795 def getFormat(self):
796 format = 0
797 for name in self.__dict__.keys():
798 format = format | valueRecordFormatDict[name][0]
799 return format
800
801 def toXML(self, xmlWriter, font, valueName, attrs=None):
802 if attrs is None:
803 simpleItems = []
804 else:
805 simpleItems = list(attrs)
806 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
807 if hasattr(self, name):
808 simpleItems.append((name, getattr(self, name)))
809 deviceItems = []
810 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
811 if hasattr(self, name):
812 device = getattr(self, name)
813 if device is not None:
814 deviceItems.append((name, device))
815 if deviceItems:
816 xmlWriter.begintag(valueName, simpleItems)
817 xmlWriter.newline()
818 for name, deviceRecord in deviceItems:
819 if deviceRecord is not None:
820 deviceRecord.toXML(xmlWriter, font)
821 xmlWriter.endtag(valueName)
822 xmlWriter.newline()
823 else:
824 xmlWriter.simpletag(valueName, simpleItems)
825 xmlWriter.newline()
826
827 def fromXML(self, (name, attrs, content), font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500828 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000829 for k, v in attrs.items():
830 setattr(self, k, int(v))
831 for element in content:
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500832 if type(element) != TupleType:
jvrd4d15132002-05-11 00:59:27 +0000833 continue
834 name, attrs, content = element
835 value = getattr(otTables, name)()
836 for elem2 in content:
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500837 if type(elem2) != TupleType:
jvrd4d15132002-05-11 00:59:27 +0000838 continue
839 value.fromXML(elem2, font)
840 setattr(self, name, value)
841
842 def __cmp__(self, other):
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100843 if type(self) != type(other): return cmp(type(self), type(other))
844 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000845
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400846 return cmp(self.__dict__, other.__dict__)