blob: ad1eb564212a3824fc8933928fac8502b37b99b1 [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,
Behdad Esfahbodab0ca1b2013-12-17 06:19:15 -0500247 # just report the current item. Otherwise...
248 if self.name not in [ 'LookupList', 'Lookup']:
jvr823f8cd2006-10-21 14:12:38 +0000249 # overflow is within a subTable. Life is more complicated.
250 # If we split the sub-table just before the current item, we may still suffer overflow.
251 # This is because duplicate table merging is done only within an Extension subTable tree;
252 # when we split the subtable in two, some items may no longer be duplicates.
253 # Get worst case by adding up all the item lengths, depth first traversal.
254 # and then report the first item that overflows a short.
255 def getDeepItemLength(table):
256 if hasattr(table, "getDataLength"):
257 length = 0
258 for item in table.items:
259 length = length + getDeepItemLength(item)
260 else:
261 length = len(table)
262 return length
263
264 length = self.getDataLength()
265 if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
266 # Coverage is first in the item list, but last in the table list,
267 # The original overflow is really in the item list. Skip the Coverage
268 # table in the following test.
269 items = items[i+1:]
270
271 for j in range(len(items)):
272 item = items[j]
273 length = length + getDeepItemLength(item)
274 if length > 65535:
275 break
276 overflowErrorRecord = self.getOverflowErrorRecord(item)
277
278
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500279 raise OTLOffsetOverflowError(overflowErrorRecord)
jvr823f8cd2006-10-21 14:12:38 +0000280
Behdad Esfahbod821572c2013-11-27 21:09:03 -0500281 return bytesjoin(items)
jvrd4d15132002-05-11 00:59:27 +0000282
jvrcfadfd02002-07-22 22:13:57 +0000283 def __hash__(self):
284 # only works after self._doneWriting() has been called
285 return hash(self.items)
286
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500287 def __ne__(self, other):
288 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500289 def __eq__(self, other):
290 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500291 return NotImplemented
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500292 return self.items == other.items
jvrcfadfd02002-07-22 22:13:57 +0000293
jvrcfadfd02002-07-22 22:13:57 +0000294 def _doneWriting(self, internedTables=None):
jvr823f8cd2006-10-21 14:12:38 +0000295 # Convert CountData references to data string items
296 # collapse duplicate table references to a unique entry
297 # "tables" are OTTableWriter objects.
298
299 # For Extension Lookup types, we can
300 # eliminate duplicates only within the tree under the Extension Lookup,
301 # as offsets may exceed 64K even between Extension LookupTable subtables.
jvrcfadfd02002-07-22 22:13:57 +0000302 if internedTables is None:
303 internedTables = {}
304 items = self.items
Behdad Esfahbod97dea0a2013-11-27 03:34:48 -0500305 iRange = list(range(len(items)))
jvr823f8cd2006-10-21 14:12:38 +0000306
307 if hasattr(self, "Extension"):
308 newTree = 1
309 else:
310 newTree = 0
311 for i in iRange:
jvrcfadfd02002-07-22 22:13:57 +0000312 item = items[i]
313 if hasattr(item, "getCountData"):
314 items[i] = item.getCountData()
315 elif hasattr(item, "getData"):
jvr823f8cd2006-10-21 14:12:38 +0000316 if newTree:
317 item._doneWriting()
jvrcfadfd02002-07-22 22:13:57 +0000318 else:
jvr823f8cd2006-10-21 14:12:38 +0000319 item._doneWriting(internedTables)
Behdad Esfahboddea08f22013-12-17 06:04:28 -0500320 internedItem = internedTables.get(item)
321 if internedItem:
322 items[i] = item = internedItem
jvr823f8cd2006-10-21 14:12:38 +0000323 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:
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500363 # We're a new parent of item
364 pass
jvr823f8cd2006-10-21 14:12:38 +0000365
jvr823f8cd2006-10-21 14:12:38 +0000366 for i in iRange:
367 item = self.items[i]
jvrcfadfd02002-07-22 22:13:57 +0000368 if not hasattr(item, "getData"):
369 continue
jvr823f8cd2006-10-21 14:12:38 +0000370
371 if sortCoverageLast and (i==1) and item.name == 'Coverage':
372 # we've already 'gathered' it above
373 continue
374
375 if appendExtensions:
Behdad Esfahbod9e6ef942013-12-04 16:31:44 -0500376 assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
jvr823f8cd2006-10-21 14:12:38 +0000377 newDone = {}
378 item._gatherTables(extTables, None, newDone)
379
Behdad Esfahbodbc5e1cb2013-11-27 02:33:03 -0500380 elif item not in done:
jvr823f8cd2006-10-21 14:12:38 +0000381 item._gatherTables(tables, extTables, done)
382 else:
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500383 # We're a new parent of item
384 pass
jvr823f8cd2006-10-21 14:12:38 +0000385
386
jvrcfadfd02002-07-22 22:13:57 +0000387 tables.append(self)
jvr823f8cd2006-10-21 14:12:38 +0000388 return tables, extTables
jvrcfadfd02002-07-22 22:13:57 +0000389
jvr4105ca02002-07-23 08:43:03 +0000390 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000391
jvr4105ca02002-07-23 08:43:03 +0000392 def getSubWriter(self):
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500393 subwriter = self.__class__(self.globalState, self.localState)
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500394 subwriter.parent = self # because some subtables have idential values, we discard
395 # the duplicates under the getAllData method. Hence some
396 # subtable writers can have more than one parent writer.
397 # But we just care about first one right now.
jvr823f8cd2006-10-21 14:12:38 +0000398 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
Behdad Esfahbodc0a9d692013-11-28 06:38:07 -0500409 b = struct.pack(">L", value)
410 self.items.append(b[1:])
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):
Behdad Esfahbod960280b2013-11-27 18:16:43 -0500419 tag = Tag(tag).tobytes()
jvrd4d15132002-05-11 00:59:27 +0000420 assert len(tag) == 4
421 self.items.append(tag)
422
423 def writeSubTable(self, subWriter):
424 self.items.append(subWriter)
425
426 def writeCountReference(self, table, name):
Behdad Esfahbod79817042013-11-24 16:59:42 -0500427 ref = CountReference(table, name)
428 self.items.append(ref)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500429 return ref
jvrd4d15132002-05-11 00:59:27 +0000430
431 def writeStruct(self, format, values):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -0500432 data = struct.pack(*(format,) + values)
jvrd4d15132002-05-11 00:59:27 +0000433 self.items.append(data)
434
jvr823f8cd2006-10-21 14:12:38 +0000435 def writeData(self, data):
436 self.items.append(data)
jvrd4d15132002-05-11 00:59:27 +0000437
jvr823f8cd2006-10-21 14:12:38 +0000438 def getOverflowErrorRecord(self, item):
439 LookupListIndex = SubTableIndex = itemName = itemIndex = None
440 if self.name == 'LookupList':
441 LookupListIndex = item.repeatIndex
442 elif self.name == 'Lookup':
443 LookupListIndex = self.repeatIndex
444 SubTableIndex = item.repeatIndex
445 else:
446 itemName = item.name
447 if hasattr(item, 'repeatIndex'):
448 itemIndex = item.repeatIndex
449 if self.name == 'SubTable':
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500450 LookupListIndex = self.parent.repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000451 SubTableIndex = self.repeatIndex
452 elif self.name == 'ExtSubTable':
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500453 LookupListIndex = self.parent.parent.repeatIndex
454 SubTableIndex = self.parent.repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000455 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 -0500456 itemName = ".".join([self.name, item.name])
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500457 p1 = self.parent
jvr823f8cd2006-10-21 14:12:38 +0000458 while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500459 itemName = ".".join([p1.name, item.name])
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500460 p1 = p1.parent
jvr823f8cd2006-10-21 14:12:38 +0000461 if p1:
462 if p1.name == 'ExtSubTable':
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500463 LookupListIndex = p1.parent.parent.repeatIndex
464 SubTableIndex = p1.parent.repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000465 else:
Behdad Esfahbodee6340f2013-12-17 06:31:37 -0500466 LookupListIndex = p1.parent.repeatIndex
Behdad Esfahbod9e482332013-12-17 05:46:51 -0500467 SubTableIndex = p1.repeatIndex
jvr823f8cd2006-10-21 14:12:38 +0000468
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500469 return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
jvr823f8cd2006-10-21 14:12:38 +0000470
jvrd4d15132002-05-11 00:59:27 +0000471
Behdad Esfahbode388db52013-11-28 14:26:58 -0500472class CountReference(object):
jvrcfadfd02002-07-22 22:13:57 +0000473 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000474 def __init__(self, table, name):
475 self.table = table
476 self.name = name
Behdad Esfahbod79817042013-11-24 16:59:42 -0500477 def setValue(self, value):
478 table = self.table
479 name = self.name
480 if table[name] is None:
481 table[name] = value
482 else:
Behdad Esfahbod1f0eed82013-11-26 18:41:53 -0500483 assert table[name] == value, (name, table[name], value)
jvrcfadfd02002-07-22 22:13:57 +0000484 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000485 return packUShort(self.table[self.name])
486
487
jvr64b5c802002-05-11 10:21:36 +0000488def packUShort(value):
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
Behdad Esfahbod398770d2013-12-19 15:30:24 -0500499 def __hash__(self):
500 return id(self)
501
jvr823f8cd2006-10-21 14:12:38 +0000502 def __getattr__(self, attr):
Behdad Esfahbodf6502632013-12-17 05:59:05 -0500503 reader = self.__dict__.get("reader")
Behdad Esfahboddafdb292013-12-17 00:58:02 -0500504 if reader:
505 del self.reader
506 font = self.font
507 del self.font
508 self.decompile(reader, font)
509 return getattr(self, attr)
510
511 raise AttributeError(attr)
jvr823f8cd2006-10-21 14:12:38 +0000512
jvr64b5c802002-05-11 10:21:36 +0000513 """Generic base class for all OpenType (sub)tables."""
514
jvrd4d15132002-05-11 00:59:27 +0000515 def getConverters(self):
516 return self.converters
517
518 def getConverterByName(self, name):
519 return self.convertersByName[name]
520
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500521 def decompile(self, reader, font):
jvrf7ef96c2002-09-10 19:26:38 +0000522 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000523 table = {}
524 self.__rawTable = table # for debugging
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500525 converters = self.getConverters()
526 for conv in converters:
jvrd4d15132002-05-11 00:59:27 +0000527 if conv.name == "SubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500528 conv = conv.getConverter(reader.globalState.tableType,
jvrd4d15132002-05-11 00:59:27 +0000529 table["LookupType"])
jvr823f8cd2006-10-21 14:12:38 +0000530 if conv.name == "ExtSubTable":
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500531 conv = conv.getConverter(reader.globalState.tableType,
jvr823f8cd2006-10-21 14:12:38 +0000532 table["ExtensionLookupType"])
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500533 if conv.name == "FeatureParams":
534 conv = conv.getConverter(reader["FeatureTag"])
jvrd4d15132002-05-11 00:59:27 +0000535 if conv.repeat:
536 l = []
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500537 if conv.repeat in table:
538 countValue = table[conv.repeat]
539 else:
540 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500541 countValue = reader[conv.repeat]
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500542 for i in range(countValue + conv.aux):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500543 l.append(conv.read(reader, font, table))
jvrd4d15132002-05-11 00:59:27 +0000544 table[conv.name] = l
545 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500546 if conv.aux and not eval(conv.aux, None, table):
547 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500548 table[conv.name] = conv.read(reader, font, table)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500549 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500550 reader[conv.name] = table[conv.name]
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500551
jvrd4d15132002-05-11 00:59:27 +0000552 self.postRead(table, font)
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500553
jvrd4d15132002-05-11 00:59:27 +0000554 del self.__rawTable # succeeded, get rid of debugging info
jvr823f8cd2006-10-21 14:12:38 +0000555
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500556 def ensureDecompiled(self):
Behdad Esfahbodf6502632013-12-17 05:59:05 -0500557 reader = self.__dict__.get("reader")
Behdad Esfahboddafdb292013-12-17 00:58:02 -0500558 if reader:
559 del self.reader
560 font = self.font
561 del self.font
562 self.decompile(reader, font)
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500563
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500564 def compile(self, writer, font):
Behdad Esfahbod3ac9e632013-11-26 19:42:55 -0500565 self.ensureDecompiled()
jvrd4d15132002-05-11 00:59:27 +0000566 table = self.preWrite(font)
jvr823f8cd2006-10-21 14:12:38 +0000567
568 if hasattr(self, 'sortCoverageLast'):
569 writer.sortCoverageLast = 1
570
Behdad Esfahbod5fec22b2013-12-17 02:42:18 -0500571 if hasattr(self.__class__, 'LookupType'):
572 writer['LookupType'].setValue(self.__class__.LookupType)
573
jvrf7ef96c2002-09-10 19:26:38 +0000574 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000575 for conv in self.getConverters():
576 value = table.get(conv.name)
577 if conv.repeat:
578 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000579 value = []
Behdad Esfahbod6b6e9fa2013-11-24 22:11:41 -0500580 countValue = len(value) - conv.aux
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500581 if conv.repeat in table:
Behdad Esfahbod6bfee2c2013-12-09 00:28:58 -0500582 CountReference(table, conv.repeat).setValue(countValue)
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500583 else:
584 # conv.repeat is a propagated count
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500585 writer[conv.repeat].setValue(countValue)
jvr823f8cd2006-10-21 14:12:38 +0000586 for i in range(len(value)):
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500587 conv.write(writer, font, table, value[i], i)
jvrd4d15132002-05-11 00:59:27 +0000588 elif conv.isCount:
589 # Special-case Count values.
590 # Assumption: a Count field will *always* precede
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500591 # the actual array(s).
jvrd4d15132002-05-11 00:59:27 +0000592 # We need a default value, as it may be set later by a nested
Behdad Esfahbod41caf2d2013-11-22 19:12:14 -0500593 # table. We will later store it here.
jvrd4d15132002-05-11 00:59:27 +0000594 # We add a reference: by the time the data is assembled
595 # the Count value will be filled in.
Behdad Esfahbodee27eb82013-11-24 17:34:43 -0500596 ref = writer.writeCountReference(table, conv.name)
Behdad Esfahbod6bfee2c2013-12-09 00:28:58 -0500597 table[conv.name] = None
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500598 if conv.isPropagated:
Behdad Esfahbod79f73442013-11-26 17:07:37 -0500599 writer[conv.name] = ref
Behdad Esfahbod5fec22b2013-12-17 02:42:18 -0500600 elif conv.isLookupType:
601 ref = writer.writeCountReference(table, conv.name)
602 table[conv.name] = None
603 writer['LookupType'] = ref
jvrd4d15132002-05-11 00:59:27 +0000604 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500605 if conv.aux and not eval(conv.aux, None, table):
606 continue
Behdad Esfahbod078b3632013-11-24 17:08:06 -0500607 conv.write(writer, font, table, value)
Behdad Esfahbod9e1bd2d2013-11-26 19:23:08 -0500608 if conv.isPropagated:
609 writer[conv.name] = value
jvrd4d15132002-05-11 00:59:27 +0000610
jvrf7ef96c2002-09-10 19:26:38 +0000611 def readFormat(self, reader):
612 pass
613
614 def writeFormat(self, writer):
615 pass
616
jvrd4d15132002-05-11 00:59:27 +0000617 def postRead(self, table, font):
618 self.__dict__.update(table)
619
620 def preWrite(self, font):
621 return self.__dict__.copy()
622
Behdad Esfahbodd76fa682013-12-09 00:39:25 -0500623 def toXML(self, xmlWriter, font, attrs=None, name=None):
624 tableName = name if name else self.__class__.__name__
jvrd4d15132002-05-11 00:59:27 +0000625 if attrs is None:
626 attrs = []
627 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000628 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000629 xmlWriter.begintag(tableName, attrs)
630 xmlWriter.newline()
631 self.toXML2(xmlWriter, font)
632 xmlWriter.endtag(tableName)
633 xmlWriter.newline()
634
635 def toXML2(self, xmlWriter, font):
636 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
637 # This is because in TTX our parent writes our main tag, and in otBase.py we
638 # do it ourselves. I think I'm getting schizophrenic...
639 for conv in self.getConverters():
jvr64b5c802002-05-11 10:21:36 +0000640 if conv.repeat:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500641 value = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000642 for i in range(len(value)):
643 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000644 conv.xmlWrite(xmlWriter, font, item, conv.name,
645 [("index", i)])
646 else:
Behdad Esfahbod5b9cabc2013-11-25 04:01:56 -0500647 if conv.aux and not eval(conv.aux, None, vars(self)):
648 continue
649 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000650 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000651
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500652 def fromXML(self, name, attrs, content, font):
jvrd4d15132002-05-11 00:59:27 +0000653 try:
654 conv = self.getConverterByName(name)
655 except KeyError:
jvrd4d15132002-05-11 00:59:27 +0000656 raise # XXX on KeyError, raise nice error
657 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000658 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000659 seq = getattr(self, conv.name, None)
660 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000661 seq = []
jvr64b5c802002-05-11 10:21:36 +0000662 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000663 seq.append(value)
664 else:
jvr64b5c802002-05-11 10:21:36 +0000665 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000666
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500667 def __ne__(self, other):
668 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500669 def __eq__(self, other):
670 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500671 return NotImplemented
Behdad Esfahbod96b321c2013-08-17 11:11:22 -0400672
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500673 self.ensureDecompiled()
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500674 other.ensureDecompiled()
Behdad Esfahbodf50d0df2013-11-20 18:38:46 -0500675
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500676 return self.__dict__ == other.__dict__
jvrd4d15132002-05-11 00:59:27 +0000677
678
679class FormatSwitchingBaseTable(BaseTable):
680
jvrcfadfd02002-07-22 22:13:57 +0000681 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000682 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
683
jvrd4d15132002-05-11 00:59:27 +0000684 def getConverters(self):
685 return self.converters[self.Format]
686
687 def getConverterByName(self, name):
688 return self.convertersByName[self.Format][name]
689
jvrf7ef96c2002-09-10 19:26:38 +0000690 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000691 self.Format = reader.readUShort()
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500692 assert self.Format != 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000693
jvrf7ef96c2002-09-10 19:26:38 +0000694 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000695 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000696
Behdad Esfahbodd76fa682013-12-09 00:39:25 -0500697 def toXML(self, xmlWriter, font, attrs=None, name=None):
698 BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
699
jvrd4d15132002-05-11 00:59:27 +0000700
jvr64b5c802002-05-11 10:21:36 +0000701#
702# Support for ValueRecords
703#
704# This data type is so different from all other OpenType data types that
705# it requires quite a bit of code for itself. It even has special support
706# in OTTableReader and OTTableWriter...
707#
708
jvrd4d15132002-05-11 00:59:27 +0000709valueRecordFormat = [
710# Mask Name isDevice signed
711 (0x0001, "XPlacement", 0, 1),
712 (0x0002, "YPlacement", 0, 1),
713 (0x0004, "XAdvance", 0, 1),
714 (0x0008, "YAdvance", 0, 1),
715 (0x0010, "XPlaDevice", 1, 0),
716 (0x0020, "YPlaDevice", 1, 0),
717 (0x0040, "XAdvDevice", 1, 0),
718 (0x0080, "YAdvDevice", 1, 0),
719# reserved:
720 (0x0100, "Reserved1", 0, 0),
721 (0x0200, "Reserved2", 0, 0),
722 (0x0400, "Reserved3", 0, 0),
723 (0x0800, "Reserved4", 0, 0),
724 (0x1000, "Reserved5", 0, 0),
725 (0x2000, "Reserved6", 0, 0),
726 (0x4000, "Reserved7", 0, 0),
727 (0x8000, "Reserved8", 0, 0),
728]
729
730def _buildDict():
731 d = {}
732 for mask, name, isDevice, signed in valueRecordFormat:
733 d[name] = mask, isDevice, signed
734 return d
735
736valueRecordFormatDict = _buildDict()
737
738
Behdad Esfahbode388db52013-11-28 14:26:58 -0500739class ValueRecordFactory(object):
jvrd4d15132002-05-11 00:59:27 +0000740
jvr64b5c802002-05-11 10:21:36 +0000741 """Given a format code, this object convert ValueRecords."""
Behdad Esfahbodd01c44a2013-11-22 15:21:41 -0500742
Behdad Esfahbod601bb942013-11-23 20:20:39 -0500743 def __init__(self, valueFormat):
jvrd4d15132002-05-11 00:59:27 +0000744 format = []
745 for mask, name, isDevice, signed in valueRecordFormat:
746 if valueFormat & mask:
747 format.append((name, isDevice, signed))
748 self.format = format
749
750 def readValueRecord(self, reader, font):
751 format = self.format
752 if not format:
753 return None
754 valueRecord = ValueRecord()
755 for name, isDevice, signed in format:
756 if signed:
757 value = reader.readShort()
758 else:
759 value = reader.readUShort()
760 if isDevice:
761 if value:
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500762 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000763 subReader = reader.getSubReader(value)
764 value = getattr(otTables, name)()
765 value.decompile(subReader, font)
766 else:
767 value = None
768 setattr(valueRecord, name, value)
769 return valueRecord
770
771 def writeValueRecord(self, writer, font, valueRecord):
772 for name, isDevice, signed in self.format:
773 value = getattr(valueRecord, name, 0)
774 if isDevice:
775 if value:
776 subWriter = writer.getSubWriter()
777 writer.writeSubTable(subWriter)
778 value.compile(subWriter, font)
779 else:
780 writer.writeUShort(0)
781 elif signed:
782 writer.writeShort(value)
783 else:
784 writer.writeUShort(value)
785
786
Behdad Esfahbode388db52013-11-28 14:26:58 -0500787class ValueRecord(object):
jvrd4d15132002-05-11 00:59:27 +0000788
789 # see ValueRecordFactory
790
791 def getFormat(self):
792 format = 0
793 for name in self.__dict__.keys():
794 format = format | valueRecordFormatDict[name][0]
795 return format
796
797 def toXML(self, xmlWriter, font, valueName, attrs=None):
798 if attrs is None:
799 simpleItems = []
800 else:
801 simpleItems = list(attrs)
802 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
803 if hasattr(self, name):
804 simpleItems.append((name, getattr(self, name)))
805 deviceItems = []
806 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
807 if hasattr(self, name):
808 device = getattr(self, name)
809 if device is not None:
810 deviceItems.append((name, device))
811 if deviceItems:
812 xmlWriter.begintag(valueName, simpleItems)
813 xmlWriter.newline()
814 for name, deviceRecord in deviceItems:
815 if deviceRecord is not None:
816 deviceRecord.toXML(xmlWriter, font)
817 xmlWriter.endtag(valueName)
818 xmlWriter.newline()
819 else:
820 xmlWriter.simpletag(valueName, simpleItems)
821 xmlWriter.newline()
822
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500823 def fromXML(self, name, attrs, content, font):
Behdad Esfahbod2b06aaa2013-11-27 02:34:11 -0500824 from . import otTables
jvrd4d15132002-05-11 00:59:27 +0000825 for k, v in attrs.items():
826 setattr(self, k, int(v))
827 for element in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500828 if not isinstance(element, tuple):
jvrd4d15132002-05-11 00:59:27 +0000829 continue
830 name, attrs, content = element
831 value = getattr(otTables, name)()
832 for elem2 in content:
Behdad Esfahbodb774f9f2013-11-27 05:17:37 -0500833 if not isinstance(elem2, tuple):
jvrd4d15132002-05-11 00:59:27 +0000834 continue
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500835 name2, attrs2, content2 = elem2
836 value.fromXML(name2, attrs2, content2, font)
jvrd4d15132002-05-11 00:59:27 +0000837 setattr(self, name, value)
838
Behdad Esfahbod8ea64392013-12-06 22:25:48 -0500839 def __ne__(self, other):
840 return not self.__eq__(other)
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500841 def __eq__(self, other):
842 if type(self) != type(other):
Behdad Esfahbod273a9002013-12-07 03:40:44 -0500843 return NotImplemented
Behdad Esfahbodb7fd2e12013-11-27 18:58:45 -0500844 return self.__dict__ == other.__dict__