blob: c53ceae3afed809e0d0b0189c0f4191106c8452e [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)
jvrf8f2a362002-07-22 22:22:47 +0000184 done[self] = 1
jvrcfadfd02002-07-22 22:13:57 +0000185 tables.append(self)
186 return tables
187
188 def getAllData(self):
jvrf8f2a362002-07-22 22:22:47 +0000189 """Return all data, including all subtables."""
jvrcfadfd02002-07-22 22:13:57 +0000190 self._doneWriting()
191 tables = self._gatherTables()
192 tables.reverse()
193
194 pos = 0
195 for table in tables:
196 table.pos = pos
197 pos = pos + len(table)
198 table.referers = [] # break cycles
199
200 data = []
201 for table in tables:
202 tableData = table.getData()
203 data.append(tableData)
204
205 return "".join(data)
206
207 def getData(self):
jvrf8f2a362002-07-22 22:22:47 +0000208 """Return the data for this writer/table, without any subtables."""
jvrcfadfd02002-07-22 22:13:57 +0000209 items = list(self.items) # make a shallow copy
jvrd4d15132002-05-11 00:59:27 +0000210 for i in range(len(items)):
211 item = items[i]
212 if hasattr(item, "getData"):
jvrcfadfd02002-07-22 22:13:57 +0000213 items[i] = packUShort(item.pos - self.pos)
jvrcfadfd02002-07-22 22:13:57 +0000214 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000215
216 def writeUShort(self, value):
217 assert 0 <= value < 0x10000
218 self.items.append(struct.pack(">H", value))
219
220 def writeShort(self, value):
221 self.items.append(struct.pack(">h", value))
222
223 def writeLong(self, value):
224 self.items.append(struct.pack(">l", value))
225
226 def writeTag(self, tag):
227 assert len(tag) == 4
228 self.items.append(tag)
229
230 def writeSubTable(self, subWriter):
231 self.items.append(subWriter)
232
233 def writeCountReference(self, table, name):
234 self.items.append(CountReference(table, name))
235
236 def writeStruct(self, format, values):
237 data = apply(struct.pack, (format,) + values)
238 self.items.append(data)
239
240 def setValueFormat(self, format, which):
241 self.valueFormat[which].setFormat(format)
242
243 def writeValueRecord(self, value, font, which):
244 return self.valueFormat[which].writeValueRecord(self, font, value)
245
246
247class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000248 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000249 def __init__(self, table, name):
250 self.table = table
251 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000252 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000253 return packUShort(self.table[self.name])
254
255
jvr64b5c802002-05-11 10:21:36 +0000256def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000257 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000258 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000259
260
261
jvr64b5c802002-05-11 10:21:36 +0000262class TableStack:
263 """A stack of table dicts, working as a stack of namespaces so we can
264 retrieve values from (and store values to) tables higher up the stack."""
265 def __init__(self):
266 self.stack = []
267 def push(self, table):
268 self.stack.insert(0, table)
269 def pop(self):
270 self.stack.pop(0)
271 def getTop(self):
272 return self.stack[0]
273 def getValue(self, name):
274 return self.__findTable(name)[name]
275 def storeValue(self, name, value):
276 table = self.__findTable(name)
277 if table[name] is None:
278 table[name] = value
279 else:
280 assert table[name] == value, (table[name], value)
281 def __findTable(self, name):
282 for table in self.stack:
283 if table.has_key(name):
284 return table
285 raise KeyError, name
286
287
jvrd4d15132002-05-11 00:59:27 +0000288class BaseTable:
289
jvr64b5c802002-05-11 10:21:36 +0000290 """Generic base class for all OpenType (sub)tables."""
291
jvrd4d15132002-05-11 00:59:27 +0000292 def getConverters(self):
293 return self.converters
294
295 def getConverterByName(self, name):
296 return self.convertersByName[name]
297
298 def decompile(self, reader, font, tableStack=None):
299 if tableStack is None:
300 tableStack = TableStack()
301 table = {}
302 self.__rawTable = table # for debugging
303 tableStack.push(table)
304 for conv in self.getConverters():
305 if conv.name == "SubTable":
306 conv = conv.getConverter(reader.tableType,
307 table["LookupType"])
308 if conv.repeat:
309 l = []
310 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
311 l.append(conv.read(reader, font, tableStack))
312 table[conv.name] = l
313 else:
314 table[conv.name] = conv.read(reader, font, tableStack)
315 tableStack.pop()
316 self.postRead(table, font)
317 del self.__rawTable # succeeded, get rid of debugging info
318
319 def compile(self, writer, font, tableStack=None):
320 if tableStack is None:
321 tableStack = TableStack()
322 table = self.preWrite(font)
323 tableStack.push(table)
324 for conv in self.getConverters():
325 value = table.get(conv.name)
326 if conv.repeat:
327 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000328 value = []
jvrd4d15132002-05-11 00:59:27 +0000329 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
330 for item in value:
331 conv.write(writer, font, tableStack, item)
332 elif conv.isCount:
333 # Special-case Count values.
334 # Assumption: a Count field will *always* precede
335 # the actual array.
336 # We need a default value, as it may be set later by a nested
337 # table. TableStack.storeValue() will then find it here.
338 table[conv.name] = None
339 # We add a reference: by the time the data is assembled
340 # the Count value will be filled in.
341 writer.writeCountReference(table, conv.name)
342 else:
343 conv.write(writer, font, tableStack, value)
344 tableStack.pop()
345
346 def postRead(self, table, font):
347 self.__dict__.update(table)
348
349 def preWrite(self, font):
350 return self.__dict__.copy()
351
352 def toXML(self, xmlWriter, font, attrs=None):
353 tableName = self.__class__.__name__
354 if attrs is None:
355 attrs = []
356 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000357 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000358 xmlWriter.begintag(tableName, attrs)
359 xmlWriter.newline()
360 self.toXML2(xmlWriter, font)
361 xmlWriter.endtag(tableName)
362 xmlWriter.newline()
363
364 def toXML2(self, xmlWriter, font):
365 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
366 # This is because in TTX our parent writes our main tag, and in otBase.py we
367 # do it ourselves. I think I'm getting schizophrenic...
368 for conv in self.getConverters():
369 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000370 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000371 for i in range(len(value)):
372 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000373 conv.xmlWrite(xmlWriter, font, item, conv.name,
374 [("index", i)])
375 else:
376 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000377
378 def fromXML(self, (name, attrs, content), font):
379 try:
380 conv = self.getConverterByName(name)
381 except KeyError:
jvr64b5c802002-05-11 10:21:36 +0000382## print self, name, attrs, content
jvrd4d15132002-05-11 00:59:27 +0000383 raise # XXX on KeyError, raise nice error
384 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000385 if conv.repeat:
386 try:
jvr64b5c802002-05-11 10:21:36 +0000387 seq = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000388 except AttributeError:
389 seq = []
jvr64b5c802002-05-11 10:21:36 +0000390 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000391 seq.append(value)
392 else:
jvr64b5c802002-05-11 10:21:36 +0000393 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000394
395 def __cmp__(self, other):
396 # this is only for debugging, so it's ok to barf
397 # when 'other' has no __dict__ or __class__
398 rv = cmp(self.__class__, other.__class__)
399 if not rv:
400 rv = cmp(self.__dict__, other.__dict__)
401 return rv
402 else:
403 return rv
404
405
406class FormatSwitchingBaseTable(BaseTable):
407
jvrcfadfd02002-07-22 22:13:57 +0000408 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000409 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
410
jvrd4d15132002-05-11 00:59:27 +0000411 def getConverters(self):
412 return self.converters[self.Format]
413
414 def getConverterByName(self, name):
415 return self.convertersByName[self.Format][name]
416
417 def decompile(self, reader, font, tableStack=None):
418 self.Format = reader.readUShort()
419 assert self.Format <> 0, (self, reader.pos, len(reader.data))
420 BaseTable.decompile(self, reader, font, tableStack)
421
422 def compile(self, writer, font, tableStack=None):
423 writer.writeUShort(self.Format)
424 BaseTable.compile(self, writer, font, tableStack)
425
426
jvr64b5c802002-05-11 10:21:36 +0000427#
428# Support for ValueRecords
429#
430# This data type is so different from all other OpenType data types that
431# it requires quite a bit of code for itself. It even has special support
432# in OTTableReader and OTTableWriter...
433#
434
jvrd4d15132002-05-11 00:59:27 +0000435valueRecordFormat = [
436# Mask Name isDevice signed
437 (0x0001, "XPlacement", 0, 1),
438 (0x0002, "YPlacement", 0, 1),
439 (0x0004, "XAdvance", 0, 1),
440 (0x0008, "YAdvance", 0, 1),
441 (0x0010, "XPlaDevice", 1, 0),
442 (0x0020, "YPlaDevice", 1, 0),
443 (0x0040, "XAdvDevice", 1, 0),
444 (0x0080, "YAdvDevice", 1, 0),
445# reserved:
446 (0x0100, "Reserved1", 0, 0),
447 (0x0200, "Reserved2", 0, 0),
448 (0x0400, "Reserved3", 0, 0),
449 (0x0800, "Reserved4", 0, 0),
450 (0x1000, "Reserved5", 0, 0),
451 (0x2000, "Reserved6", 0, 0),
452 (0x4000, "Reserved7", 0, 0),
453 (0x8000, "Reserved8", 0, 0),
454]
455
456def _buildDict():
457 d = {}
458 for mask, name, isDevice, signed in valueRecordFormat:
459 d[name] = mask, isDevice, signed
460 return d
461
462valueRecordFormatDict = _buildDict()
463
464
465class ValueRecordFactory:
466
jvr64b5c802002-05-11 10:21:36 +0000467 """Given a format code, this object convert ValueRecords."""
468
jvrd4d15132002-05-11 00:59:27 +0000469 def setFormat(self, valueFormat):
470 format = []
471 for mask, name, isDevice, signed in valueRecordFormat:
472 if valueFormat & mask:
473 format.append((name, isDevice, signed))
474 self.format = format
475
476 def readValueRecord(self, reader, font):
477 format = self.format
478 if not format:
479 return None
480 valueRecord = ValueRecord()
481 for name, isDevice, signed in format:
482 if signed:
483 value = reader.readShort()
484 else:
485 value = reader.readUShort()
486 if isDevice:
487 if value:
488 import otTables
489 subReader = reader.getSubReader(value)
490 value = getattr(otTables, name)()
491 value.decompile(subReader, font)
492 else:
493 value = None
494 setattr(valueRecord, name, value)
495 return valueRecord
496
497 def writeValueRecord(self, writer, font, valueRecord):
498 for name, isDevice, signed in self.format:
499 value = getattr(valueRecord, name, 0)
500 if isDevice:
501 if value:
502 subWriter = writer.getSubWriter()
503 writer.writeSubTable(subWriter)
504 value.compile(subWriter, font)
505 else:
506 writer.writeUShort(0)
507 elif signed:
508 writer.writeShort(value)
509 else:
510 writer.writeUShort(value)
511
512
513class ValueRecord:
514
515 # see ValueRecordFactory
516
517 def getFormat(self):
518 format = 0
519 for name in self.__dict__.keys():
520 format = format | valueRecordFormatDict[name][0]
521 return format
522
523 def toXML(self, xmlWriter, font, valueName, attrs=None):
524 if attrs is None:
525 simpleItems = []
526 else:
527 simpleItems = list(attrs)
528 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
529 if hasattr(self, name):
530 simpleItems.append((name, getattr(self, name)))
531 deviceItems = []
532 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
533 if hasattr(self, name):
534 device = getattr(self, name)
535 if device is not None:
536 deviceItems.append((name, device))
537 if deviceItems:
538 xmlWriter.begintag(valueName, simpleItems)
539 xmlWriter.newline()
540 for name, deviceRecord in deviceItems:
541 if deviceRecord is not None:
542 deviceRecord.toXML(xmlWriter, font)
543 xmlWriter.endtag(valueName)
544 xmlWriter.newline()
545 else:
546 xmlWriter.simpletag(valueName, simpleItems)
547 xmlWriter.newline()
548
549 def fromXML(self, (name, attrs, content), font):
550 import otTables
551 for k, v in attrs.items():
552 setattr(self, k, int(v))
553 for element in content:
554 if type(element) <> TupleType:
555 continue
556 name, attrs, content = element
557 value = getattr(otTables, name)()
558 for elem2 in content:
559 if type(elem2) <> TupleType:
560 continue
561 value.fromXML(elem2, font)
562 setattr(self, name, value)
563
564 def __cmp__(self, other):
565 # this is only for debugging, so it's ok to barf
566 # when 'other' has no __dict__ or __class__
567 rv = cmp(self.__class__, other.__class__)
568 if not rv:
569 rv = cmp(self.__dict__, other.__dict__)
570 return rv
571 else:
572 return rv
573