blob: f42539eef0143b47eceb1b9a4afc8088a625c453 [file] [log] [blame]
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -05001from __future__ import print_function, division
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05002from fontTools.misc.py23 import *
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -05003from .DefaultTable import DefaultTable
jvrd4d15132002-05-11 00:59:27 +00004import struct
jvrd4d15132002-05-11 00:59:27 +00005
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:
Behdad Esfahbodac1b4352013-11-27 04:15:34 -050046 stats = sorted([(v, k) for k, v in cachingStats.items()])
jvrcfadfd02002-07-22 22:13:57 +000047 stats.reverse()
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050048 print("cachingsstats for ", self.tableTag)
jvrcfadfd02002-07-22 22:13:57 +000049 for v, k in stats:
50 if v < 2:
51 break
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -050052 print(v, k)
53 print("---", len(stats))
jvrd4d15132002-05-11 00:59:27 +000054
55 def compile(self, font):
jvr823f8cd2006-10-21 14:12:38 +000056 """ Create a top-level OTFWriter for the GPOS/GSUB table.
57 Call the compile method for the the table
jvr1c734522008-03-09 20:13:16 +000058 for each 'converter' record in the table converter list
59 call converter's write method for each item in the value.
jvr823f8cd2006-10-21 14:12:38 +000060 - For simple items, the write method adds a string to the
61 writer's self.items list.
jvr1c734522008-03-09 20:13:16 +000062 - For Struct/Table/Subtable items, it add first adds new writer to the
jvr823f8cd2006-10-21 14:12:38 +000063 to the writer's self.items, then calls the item's compile method.
64 This creates a tree of writers, rooted at the GUSB/GPOS writer, with
65 each writer representing a table, and the writer.items list containing
66 the child data strings and writers.
jvr1c734522008-03-09 20:13:16 +000067 call the getAllData method
jvr823f8cd2006-10-21 14:12:38 +000068 call _doneWriting, which removes duplicates
jvr1c734522008-03-09 20:13:16 +000069 call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
70 Traverse the flat list of tables, calling getDataLength on each to update their position
71 Traverse the flat list of tables again, calling getData each get the data in the table, now that
72 pos's and offset are known.
jvr823f8cd2006-10-21 14:12:38 +000073
74 If a lookup subtable overflows an offset, we have to start all over.
75 """
Behdad Esfahbod79f73442013-11-26 17:07:37 -050076 class GlobalState:
77 def __init__(self, tableType):
78 self.tableType = tableType
79 globalState = GlobalState(tableType=self.tableTag)
80 writer = OTTableWriter(globalState)
jvr823f8cd2006-10-21 14:12:38 +000081 writer.parent = None
jvrd4d15132002-05-11 00:59:27 +000082 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000083 return writer.getAllData()
jvr823f8cd2006-10-21 14:12:38 +000084
jvrd4d15132002-05-11 00:59:27 +000085 def toXML(self, writer, font):
86 self.table.toXML2(writer, font)
87
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050088 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -050089 from . import otTables
jvrd4d15132002-05-11 00:59:27 +000090 if not hasattr(self, "table"):
91 tableClass = getattr(otTables, self.tableTag)
92 self.table = tableClass()
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050093 self.table.fromXML(name, attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +000094
95
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050096class OTTableReader(object):
Behdad Esfahbod79817042013-11-24 16:59:42 -050097
jvr64b5c802002-05-11 10:21:36 +000098 """Helper class to retrieve data from an OpenType table."""
Behdad Esfahbod3879cf92013-11-22 19:23:35 -050099
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500100 __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
Behdad Esfahbod79817042013-11-24 16:59:42 -0500101
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500102 def __init__(self, data, globalState={}, localState=None, offset=0):
jvrd4d15132002-05-11 00:59:27 +0000103 self.data = data
104 self.offset = offset
105 self.pos = offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500106 self.globalState = globalState
107 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500108
Behdad Esfahbod0fac7fe2013-11-24 18:04:29 -0500109 def getSubReader(self, offset):
jvrd4d15132002-05-11 00:59:27 +0000110 offset = self.offset + offset
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500111 cachingStats = self.globalState.cachingStats
112 if cachingStats is not None:
113 cachingStats[offset] = cachingStats.get(offset, 0) + 1
114 return self.__class__(self.data, self.globalState, self.localState, offset)
Behdad Esfahbod79817042013-11-24 16:59:42 -0500115
jvrd4d15132002-05-11 00:59:27 +0000116 def readUShort(self):
117 pos = self.pos
118 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000119 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000120 self.pos = newpos
121 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500122
jvrd4d15132002-05-11 00:59:27 +0000123 def readShort(self):
124 pos = self.pos
125 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +0000126 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000127 self.pos = newpos
128 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500129
jvrd4d15132002-05-11 00:59:27 +0000130 def readLong(self):
131 pos = self.pos
132 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +0000133 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000134 self.pos = newpos
135 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500136
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500137 def readUInt24(self):
138 pos = self.pos
139 newpos = pos + 3
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500140 value = (byteord(self.data[pos]) << 16) | (byteord(self.data[pos+1]) << 8) | byteord(self.data[pos+2])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500141 value, = struct.unpack(">H", self.data[pos:newpos])
142 self.pos = newpos
143 return value
144
jvr823f8cd2006-10-21 14:12:38 +0000145 def readULong(self):
146 pos = self.pos
147 newpos = pos + 4
148 value, = struct.unpack(">L", self.data[pos:newpos])
149 self.pos = newpos
150 return value
151
jvrd4d15132002-05-11 00:59:27 +0000152 def readTag(self):
153 pos = self.pos
154 newpos = pos + 4
155 value = self.data[pos:newpos]
156 assert len(value) == 4
157 self.pos = newpos
158 return value
Behdad Esfahbod79817042013-11-24 16:59:42 -0500159
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500160 def __setitem__(self, name, value):
161 state = self.localState.copy() if self.localState else dict()
162 state[name] = value
163 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500164
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500165 def __getitem__(self, name):
166 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500167
jvrd4d15132002-05-11 00:59:27 +0000168
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
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500173 def __init__(self, globalState, localState=None):
jvrd4d15132002-05-11 00:59:27 +0000174 self.items = []
jvrcfadfd02002-07-22 22:13:57 +0000175 self.pos = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500176 self.globalState = globalState
177 self.localState = localState
Behdad Esfahbod79817042013-11-24 16:59:42 -0500178
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500179 def __setitem__(self, name, value):
180 state = self.localState.copy() if self.localState else dict()
181 state[name] = value
182 self.localState = state
Behdad Esfahbod79817042013-11-24 16:59:42 -0500183
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500184 def __getitem__(self, name):
185 return self.localState[name]
Behdad Esfahbod79817042013-11-24 16:59:42 -0500186
jvr4105ca02002-07-23 08:43:03 +0000187 # assembler interface
188
189 def getAllData(self):
190 """Assemble all data, including all subtables."""
191 self._doneWriting()
jvr823f8cd2006-10-21 14:12:38 +0000192 tables, extTables = self._gatherTables()
jvr4105ca02002-07-23 08:43:03 +0000193 tables.reverse()
jvr823f8cd2006-10-21 14:12:38 +0000194 extTables.reverse()
jvr4105ca02002-07-23 08:43:03 +0000195 # Gather all data in two passes: the absolute positions of all
196 # subtable are needed before the actual data can be assembled.
197 pos = 0
198 for table in tables:
199 table.pos = pos
200 pos = pos + table.getDataLength()
jvr823f8cd2006-10-21 14:12:38 +0000201
202 for table in extTables:
203 table.pos = pos
204 pos = pos + table.getDataLength()
205
206
jvr4105ca02002-07-23 08:43:03 +0000207 data = []
208 for table in tables:
209 tableData = table.getData()
210 data.append(tableData)
jvr823f8cd2006-10-21 14:12:38 +0000211
212 for table in extTables:
213 tableData = table.getData()
214 data.append(tableData)
215
jvr4105ca02002-07-23 08:43:03 +0000216 return "".join(data)
217
218 def getDataLength(self):
219 """Return the length of this table in bytes, without subtables."""
220 l = 0
221 for item in self.items:
222 if hasattr(item, "getData") or hasattr(item, "getCountData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500223 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000224 l = l + 4 # sizeof(ULong)
225 else:
226 l = l + 2 # sizeof(UShort)
jvr4105ca02002-07-23 08:43:03 +0000227 else:
228 l = l + len(item)
229 return l
230
231 def getData(self):
232 """Assemble the data for this writer/table, without subtables."""
233 items = list(self.items) # make a shallow copy
jvr823f8cd2006-10-21 14:12:38 +0000234 pos = self.pos
235 numItems = len(items)
236 for i in range(numItems):
jvr4105ca02002-07-23 08:43:03 +0000237 item = items[i]
jvr823f8cd2006-10-21 14:12:38 +0000238
jvr4105ca02002-07-23 08:43:03 +0000239 if hasattr(item, "getData"):
Behdad Esfahbode0c2e8e2013-11-25 05:32:17 -0500240 if item.longOffset:
jvr823f8cd2006-10-21 14:12:38 +0000241 items[i] = packULong(item.pos - pos)
242 else:
243 try:
244 items[i] = packUShort(item.pos - pos)
245 except AssertionError:
246 # provide data to fix overflow problem.
Behdad Esfahbod58acba22013-11-24 20:08:05 -0500247 # If the overflow is to a lookup, or from a lookup to a subtable,
jvr823f8cd2006-10-21 14:12:38 +0000248 # just report the current item.
249 if self.name in [ 'LookupList', 'Lookup']:
250 overflowErrorRecord = self.getOverflowErrorRecord(item)
251 else:
252 # overflow is within a subTable. Life is more complicated.
253 # If we split the sub-table just before the current item, we may still suffer overflow.
254 # This is because duplicate table merging is done only within an Extension subTable tree;
255 # when we split the subtable in two, some items may no longer be duplicates.
256 # Get worst case by adding up all the item lengths, depth first traversal.
257 # and then report the first item that overflows a short.
258 def getDeepItemLength(table):
259 if hasattr(table, "getDataLength"):
260 length = 0
261 for item in table.items:
262 length = length + getDeepItemLength(item)
263 else:
264 length = len(table)
265 return length
266
267 length = self.getDataLength()
268 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
269 # Coverage is first in the item list, but last in the table list,
270 # The original overflow is really in the item list. Skip the Coverage
271 # table in the following test.
272 items = items[i+1:]
273
274 for j in range(len(items)):
275 item = items[j]
276 length = length + getDeepItemLength(item)
277 if length > 65535:
278 break
279 overflowErrorRecord = self.getOverflowErrorRecord(item)
280
281
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500282 raise OTLOffsetOverflowError(overflowErrorRecord)
jvr823f8cd2006-10-21 14:12:38 +0000283
jvr4105ca02002-07-23 08:43:03 +0000284 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000285
jvrcfadfd02002-07-22 22:13:57 +0000286 def __hash__(self):
287 # only works after self._doneWriting() has been called
288 return hash(self.items)
289
290 def __cmp__(self, other):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500291 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100292 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400293
294 return cmp(self.items, other.items)
jvrcfadfd02002-07-22 22:13:57 +0000295
jvrcfadfd02002-07-22 22:13:57 +0000296 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000297 # Convert CountData references to data string items
298 # collapse duplicate table references to a unique entry
299 # "tables" are OTTableWriter objects.
300
301 # For Extension Lookup types, we can
302 # eliminate duplicates only within the tree under the Extension Lookup,
303 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000304 if internedTables is None:
305 internedTables = {}
306 items = self.items
Behdad Esfahbod97dea0a2013-11-27 03:34:48 -0500307 iRange = list(range(len(items)))
jvr823f8cd2006-10-21 14:12:38 +0000308
309 if hasattr(self, "Extension"):
310 newTree = 1
311 else:
312 newTree = 0
313 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000314 item = items[i]
315 if hasattr(item, "getCountData"):
316 items[i] = item.getCountData()
317 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000318 if newTree:
319 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000320 else:
jvr823f8cd2006-10-21 14:12:38 +0000321 item._doneWriting(internedTables)
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500322 if item in internedTables:
jvr823f8cd2006-10-21 14:12:38 +0000323 items[i] = item = internedTables[item]
324 else:
325 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000326 self.items = tuple(items)
327
jvr823f8cd2006-10-21 14:12:38 +0000328 def _gatherTables(self, tables=None, extTables=None, done=None):
329 # Convert table references in self.items tree to a flat
330 # list of tables in depth-first traversal order.
331 # "tables" are OTTableWriter objects.
332 # We do the traversal in reverse order at each level, in order to
333 # resolve duplicate references to be the last reference in the list of tables.
334 # For extension lookups, duplicate references can be merged only within the
335 # writer tree under the extension lookup.
336 if tables is None: # init call for first time.
jvrcfadfd02002-07-22 22:13:57 +0000337 tables = []
jvr823f8cd2006-10-21 14:12:38 +0000338 extTables = []
jvrcfadfd02002-07-22 22:13:57 +0000339 done = {}
jvr823f8cd2006-10-21 14:12:38 +0000340
341 done[self] = 1
342
343 numItems = len(self.items)
Behdad Esfahbod97dea0a2013-11-27 03:34:48 -0500344 iRange = list(range(numItems))
jvr823f8cd2006-10-21 14:12:38 +0000345 iRange.reverse()
346
347 if hasattr(self, "Extension"):
348 appendExtensions = 1
349 else:
350 appendExtensions = 0
351
352 # add Coverage table if it is sorted last.
353 sortCoverageLast = 0
354 if hasattr(self, "sortCoverageLast"):
355 # Find coverage table
356 for i in range(numItems):
357 item = self.items[i]
358 if hasattr(item, "name") and (item.name == "Coverage"):
359 sortCoverageLast = 1
360 break
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500361 if item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000362 item._gatherTables(tables, extTables, done)
363 else:
364 index = max(item.parent.keys())
365 item.parent[index + 1] = self
366
367 saveItem = None
368 for i in iRange:
369 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000370 if not hasattr(item, "getData"):
371 continue
jvr823f8cd2006-10-21 14:12:38 +0000372
373 if sortCoverageLast and (i==1) and item.name == 'Coverage':
374 # we've already 'gathered' it above
375 continue
376
377 if appendExtensions:
378 assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
379 newDone = {}
380 item._gatherTables(extTables, None, newDone)
381
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500382 elif item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000383 item._gatherTables(tables, extTables, done)
384 else:
385 index = max(item.parent.keys())
386 item.parent[index + 1] = self
387
388
jvrcfadfd02002-07-22 22:13:57 +0000389 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000390 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000391
jvr4105ca02002-07-23 08:43:03 +0000392 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000393
jvr4105ca02002-07-23 08:43:03 +0000394 def getSubWriter(self):
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500395 subwriter = self.__class__(self.globalState, self.localState)
jvr823f8cd2006-10-21 14:12:38 +0000396 subwriter.parent = {0:self} # because some subtables have idential values, we discard
397 # the duplicates under the getAllData method. Hence some
398 # subtable writers can have more than one parent writer.
399 return subwriter
jvrd4d15132002-05-11 00:59:27 +0000400
401 def writeUShort(self, value):
402 assert 0 <= value < 0x10000
403 self.items.append(struct.pack(">H", value))
404
405 def writeShort(self, value):
406 self.items.append(struct.pack(">h", value))
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500407
408 def writeUInt24(self, value):
409 assert 0 <= value < 0x1000000
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500410 self.items.append(''.join(bytechr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff)))
jvrd4d15132002-05-11 00:59:27 +0000411
412 def writeLong(self, value):
413 self.items.append(struct.pack(">l", value))
414
jvr823f8cd2006-10-21 14:12:38 +0000415 def writeULong(self, value):
416 self.items.append(struct.pack(">L", value))
417
jvrd4d15132002-05-11 00:59:27 +0000418 def writeTag(self, tag):
419 assert len(tag) == 4
420 self.items.append(tag)
421
422 def writeSubTable(self, subWriter):
423 self.items.append(subWriter)
424
425 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500426 ref = CountReference(table, name)
427 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500428 return ref
jvrd4d15132002-05-11 00:59:27 +0000429
430 def writeStruct(self, format, values):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -0500431 data = struct.pack(*(format,) + values)
jvrd4d15132002-05-11 00:59:27 +0000432 self.items.append(data)
433
jvr823f8cd2006-10-21 14:12:38 +0000434 def writeData(self, data):
435 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000436
jvr823f8cd2006-10-21 14:12:38 +0000437 def getOverflowErrorRecord(self, item):
438 LookupListIndex = SubTableIndex = itemName = itemIndex = None
439 if self.name == 'LookupList':
440 LookupListIndex = item.repeatIndex
441 elif self.name == 'Lookup':
442 LookupListIndex = self.repeatIndex
443 SubTableIndex = item.repeatIndex
444 else:
445 itemName = item.name
446 if hasattr(item, 'repeatIndex'):
447 itemIndex = item.repeatIndex
448 if self.name == 'SubTable':
449 LookupListIndex = self.parent[0].repeatIndex
450 SubTableIndex = self.repeatIndex
451 elif self.name == 'ExtSubTable':
452 LookupListIndex = self.parent[0].parent[0].repeatIndex
453 SubTableIndex = self.parent[0].repeatIndex
454 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
455 itemName = ".".join(self.name, item.name)
456 p1 = self.parent[0]
457 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
458 itemName = ".".join(p1.name, item.name)
459 p1 = p1.parent[0]
460 if p1:
461 if p1.name == 'ExtSubTable':
462 LookupListIndex = self.parent[0].parent[0].repeatIndex
463 SubTableIndex = self.parent[0].repeatIndex
464 else:
465 LookupListIndex = self.parent[0].repeatIndex
466 SubTableIndex = self.repeatIndex
467
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500468 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000469
jvrd4d15132002-05-11 00:59:27 +0000470
471class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000472 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000473 def __init__(self, table, name):
474 self.table = table
475 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500476 def setValue(self, value):
477 table = self.table
478 name = self.name
479 if table[name] is None:
480 table[name] = value
481 else:
Behdad Esfahbod1f0eed82013-11-26 18:41:53 -0500482 assert table[name] == value, (name, table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000483 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000484 return packUShort(self.table[self.name])
485
486
jvr64b5c802002-05-11 10:21:36 +0000487def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000488 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000489 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000490
491
jvr823f8cd2006-10-21 14:12:38 +0000492def packULong(value):
jvrce47e0d2008-03-09 20:48:45 +0000493 assert 0 <= value < 0x100000000, value
jvr823f8cd2006-10-21 14:12:38 +0000494 return struct.pack(">L", value)
495
496
Behdad Esfahbod5988cc32013-11-19 17:20:54 -0500497class BaseTable(object):
jvr823f8cd2006-10-21 14:12:38 +0000498 def __init__(self):
499 self.compileStatus = 0 # 0 means table was created
500 # 1 means the table.read() function was called by a table which is subject
501 # to delayed compilation
502 # 2 means that it was subject to delayed compilation, and
503 # has been decompiled
jvr823f8cd2006-10-21 14:12:38 +0000504
505 self.recurse = 0
jvrd4d15132002-05-11 00:59:27 +0000506
jvr823f8cd2006-10-21 14:12:38 +0000507 def __getattr__(self, attr):
508 # we get here only when the table does not have the attribute.
509 # This method ovveride exists so that we can try to de-compile
510 # a table which is subject to delayed decompilation, and then try
511 # to get the value again after decompilation.
512 self.recurse +=1
513 if self.recurse > 2:
514 # shouldn't ever get here - we should only get to two levels of recursion.
515 # this guards against self.decompile NOT setting compileStatus to other than 1.
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500516 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000517 if self.compileStatus == 1:
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500518 self.ensureDecompiled()
jvr823f8cd2006-10-21 14:12:38 +0000519 val = getattr(self, attr)
520 self.recurse -=1
521 return val
522
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500523 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000524
525
jvr64b5c802002-05-11 10:21:36 +0000526 """Generic base class for all OpenType (sub)tables."""
527
jvrd4d15132002-05-11 00:59:27 +0000528 def getConverters(self):
529 return self.converters
530
531 def getConverterByName(self, name):
532 return self.convertersByName[name]
533
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500534 def decompile(self, reader, font):
jvr823f8cd2006-10-21 14:12:38 +0000535 self.compileStatus = 2 # table has been decompiled.
jvrf7ef96c2002-09-10 19:26:38 +0000536 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000537 table = {}
538 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500539 converters = self.getConverters()
540 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000541 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500542 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000543 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000544 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500545 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000546 table["ExtensionLookupType"])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500547 if conv.name == "FeatureParams":
548 conv = conv.getConverter(reader["FeatureTag"])
jvrd4d15132002-05-11 00:59:27 +0000549 if conv.repeat:
550 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500551 if conv.repeat in table:
552 countValue = table[conv.repeat]
553 else:
554 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500555 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500556 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500557 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000558 table[conv.name] = l
559 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500560 if conv.aux and not eval(conv.aux, None, table):
561 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500562 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500563 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500564 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500565
jvrd4d15132002-05-11 00:59:27 +0000566 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500567
jvrd4d15132002-05-11 00:59:27 +0000568 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000569
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500570 def ensureDecompiled(self):
571 if self.compileStatus != 1:
572 return
Behdad Esfahbodf4e61ae2013-11-26 17:37:03 -0500573 self.decompile(self.reader, self.font)
574 del self.reader, self.font
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500575
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500576 def compile(self, writer, font):
Behdad Esfahbod3ac9e632013-11-26 19:42:55 -0500577 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000578 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000579
580 if hasattr(self, 'sortCoverageLast'):
581 writer.sortCoverageLast = 1
582
jvrf7ef96c2002-09-10 19:26:38 +0000583 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000584 for conv in self.getConverters():
585 value = table.get(conv.name)
586 if conv.repeat:
587 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000588 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500589 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500590 if conv.repeat in table:
591 ref = table[conv.repeat]
592 table[conv.repeat] = None
593 ref.setValue(countValue)
594 else:
595 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500596 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000597 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500598 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000599 elif conv.isCount:
600 # Special-case Count values.
601 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500602 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000603 # 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 # We add a reference: by the time the data is assembled
606 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500607 ref = writer.writeCountReference(table, conv.name)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500608 if conv.isPropagated:
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500609 table[conv.name] = None
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500610 writer[conv.name] = ref
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500611 else:
612 table[conv.name] = ref
jvrd4d15132002-05-11 00:59:27 +0000613 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500614 if conv.aux and not eval(conv.aux, None, table):
615 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500616 conv.write(writer, font, table, value)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500617 if conv.isPropagated:
618 writer[conv.name] = value
jvrd4d15132002-05-11 00:59:27 +0000619
jvrf7ef96c2002-09-10 19:26:38 +0000620 def readFormat(self, reader):
621 pass
622
623 def writeFormat(self, writer):
624 pass
625
jvrd4d15132002-05-11 00:59:27 +0000626 def postRead(self, table, font):
627 self.__dict__.update(table)
628
629 def preWrite(self, font):
630 return self.__dict__.copy()
631
632 def toXML(self, xmlWriter, font, attrs=None):
633 tableName = self.__class__.__name__
634 if attrs is None:
635 attrs = []
636 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000637 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000638 xmlWriter.begintag(tableName, attrs)
639 xmlWriter.newline()
640 self.toXML2(xmlWriter, font)
641 xmlWriter.endtag(tableName)
642 xmlWriter.newline()
643
644 def toXML2(self, xmlWriter, font):
645 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
646 # This is because in TTX our parent writes our main tag, and in otBase.py we
647 # do it ourselves. I think I'm getting schizophrenic...
648 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000649 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500650 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000651 for i in range(len(value)):
652 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000653 conv.xmlWrite(xmlWriter, font, item, conv.name,
654 [("index", i)])
655 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500656 if conv.aux and not eval(conv.aux, None, vars(self)):
657 continue
658 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000659 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000660
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500661 def fromXML(self, name, attrs, content, font):
jvrd4d15132002-05-11 00:59:27 +0000662 try:
663 conv = self.getConverterByName(name)
664 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000665 raise # XXX on KeyError, raise nice error
666 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000667 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000668 seq = getattr(self, conv.name, None)
669 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000670 seq = []
jvr64b5c802002-05-11 10:21:36 +0000671 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000672 seq.append(value)
673 else:
jvr64b5c802002-05-11 10:21:36 +0000674 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000675
676 def __cmp__(self, other):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500677 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100678 if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400679
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500680 self.ensureDecompiled()
681
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400682 return cmp(self.__dict__, other.__dict__)
jvrd4d15132002-05-11 00:59:27 +0000683
684
685class FormatSwitchingBaseTable(BaseTable):
686
jvrcfadfd02002-07-22 22:13:57 +0000687 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000688 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
689
jvrd4d15132002-05-11 00:59:27 +0000690 def getConverters(self):
691 return self.converters[self.Format]
692
693 def getConverterByName(self, name):
694 return self.convertersByName[self.Format][name]
695
jvrf7ef96c2002-09-10 19:26:38 +0000696 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000697 self.Format = reader.readUShort()
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500698 assert self.Format != 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000699
jvrf7ef96c2002-09-10 19:26:38 +0000700 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000701 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000702
703
jvr64b5c802002-05-11 10:21:36 +0000704#
705# Support for ValueRecords
706#
707# This data type is so different from all other OpenType data types that
708# it requires quite a bit of code for itself. It even has special support
709# in OTTableReader and OTTableWriter...
710#
711
jvrd4d15132002-05-11 00:59:27 +0000712valueRecordFormat = [
713# Mask Name isDevice signed
714 (0x0001, "XPlacement", 0, 1),
715 (0x0002, "YPlacement", 0, 1),
716 (0x0004, "XAdvance", 0, 1),
717 (0x0008, "YAdvance", 0, 1),
718 (0x0010, "XPlaDevice", 1, 0),
719 (0x0020, "YPlaDevice", 1, 0),
720 (0x0040, "XAdvDevice", 1, 0),
721 (0x0080, "YAdvDevice", 1, 0),
722# reserved:
723 (0x0100, "Reserved1", 0, 0),
724 (0x0200, "Reserved2", 0, 0),
725 (0x0400, "Reserved3", 0, 0),
726 (0x0800, "Reserved4", 0, 0),
727 (0x1000, "Reserved5", 0, 0),
728 (0x2000, "Reserved6", 0, 0),
729 (0x4000, "Reserved7", 0, 0),
730 (0x8000, "Reserved8", 0, 0),
731]
732
733def _buildDict():
734 d = {}
735 for mask, name, isDevice, signed in valueRecordFormat:
736 d[name] = mask, isDevice, signed
737 return d
738
739valueRecordFormatDict = _buildDict()
740
741
742class ValueRecordFactory:
743
jvr64b5c802002-05-11 10:21:36 +0000744 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500745
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500746 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000747 format = []
748 for mask, name, isDevice, signed in valueRecordFormat:
749 if valueFormat & mask:
750 format.append((name, isDevice, signed))
751 self.format = format
752
753 def readValueRecord(self, reader, font):
754 format = self.format
755 if not format:
756 return None
757 valueRecord = ValueRecord()
758 for name, isDevice, signed in format:
759 if signed:
760 value = reader.readShort()
761 else:
762 value = reader.readUShort()
763 if isDevice:
764 if value:
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500765 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000766 subReader = reader.getSubReader(value)
767 value = getattr(otTables, name)()
768 value.decompile(subReader, font)
769 else:
770 value = None
771 setattr(valueRecord, name, value)
772 return valueRecord
773
774 def writeValueRecord(self, writer, font, valueRecord):
775 for name, isDevice, signed in self.format:
776 value = getattr(valueRecord, name, 0)
777 if isDevice:
778 if value:
779 subWriter = writer.getSubWriter()
780 writer.writeSubTable(subWriter)
781 value.compile(subWriter, font)
782 else:
783 writer.writeUShort(0)
784 elif signed:
785 writer.writeShort(value)
786 else:
787 writer.writeUShort(value)
788
789
790class ValueRecord:
791
792 # see ValueRecordFactory
793
794 def getFormat(self):
795 format = 0
796 for name in self.__dict__.keys():
797 format = format | valueRecordFormatDict[name][0]
798 return format
799
800 def toXML(self, xmlWriter, font, valueName, attrs=None):
801 if attrs is None:
802 simpleItems = []
803 else:
804 simpleItems = list(attrs)
805 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
806 if hasattr(self, name):
807 simpleItems.append((name, getattr(self, name)))
808 deviceItems = []
809 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
810 if hasattr(self, name):
811 device = getattr(self, name)
812 if device is not None:
813 deviceItems.append((name, device))
814 if deviceItems:
815 xmlWriter.begintag(valueName, simpleItems)
816 xmlWriter.newline()
817 for name, deviceRecord in deviceItems:
818 if deviceRecord is not None:
819 deviceRecord.toXML(xmlWriter, font)
820 xmlWriter.endtag(valueName)
821 xmlWriter.newline()
822 else:
823 xmlWriter.simpletag(valueName, simpleItems)
824 xmlWriter.newline()
825
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500826 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500827 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000828 for k, v in attrs.items():
829 setattr(self, k, int(v))
830 for element in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500831 if not isinstance(element, tuple):
jvrd4d15132002-05-11 00:59:27 +0000832 continue
833 name, attrs, content = element
834 value = getattr(otTables, name)()
835 for elem2 in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500836 if not isinstance(elem2, tuple):
jvrd4d15132002-05-11 00:59:27 +0000837 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500838 name2, attrs2, content2 = elem2
839 value.fromXML(name2, attrs2, content2, font)
jvrd4d15132002-05-11 00:59:27 +0000840 setattr(self, name, value)
841
842 def __cmp__(self, other):
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500843 if not isinstance(self, type(other)): return cmp(type(self), type(other))
Behdad Esfahbod0ba7aa72013-10-28 12:07:15 +0100844 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__)