blob: a2a107756ddbddb09819ef5d701588d7a8b2ba49 [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
jvrd4d15132002-05-11 00:59:27 +00004
jvr823f8cd2006-10-21 14:12:38 +00005class OverflowErrorRecord:
6 def __init__(self, overflowTuple):
7 self.tableType = overflowTuple[0]
8 self.LookupListIndex = overflowTuple[1]
9 self.SubTableIndex = overflowTuple[2]
10 self.itemName = overflowTuple[3]
11 self.itemIndex = overflowTuple[4]
12
13 def __repr__(self):
14 return str((self.tableType, "LookupIndex:", self.LookupListIndex, "SubTableIndex:", self.SubTableIndex, "ItemName:", self.itemName, "ItemIndex:", self.itemIndex))
15
16class OTLOffsetOverflowError(Exception):
17 def __init__(self, overflowErrorRecord):
18 self.value = overflowErrorRecord
19
20 def __str__(self):
21 return repr(self.value)
22
jvrd4d15132002-05-11 00:59:27 +000023
24class BaseTTXConverter(DefaultTable):
25
jvr3a6aa232003-09-02 19:23:13 +000026 """Generic base class for TTX table converters. It functions as an
27 adapter between the TTX (ttLib actually) table model and the model
28 we use for OpenType tables, which is necessarily subtly different.
29 """
jvr64b5c802002-05-11 10:21:36 +000030
jvrd4d15132002-05-11 00:59:27 +000031 def decompile(self, data, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -050032 from . import otTables
Behdad Esfahbod0585b642013-11-22 16:20:59 -050033 cachingStats = None if True else {}
Behdad Esfahbod79f73442013-11-26 17:07:37 -050034 class GlobalState:
35 def __init__(self, tableType, cachingStats):
36 self.tableType = tableType
37 self.cachingStats = cachingStats
38 globalState = GlobalState(tableType=self.tableTag,
39 cachingStats=cachingStats)
40 reader = OTTableReader(data, globalState)
jvrd4d15132002-05-11 00:59:27 +000041 tableClass = getattr(otTables, self.tableTag)
42 self.table = tableClass()
43 self.table.decompile(reader, font)
Behdad Esfahbod0585b642013-11-22 16:20:59 -050044 if cachingStats:
Behdad Esfahbodac1b4352013-11-27 04:15:34 -050045 stats = sorted([(v, k) for k, v in cachingStats.items()])
jvrcfadfd02002-07-22 22:13:57 +000046 stats.reverse()
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050047 print("cachingsstats for ", self.tableTag)
jvrcfadfd02002-07-22 22:13:57 +000048 for v, k in stats:
49 if v < 2:
50 break
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050051 print(v, k)
52 print("---", len(stats))
jvrd4d15132002-05-11 00:59:27 +000053
54 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000055 """ Create a top-level OTFWriter for the GPOS/GSUB table.
56 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000057 for each 'converter' record in the table converter list
58 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000059 - For simple items, the write method adds a string to the
60 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000061 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000062 to the writer's self.items, then calls the item's compile method.
63 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
64 each writer representing a table, and the writer.items list containing
65 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000066 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000067 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000068 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
69 Traverse the flat list of tables, calling getDataLength on each to update their position
70 Traverse the flat list of tables again, calling getData each get the data in the table, now that
71 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000072
73 If a lookup subtable overflows an offset, we have to start all over.
74 """
Behdad Esfahbod79f73442013-11-26 17:07:37 -050075 class GlobalState:
76 def __init__(self, tableType):
77 self.tableType = tableType
78 globalState = GlobalState(tableType=self.tableTag)
79 writer = OTTableWriter(globalState)
jvr823f8cd2006-10-21 14:12:38 +000080 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000081 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000082 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000083
jvrd4d15132002-05-11 00:59:27 +000084 def toXML(self, writer, font):
85 self.table.toXML2(writer, font)
86
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050087 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -050088 from . import otTables
jvrd4d15132002-05-11 00:59:27 +000089 if not hasattr(self, "table"):
90 tableClass = getattr(otTables, self.tableTag)
91 self.table = tableClass()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050092 self.table.fromXML(name, attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +000093
94
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050095class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050096
jvr64b5c802002-05-11 10:21:36 +000097 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050098
Behdad Esfahbod79f73442013-11-26 17:07:37 -050099 __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
Behdad Esfahbod79817042013-11-24 16:59:42 -0500100
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500101 def __init__(self, data, globalState={}, localState=None, offset=0):
jvrd4d15132002-05-11 00:59:27 +0000102 self.data = data
103 self.offset = offset
104 self.pos = offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500105 self.globalState = globalState
106 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500107
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500108 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000109 offset = self.offset + offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500110 cachingStats = self.globalState.cachingStats
111 if cachingStats is not None:
112 cachingStats[offset] = cachingStats.get(offset, 0) + 1
113 return self.__class__(self.data, self.globalState, self.localState, offset)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500114
jvrd4d15132002-05-11 00:59:27 +0000115 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500121
jvrd4d15132002-05-11 00:59:27 +0000122 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500128
jvrd4d15132002-05-11 00:59:27 +0000129 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
Behdad Esfahbod79817042013-11-24 16:59:42 -0500135
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500136 def readUInt24(self):
137 pos = self.pos
138 newpos = pos + 3
139 value = (ord(self.data[pos]) << 16) | (ord(self.data[pos+1]) << 8) | ord(self.data[pos+2])
140 value, = struct.unpack(">H", self.data[pos:newpos])
141 self.pos = newpos
142 return value
143
jvr823f8cd2006-10-21 14:12:38 +0000144 def readULong(self):
145 pos = self.pos
146 newpos = pos + 4
147 value, = struct.unpack(">L", self.data[pos:newpos])
148 self.pos = newpos
149 return value
150
jvrd4d15132002-05-11 00:59:27 +0000151 def readTag(self):
152 pos = self.pos
153 newpos = pos + 4
154 value = self.data[pos:newpos]
155 assert len(value) == 4
156 self.pos = newpos
157 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500158
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500159 def __setitem__(self, name, value):
160 state = self.localState.copy() if self.localState else dict()
161 state[name] = value
162 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500163
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500164 def __getitem__(self, name):
165 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500166
jvrd4d15132002-05-11 00:59:27 +0000167
Behdad Esfahbod3879cf92013-11-22 19:23:35 -0500168class OTTableWriter(object):
jvrd4d15132002-05-11 00:59:27 +0000169
jvr64b5c802002-05-11 10:21:36 +0000170 """Helper class to gather and assemble data for OpenType tables."""
171
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500172 def __init__(self, globalState, localState=None):
jvrd4d15132002-05-11 00:59:27 +0000173 self.items = []
jvrcfadfd02002-07-22 22:13:57 +0000174 self.pos = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500175 self.globalState = globalState
176 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500177
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500178 def __setitem__(self, name, value):
179 state = self.localState.copy() if self.localState else dict()
180 state[name] = value
181 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500182
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500183 def __getitem__(self, name):
184 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500185
jvr4105ca02002-07-23 08:43:03 +0000186 # assembler interface
187
188 def getAllData(self):
189 """Assemble all data, including all subtables."""
190 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000191 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000192 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000193 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000194 # Gather all data in two passes: the absolute positions of all
195 # subtable are needed before the actual data can be assembled.
196 pos = 0
197 for table in tables:
198 table.pos = pos
199 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000200
201 for table in extTables:
202 table.pos = pos
203 pos = pos + table.getDataLength()
204
205
jvr4105ca02002-07-23 08:43:03 +0000206 data = []
207 for table in tables:
208 tableData = table.getData()
209 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000210
211 for table in extTables:
212 tableData = table.getData()
213 data.append(tableData)
214
jvr4105ca02002-07-23 08:43:03 +0000215 return "".join(data)
216
217 def getDataLength(self):
218 """Return the length of this table in bytes, without subtables."""
219 l = 0
220 for item in self.items:
221 if hasattr(item, "getData") or hasattr(item, "getCountData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500222 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000223 l = l + 4 # sizeof(ULong)
224 else:
225 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000226 else:
227 l = l + len(item)
228 return l
229
230 def getData(self):
231 """Assemble the data for this writer/table, without subtables."""
232 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000233 pos = self.pos
234 numItems = len(items)
235 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000236 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000237
jvr4105ca02002-07-23 08:43:03 +0000238 if hasattr(item, "getData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500239 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000240 items[i] = packULong(item.pos - pos)
241 else:
242 try:
243 items[i] = packUShort(item.pos - pos)
244 except AssertionError:
245 # provide data to fix overflow problem.
Behdad Esfahbod58acba22013-11-24 20:08:05 -0500246 # If the overflow is to a lookup, or from a lookup to a subtable,
jvr823f8cd2006-10-21 14:12:38 +0000247 # just report the current item.
248 if self.name in [ 'LookupList', 'Lookup']:
249 overflowErrorRecord = self.getOverflowErrorRecord(item)
250 else:
251 # overflow is within a subTable. Life is more complicated.
252 # If we split the sub-table just before the current item, we may still suffer overflow.
253 # This is because duplicate table merging is done only within an Extension subTable tree;
254 # when we split the subtable in two, some items may no longer be duplicates.
255 # Get worst case by adding up all the item lengths, depth first traversal.
256 # and then report the first item that overflows a short.
257 def getDeepItemLength(table):
258 if hasattr(table, "getDataLength"):
259 length = 0
260 for item in table.items:
261 length = length + getDeepItemLength(item)
262 else:
263 length = len(table)
264 return length
265
266 length = self.getDataLength()
267 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
268 # Coverage is first in the item list, but last in the table list,
269 # The original overflow is really in the item list. Skip the Coverage
270 # table in the following test.
271 items = items[i+1:]
272
273 for j in range(len(items)):
274 item = items[j]
275 length = length + getDeepItemLength(item)
276 if length > 65535:
277 break
278 overflowErrorRecord = self.getOverflowErrorRecord(item)
279
280
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500281 raise OTLOffsetOverflowError(overflowErrorRecord)
jvr823f8cd2006-10-21 14:12:38 +0000282
jvr4105ca02002-07-23 08:43:03 +0000283 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000284
jvrcfadfd02002-07-22 22:13:57 +0000285 def __hash__(self):
286 # only works after self._doneWriting() has been called
287 return hash(self.items)
288
289 def __cmp__(self, other):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500290 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100291 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400292
293 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000294
jvrcfadfd02002-07-22 22:13:57 +0000295 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000296 # Convert CountData references to data string items
297 # collapse duplicate table references to a unique entry
298 # "tables" are OTTableWriter objects.
299
300 # For Extension Lookup types, we can
301 # eliminate duplicates only within the tree under the Extension Lookup,
302 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000303 if internedTables is None:
304 internedTables = {}
305 items = self.items
Behdad Esfahbod97dea0a2013-11-27 03:34:48 -0500306 iRange = list(range(len(items)))
jvr823f8cd2006-10-21 14:12:38 +0000307
308 if hasattr(self, "Extension"):
309 newTree = 1
310 else:
311 newTree = 0
312 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000313 item = items[i]
314 if hasattr(item, "getCountData"):
315 items[i] = item.getCountData()
316 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000317 if newTree:
318 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000319 else:
jvr823f8cd2006-10-21 14:12:38 +0000320 item._doneWriting(internedTables)
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500321 if item in internedTables:
jvr823f8cd2006-10-21 14:12:38 +0000322 items[i] = item = internedTables[item]
323 else:
324 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000325 self.items = tuple(items)
326
jvr823f8cd2006-10-21 14:12:38 +0000327 def _gatherTables(self, tables=None, extTables=None, done=None):
328 # Convert table references in self.items tree to a flat
329 # list of tables in depth-first traversal order.
330 # "tables" are OTTableWriter objects.
331 # We do the traversal in reverse order at each level, in order to
332 # resolve duplicate references to be the last reference in the list of tables.
333 # For extension lookups, duplicate references can be merged only within the
334 # writer tree under the extension lookup.
335 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000336 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000337 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000338 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000339
340 done[self] = 1
341
342 numItems = len(self.items)
Behdad Esfahbod97dea0a2013-11-27 03:34:48 -0500343 iRange = list(range(numItems))
jvr823f8cd2006-10-21 14:12:38 +0000344 iRange.reverse()
345
346 if hasattr(self, "Extension"):
347 appendExtensions = 1
348 else:
349 appendExtensions = 0
350
351 # add Coverage table if it is sorted last.
352 sortCoverageLast = 0
353 if hasattr(self, "sortCoverageLast"):
354 # Find coverage table
355 for i in range(numItems):
356 item = self.items[i]
357 if hasattr(item, "name") and (item.name == "Coverage"):
358 sortCoverageLast = 1
359 break
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500360 if item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000361 item._gatherTables(tables, extTables, done)
362 else:
363 index = max(item.parent.keys())
364 item.parent[index + 1] = self
365
366 saveItem = None
367 for i in iRange:
368 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000369 if not hasattr(item, "getData"):
370 continue
jvr823f8cd2006-10-21 14:12:38 +0000371
372 if sortCoverageLast and (i==1) and item.name == 'Coverage':
373 # we've already 'gathered' it above
374 continue
375
376 if appendExtensions:
377 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
378 newDone = {}
379 item._gatherTables(extTables, None, newDone)
380
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500381 elif item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000382 item._gatherTables(tables, extTables, done)
383 else:
384 index = max(item.parent.keys())
385 item.parent[index + 1] = self
386
387
jvrcfadfd02002-07-22 22:13:57 +0000388 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000389 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000390
jvr4105ca02002-07-23 08:43:03 +0000391 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000392
jvr4105ca02002-07-23 08:43:03 +0000393 def getSubWriter(self):
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500394 subwriter = self.__class__(self.globalState, self.localState)
jvr823f8cd2006-10-21 14:12:38 +0000395 subwriter.parent = {0:self} # because some subtables have idential values, we discard
396 # the duplicates under the getAllData method. Hence some
397 # subtable writers can have more than one parent writer.
398 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000399
400 def writeUShort(self, value):
401 assert 0 <= value < 0x10000
402 self.items.append(struct.pack(">H", value))
403
404 def writeShort(self, value):
405 self.items.append(struct.pack(">h", value))
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500406
407 def writeUInt24(self, value):
408 assert 0 <= value < 0x1000000
409 self.items.append(''.join(chr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff)))
jvrd4d15132002-05-11 00:59:27 +0000410
411 def writeLong(self, value):
412 self.items.append(struct.pack(">l", value))
413
jvr823f8cd2006-10-21 14:12:38 +0000414 def writeULong(self, value):
415 self.items.append(struct.pack(">L", value))
416
jvrd4d15132002-05-11 00:59:27 +0000417 def writeTag(self, tag):
418 assert len(tag) == 4
419 self.items.append(tag)
420
421 def writeSubTable(self, subWriter):
422 self.items.append(subWriter)
423
424 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500425 ref = CountReference(table, name)
426 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500427 return ref
jvrd4d15132002-05-11 00:59:27 +0000428
429 def writeStruct(self, format, values):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -0500430 data = struct.pack(*(format,) + values)
jvrd4d15132002-05-11 00:59:27 +0000431 self.items.append(data)
432
jvr823f8cd2006-10-21 14:12:38 +0000433 def writeData(self, data):
434 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000435
jvr823f8cd2006-10-21 14:12:38 +0000436 def getOverflowErrorRecord(self, item):
437 LookupListIndex = SubTableIndex = itemName = itemIndex = None
438 if self.name == 'LookupList':
439 LookupListIndex = item.repeatIndex
440 elif self.name == 'Lookup':
441 LookupListIndex = self.repeatIndex
442 SubTableIndex = item.repeatIndex
443 else:
444 itemName = item.name
445 if hasattr(item, 'repeatIndex'):
446 itemIndex = item.repeatIndex
447 if self.name == 'SubTable':
448 LookupListIndex = self.parent[0].repeatIndex
449 SubTableIndex = self.repeatIndex
450 elif self.name == 'ExtSubTable':
451 LookupListIndex = self.parent[0].parent[0].repeatIndex
452 SubTableIndex = self.parent[0].repeatIndex
453 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
454 itemName = ".".join(self.name, item.name)
455 p1 = self.parent[0]
456 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
457 itemName = ".".join(p1.name, item.name)
458 p1 = p1.parent[0]
459 if p1:
460 if p1.name == 'ExtSubTable':
461 LookupListIndex = self.parent[0].parent[0].repeatIndex
462 SubTableIndex = self.parent[0].repeatIndex
463 else:
464 LookupListIndex = self.parent[0].repeatIndex
465 SubTableIndex = self.repeatIndex
466
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500467 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000468
jvrd4d15132002-05-11 00:59:27 +0000469
470class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000471 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000472 def __init__(self, table, name):
473 self.table = table
474 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500475 def setValue(self, value):
476 table = self.table
477 name = self.name
478 if table[name] is None:
479 table[name] = value
480 else:
Behdad Esfahbod1f0eed82013-11-26 18:41:53 -0500481 assert table[name] == value, (name, table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000482 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000483 return packUShort(self.table[self.name])
484
485
jvr64b5c802002-05-11 10:21:36 +0000486def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000487 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000488 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000489
490
jvr823f8cd2006-10-21 14:12:38 +0000491def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000492 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000493 return struct.pack(">L", value)
494
495
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500496class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000497 def __init__(self):
498 self.compileStatus = 0 # 0 means table was created
499 # 1 means the table.read() function was called by a table which is subject
500 # to delayed compilation
501 # 2 means that it was subject to delayed compilation, and
502 # has been decompiled
jvr823f8cd2006-10-21 14:12:38 +0000503
504 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000505
jvr823f8cd2006-10-21 14:12:38 +0000506 def __getattr__(self, attr):
507 # we get here only when the table does not have the attribute.
508 # This method ovveride exists so that we can try to de-compile
509 # a table which is subject to delayed decompilation, and then try
510 # to get the value again after decompilation.
511 self.recurse +=1
512 if self.recurse > 2:
513 # shouldn't ever get here - we should only get to two levels of recursion.
514 # this guards against self.decompile NOT setting compileStatus to other than 1.
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500515 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000516 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500517 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000518 val = getattr(self, attr)
519 self.recurse -=1
520 return val
521
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500522 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000523
524
jvr64b5c802002-05-11 10:21:36 +0000525 """Generic base class for all OpenType (sub)tables."""
526
jvrd4d15132002-05-11 00:59:27 +0000527 def getConverters(self):
528 return self.converters
529
530 def getConverterByName(self, name):
531 return self.convertersByName[name]
532
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500533 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000534 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000535 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000536 table = {}
537 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500538 converters = self.getConverters()
539 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000540 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500541 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000542 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000543 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500544 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000545 table["ExtensionLookupType"])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500546 if conv.name == "FeatureParams":
547 conv = conv.getConverter(reader["FeatureTag"])
jvrd4d15132002-05-11 00:59:27 +0000548 if conv.repeat:
549 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500550 if conv.repeat in table:
551 countValue = table[conv.repeat]
552 else:
553 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500554 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500555 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500556 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000557 table[conv.name] = l
558 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500559 if conv.aux and not eval(conv.aux, None, table):
560 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500561 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500562 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500563 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500564
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 Esfahbodf4e61ae2013-11-26 17:37:03 -0500572 self.decompile(self.reader, self.font)
573 del self.reader, self.font
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500574
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500575 def compile(self, writer, font):
Behdad Esfahbod3ac9e632013-11-26 19:42:55 -0500576 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000577 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000578
579 if hasattr(self, 'sortCoverageLast'):
580 writer.sortCoverageLast = 1
581
jvrf7ef96c2002-09-10 19:26:38 +0000582 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000583 for conv in self.getConverters():
584 value = table.get(conv.name)
585 if conv.repeat:
586 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000587 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500588 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500589 if conv.repeat in table:
590 ref = table[conv.repeat]
591 table[conv.repeat] = None
592 ref.setValue(countValue)
593 else:
594 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500595 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000596 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500597 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000598 elif conv.isCount:
599 # Special-case Count values.
600 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500601 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000602 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500603 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000604 # We add a reference: by the time the data is assembled
605 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500606 ref = writer.writeCountReference(table, conv.name)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500607 if conv.isPropagated:
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500608 table[conv.name] = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500609 writer[conv.name] = ref
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500610 else:
611 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000612 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500613 if conv.aux and not eval(conv.aux, None, table):
614 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500615 conv.write(writer, font, table, value)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500616 if conv.isPropagated:
617 writer[conv.name] = value
jvrd4d15132002-05-11 00:59:27 +0000618
jvrf7ef96c2002-09-10 19:26:38 +0000619 def readFormat(self, reader):
620 pass
621
622 def writeFormat(self, writer):
623 pass
624
jvrd4d15132002-05-11 00:59:27 +0000625 def postRead(self, table, font):
626 self.__dict__.update(table)
627
628 def preWrite(self, font):
629 return self.__dict__.copy()
630
631 def toXML(self, xmlWriter, font, attrs=None):
632 tableName = self.__class__.__name__
633 if attrs is None:
634 attrs = []
635 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000636 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000637 xmlWriter.begintag(tableName, attrs)
638 xmlWriter.newline()
639 self.toXML2(xmlWriter, font)
640 xmlWriter.endtag(tableName)
641 xmlWriter.newline()
642
643 def toXML2(self, xmlWriter, font):
644 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
645 # This is because in TTX our parent writes our main tag, and in otBase.py we
646 # do it ourselves. I think I'm getting schizophrenic...
647 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000648 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500649 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000650 for i in range(len(value)):
651 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000652 conv.xmlWrite(xmlWriter, font, item, conv.name,
653 [("index", i)])
654 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500655 if conv.aux and not eval(conv.aux, None, vars(self)):
656 continue
657 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000658 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000659
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500660 def fromXML(self, name, attrs, content, font):
jvrd4d15132002-05-11 00:59:27 +0000661 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 Esfahbodac1b4352013-11-27 04:15:34 -0500676 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100677 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()
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500697 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 Esfahbod601bb942013-11-23 20:20:39 -0500745 def __init__(self, valueFormat):
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:
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500764 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000765 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
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500825 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500826 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000827 for k, v in attrs.items():
828 setattr(self, k, int(v))
829 for element in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500830 if not isinstance(element, tuple):
jvrd4d15132002-05-11 00:59:27 +0000831 continue
832 name, attrs, content = element
833 value = getattr(otTables, name)()
834 for elem2 in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500835 if not isinstance(elem2, tuple):
jvrd4d15132002-05-11 00:59:27 +0000836 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500837 name2, attrs2, content2 = elem2
838 value.fromXML(name2, attrs2, content2, font)
jvrd4d15132002-05-11 00:59:27 +0000839 setattr(self, name, value)
840
841 def __cmp__(self, other):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500842 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100843 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
jvrd4d15132002-05-11 00:59:27 +0000844
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400845 return cmp(self.__dict__, other.__dict__)