blob: ceb15718058836c5455e34d3441ca7926973b4c5 [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:
jvr52966bb2002-09-12 16:45:48 +0000395 seq = getattr(self, conv.name, None)
396 if seq is None:
jvrd4d15132002-05-11 00:59:27 +0000397 seq = []
jvr64b5c802002-05-11 10:21:36 +0000398 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000399 seq.append(value)
400 else:
jvr64b5c802002-05-11 10:21:36 +0000401 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000402
403 def __cmp__(self, other):
404 # this is only for debugging, so it's ok to barf
405 # when 'other' has no __dict__ or __class__
406 rv = cmp(self.__class__, other.__class__)
407 if not rv:
408 rv = cmp(self.__dict__, other.__dict__)
409 return rv
410 else:
411 return rv
412
413
414class FormatSwitchingBaseTable(BaseTable):
415
jvrcfadfd02002-07-22 22:13:57 +0000416 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000417 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
418
jvrd4d15132002-05-11 00:59:27 +0000419 def getConverters(self):
420 return self.converters[self.Format]
421
422 def getConverterByName(self, name):
423 return self.convertersByName[self.Format][name]
424
jvrf7ef96c2002-09-10 19:26:38 +0000425 def readFormat(self, reader):
jvrd4d15132002-05-11 00:59:27 +0000426 self.Format = reader.readUShort()
427 assert self.Format <> 0, (self, reader.pos, len(reader.data))
jvrd4d15132002-05-11 00:59:27 +0000428
jvrf7ef96c2002-09-10 19:26:38 +0000429 def writeFormat(self, writer):
jvrd4d15132002-05-11 00:59:27 +0000430 writer.writeUShort(self.Format)
jvrd4d15132002-05-11 00:59:27 +0000431
432
jvr64b5c802002-05-11 10:21:36 +0000433#
434# Support for ValueRecords
435#
436# This data type is so different from all other OpenType data types that
437# it requires quite a bit of code for itself. It even has special support
438# in OTTableReader and OTTableWriter...
439#
440
jvrd4d15132002-05-11 00:59:27 +0000441valueRecordFormat = [
442# Mask Name isDevice signed
443 (0x0001, "XPlacement", 0, 1),
444 (0x0002, "YPlacement", 0, 1),
445 (0x0004, "XAdvance", 0, 1),
446 (0x0008, "YAdvance", 0, 1),
447 (0x0010, "XPlaDevice", 1, 0),
448 (0x0020, "YPlaDevice", 1, 0),
449 (0x0040, "XAdvDevice", 1, 0),
450 (0x0080, "YAdvDevice", 1, 0),
451# reserved:
452 (0x0100, "Reserved1", 0, 0),
453 (0x0200, "Reserved2", 0, 0),
454 (0x0400, "Reserved3", 0, 0),
455 (0x0800, "Reserved4", 0, 0),
456 (0x1000, "Reserved5", 0, 0),
457 (0x2000, "Reserved6", 0, 0),
458 (0x4000, "Reserved7", 0, 0),
459 (0x8000, "Reserved8", 0, 0),
460]
461
462def _buildDict():
463 d = {}
464 for mask, name, isDevice, signed in valueRecordFormat:
465 d[name] = mask, isDevice, signed
466 return d
467
468valueRecordFormatDict = _buildDict()
469
470
471class ValueRecordFactory:
472
jvr64b5c802002-05-11 10:21:36 +0000473 """Given a format code, this object convert ValueRecords."""
474
jvrd4d15132002-05-11 00:59:27 +0000475 def setFormat(self, valueFormat):
476 format = []
477 for mask, name, isDevice, signed in valueRecordFormat:
478 if valueFormat & mask:
479 format.append((name, isDevice, signed))
480 self.format = format
481
482 def readValueRecord(self, reader, font):
483 format = self.format
484 if not format:
485 return None
486 valueRecord = ValueRecord()
487 for name, isDevice, signed in format:
488 if signed:
489 value = reader.readShort()
490 else:
491 value = reader.readUShort()
492 if isDevice:
493 if value:
494 import otTables
495 subReader = reader.getSubReader(value)
496 value = getattr(otTables, name)()
497 value.decompile(subReader, font)
498 else:
499 value = None
500 setattr(valueRecord, name, value)
501 return valueRecord
502
503 def writeValueRecord(self, writer, font, valueRecord):
504 for name, isDevice, signed in self.format:
505 value = getattr(valueRecord, name, 0)
506 if isDevice:
507 if value:
508 subWriter = writer.getSubWriter()
509 writer.writeSubTable(subWriter)
510 value.compile(subWriter, font)
511 else:
512 writer.writeUShort(0)
513 elif signed:
514 writer.writeShort(value)
515 else:
516 writer.writeUShort(value)
517
518
519class ValueRecord:
520
521 # see ValueRecordFactory
522
523 def getFormat(self):
524 format = 0
525 for name in self.__dict__.keys():
526 format = format | valueRecordFormatDict[name][0]
527 return format
528
529 def toXML(self, xmlWriter, font, valueName, attrs=None):
530 if attrs is None:
531 simpleItems = []
532 else:
533 simpleItems = list(attrs)
534 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
535 if hasattr(self, name):
536 simpleItems.append((name, getattr(self, name)))
537 deviceItems = []
538 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
539 if hasattr(self, name):
540 device = getattr(self, name)
541 if device is not None:
542 deviceItems.append((name, device))
543 if deviceItems:
544 xmlWriter.begintag(valueName, simpleItems)
545 xmlWriter.newline()
546 for name, deviceRecord in deviceItems:
547 if deviceRecord is not None:
548 deviceRecord.toXML(xmlWriter, font)
549 xmlWriter.endtag(valueName)
550 xmlWriter.newline()
551 else:
552 xmlWriter.simpletag(valueName, simpleItems)
553 xmlWriter.newline()
554
555 def fromXML(self, (name, attrs, content), font):
556 import otTables
557 for k, v in attrs.items():
558 setattr(self, k, int(v))
559 for element in content:
560 if type(element) <> TupleType:
561 continue
562 name, attrs, content = element
563 value = getattr(otTables, name)()
564 for elem2 in content:
565 if type(elem2) <> TupleType:
566 continue
567 value.fromXML(elem2, font)
568 setattr(self, name, value)
569
570 def __cmp__(self, other):
571 # this is only for debugging, so it's ok to barf
572 # when 'other' has no __dict__ or __class__
573 rv = cmp(self.__class__, other.__class__)
574 if not rv:
575 rv = cmp(self.__dict__, other.__dict__)
576 return rv
577 else:
578 return rv
579