blob: 85ffcd7f9956d14d67282364c0b894dc2062e7bd [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
9 def decompile(self, data, font):
10 import otTables
11 reader = OTTableReader(data, self.tableTag)
12 tableClass = getattr(otTables, self.tableTag)
13 self.table = tableClass()
14 self.table.decompile(reader, font)
15
16 def compile(self, font):
17 writer = OTTableWriter(self.tableTag)
18 self.table.compile(writer, font)
19 return writer.getData()
20
21 def toXML(self, writer, font):
22 self.table.toXML2(writer, font)
23
24 def fromXML(self, (name, attrs, content), font):
25 import otTables
26 if not hasattr(self, "table"):
27 tableClass = getattr(otTables, self.tableTag)
28 self.table = tableClass()
29 self.table.fromXML((name, attrs, content), font)
30
31
32class OTTableReader:
33
34 def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
35 self.data = data
36 self.offset = offset
37 self.pos = offset
38 self.tableType = tableType
39 if valueFormat is None:
40 valueFormat = (ValueRecordFactory(), ValueRecordFactory())
41 self.valueFormat = valueFormat
42 self.cachingStats = cachingStats
43
44 def getSubReader(self, offset):
45 offset = self.offset + offset
46 if self.cachingStats is not None:
47 try:
48 self.cachingStats[offset] = self.cachingStats[offset] + 1
49 except KeyError:
50 self.cachingStats[offset] = 1
51
52 subReader = self.__class__(self.data, self.tableType, offset,
53 self.valueFormat, self.cachingStats)
54 return subReader
55
56 def readUShort(self):
57 pos = self.pos
58 newpos = pos + 2
59 value = struct.unpack(">H", self.data[pos:newpos])[0]
60 self.pos = newpos
61 return value
62
63 def readShort(self):
64 pos = self.pos
65 newpos = pos + 2
66 value = struct.unpack(">h", self.data[pos:newpos])[0]
67 self.pos = newpos
68 return value
69
70 def readLong(self):
71 pos = self.pos
72 newpos = pos + 4
73 value = struct.unpack(">l", self.data[pos:newpos])[0]
74 self.pos = newpos
75 return value
76
77 def readTag(self):
78 pos = self.pos
79 newpos = pos + 4
80 value = self.data[pos:newpos]
81 assert len(value) == 4
82 self.pos = newpos
83 return value
84
85 def readStruct(self, format, size=None):
86 if size is None:
87 size = struct.calcsize(format)
88 else:
89 assert size == struct.calcsize(format)
90 pos = self.pos
91 newpos = pos + size
92 values = struct.unpack(format, self.data[pos:newpos])
93 self.pos = newpos
94 return values
95
96 def setValueFormat(self, format, which):
97 self.valueFormat[which].setFormat(format)
98
99 def readValueRecord(self, font, which):
100 return self.valueFormat[which].readValueRecord(self, font)
101
102
103class OTTableWriter:
104
105 def __init__(self, tableType, valueFormat=None):
106 self.items = []
107 self.tableType = tableType
108 if valueFormat is None:
109 valueFormat = ValueRecordFactory(), ValueRecordFactory()
110 self.valueFormat = valueFormat
111
112 def getSubWriter(self):
113 return self.__class__(self.tableType, self.valueFormat)
114
115 def getData(self):
116 items = list(self.items)
117 offset = 0
118 for item in items:
119 if hasattr(item, "getData") or hasattr(item, "getCount"):
120 offset = offset + 2 # sizeof(UShort)
121 else:
122 offset = offset + len(item)
123 subTables = []
124 cache = {}
125 for i in range(len(items)):
126 item = items[i]
127 if hasattr(item, "getData"):
128 subTableData = item.getData()
129 if cache.has_key(subTableData):
130 items[i] = packUShort(cache[subTableData])
131 else:
132 items[i] = packUShort(offset)
133 subTables.append(subTableData)
134 cache[subTableData] = offset
135 offset = offset + len(subTableData)
136 elif hasattr(item, "getCount"):
137 items[i] = item.getCount()
138 return "".join(items + subTables)
139
140 def writeUShort(self, value):
141 assert 0 <= value < 0x10000
142 self.items.append(struct.pack(">H", value))
143
144 def writeShort(self, value):
145 self.items.append(struct.pack(">h", value))
146
147 def writeLong(self, value):
148 self.items.append(struct.pack(">l", value))
149
150 def writeTag(self, tag):
151 assert len(tag) == 4
152 self.items.append(tag)
153
154 def writeSubTable(self, subWriter):
155 self.items.append(subWriter)
156
157 def writeCountReference(self, table, name):
158 self.items.append(CountReference(table, name))
159
160 def writeStruct(self, format, values):
161 data = apply(struct.pack, (format,) + values)
162 self.items.append(data)
163
164 def setValueFormat(self, format, which):
165 self.valueFormat[which].setFormat(format)
166
167 def writeValueRecord(self, value, font, which):
168 return self.valueFormat[which].writeValueRecord(self, font, value)
169
170
171class CountReference:
172 def __init__(self, table, name):
173 self.table = table
174 self.name = name
175 def getCount(self):
176 return packUShort(self.table[self.name])
177
178
179def packUShort(offset):
180 assert 0 <= offset < 0x10000
181 return struct.pack(">H", offset)
182
183
184
185class BaseTable:
186
187 def getConverters(self):
188 return self.converters
189
190 def getConverterByName(self, name):
191 return self.convertersByName[name]
192
193 def decompile(self, reader, font, tableStack=None):
194 if tableStack is None:
195 tableStack = TableStack()
196 table = {}
197 self.__rawTable = table # for debugging
198 tableStack.push(table)
199 for conv in self.getConverters():
200 if conv.name == "SubTable":
201 conv = conv.getConverter(reader.tableType,
202 table["LookupType"])
203 if conv.repeat:
204 l = []
205 for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
206 l.append(conv.read(reader, font, tableStack))
207 table[conv.name] = l
208 else:
209 table[conv.name] = conv.read(reader, font, tableStack)
210 tableStack.pop()
211 self.postRead(table, font)
212 del self.__rawTable # succeeded, get rid of debugging info
213
214 def compile(self, writer, font, tableStack=None):
215 if tableStack is None:
216 tableStack = TableStack()
217 table = self.preWrite(font)
218 tableStack.push(table)
219 for conv in self.getConverters():
220 value = table.get(conv.name)
221 if conv.repeat:
222 if value is None:
223 value = [] # XXXXXX
224 tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
225 for item in value:
226 conv.write(writer, font, tableStack, item)
227 elif conv.isCount:
228 # Special-case Count values.
229 # Assumption: a Count field will *always* precede
230 # the actual array.
231 # We need a default value, as it may be set later by a nested
232 # table. TableStack.storeValue() will then find it here.
233 table[conv.name] = None
234 # We add a reference: by the time the data is assembled
235 # the Count value will be filled in.
236 writer.writeCountReference(table, conv.name)
237 else:
238 conv.write(writer, font, tableStack, value)
239 tableStack.pop()
240
241 def postRead(self, table, font):
242 self.__dict__.update(table)
243
244 def preWrite(self, font):
245 return self.__dict__.copy()
246
247 def toXML(self, xmlWriter, font, attrs=None):
248 tableName = self.__class__.__name__
249 if attrs is None:
250 attrs = []
251 if hasattr(self, "Format"):
252 attrs = attrs + [("Format", str(self.Format))]
253 xmlWriter.begintag(tableName, attrs)
254 xmlWriter.newline()
255 self.toXML2(xmlWriter, font)
256 xmlWriter.endtag(tableName)
257 xmlWriter.newline()
258
259 def toXML2(self, xmlWriter, font):
260 # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
261 # This is because in TTX our parent writes our main tag, and in otBase.py we
262 # do it ourselves. I think I'm getting schizophrenic...
263 for conv in self.getConverters():
264 value = getattr(self, conv.name)
265 if not conv.repeat:
266 conv.xmlWrite(xmlWriter, font, value, conv.name, [])
267 else:
268 for i in range(len(value)):
269 item = value[i]
270 conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)])
271
272 def fromXML(self, (name, attrs, content), font):
273 try:
274 conv = self.getConverterByName(name)
275 except KeyError:
276 print self, name, attrs, content
277 raise # XXX on KeyError, raise nice error
278 value = conv.xmlRead(attrs, content, font)
279 name = conv.name
280 if conv.repeat:
281 try:
282 seq = getattr(self, name)
283 except AttributeError:
284 seq = []
285 setattr(self, name, seq)
286 seq.append(value)
287 else:
288 setattr(self, name, value)
289
290 def __cmp__(self, other):
291 # this is only for debugging, so it's ok to barf
292 # when 'other' has no __dict__ or __class__
293 rv = cmp(self.__class__, other.__class__)
294 if not rv:
295 rv = cmp(self.__dict__, other.__dict__)
296 return rv
297 else:
298 return rv
299
300
301class FormatSwitchingBaseTable(BaseTable):
302
303 def getConverters(self):
304 return self.converters[self.Format]
305
306 def getConverterByName(self, name):
307 return self.convertersByName[self.Format][name]
308
309 def decompile(self, reader, font, tableStack=None):
310 self.Format = reader.readUShort()
311 assert self.Format <> 0, (self, reader.pos, len(reader.data))
312 BaseTable.decompile(self, reader, font, tableStack)
313
314 def compile(self, writer, font, tableStack=None):
315 writer.writeUShort(self.Format)
316 BaseTable.compile(self, writer, font, tableStack)
317
318
319valueRecordFormat = [
320# Mask Name isDevice signed
321 (0x0001, "XPlacement", 0, 1),
322 (0x0002, "YPlacement", 0, 1),
323 (0x0004, "XAdvance", 0, 1),
324 (0x0008, "YAdvance", 0, 1),
325 (0x0010, "XPlaDevice", 1, 0),
326 (0x0020, "YPlaDevice", 1, 0),
327 (0x0040, "XAdvDevice", 1, 0),
328 (0x0080, "YAdvDevice", 1, 0),
329# reserved:
330 (0x0100, "Reserved1", 0, 0),
331 (0x0200, "Reserved2", 0, 0),
332 (0x0400, "Reserved3", 0, 0),
333 (0x0800, "Reserved4", 0, 0),
334 (0x1000, "Reserved5", 0, 0),
335 (0x2000, "Reserved6", 0, 0),
336 (0x4000, "Reserved7", 0, 0),
337 (0x8000, "Reserved8", 0, 0),
338]
339
340def _buildDict():
341 d = {}
342 for mask, name, isDevice, signed in valueRecordFormat:
343 d[name] = mask, isDevice, signed
344 return d
345
346valueRecordFormatDict = _buildDict()
347
348
349class ValueRecordFactory:
350
351 def setFormat(self, valueFormat):
352 format = []
353 for mask, name, isDevice, signed in valueRecordFormat:
354 if valueFormat & mask:
355 format.append((name, isDevice, signed))
356 self.format = format
357
358 def readValueRecord(self, reader, font):
359 format = self.format
360 if not format:
361 return None
362 valueRecord = ValueRecord()
363 for name, isDevice, signed in format:
364 if signed:
365 value = reader.readShort()
366 else:
367 value = reader.readUShort()
368 if isDevice:
369 if value:
370 import otTables
371 subReader = reader.getSubReader(value)
372 value = getattr(otTables, name)()
373 value.decompile(subReader, font)
374 else:
375 value = None
376 setattr(valueRecord, name, value)
377 return valueRecord
378
379 def writeValueRecord(self, writer, font, valueRecord):
380 for name, isDevice, signed in self.format:
381 value = getattr(valueRecord, name, 0)
382 if isDevice:
383 if value:
384 subWriter = writer.getSubWriter()
385 writer.writeSubTable(subWriter)
386 value.compile(subWriter, font)
387 else:
388 writer.writeUShort(0)
389 elif signed:
390 writer.writeShort(value)
391 else:
392 writer.writeUShort(value)
393
394
395class ValueRecord:
396
397 # see ValueRecordFactory
398
399 def getFormat(self):
400 format = 0
401 for name in self.__dict__.keys():
402 format = format | valueRecordFormatDict[name][0]
403 return format
404
405 def toXML(self, xmlWriter, font, valueName, attrs=None):
406 if attrs is None:
407 simpleItems = []
408 else:
409 simpleItems = list(attrs)
410 for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
411 if hasattr(self, name):
412 simpleItems.append((name, getattr(self, name)))
413 deviceItems = []
414 for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
415 if hasattr(self, name):
416 device = getattr(self, name)
417 if device is not None:
418 deviceItems.append((name, device))
419 if deviceItems:
420 xmlWriter.begintag(valueName, simpleItems)
421 xmlWriter.newline()
422 for name, deviceRecord in deviceItems:
423 if deviceRecord is not None:
424 deviceRecord.toXML(xmlWriter, font)
425 xmlWriter.endtag(valueName)
426 xmlWriter.newline()
427 else:
428 xmlWriter.simpletag(valueName, simpleItems)
429 xmlWriter.newline()
430
431 def fromXML(self, (name, attrs, content), font):
432 import otTables
433 for k, v in attrs.items():
434 setattr(self, k, int(v))
435 for element in content:
436 if type(element) <> TupleType:
437 continue
438 name, attrs, content = element
439 value = getattr(otTables, name)()
440 for elem2 in content:
441 if type(elem2) <> TupleType:
442 continue
443 value.fromXML(elem2, font)
444 setattr(self, name, value)
445
446 def __cmp__(self, other):
447 # this is only for debugging, so it's ok to barf
448 # when 'other' has no __dict__ or __class__
449 rv = cmp(self.__class__, other.__class__)
450 if not rv:
451 rv = cmp(self.__dict__, other.__dict__)
452 return rv
453 else:
454 return rv
455
456
457class TableStack:
458 def __init__(self):
459 self.stack = []
460 def push(self, table):
461 self.stack.insert(0, table)
462 def pop(self):
463 self.stack.pop(0)
464 def getTop(self):
465 return self.stack[0]
466 def getValue(self, name):
467 return self.__findTable(name)[name]
468 def storeValue(self, name, value):
469 table = self.__findTable(name)
470 if table[name] is None:
471 table[name] = value
472 else:
473 assert table[name] == value, (table[name], value)
474 def __findTable(self, name):
475 for table in self.stack:
476 if table.has_key(name):
477 return table
478 raise KeyError, name
479