blob: e024c7b86d91fcc18fe35ff0417889b9bf6ca936 [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.referers = []
131 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000132
133 def getSubWriter(self):
134 return self.__class__(self.tableType, self.valueFormat)
135
jvrcfadfd02002-07-22 22:13:57 +0000136 def __hash__(self):
137 # only works after self._doneWriting() has been called
138 return hash(self.items)
139
140 def __cmp__(self, other):
141 if hasattr(other, "items"):
142 return cmp(self.items, other.items)
143 else:
144 return cmp(id(self), id(other))
145
146 def __len__(self):
147 """Return the length of this table in bytes, without subtables."""
148 l = 0
149 for item in self.items:
150 if hasattr(item, "getData") or hasattr(item, "getCountData"):
151 l = l + 2 # sizeof(UShort)
jvrd4d15132002-05-11 00:59:27 +0000152 else:
jvrcfadfd02002-07-22 22:13:57 +0000153 l = l + len(item)
154 return l
155
156 def _doneWriting(self, internedTables=None):
157 if internedTables is None:
158 internedTables = {}
159 items = self.items
160 for i in range(len(items)):
161 item = items[i]
162 if hasattr(item, "getCountData"):
163 items[i] = item.getCountData()
164 elif hasattr(item, "getData"):
165 assert not item.referers
166 item._doneWriting(internedTables)
167 if internedTables.has_key(item):
168 items[i] = item = internedTables[item]
169 else:
170 internedTables[item] = item
171 if self not in item.referers:
172 item.referers.append(self) # temp. cycle, gets broken by getAllData()
173 self.items = tuple(items)
174
175 def _gatherTables(self, tables=None, done=None):
176 if tables is None:
177 tables = []
178 done = {}
179 for item in self.items:
180 if not hasattr(item, "getData"):
181 continue
182 if not done.has_key(item):
183 item._gatherTables(tables, done)
184 done[self] = len(tables)
185 tables.append(self)
186 return tables
187
188 def getAllData(self):
189 self._doneWriting()
190 tables = self._gatherTables()
191 tables.reverse()
192
193 pos = 0
194 for table in tables:
195 table.pos = pos
196 pos = pos + len(table)
197 table.referers = [] # break cycles
198
199 data = []
200 for table in tables:
201 tableData = table.getData()
202 data.append(tableData)
203
204 return "".join(data)
205
206 def getData(self):
207 for item in self.items:
208 if hasattr(item, "getData"):
209 assert item.pos is not None
210
211 items = list(self.items) # make a shallow copy
jvrd4d15132002-05-11 00:59:27 +0000212 for i in range(len(items)):
213 item = items[i]
214 if hasattr(item, "getData"):
jvrcfadfd02002-07-22 22:13:57 +0000215 items[i] = packUShort(item.pos - self.pos)
216
217 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000218
219 def writeUShort(self, value):
220 assert 0 <= value < 0x10000
221 self.items.append(struct.pack(">H", value))
222
223 def writeShort(self, value):
224 self.items.append(struct.pack(">h", value))
225
226 def writeLong(self, value):
227 self.items.append(struct.pack(">l", value))
228
229 def writeTag(self, tag):
230 assert len(tag) == 4
231 self.items.append(tag)
232
233 def writeSubTable(self, subWriter):
234 self.items.append(subWriter)
235
236 def writeCountReference(self, table, name):
237 self.items.append(CountReference(table, name))
238
239 def writeStruct(self, format, values):
240 data = apply(struct.pack, (format,) + values)
241 self.items.append(data)
242
243 def setValueFormat(self, format, which):
244 self.valueFormat[which].setFormat(format)
245
246 def writeValueRecord(self, value, font, which):
247 return self.valueFormat[which].writeValueRecord(self, font, value)
248
249
250class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000251 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000252 def __init__(self, table, name):
253 self.table = table
254 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000255 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000256 return packUShort(self.table[self.name])
257
258
jvr64b5c802002-05-11 10:21:36 +0000259def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000260 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000261 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000262
263
264
jvr64b5c802002-05-11 10:21:36 +0000265class TableStack:
266 """A stack of table dicts, working as a stack of namespaces so we can
267 retrieve values from (and store values to) tables higher up the stack."""
268 def __init__(self):
269 self.stack = []
270 def push(self, table):
271 self.stack.insert(0, table)
272 def pop(self):
273 self.stack.pop(0)
274 def getTop(self):
275 return self.stack[0]
276 def getValue(self, name):
277 return self.__findTable(name)[name]
278 def storeValue(self, name, value):
279 table = self.__findTable(name)
280 if table[name] is None:
281 table[name] = value
282 else:
283 assert table[name] == value, (table[name], value)
284 def __findTable(self, name):
285 for table in self.stack:
286 if table.has_key(name):
287 return table
288 raise KeyError, name
289
290
jvrd4d15132002-05-11 00:59:27 +0000291class BaseTable:
292
jvr64b5c802002-05-11 10:21:36 +0000293 """Generic base class for all OpenType (sub)tables."""
294
jvrd4d15132002-05-11 00:59:27 +0000295 def getConverters(self):
296 return self.converters
297
298 def getConverterByName(self, name):
299 return self.convertersByName[name]
300
301 def decompile(self, reader, font, tableStack=None):
302 if tableStack is None:
303 tableStack = TableStack()
304 table = {}
305 self.__rawTable = table # for debugging
306 tableStack.push(table)
307 for conv in self.getConverters():
308 if conv.name == "SubTable":
309 conv = conv.getConverter(reader.tableType,
310 table["LookupType"])
311 if conv.repeat:
312 l = []
313 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
314 l.append(conv.read(reader, font, tableStack))
315 table[conv.name] = l
316 else:
317 table[conv.name] = conv.read(reader, font, tableStack)
318 tableStack.pop()
319 self.postRead(table, font)
320 del self.__rawTable # succeeded, get rid of debugging info
321
322 def compile(self, writer, font, tableStack=None):
323 if tableStack is None:
324 tableStack = TableStack()
325 table = self.preWrite(font)
326 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
349 def postRead(self, table, font):
350 self.__dict__.update(table)
351
352 def preWrite(self, font):
353 return self.__dict__.copy()
354
355 def toXML(self, xmlWriter, font, attrs=None):
356 tableName = self.__class__.__name__
357 if attrs is None:
358 attrs = []
359 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000360 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000361 xmlWriter.begintag(tableName, attrs)
362 xmlWriter.newline()
363 self.toXML2(xmlWriter, font)
364 xmlWriter.endtag(tableName)
365 xmlWriter.newline()
366
367 def toXML2(self, xmlWriter, font):
368 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
369 # This is because in TTX our parent writes our main tag, and in otBase.py we
370 # do it ourselves. I think I'm getting schizophrenic...
371 for conv in self.getConverters():
372 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000373 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000374 for i in range(len(value)):
375 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000376 conv.xmlWrite(xmlWriter, font, item, conv.name,
377 [("index", i)])
378 else:
379 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000380
381 def fromXML(self, (name, attrs, content), font):
382 try:
383 conv = self.getConverterByName(name)
384 except KeyError:
jvr64b5c802002-05-11 10:21:36 +0000385## print self, name, attrs, content
jvrd4d15132002-05-11 00:59:27 +0000386 raise # XXX on KeyError, raise nice error
387 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000388 if conv.repeat:
389 try:
jvr64b5c802002-05-11 10:21:36 +0000390 seq = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000391 except AttributeError:
392 seq = []
jvr64b5c802002-05-11 10:21:36 +0000393 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000394 seq.append(value)
395 else:
jvr64b5c802002-05-11 10:21:36 +0000396 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000397
398 def __cmp__(self, other):
399 # this is only for debugging, so it's ok to barf
400 # when 'other' has no __dict__ or __class__
401 rv = cmp(self.__class__, other.__class__)
402 if not rv:
403 rv = cmp(self.__dict__, other.__dict__)
404 return rv
405 else:
406 return rv
407
408
409class FormatSwitchingBaseTable(BaseTable):
410
jvrcfadfd02002-07-22 22:13:57 +0000411 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000412 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
413
jvrd4d15132002-05-11 00:59:27 +0000414 def getConverters(self):
415 return self.converters[self.Format]
416
417 def getConverterByName(self, name):
418 return self.convertersByName[self.Format][name]
419
420 def decompile(self, reader, font, tableStack=None):
421 self.Format = reader.readUShort()
422 assert self.Format <> 0, (self, reader.pos, len(reader.data))
423 BaseTable.decompile(self, reader, font, tableStack)
424
425 def compile(self, writer, font, tableStack=None):
426 writer.writeUShort(self.Format)
427 BaseTable.compile(self, writer, font, tableStack)
428
429
jvr64b5c802002-05-11 10:21:36 +0000430#
431# Support for ValueRecords
432#
433# This data type is so different from all other OpenType data types that
434# it requires quite a bit of code for itself. It even has special support
435# in OTTableReader and OTTableWriter...
436#
437
jvrd4d15132002-05-11 00:59:27 +0000438valueRecordFormat = [
439# Mask Name isDevice signed
440 (0x0001, "XPlacement", 0, 1),
441 (0x0002, "YPlacement", 0, 1),
442 (0x0004, "XAdvance", 0, 1),
443 (0x0008, "YAdvance", 0, 1),
444 (0x0010, "XPlaDevice", 1, 0),
445 (0x0020, "YPlaDevice", 1, 0),
446 (0x0040, "XAdvDevice", 1, 0),
447 (0x0080, "YAdvDevice", 1, 0),
448# reserved:
449 (0x0100, "Reserved1", 0, 0),
450 (0x0200, "Reserved2", 0, 0),
451 (0x0400, "Reserved3", 0, 0),
452 (0x0800, "Reserved4", 0, 0),
453 (0x1000, "Reserved5", 0, 0),
454 (0x2000, "Reserved6", 0, 0),
455 (0x4000, "Reserved7", 0, 0),
456 (0x8000, "Reserved8", 0, 0),
457]
458
459def _buildDict():
460 d = {}
461 for mask, name, isDevice, signed in valueRecordFormat:
462 d[name] = mask, isDevice, signed
463 return d
464
465valueRecordFormatDict = _buildDict()
466
467
468class ValueRecordFactory:
469
jvr64b5c802002-05-11 10:21:36 +0000470 """Given a format code, this object convert ValueRecords."""
471
jvrd4d15132002-05-11 00:59:27 +0000472 def setFormat(self, valueFormat):
473 format = []
474 for mask, name, isDevice, signed in valueRecordFormat:
475 if valueFormat & mask:
476 format.append((name, isDevice, signed))
477 self.format = format
478
479 def readValueRecord(self, reader, font):
480 format = self.format
481 if not format:
482 return None
483 valueRecord = ValueRecord()
484 for name, isDevice, signed in format:
485 if signed:
486 value = reader.readShort()
487 else:
488 value = reader.readUShort()
489 if isDevice:
490 if value:
491 import otTables
492 subReader = reader.getSubReader(value)
493 value = getattr(otTables, name)()
494 value.decompile(subReader, font)
495 else:
496 value = None
497 setattr(valueRecord, name, value)
498 return valueRecord
499
500 def writeValueRecord(self, writer, font, valueRecord):
501 for name, isDevice, signed in self.format:
502 value = getattr(valueRecord, name, 0)
503 if isDevice:
504 if value:
505 subWriter = writer.getSubWriter()
506 writer.writeSubTable(subWriter)
507 value.compile(subWriter, font)
508 else:
509 writer.writeUShort(0)
510 elif signed:
511 writer.writeShort(value)
512 else:
513 writer.writeUShort(value)
514
515
516class ValueRecord:
517
518 # see ValueRecordFactory
519
520 def getFormat(self):
521 format = 0
522 for name in self.__dict__.keys():
523 format = format | valueRecordFormatDict[name][0]
524 return format
525
526 def toXML(self, xmlWriter, font, valueName, attrs=None):
527 if attrs is None:
528 simpleItems = []
529 else:
530 simpleItems = list(attrs)
531 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
532 if hasattr(self, name):
533 simpleItems.append((name, getattr(self, name)))
534 deviceItems = []
535 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
536 if hasattr(self, name):
537 device = getattr(self, name)
538 if device is not None:
539 deviceItems.append((name, device))
540 if deviceItems:
541 xmlWriter.begintag(valueName, simpleItems)
542 xmlWriter.newline()
543 for name, deviceRecord in deviceItems:
544 if deviceRecord is not None:
545 deviceRecord.toXML(xmlWriter, font)
546 xmlWriter.endtag(valueName)
547 xmlWriter.newline()
548 else:
549 xmlWriter.simpletag(valueName, simpleItems)
550 xmlWriter.newline()
551
552 def fromXML(self, (name, attrs, content), font):
553 import otTables
554 for k, v in attrs.items():
555 setattr(self, k, int(v))
556 for element in content:
557 if type(element) <> TupleType:
558 continue
559 name, attrs, content = element
560 value = getattr(otTables, name)()
561 for elem2 in content:
562 if type(elem2) <> TupleType:
563 continue
564 value.fromXML(elem2, font)
565 setattr(self, name, value)
566
567 def __cmp__(self, other):
568 # this is only for debugging, so it's ok to barf
569 # when 'other' has no __dict__ or __class__
570 rv = cmp(self.__class__, other.__class__)
571 if not rv:
572 rv = cmp(self.__dict__, other.__dict__)
573 return rv
574 else:
575 return rv
576