blob: 1ddea2ddbe5e292bbd6ddbdb7ac20fe97e50b9c2 [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()
302 table = {}
303 self.__rawTable = table # for debugging
304 tableStack.push(table)
305 for conv in self.getConverters():
306 if conv.name == "SubTable":
307 conv = conv.getConverter(reader.tableType,
308 table["LookupType"])
309 if conv.repeat:
310 l = []
311 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
312 l.append(conv.read(reader, font, tableStack))
313 table[conv.name] = l
314 else:
315 table[conv.name] = conv.read(reader, font, tableStack)
316 tableStack.pop()
317 self.postRead(table, font)
318 del self.__rawTable # succeeded, get rid of debugging info
319
320 def compile(self, writer, font, tableStack=None):
321 if tableStack is None:
322 tableStack = TableStack()
323 table = self.preWrite(font)
324 tableStack.push(table)
325 for conv in self.getConverters():
326 value = table.get(conv.name)
327 if conv.repeat:
328 if value is None:
jvr64b5c802002-05-11 10:21:36 +0000329 value = []
jvrd4d15132002-05-11 00:59:27 +0000330 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
331 for item in value:
332 conv.write(writer, font, tableStack, item)
333 elif conv.isCount:
334 # Special-case Count values.
335 # Assumption: a Count field will *always* precede
336 # the actual array.
337 # We need a default value, as it may be set later by a nested
338 # table. TableStack.storeValue() will then find it here.
339 table[conv.name] = None
340 # We add a reference: by the time the data is assembled
341 # the Count value will be filled in.
342 writer.writeCountReference(table, conv.name)
343 else:
344 conv.write(writer, font, tableStack, value)
345 tableStack.pop()
346
347 def postRead(self, table, font):
348 self.__dict__.update(table)
349
350 def preWrite(self, font):
351 return self.__dict__.copy()
352
353 def toXML(self, xmlWriter, font, attrs=None):
354 tableName = self.__class__.__name__
355 if attrs is None:
356 attrs = []
357 if hasattr(self, "Format"):
jvr64b5c802002-05-11 10:21:36 +0000358 attrs = attrs + [("Format", self.Format)]
jvrd4d15132002-05-11 00:59:27 +0000359 xmlWriter.begintag(tableName, attrs)
360 xmlWriter.newline()
361 self.toXML2(xmlWriter, font)
362 xmlWriter.endtag(tableName)
363 xmlWriter.newline()
364
365 def toXML2(self, xmlWriter, font):
366 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
367 # This is because in TTX our parent writes our main tag, and in otBase.py we
368 # do it ourselves. I think I'm getting schizophrenic...
369 for conv in self.getConverters():
370 value = getattr(self, conv.name)
jvr64b5c802002-05-11 10:21:36 +0000371 if conv.repeat:
jvrd4d15132002-05-11 00:59:27 +0000372 for i in range(len(value)):
373 item = value[i]
jvr64b5c802002-05-11 10:21:36 +0000374 conv.xmlWrite(xmlWriter, font, item, conv.name,
375 [("index", i)])
376 else:
377 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
jvrd4d15132002-05-11 00:59:27 +0000378
379 def fromXML(self, (name, attrs, content), font):
380 try:
381 conv = self.getConverterByName(name)
382 except KeyError:
jvr64b5c802002-05-11 10:21:36 +0000383## print self, name, attrs, content
jvrd4d15132002-05-11 00:59:27 +0000384 raise # XXX on KeyError, raise nice error
385 value = conv.xmlRead(attrs, content, font)
jvrd4d15132002-05-11 00:59:27 +0000386 if conv.repeat:
387 try:
jvr64b5c802002-05-11 10:21:36 +0000388 seq = getattr(self, conv.name)
jvrd4d15132002-05-11 00:59:27 +0000389 except AttributeError:
390 seq = []
jvr64b5c802002-05-11 10:21:36 +0000391 setattr(self, conv.name, seq)
jvrd4d15132002-05-11 00:59:27 +0000392 seq.append(value)
393 else:
jvr64b5c802002-05-11 10:21:36 +0000394 setattr(self, conv.name, value)
jvrd4d15132002-05-11 00:59:27 +0000395
396 def __cmp__(self, other):
397 # this is only for debugging, so it's ok to barf
398 # when 'other' has no __dict__ or __class__
399 rv = cmp(self.__class__, other.__class__)
400 if not rv:
401 rv = cmp(self.__dict__, other.__dict__)
402 return rv
403 else:
404 return rv
405
406
407class FormatSwitchingBaseTable(BaseTable):
408
jvrcfadfd02002-07-22 22:13:57 +0000409 """Minor specialization of BaseTable, for tables that have multiple
jvr64b5c802002-05-11 10:21:36 +0000410 formats, eg. CoverageFormat1 vs. CoverageFormat2."""
411
jvrd4d15132002-05-11 00:59:27 +0000412 def getConverters(self):
413 return self.converters[self.Format]
414
415 def getConverterByName(self, name):
416 return self.convertersByName[self.Format][name]
417
418 def decompile(self, reader, font, tableStack=None):
419 self.Format = reader.readUShort()
420 assert self.Format <> 0, (self, reader.pos, len(reader.data))
421 BaseTable.decompile(self, reader, font, tableStack)
422
423 def compile(self, writer, font, tableStack=None):
424 writer.writeUShort(self.Format)
425 BaseTable.compile(self, writer, font, tableStack)
426
427
jvr64b5c802002-05-11 10:21:36 +0000428#
429# Support for ValueRecords
430#
431# This data type is so different from all other OpenType data types that
432# it requires quite a bit of code for itself. It even has special support
433# in OTTableReader and OTTableWriter...
434#
435
jvrd4d15132002-05-11 00:59:27 +0000436valueRecordFormat = [
437# Mask Name isDevice signed
438 (0x0001, "XPlacement", 0, 1),
439 (0x0002, "YPlacement", 0, 1),
440 (0x0004, "XAdvance", 0, 1),
441 (0x0008, "YAdvance", 0, 1),
442 (0x0010, "XPlaDevice", 1, 0),
443 (0x0020, "YPlaDevice", 1, 0),
444 (0x0040, "XAdvDevice", 1, 0),
445 (0x0080, "YAdvDevice", 1, 0),
446# reserved:
447 (0x0100, "Reserved1", 0, 0),
448 (0x0200, "Reserved2", 0, 0),
449 (0x0400, "Reserved3", 0, 0),
450 (0x0800, "Reserved4", 0, 0),
451 (0x1000, "Reserved5", 0, 0),
452 (0x2000, "Reserved6", 0, 0),
453 (0x4000, "Reserved7", 0, 0),
454 (0x8000, "Reserved8", 0, 0),
455]
456
457def _buildDict():
458 d = {}
459 for mask, name, isDevice, signed in valueRecordFormat:
460 d[name] = mask, isDevice, signed
461 return d
462
463valueRecordFormatDict = _buildDict()
464
465
466class ValueRecordFactory:
467
jvr64b5c802002-05-11 10:21:36 +0000468 """Given a format code, this object convert ValueRecords."""
469
jvrd4d15132002-05-11 00:59:27 +0000470 def setFormat(self, valueFormat):
471 format = []
472 for mask, name, isDevice, signed in valueRecordFormat:
473 if valueFormat & mask:
474 format.append((name, isDevice, signed))
475 self.format = format
476
477 def readValueRecord(self, reader, font):
478 format = self.format
479 if not format:
480 return None
481 valueRecord = ValueRecord()
482 for name, isDevice, signed in format:
483 if signed:
484 value = reader.readShort()
485 else:
486 value = reader.readUShort()
487 if isDevice:
488 if value:
489 import otTables
490 subReader = reader.getSubReader(value)
491 value = getattr(otTables, name)()
492 value.decompile(subReader, font)
493 else:
494 value = None
495 setattr(valueRecord, name, value)
496 return valueRecord
497
498 def writeValueRecord(self, writer, font, valueRecord):
499 for name, isDevice, signed in self.format:
500 value = getattr(valueRecord, name, 0)
501 if isDevice:
502 if value:
503 subWriter = writer.getSubWriter()
504 writer.writeSubTable(subWriter)
505 value.compile(subWriter, font)
506 else:
507 writer.writeUShort(0)
508 elif signed:
509 writer.writeShort(value)
510 else:
511 writer.writeUShort(value)
512
513
514class ValueRecord:
515
516 # see ValueRecordFactory
517
518 def getFormat(self):
519 format = 0
520 for name in self.__dict__.keys():
521 format = format | valueRecordFormatDict[name][0]
522 return format
523
524 def toXML(self, xmlWriter, font, valueName, attrs=None):
525 if attrs is None:
526 simpleItems = []
527 else:
528 simpleItems = list(attrs)
529 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
530 if hasattr(self, name):
531 simpleItems.append((name, getattr(self, name)))
532 deviceItems = []
533 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
534 if hasattr(self, name):
535 device = getattr(self, name)
536 if device is not None:
537 deviceItems.append((name, device))
538 if deviceItems:
539 xmlWriter.begintag(valueName, simpleItems)
540 xmlWriter.newline()
541 for name, deviceRecord in deviceItems:
542 if deviceRecord is not None:
543 deviceRecord.toXML(xmlWriter, font)
544 xmlWriter.endtag(valueName)
545 xmlWriter.newline()
546 else:
547 xmlWriter.simpletag(valueName, simpleItems)
548 xmlWriter.newline()
549
550 def fromXML(self, (name, attrs, content), font):
551 import otTables
552 for k, v in attrs.items():
553 setattr(self, k, int(v))
554 for element in content:
555 if type(element) <> TupleType:
556 continue
557 name, attrs, content = element
558 value = getattr(otTables, name)()
559 for elem2 in content:
560 if type(elem2) <> TupleType:
561 continue
562 value.fromXML(elem2, font)
563 setattr(self, name, value)
564
565 def __cmp__(self, other):
566 # this is only for debugging, so it's ok to barf
567 # when 'other' has no __dict__ or __class__
568 rv = cmp(self.__class__, other.__class__)
569 if not rv:
570 rv = cmp(self.__dict__, other.__dict__)
571 return rv
572 else:
573 return rv
574