blob: 9d18cab12085d1de04fd87215c546672d7be9c1b [file] [log] [blame]
jvrd4d15132002-05-11 00:59:27 +00001from DefaultTable import DefaultTable
2import otData
3import struct
4from types import TupleType
5
6
7class BaseTTXConverter(DefaultTable):
8
jvr64b5c802002-05-11 10:21:36 +00009 """Generic base class for TTX table converters. Functions as an adapter
10 between the TTX (ttLib actually) table model and the model we use for
11 OpenType tables, which is neccesarily subtly different."""
12
jvrd4d15132002-05-11 00:59:27 +000013 def decompile(self, data, font):
14 import otTables
jvrcfadfd02002-07-22 22:13:57 +000015 cachingStats = None
16 reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
jvrd4d15132002-05-11 00:59:27 +000017 tableClass = getattr(otTables, self.tableTag)
18 self.table = tableClass()
19 self.table.decompile(reader, font)
jvrcfadfd02002-07-22 22:13:57 +000020 if 0:
21 stats = [(v, k) for k, v in cachingStats.items()]
22 stats.sort()
23 stats.reverse()
24 print "cachingsstats for ", self.tableTag
25 for v, k in stats:
26 if v < 2:
27 break
28 print v, k
29 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000030
31 def compile(self, font):
32 writer = OTTableWriter(self.tableTag)
33 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000034 return writer.getAllData()
jvrd4d15132002-05-11 00:59:27 +000035
36 def toXML(self, writer, font):
37 self.table.toXML2(writer, font)
38
39 def fromXML(self, (name, attrs, content), font):
40 import otTables
41 if not hasattr(self, "table"):
42 tableClass = getattr(otTables, self.tableTag)
43 self.table = tableClass()
44 self.table.fromXML((name, attrs, content), font)
45
46
47class OTTableReader:
48
jvr64b5c802002-05-11 10:21:36 +000049 """Helper class to retrieve data from an OpenType table."""
50
jvrd4d15132002-05-11 00:59:27 +000051 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
52 self.data = data
53 self.offset = offset
54 self.pos = offset
55 self.tableType = tableType
56 if valueFormat is None:
57 valueFormat = (ValueRecordFactory(), ValueRecordFactory())
58 self.valueFormat = valueFormat
59 self.cachingStats = cachingStats
60
61 def getSubReader(self, offset):
62 offset = self.offset + offset
63 if self.cachingStats is not None:
64 try:
65 self.cachingStats[offset] = self.cachingStats[offset] + 1
66 except KeyError:
67 self.cachingStats[offset] = 1
68
69 subReader = self.__class__(self.data, self.tableType, offset,
70 self.valueFormat, self.cachingStats)
71 return subReader
72
73 def readUShort(self):
74 pos = self.pos
75 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +000076 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000077 self.pos = newpos
78 return value
79
80 def readShort(self):
81 pos = self.pos
82 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +000083 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000084 self.pos = newpos
85 return value
86
87 def readLong(self):
88 pos = self.pos
89 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +000090 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000091 self.pos = newpos
92 return value
93
94 def readTag(self):
95 pos = self.pos
96 newpos = pos + 4
97 value = self.data[pos:newpos]
98 assert len(value) == 4
99 self.pos = newpos
100 return value
101
102 def readStruct(self, format, size=None):
103 if size is None:
104 size = struct.calcsize(format)
105 else:
106 assert size == struct.calcsize(format)
107 pos = self.pos
108 newpos = pos + size
109 values = struct.unpack(format, self.data[pos:newpos])
110 self.pos = newpos
111 return values
112
113 def setValueFormat(self, format, which):
114 self.valueFormat[which].setFormat(format)
115
116 def readValueRecord(self, font, which):
117 return self.valueFormat[which].readValueRecord(self, font)
118
119
120class OTTableWriter:
121
jvr64b5c802002-05-11 10:21:36 +0000122 """Helper class to gather and assemble data for OpenType tables."""
123
jvrd4d15132002-05-11 00:59:27 +0000124 def __init__(self, tableType, valueFormat=None):
125 self.items = []
126 self.tableType = tableType
127 if valueFormat is None:
128 valueFormat = ValueRecordFactory(), ValueRecordFactory()
129 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000130 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000131
jvr4105ca02002-07-23 08:43:03 +0000132 # assembler interface
133
134 def getAllData(self):
135 """Assemble all data, including all subtables."""
136 self._doneWriting()
137 tables = self._gatherTables()
138 tables.reverse()
139
140 # Gather all data in two passes: the absolute positions of all
141 # subtable are needed before the actual data can be assembled.
142 pos = 0
143 for table in tables:
144 table.pos = pos
145 pos = pos + table.getDataLength()
146
147 data = []
148 for table in tables:
149 tableData = table.getData()
150 data.append(tableData)
151
152 return "".join(data)
153
154 def getDataLength(self):
155 """Return the length of this table in bytes, without subtables."""
156 l = 0
157 for item in self.items:
158 if hasattr(item, "getData") or hasattr(item, "getCountData"):
159 l = l + 2 # sizeof(UShort)
160 else:
161 l = l + len(item)
162 return l
163
164 def getData(self):
165 """Assemble the data for this writer/table, without subtables."""
166 items = list(self.items) # make a shallow copy
167 for i in range(len(items)):
168 item = items[i]
169 if hasattr(item, "getData"):
170 items[i] = packUShort(item.pos - self.pos)
171 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000172
jvrcfadfd02002-07-22 22:13:57 +0000173 def __hash__(self):
174 # only works after self._doneWriting() has been called
175 return hash(self.items)
176
177 def __cmp__(self, other):
178 if hasattr(other, "items"):
179 return cmp(self.items, other.items)
180 else:
181 return cmp(id(self), id(other))
182
jvrcfadfd02002-07-22 22:13:57 +0000183 def _doneWriting(self, internedTables=None):
184 if internedTables is None:
185 internedTables = {}
186 items = self.items
187 for i in range(len(items)):
188 item = items[i]
189 if hasattr(item, "getCountData"):
190 items[i] = item.getCountData()
191 elif hasattr(item, "getData"):
jvrcfadfd02002-07-22 22:13:57 +0000192 item._doneWriting(internedTables)
193 if internedTables.has_key(item):
194 items[i] = item = internedTables[item]
195 else:
196 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000197 self.items = tuple(items)
198
199 def _gatherTables(self, tables=None, done=None):
200 if tables is None:
201 tables = []
202 done = {}
203 for item in self.items:
204 if not hasattr(item, "getData"):
205 continue
206 if not done.has_key(item):
207 item._gatherTables(tables, done)
jvrf8f2a362002-07-22 22:22:47 +0000208 done[self] = 1
jvrcfadfd02002-07-22 22:13:57 +0000209 tables.append(self)
210 return tables
211
jvr4105ca02002-07-23 08:43:03 +0000212 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000213
jvr4105ca02002-07-23 08:43:03 +0000214 def getSubWriter(self):
215 return self.__class__(self.tableType, self.valueFormat)
jvrd4d15132002-05-11 00:59:27 +0000216
217 def writeUShort(self, value):
218 assert 0 <= value < 0x10000
219 self.items.append(struct.pack(">H", value))
220
221 def writeShort(self, value):
222 self.items.append(struct.pack(">h", value))
223
224 def writeLong(self, value):
225 self.items.append(struct.pack(">l", value))
226
227 def writeTag(self, tag):
228 assert len(tag) == 4
229 self.items.append(tag)
230
231 def writeSubTable(self, subWriter):
232 self.items.append(subWriter)
233
234 def writeCountReference(self, table, name):
235 self.items.append(CountReference(table, name))
236
237 def writeStruct(self, format, values):
238 data = apply(struct.pack, (format,) + values)
239 self.items.append(data)
240
241 def setValueFormat(self, format, which):
242 self.valueFormat[which].setFormat(format)
243
244 def writeValueRecord(self, value, font, which):
245 return self.valueFormat[which].writeValueRecord(self, font, value)
246
247
248class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000249 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000250 def __init__(self, table, name):
251 self.table = table
252 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000253 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000254 return packUShort(self.table[self.name])
255
256
jvr64b5c802002-05-11 10:21:36 +0000257def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000258 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000259 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000260
261
262
jvr64b5c802002-05-11 10:21:36 +0000263class TableStack:
264 """A stack of table dicts, working as a stack of namespaces so we can
265 retrieve values from (and store values to) tables higher up the stack."""
266 def __init__(self):
267 self.stack = []
268 def push(self, table):
269 self.stack.insert(0, table)
270 def pop(self):
271 self.stack.pop(0)
272 def getTop(self):
273 return self.stack[0]
274 def getValue(self, name):
275 return self.__findTable(name)[name]
276 def storeValue(self, name, value):
277 table = self.__findTable(name)
278 if table[name] is None:
279 table[name] = value
280 else:
281 assert table[name] == value, (table[name], value)
282 def __findTable(self, name):
283 for table in self.stack:
284 if table.has_key(name):
285 return table
286 raise KeyError, name
287
288
jvrd4d15132002-05-11 00:59:27 +0000289class BaseTable:
290
jvr64b5c802002-05-11 10:21:36 +0000291 """Generic base class for all OpenType (sub)tables."""
292
jvrd4d15132002-05-11 00:59:27 +0000293 def getConverters(self):
294 return self.converters
295
296 def getConverterByName(self, name):
297 return self.convertersByName[name]
298
299 def decompile(self, reader, font, tableStack=None):
300 if tableStack is None:
301 tableStack = TableStack()
jvrf7ef96c2002-09-10 19:26:38 +0000302 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000303 table = {}
304 self.__rawTable = table # for debugging
305 tableStack.push(table)
306 for conv in self.getConverters():
307 if conv.name == "SubTable":
308 conv = conv.getConverter(reader.tableType,
309 table["LookupType"])
310 if conv.repeat:
311 l = []
312 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
313 l.append(conv.read(reader, font, tableStack))
314 table[conv.name] = l
315 else:
316 table[conv.name] = conv.read(reader, font, tableStack)
317 tableStack.pop()
318 self.postRead(table, font)
319 del self.__rawTable # succeeded, get rid of debugging info
320
321 def compile(self, writer, font, tableStack=None):
322 if tableStack is None:
323 tableStack = TableStack()
324 table = self.preWrite(font)
jvrf7ef96c2002-09-10 19:26:38 +0000325 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000326 tableStack.push(table)
327 for conv in self.getConverters():
328 value = table.get(conv.name)
329 if conv.repeat:
330 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000331 value = []
jvrd4d15132002-05-11 00:59:27 +0000332 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
333 for item in value:
334 conv.write(writer, font, tableStack, item)
335 elif conv.isCount:
336 # Special-case Count values.
337 # Assumption: a Count field will *always* precede
338 # the actual array.
339 # We need a default value, as it may be set later by a nested
340 # table. TableStack.storeValue() will then find it here.
341 table[conv.name] = None
342 # We add a reference: by the time the data is assembled
343 # the Count value will be filled in.
344 writer.writeCountReference(table, conv.name)
345 else:
346 conv.write(writer, font, tableStack, value)
347 tableStack.pop()
348
jvrf7ef96c2002-09-10 19:26:38 +0000349 def readFormat(self, reader):
350 pass
351
352 def writeFormat(self, writer):
353 pass
354
jvrd4d15132002-05-11 00:59:27 +0000355 def postRead(self, table, font):
356 self.__dict__.update(table)
357
358 def preWrite(self, font):
359 return self.__dict__.copy()
360
361 def toXML(self, xmlWriter, font, attrs=None):
362 tableName = self.__class__.__name__
363 if attrs is None:
364 attrs = []
365 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000366 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000367 xmlWriter.begintag(tableName, attrs)
368 xmlWriter.newline()
369 self.toXML2(xmlWriter, font)
370 xmlWriter.endtag(tableName)
371 xmlWriter.newline()
372
373 def toXML2(self, xmlWriter, font):
374 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
375 # This is because in TTX our parent writes our main tag, and in otBase.py we
376 # do it ourselves. I think I'm getting schizophrenic...
377 for conv in self.getConverters():
378 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000379 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000380 for i in range(len(value)):
381 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000382 conv.xmlWrite(xmlWriter, font, item, conv.name,
383 [("index", i)])
384 else:
385 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000386
387 def fromXML(self, (name, attrs, content), font):
388 try:
389 conv = self.getConverterByName(name)
390 except KeyError:
jvr64b5c802002-05-11 10:21:36 +0000391## print self, name, attrs, content
jvrd4d15132002-05-11 00:59:27 +0000392 raise # XXX on KeyError, raise nice error
393 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000394 if conv.repeat:
395 try:
jvr64b5c802002-05-11 10:21:36 +0000396 seq = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000397 except AttributeError:
398 seq = []
jvr64b5c802002-05-11 10:21:36 +0000399 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000400 seq.append(value)
401 else:
jvr64b5c802002-05-11 10:21:36 +0000402 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000403
404 def __cmp__(self, other):
405 # this is only for debugging, so it's ok to barf
406 # when 'other' has no __dict__ or __class__
407 rv = cmp(self.__class__, other.__class__)
408 if not rv:
409 rv = cmp(self.__dict__, other.__dict__)
410 return rv
411 else:
412 return rv
413
414
415class FormatSwitchingBaseTable(BaseTable):
416
jvrcfadfd02002-07-22 22:13:57 +0000417 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000418 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
419
jvrd4d15132002-05-11 00:59:27 +0000420 def getConverters(self):
421 return self.converters[self.Format]
422
423 def getConverterByName(self, name):
424 return self.convertersByName[self.Format][name]
425
jvrf7ef96c2002-09-10 19:26:38 +0000426 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000427 self.Format = reader.readUShort()
428 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000429
jvrf7ef96c2002-09-10 19:26:38 +0000430 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000431 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000432
433
jvr64b5c802002-05-11 10:21:36 +0000434#
435# Support for ValueRecords
436#
437# This data type is so different from all other OpenType data types that
438# it requires quite a bit of code for itself. It even has special support
439# in OTTableReader and OTTableWriter...
440#
441
jvrd4d15132002-05-11 00:59:27 +0000442valueRecordFormat = [
443# Mask Name isDevice signed
444 (0x0001, "XPlacement", 0, 1),
445 (0x0002, "YPlacement", 0, 1),
446 (0x0004, "XAdvance", 0, 1),
447 (0x0008, "YAdvance", 0, 1),
448 (0x0010, "XPlaDevice", 1, 0),
449 (0x0020, "YPlaDevice", 1, 0),
450 (0x0040, "XAdvDevice", 1, 0),
451 (0x0080, "YAdvDevice", 1, 0),
452# reserved:
453 (0x0100, "Reserved1", 0, 0),
454 (0x0200, "Reserved2", 0, 0),
455 (0x0400, "Reserved3", 0, 0),
456 (0x0800, "Reserved4", 0, 0),
457 (0x1000, "Reserved5", 0, 0),
458 (0x2000, "Reserved6", 0, 0),
459 (0x4000, "Reserved7", 0, 0),
460 (0x8000, "Reserved8", 0, 0),
461]
462
463def _buildDict():
464 d = {}
465 for mask, name, isDevice, signed in valueRecordFormat:
466 d[name] = mask, isDevice, signed
467 return d
468
469valueRecordFormatDict = _buildDict()
470
471
472class ValueRecordFactory:
473
jvr64b5c802002-05-11 10:21:36 +0000474 """Given a format code, this object convert ValueRecords."""
475
jvrd4d15132002-05-11 00:59:27 +0000476 def setFormat(self, valueFormat):
477 format = []
478 for mask, name, isDevice, signed in valueRecordFormat:
479 if valueFormat & mask:
480 format.append((name, isDevice, signed))
481 self.format = format
482
483 def readValueRecord(self, reader, font):
484 format = self.format
485 if not format:
486 return None
487 valueRecord = ValueRecord()
488 for name, isDevice, signed in format:
489 if signed:
490 value = reader.readShort()
491 else:
492 value = reader.readUShort()
493 if isDevice:
494 if value:
495 import otTables
496 subReader = reader.getSubReader(value)
497 value = getattr(otTables, name)()
498 value.decompile(subReader, font)
499 else:
500 value = None
501 setattr(valueRecord, name, value)
502 return valueRecord
503
504 def writeValueRecord(self, writer, font, valueRecord):
505 for name, isDevice, signed in self.format:
506 value = getattr(valueRecord, name, 0)
507 if isDevice:
508 if value:
509 subWriter = writer.getSubWriter()
510 writer.writeSubTable(subWriter)
511 value.compile(subWriter, font)
512 else:
513 writer.writeUShort(0)
514 elif signed:
515 writer.writeShort(value)
516 else:
517 writer.writeUShort(value)
518
519
520class ValueRecord:
521
522 # see ValueRecordFactory
523
524 def getFormat(self):
525 format = 0
526 for name in self.__dict__.keys():
527 format = format | valueRecordFormatDict[name][0]
528 return format
529
530 def toXML(self, xmlWriter, font, valueName, attrs=None):
531 if attrs is None:
532 simpleItems = []
533 else:
534 simpleItems = list(attrs)
535 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
536 if hasattr(self, name):
537 simpleItems.append((name, getattr(self, name)))
538 deviceItems = []
539 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
540 if hasattr(self, name):
541 device = getattr(self, name)
542 if device is not None:
543 deviceItems.append((name, device))
544 if deviceItems:
545 xmlWriter.begintag(valueName, simpleItems)
546 xmlWriter.newline()
547 for name, deviceRecord in deviceItems:
548 if deviceRecord is not None:
549 deviceRecord.toXML(xmlWriter, font)
550 xmlWriter.endtag(valueName)
551 xmlWriter.newline()
552 else:
553 xmlWriter.simpletag(valueName, simpleItems)
554 xmlWriter.newline()
555
556 def fromXML(self, (name, attrs, content), font):
557 import otTables
558 for k, v in attrs.items():
559 setattr(self, k, int(v))
560 for element in content:
561 if type(element) <> TupleType:
562 continue
563 name, attrs, content = element
564 value = getattr(otTables, name)()
565 for elem2 in content:
566 if type(elem2) <> TupleType:
567 continue
568 value.fromXML(elem2, font)
569 setattr(self, name, value)
570
571 def __cmp__(self, other):
572 # this is only for debugging, so it's ok to barf
573 # when 'other' has no __dict__ or __class__
574 rv = cmp(self.__class__, other.__class__)
575 if not rv:
576 rv = cmp(self.__dict__, other.__dict__)
577 return rv
578 else:
579 return rv
580