blob: 70884c9b2d515cf7703eb0555bc0e15c61e7ea71 [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
jvr3a6aa232003-09-02 19:23:13 +00009 """Generic base class for TTX table converters. It functions as an
10 adapter between the TTX (ttLib actually) table model and the model
11 we use for OpenType tables, which is necessarily subtly different.
12 """
jvr64b5c802002-05-11 10:21:36 +000013
jvrd4d15132002-05-11 00:59:27 +000014 def decompile(self, data, font):
15 import otTables
jvrcfadfd02002-07-22 22:13:57 +000016 cachingStats = None
17 reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
jvrd4d15132002-05-11 00:59:27 +000018 tableClass = getattr(otTables, self.tableTag)
19 self.table = tableClass()
20 self.table.decompile(reader, font)
jvrcfadfd02002-07-22 22:13:57 +000021 if 0:
22 stats = [(v, k) for k, v in cachingStats.items()]
23 stats.sort()
24 stats.reverse()
25 print "cachingsstats for ", self.tableTag
26 for v, k in stats:
27 if v < 2:
28 break
29 print v, k
30 print "---", len(stats)
jvrd4d15132002-05-11 00:59:27 +000031
32 def compile(self, font):
33 writer = OTTableWriter(self.tableTag)
34 self.table.compile(writer, font)
jvrcfadfd02002-07-22 22:13:57 +000035 return writer.getAllData()
jvrd4d15132002-05-11 00:59:27 +000036
37 def toXML(self, writer, font):
38 self.table.toXML2(writer, font)
39
40 def fromXML(self, (name, attrs, content), font):
41 import otTables
42 if not hasattr(self, "table"):
43 tableClass = getattr(otTables, self.tableTag)
44 self.table = tableClass()
45 self.table.fromXML((name, attrs, content), font)
46
47
48class OTTableReader:
49
jvr64b5c802002-05-11 10:21:36 +000050 """Helper class to retrieve data from an OpenType table."""
51
jvrd4d15132002-05-11 00:59:27 +000052 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
53 self.data = data
54 self.offset = offset
55 self.pos = offset
56 self.tableType = tableType
57 if valueFormat is None:
58 valueFormat = (ValueRecordFactory(), ValueRecordFactory())
59 self.valueFormat = valueFormat
60 self.cachingStats = cachingStats
61
62 def getSubReader(self, offset):
63 offset = self.offset + offset
64 if self.cachingStats is not None:
65 try:
66 self.cachingStats[offset] = self.cachingStats[offset] + 1
67 except KeyError:
68 self.cachingStats[offset] = 1
69
70 subReader = self.__class__(self.data, self.tableType, offset,
71 self.valueFormat, self.cachingStats)
72 return subReader
73
74 def readUShort(self):
75 pos = self.pos
76 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +000077 value, = struct.unpack(">H", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000078 self.pos = newpos
79 return value
80
81 def readShort(self):
82 pos = self.pos
83 newpos = pos + 2
jvre69caf82002-05-13 18:08:19 +000084 value, = struct.unpack(">h", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000085 self.pos = newpos
86 return value
87
88 def readLong(self):
89 pos = self.pos
90 newpos = pos + 4
jvre69caf82002-05-13 18:08:19 +000091 value, = struct.unpack(">l", self.data[pos:newpos])
jvrd4d15132002-05-11 00:59:27 +000092 self.pos = newpos
93 return value
94
95 def readTag(self):
96 pos = self.pos
97 newpos = pos + 4
98 value = self.data[pos:newpos]
99 assert len(value) == 4
100 self.pos = newpos
101 return value
102
103 def readStruct(self, format, size=None):
104 if size is None:
105 size = struct.calcsize(format)
106 else:
107 assert size == struct.calcsize(format)
108 pos = self.pos
109 newpos = pos + size
110 values = struct.unpack(format, self.data[pos:newpos])
111 self.pos = newpos
112 return values
113
114 def setValueFormat(self, format, which):
115 self.valueFormat[which].setFormat(format)
116
117 def readValueRecord(self, font, which):
118 return self.valueFormat[which].readValueRecord(self, font)
119
120
121class OTTableWriter:
122
jvr64b5c802002-05-11 10:21:36 +0000123 """Helper class to gather and assemble data for OpenType tables."""
124
jvrd4d15132002-05-11 00:59:27 +0000125 def __init__(self, tableType, valueFormat=None):
126 self.items = []
127 self.tableType = tableType
128 if valueFormat is None:
129 valueFormat = ValueRecordFactory(), ValueRecordFactory()
130 self.valueFormat = valueFormat
jvrcfadfd02002-07-22 22:13:57 +0000131 self.pos = None
jvrd4d15132002-05-11 00:59:27 +0000132
jvr4105ca02002-07-23 08:43:03 +0000133 # assembler interface
134
135 def getAllData(self):
136 """Assemble all data, including all subtables."""
137 self._doneWriting()
138 tables = self._gatherTables()
139 tables.reverse()
140
141 # Gather all data in two passes: the absolute positions of all
142 # subtable are needed before the actual data can be assembled.
143 pos = 0
144 for table in tables:
145 table.pos = pos
146 pos = pos + table.getDataLength()
147
148 data = []
149 for table in tables:
150 tableData = table.getData()
151 data.append(tableData)
152
153 return "".join(data)
154
155 def getDataLength(self):
156 """Return the length of this table in bytes, without subtables."""
157 l = 0
158 for item in self.items:
159 if hasattr(item, "getData") or hasattr(item, "getCountData"):
160 l = l + 2 # sizeof(UShort)
161 else:
162 l = l + len(item)
163 return l
164
165 def getData(self):
166 """Assemble the data for this writer/table, without subtables."""
167 items = list(self.items) # make a shallow copy
168 for i in range(len(items)):
169 item = items[i]
170 if hasattr(item, "getData"):
171 items[i] = packUShort(item.pos - self.pos)
172 return "".join(items)
jvrd4d15132002-05-11 00:59:27 +0000173
jvrcfadfd02002-07-22 22:13:57 +0000174 def __hash__(self):
175 # only works after self._doneWriting() has been called
176 return hash(self.items)
177
178 def __cmp__(self, other):
179 if hasattr(other, "items"):
180 return cmp(self.items, other.items)
181 else:
182 return cmp(id(self), id(other))
183
jvrcfadfd02002-07-22 22:13:57 +0000184 def _doneWriting(self, internedTables=None):
185 if internedTables is None:
186 internedTables = {}
187 items = self.items
188 for i in range(len(items)):
189 item = items[i]
190 if hasattr(item, "getCountData"):
191 items[i] = item.getCountData()
192 elif hasattr(item, "getData"):
jvrcfadfd02002-07-22 22:13:57 +0000193 item._doneWriting(internedTables)
194 if internedTables.has_key(item):
195 items[i] = item = internedTables[item]
196 else:
197 internedTables[item] = item
jvrcfadfd02002-07-22 22:13:57 +0000198 self.items = tuple(items)
199
200 def _gatherTables(self, tables=None, done=None):
201 if tables is None:
202 tables = []
203 done = {}
204 for item in self.items:
205 if not hasattr(item, "getData"):
206 continue
207 if not done.has_key(item):
208 item._gatherTables(tables, done)
jvrf8f2a362002-07-22 22:22:47 +0000209 done[self] = 1
jvrcfadfd02002-07-22 22:13:57 +0000210 tables.append(self)
211 return tables
212
jvr4105ca02002-07-23 08:43:03 +0000213 # interface for gathering data, as used by table.compile()
jvrcfadfd02002-07-22 22:13:57 +0000214
jvr4105ca02002-07-23 08:43:03 +0000215 def getSubWriter(self):
216 return self.__class__(self.tableType, self.valueFormat)
jvrd4d15132002-05-11 00:59:27 +0000217
218 def writeUShort(self, value):
219 assert 0 <= value < 0x10000
220 self.items.append(struct.pack(">H", value))
221
222 def writeShort(self, value):
223 self.items.append(struct.pack(">h", value))
224
225 def writeLong(self, value):
226 self.items.append(struct.pack(">l", value))
227
228 def writeTag(self, tag):
229 assert len(tag) == 4
230 self.items.append(tag)
231
232 def writeSubTable(self, subWriter):
233 self.items.append(subWriter)
234
235 def writeCountReference(self, table, name):
236 self.items.append(CountReference(table, name))
237
238 def writeStruct(self, format, values):
239 data = apply(struct.pack, (format,) + values)
240 self.items.append(data)
241
242 def setValueFormat(self, format, which):
243 self.valueFormat[which].setFormat(format)
244
245 def writeValueRecord(self, value, font, which):
246 return self.valueFormat[which].writeValueRecord(self, font, value)
247
248
249class CountReference:
jvrcfadfd02002-07-22 22:13:57 +0000250 """A reference to a Count value, not a count of references."""
jvrd4d15132002-05-11 00:59:27 +0000251 def __init__(self, table, name):
252 self.table = table
253 self.name = name
jvrcfadfd02002-07-22 22:13:57 +0000254 def getCountData(self):
jvrd4d15132002-05-11 00:59:27 +0000255 return packUShort(self.table[self.name])
256
257
jvr64b5c802002-05-11 10:21:36 +0000258def packUShort(value):
jvrcfadfd02002-07-22 22:13:57 +0000259 assert 0 <= value < 0x10000, value
jvr64b5c802002-05-11 10:21:36 +0000260 return struct.pack(">H", value)
jvrd4d15132002-05-11 00:59:27 +0000261
262
263
jvr64b5c802002-05-11 10:21:36 +0000264class TableStack:
265 """A stack of table dicts, working as a stack of namespaces so we can
266 retrieve values from (and store values to) tables higher up the stack."""
267 def __init__(self):
268 self.stack = []
269 def push(self, table):
270 self.stack.insert(0, table)
271 def pop(self):
272 self.stack.pop(0)
273 def getTop(self):
274 return self.stack[0]
275 def getValue(self, name):
276 return self.__findTable(name)[name]
277 def storeValue(self, name, value):
278 table = self.__findTable(name)
279 if table[name] is None:
280 table[name] = value
281 else:
282 assert table[name] == value, (table[name], value)
283 def __findTable(self, name):
284 for table in self.stack:
285 if table.has_key(name):
286 return table
287 raise KeyError, name
288
289
jvrd4d15132002-05-11 00:59:27 +0000290class BaseTable:
291
jvr64b5c802002-05-11 10:21:36 +0000292 """Generic base class for all OpenType (sub)tables."""
293
jvrd4d15132002-05-11 00:59:27 +0000294 def getConverters(self):
295 return self.converters
296
297 def getConverterByName(self, name):
298 return self.convertersByName[name]
299
300 def decompile(self, reader, font, tableStack=None):
301 if tableStack is None:
302 tableStack = TableStack()
jvrf7ef96c2002-09-10 19:26:38 +0000303 self.readFormat(reader)
jvrd4d15132002-05-11 00:59:27 +0000304 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)
jvrf7ef96c2002-09-10 19:26:38 +0000326 self.writeFormat(writer)
jvrd4d15132002-05-11 00:59:27 +0000327 tableStack.push(table)
328 for conv in self.getConverters():
329 value = table.get(conv.name)
330 if conv.repeat:
331 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000332 value = []
jvrd4d15132002-05-11 00:59:27 +0000333 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
334 for item in value:
335 conv.write(writer, font, tableStack, item)
336 elif conv.isCount:
337 # Special-case Count values.
338 # Assumption: a Count field will *always* precede
339 # the actual array.
340 # We need a default value, as it may be set later by a nested
341 # table. TableStack.storeValue() will then find it here.
342 table[conv.name] = None
343 # We add a reference: by the time the data is assembled
344 # the Count value will be filled in.
345 writer.writeCountReference(table, conv.name)
346 else:
347 conv.write(writer, font, tableStack, value)
348 tableStack.pop()
349
jvrf7ef96c2002-09-10 19:26:38 +0000350 def readFormat(self, reader):
351 pass
352
353 def writeFormat(self, writer):
354 pass
355
jvrd4d15132002-05-11 00:59:27 +0000356 def postRead(self, table, font):
357 self.__dict__.update(table)
358
359 def preWrite(self, font):
360 return self.__dict__.copy()
361
362 def toXML(self, xmlWriter, font, attrs=None):
363 tableName = self.__class__.__name__
364 if attrs is None:
365 attrs = []
366 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000367 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000368 xmlWriter.begintag(tableName, attrs)
369 xmlWriter.newline()
370 self.toXML2(xmlWriter, font)
371 xmlWriter.endtag(tableName)
372 xmlWriter.newline()
373
374 def toXML2(self, xmlWriter, font):
375 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
376 # This is because in TTX our parent writes our main tag, and in otBase.py we
377 # do it ourselves. I think I'm getting schizophrenic...
378 for conv in self.getConverters():
379 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000380 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000381 for i in range(len(value)):
382 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000383 conv.xmlWrite(xmlWriter, font, item, conv.name,
384 [("index", i)])
385 else:
386 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000387
388 def fromXML(self, (name, attrs, content), font):
389 try:
390 conv = self.getConverterByName(name)
391 except KeyError:
jvr64b5c802002-05-11 10:21:36 +0000392## print self, name, attrs, content
jvrd4d15132002-05-11 00:59:27 +0000393 raise # XXX on KeyError, raise nice error
394 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000395 if conv.repeat:
jvr52966bb2002-09-12 16:45:48 +0000396 seq = getattr(self, conv.name, None)
397 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000398 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