blob: 7f56af7ef033fb7278ca213adf2e68267979fb11 [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
Behdad Esfahbode388db52013-11-28 14:26:58 -05006class OverflowErrorRecord(object):
jvr823f8cd2006-10-21 14:12:38 +00007 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 Esfahbode388db52013-11-28 14:26:58 -050035 class GlobalState(object):
Behdad Esfahbod79f73442013-11-26 17:07:37 -050036 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 Esfahbode388db52013-11-28 14:26:58 -050076 class GlobalState(object):
Behdad Esfahbod79f73442013-11-26 17:07:37 -050077 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 Esfahbodc0a9d692013-11-28 06:38:07 -0500140 value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500141 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
Behdad Esfahbod960280b2013-11-27 18:16:43 -0500154 value = Tag(self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +0000155 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
Behdad Esfahbod821572c2013-11-27 21:09:03 -0500215 return bytesjoin(data)
jvr4105ca02002-07-23 08:43:03 +0000216
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)
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500244 except struct.error:
jvr823f8cd2006-10-21 14:12:38 +0000245 # 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
Behdad Esfahbod821572c2013-11-27 21:09:03 -0500283 return bytesjoin(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
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500289 def __ne__(self, other):
290 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500291 def __eq__(self, other):
292 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500293 return NotImplemented
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500294 return 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:
Behdad Esfahbod9e6ef942013-12-04 16:31:44 -0500378 assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
jvr823f8cd2006-10-21 14:12:38 +0000379 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 Esfahbodc0a9d692013-11-28 06:38:07 -0500410 b = struct.pack(">L", value)
411 self.items.append(b[1:])
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):
Behdad Esfahbod960280b2013-11-27 18:16:43 -0500420 tag = Tag(tag).tobytes()
jvrd4d15132002-05-11 00:59:27 +0000421 assert len(tag) == 4
422 self.items.append(tag)
423
424 def writeSubTable(self, subWriter):
425 self.items.append(subWriter)
426
427 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500428 ref = CountReference(table, name)
429 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500430 return ref
jvrd4d15132002-05-11 00:59:27 +0000431
432 def writeStruct(self, format, values):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -0500433 data = struct.pack(*(format,) + values)
jvrd4d15132002-05-11 00:59:27 +0000434 self.items.append(data)
435
jvr823f8cd2006-10-21 14:12:38 +0000436 def writeData(self, data):
437 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000438
jvr823f8cd2006-10-21 14:12:38 +0000439 def getOverflowErrorRecord(self, item):
440 LookupListIndex = SubTableIndex = itemName = itemIndex = None
441 if self.name == 'LookupList':
442 LookupListIndex = item.repeatIndex
443 elif self.name == 'Lookup':
444 LookupListIndex = self.repeatIndex
445 SubTableIndex = item.repeatIndex
446 else:
447 itemName = item.name
448 if hasattr(item, 'repeatIndex'):
449 itemIndex = item.repeatIndex
450 if self.name == 'SubTable':
451 LookupListIndex = self.parent[0].repeatIndex
452 SubTableIndex = self.repeatIndex
453 elif self.name == 'ExtSubTable':
454 LookupListIndex = self.parent[0].parent[0].repeatIndex
455 SubTableIndex = self.parent[0].repeatIndex
456 else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500457 itemName = ".".join([self.name, item.name])
jvr823f8cd2006-10-21 14:12:38 +0000458 p1 = self.parent[0]
459 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500460 itemName = ".".join([p1.name, item.name])
jvr823f8cd2006-10-21 14:12:38 +0000461 p1 = p1.parent[0]
462 if p1:
463 if p1.name == 'ExtSubTable':
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500464 LookupListIndex = p1.parent[0].parent[0].repeatIndex
465 SubTableIndex = p1.parent[0].repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000466 else:
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500467 LookupListIndex = p1.parent[0].repeatIndex
468 SubTableIndex = p1.repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000469
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500470 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000471
jvrd4d15132002-05-11 00:59:27 +0000472
Behdad Esfahbode388db52013-11-28 14:26:58 -0500473class CountReference(object):
jvrcfadfd02002-07-22 22:13:57 +0000474 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000475 def __init__(self, table, name):
476 self.table = table
477 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500478 def setValue(self, value):
479 table = self.table
480 name = self.name
481 if table[name] is None:
482 table[name] = value
483 else:
Behdad Esfahbod1f0eed82013-11-26 18:41:53 -0500484 assert table[name] == value, (name, table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000485 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000486 return packUShort(self.table[self.name])
487
488
jvr64b5c802002-05-11 10:21:36 +0000489def packUShort(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
jvr823f8cd2006-10-21 14:12:38 +0000500 def __getattr__(self, attr):
Behdad Esfahboddafdb292013-12-17 00:58:02 -0500501 reader = self.__dict__.get("reader", None)
502 if reader:
503 del self.reader
504 font = self.font
505 del self.font
506 self.decompile(reader, font)
507 return getattr(self, attr)
508
509 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000510
511
jvr64b5c802002-05-11 10:21:36 +0000512 """Generic base class for all OpenType (sub)tables."""
513
jvrd4d15132002-05-11 00:59:27 +0000514 def getConverters(self):
515 return self.converters
516
517 def getConverterByName(self, name):
518 return self.convertersByName[name]
519
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500520 def decompile(self, reader, font):
jvrf7ef96c2002-09-10 19:26:38 +0000521 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000522 table = {}
523 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500524 converters = self.getConverters()
525 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000526 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500527 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000528 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000529 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500530 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000531 table["ExtensionLookupType"])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500532 if conv.name == "FeatureParams":
533 conv = conv.getConverter(reader["FeatureTag"])
jvrd4d15132002-05-11 00:59:27 +0000534 if conv.repeat:
535 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500536 if conv.repeat in table:
537 countValue = table[conv.repeat]
538 else:
539 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500540 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500541 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500542 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000543 table[conv.name] = l
544 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500545 if conv.aux and not eval(conv.aux, None, table):
546 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500547 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500548 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500549 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500550
jvrd4d15132002-05-11 00:59:27 +0000551 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500552
jvrd4d15132002-05-11 00:59:27 +0000553 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000554
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500555 def ensureDecompiled(self):
Behdad Esfahboddafdb292013-12-17 00:58:02 -0500556 reader = self.__dict__.get("reader", None)
557 if reader:
558 del self.reader
559 font = self.font
560 del self.font
561 self.decompile(reader, font)
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500562
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500563 def compile(self, writer, font):
Behdad Esfahbod3ac9e632013-11-26 19:42:55 -0500564 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000565 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000566
567 if hasattr(self, 'sortCoverageLast'):
568 writer.sortCoverageLast = 1
569
Behdad Esfahbod5fec22b2013-12-17 02:42:18 -0500570 if hasattr(self.__class__, 'LookupType'):
571 writer['LookupType'].setValue(self.__class__.LookupType)
572
jvrf7ef96c2002-09-10 19:26:38 +0000573 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000574 for conv in self.getConverters():
575 value = table.get(conv.name)
576 if conv.repeat:
577 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000578 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500579 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500580 if conv.repeat in table:
Behdad Esfahbod6bfee2c2013-12-09 00:28:58 -0500581 CountReference(table, conv.repeat).setValue(countValue)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500582 else:
583 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500584 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000585 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500586 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000587 elif conv.isCount:
588 # Special-case Count values.
589 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500590 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000591 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500592 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000593 # We add a reference: by the time the data is assembled
594 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500595 ref = writer.writeCountReference(table, conv.name)
Behdad Esfahbod6bfee2c2013-12-09 00:28:58 -0500596 table[conv.name] = None
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500597 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500598 writer[conv.name] = ref
Behdad Esfahbod5fec22b2013-12-17 02:42:18 -0500599 elif conv.isLookupType:
600 ref = writer.writeCountReference(table, conv.name)
601 table[conv.name] = None
602 writer['LookupType'] = ref
jvrd4d15132002-05-11 00:59:27 +0000603 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500604 if conv.aux and not eval(conv.aux, None, table):
605 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500606 conv.write(writer, font, table, value)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500607 if conv.isPropagated:
608 writer[conv.name] = value
jvrd4d15132002-05-11 00:59:27 +0000609
jvrf7ef96c2002-09-10 19:26:38 +0000610 def readFormat(self, reader):
611 pass
612
613 def writeFormat(self, writer):
614 pass
615
jvrd4d15132002-05-11 00:59:27 +0000616 def postRead(self, table, font):
617 self.__dict__.update(table)
618
619 def preWrite(self, font):
620 return self.__dict__.copy()
621
Behdad Esfahbodd76fa682013-12-09 00:39:25 -0500622 def toXML(self, xmlWriter, font, attrs=None, name=None):
623 tableName = name if name else self.__class__.__name__
jvrd4d15132002-05-11 00:59:27 +0000624 if attrs is None:
625 attrs = []
626 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000627 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000628 xmlWriter.begintag(tableName, attrs)
629 xmlWriter.newline()
630 self.toXML2(xmlWriter, font)
631 xmlWriter.endtag(tableName)
632 xmlWriter.newline()
633
634 def toXML2(self, xmlWriter, font):
635 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
636 # This is because in TTX our parent writes our main tag, and in otBase.py we
637 # do it ourselves. I think I'm getting schizophrenic...
638 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000639 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500640 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000641 for i in range(len(value)):
642 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000643 conv.xmlWrite(xmlWriter, font, item, conv.name,
644 [("index", i)])
645 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500646 if conv.aux and not eval(conv.aux, None, vars(self)):
647 continue
648 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000649 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000650
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500651 def fromXML(self, name, attrs, content, font):
jvrd4d15132002-05-11 00:59:27 +0000652 try:
653 conv = self.getConverterByName(name)
654 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000655 raise # XXX on KeyError, raise nice error
656 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000657 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000658 seq = getattr(self, conv.name, None)
659 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000660 seq = []
jvr64b5c802002-05-11 10:21:36 +0000661 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000662 seq.append(value)
663 else:
jvr64b5c802002-05-11 10:21:36 +0000664 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000665
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500666 def __ne__(self, other):
667 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500668 def __eq__(self, other):
669 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500670 return NotImplemented
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400671
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500672 self.ensureDecompiled()
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500673 other.ensureDecompiled()
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500674
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500675 return self.__dict__ == other.__dict__
jvrd4d15132002-05-11 00:59:27 +0000676
677
678class FormatSwitchingBaseTable(BaseTable):
679
jvrcfadfd02002-07-22 22:13:57 +0000680 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000681 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
682
jvrd4d15132002-05-11 00:59:27 +0000683 def getConverters(self):
684 return self.converters[self.Format]
685
686 def getConverterByName(self, name):
687 return self.convertersByName[self.Format][name]
688
jvrf7ef96c2002-09-10 19:26:38 +0000689 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000690 self.Format = reader.readUShort()
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500691 assert self.Format != 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000692
jvrf7ef96c2002-09-10 19:26:38 +0000693 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000694 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000695
Behdad Esfahbodd76fa682013-12-09 00:39:25 -0500696 def toXML(self, xmlWriter, font, attrs=None, name=None):
697 BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
698
jvrd4d15132002-05-11 00:59:27 +0000699
jvr64b5c802002-05-11 10:21:36 +0000700#
701# Support for ValueRecords
702#
703# This data type is so different from all other OpenType data types that
704# it requires quite a bit of code for itself. It even has special support
705# in OTTableReader and OTTableWriter...
706#
707
jvrd4d15132002-05-11 00:59:27 +0000708valueRecordFormat = [
709# Mask Name isDevice signed
710 (0x0001, "XPlacement", 0, 1),
711 (0x0002, "YPlacement", 0, 1),
712 (0x0004, "XAdvance", 0, 1),
713 (0x0008, "YAdvance", 0, 1),
714 (0x0010, "XPlaDevice", 1, 0),
715 (0x0020, "YPlaDevice", 1, 0),
716 (0x0040, "XAdvDevice", 1, 0),
717 (0x0080, "YAdvDevice", 1, 0),
718# reserved:
719 (0x0100, "Reserved1", 0, 0),
720 (0x0200, "Reserved2", 0, 0),
721 (0x0400, "Reserved3", 0, 0),
722 (0x0800, "Reserved4", 0, 0),
723 (0x1000, "Reserved5", 0, 0),
724 (0x2000, "Reserved6", 0, 0),
725 (0x4000, "Reserved7", 0, 0),
726 (0x8000, "Reserved8", 0, 0),
727]
728
729def _buildDict():
730 d = {}
731 for mask, name, isDevice, signed in valueRecordFormat:
732 d[name] = mask, isDevice, signed
733 return d
734
735valueRecordFormatDict = _buildDict()
736
737
Behdad Esfahbode388db52013-11-28 14:26:58 -0500738class ValueRecordFactory(object):
jvrd4d15132002-05-11 00:59:27 +0000739
jvr64b5c802002-05-11 10:21:36 +0000740 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500741
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500742 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000743 format = []
744 for mask, name, isDevice, signed in valueRecordFormat:
745 if valueFormat & mask:
746 format.append((name, isDevice, signed))
747 self.format = format
748
749 def readValueRecord(self, reader, font):
750 format = self.format
751 if not format:
752 return None
753 valueRecord = ValueRecord()
754 for name, isDevice, signed in format:
755 if signed:
756 value = reader.readShort()
757 else:
758 value = reader.readUShort()
759 if isDevice:
760 if value:
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500761 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000762 subReader = reader.getSubReader(value)
763 value = getattr(otTables, name)()
764 value.decompile(subReader, font)
765 else:
766 value = None
767 setattr(valueRecord, name, value)
768 return valueRecord
769
770 def writeValueRecord(self, writer, font, valueRecord):
771 for name, isDevice, signed in self.format:
772 value = getattr(valueRecord, name, 0)
773 if isDevice:
774 if value:
775 subWriter = writer.getSubWriter()
776 writer.writeSubTable(subWriter)
777 value.compile(subWriter, font)
778 else:
779 writer.writeUShort(0)
780 elif signed:
781 writer.writeShort(value)
782 else:
783 writer.writeUShort(value)
784
785
Behdad Esfahbode388db52013-11-28 14:26:58 -0500786class ValueRecord(object):
jvrd4d15132002-05-11 00:59:27 +0000787
788 # see ValueRecordFactory
789
790 def getFormat(self):
791 format = 0
792 for name in self.__dict__.keys():
793 format = format | valueRecordFormatDict[name][0]
794 return format
795
796 def toXML(self, xmlWriter, font, valueName, attrs=None):
797 if attrs is None:
798 simpleItems = []
799 else:
800 simpleItems = list(attrs)
801 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
802 if hasattr(self, name):
803 simpleItems.append((name, getattr(self, name)))
804 deviceItems = []
805 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
806 if hasattr(self, name):
807 device = getattr(self, name)
808 if device is not None:
809 deviceItems.append((name, device))
810 if deviceItems:
811 xmlWriter.begintag(valueName, simpleItems)
812 xmlWriter.newline()
813 for name, deviceRecord in deviceItems:
814 if deviceRecord is not None:
815 deviceRecord.toXML(xmlWriter, font)
816 xmlWriter.endtag(valueName)
817 xmlWriter.newline()
818 else:
819 xmlWriter.simpletag(valueName, simpleItems)
820 xmlWriter.newline()
821
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500822 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500823 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000824 for k, v in attrs.items():
825 setattr(self, k, int(v))
826 for element in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500827 if not isinstance(element, tuple):
jvrd4d15132002-05-11 00:59:27 +0000828 continue
829 name, attrs, content = element
830 value = getattr(otTables, name)()
831 for elem2 in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500832 if not isinstance(elem2, tuple):
jvrd4d15132002-05-11 00:59:27 +0000833 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500834 name2, attrs2, content2 = elem2
835 value.fromXML(name2, attrs2, content2, font)
jvrd4d15132002-05-11 00:59:27 +0000836 setattr(self, name, value)
837
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500838 def __ne__(self, other):
839 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500840 def __eq__(self, other):
841 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500842 return NotImplemented
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500843 return self.__dict__ == other.__dict__