otBase.py revision e69caf8771a0dd675ca3c4490625dac963a6b65c
1d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom DefaultTable import DefaultTable 2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport otData 3d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct 4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom types import TupleType 5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTTXConverter(DefaultTable): 8d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 964b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for TTX table converters. Functions as an adapter 1064b5c80e80444a124da335e8d4d208bffcf2737bjvr between the TTX (ttLib actually) table model and the model we use for 1164b5c80e80444a124da335e8d4d208bffcf2737bjvr OpenType tables, which is neccesarily subtly different.""" 1264b5c80e80444a124da335e8d4d208bffcf2737bjvr 13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def decompile(self, data, font): 14d4d151390d1288f8d2df30f6dfa26a309c7334dajvr import otTables 15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr reader = OTTableReader(data, self.tableTag) 16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = getattr(otTables, self.tableTag) 17d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = tableClass() 18d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.decompile(reader, font) 19d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 20d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def compile(self, font): 21d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer = OTTableWriter(self.tableTag) 22d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.compile(writer, font) 23d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return writer.getData() 24d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 25d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, writer, font): 26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.toXML2(writer, font) 27d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 28d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def fromXML(self, (name, attrs, content), font): 29d4d151390d1288f8d2df30f6dfa26a309c7334dajvr import otTables 30d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(self, "table"): 31d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = getattr(otTables, self.tableTag) 32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = tableClass() 33d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.fromXML((name, attrs, content), font) 34d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 35d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 36d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass OTTableReader: 37d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3864b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to retrieve data from an OpenType table.""" 3964b5c80e80444a124da335e8d4d208bffcf2737bjvr 40d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None): 41d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.data = data 42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.offset = offset 43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = offset 44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.tableType = tableType 45d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat is None: 46d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueFormat = (ValueRecordFactory(), ValueRecordFactory()) 47d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.valueFormat = valueFormat 48d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.cachingStats = cachingStats 49d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 50d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getSubReader(self, offset): 51d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = self.offset + offset 52d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if self.cachingStats is not None: 53d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 54d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.cachingStats[offset] = self.cachingStats[offset] + 1 55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 56d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.cachingStats[offset] = 1 57d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 58d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = self.__class__(self.data, self.tableType, offset, 59d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.valueFormat, self.cachingStats) 60d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return subReader 61d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 62d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readUShort(self): 63d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 64d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 65e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">H", self.data[pos:newpos]) 66d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 67d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 68d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 69d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readShort(self): 70d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 71d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 72e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">h", self.data[pos:newpos]) 73d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 74d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 75d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 76d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readLong(self): 77d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 78d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 79e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">l", self.data[pos:newpos]) 80d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 81d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 83d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readTag(self): 84d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 85d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = self.data[pos:newpos] 87d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(value) == 4 88d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 89d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 90d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readStruct(self, format, size=None): 92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if size is None: 93d4d151390d1288f8d2df30f6dfa26a309c7334dajvr size = struct.calcsize(format) 94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 95d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert size == struct.calcsize(format) 96d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 97d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + size 98d4d151390d1288f8d2df30f6dfa26a309c7334dajvr values = struct.unpack(format, self.data[pos:newpos]) 99d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 100d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return values 101d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 102d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def setValueFormat(self, format, which): 103d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.valueFormat[which].setFormat(format) 104d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 105d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, font, which): 106d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.valueFormat[which].readValueRecord(self, font) 107d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 108d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 109d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass OTTableWriter: 110d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 11164b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to gather and assemble data for OpenType tables.""" 11264b5c80e80444a124da335e8d4d208bffcf2737bjvr 113d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, tableType, valueFormat=None): 114d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items = [] 115d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.tableType = tableType 116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat is None: 117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueFormat = ValueRecordFactory(), ValueRecordFactory() 118d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.valueFormat = valueFormat 119d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 120d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getSubWriter(self): 121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__class__(self.tableType, self.valueFormat) 122d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getData(self): 124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr items = list(self.items) 125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = 0 126d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for item in items: 127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(item, "getData") or hasattr(item, "getCount"): 128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = offset + 2 # sizeof(UShort) 129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = offset + len(item) 131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subTables = [] 132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cache = {} 133d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(items)): 134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = items[i] 135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(item, "getData"): 136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subTableData = item.getData() 137d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if cache.has_key(subTableData): 138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr items[i] = packUShort(cache[subTableData]) 139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr items[i] = packUShort(offset) 141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subTables.append(subTableData) 142d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cache[subTableData] = offset 143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = offset + len(subTableData) 144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif hasattr(item, "getCount"): 145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr items[i] = item.getCount() 146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return "".join(items + subTables) 147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 148d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeUShort(self, value): 149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert 0 <= value < 0x10000 150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">H", value)) 151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeShort(self, value): 153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">h", value)) 154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeLong(self, value): 156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">l", value)) 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeTag(self, tag): 159d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(tag) == 4 160d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(tag) 161d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 162d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeSubTable(self, subWriter): 163d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(subWriter) 164d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 165d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeCountReference(self, table, name): 166d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(CountReference(table, name)) 167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeStruct(self, format, values): 169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr data = apply(struct.pack, (format,) + values) 170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(data) 171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 172d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def setValueFormat(self, format, which): 173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.valueFormat[which].setFormat(format) 174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, value, font, which): 176d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.valueFormat[which].writeValueRecord(self, font, value) 177d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 179d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass CountReference: 18064b5c80e80444a124da335e8d4d208bffcf2737bjvr """A reference to a Count value, not a count of a reference.""" 181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, table, name): 182d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = table 183d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 184d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getCount(self): 185d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return packUShort(self.table[self.name]) 186d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 187d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 18864b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value): 18964b5c80e80444a124da335e8d4d208bffcf2737bjvr assert 0 <= value < 0x10000 19064b5c80e80444a124da335e8d4d208bffcf2737bjvr return struct.pack(">H", value) 191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 19464b5c80e80444a124da335e8d4d208bffcf2737bjvrclass TableStack: 19564b5c80e80444a124da335e8d4d208bffcf2737bjvr """A stack of table dicts, working as a stack of namespaces so we can 19664b5c80e80444a124da335e8d4d208bffcf2737bjvr retrieve values from (and store values to) tables higher up the stack.""" 19764b5c80e80444a124da335e8d4d208bffcf2737bjvr def __init__(self): 19864b5c80e80444a124da335e8d4d208bffcf2737bjvr self.stack = [] 19964b5c80e80444a124da335e8d4d208bffcf2737bjvr def push(self, table): 20064b5c80e80444a124da335e8d4d208bffcf2737bjvr self.stack.insert(0, table) 20164b5c80e80444a124da335e8d4d208bffcf2737bjvr def pop(self): 20264b5c80e80444a124da335e8d4d208bffcf2737bjvr self.stack.pop(0) 20364b5c80e80444a124da335e8d4d208bffcf2737bjvr def getTop(self): 20464b5c80e80444a124da335e8d4d208bffcf2737bjvr return self.stack[0] 20564b5c80e80444a124da335e8d4d208bffcf2737bjvr def getValue(self, name): 20664b5c80e80444a124da335e8d4d208bffcf2737bjvr return self.__findTable(name)[name] 20764b5c80e80444a124da335e8d4d208bffcf2737bjvr def storeValue(self, name, value): 20864b5c80e80444a124da335e8d4d208bffcf2737bjvr table = self.__findTable(name) 20964b5c80e80444a124da335e8d4d208bffcf2737bjvr if table[name] is None: 21064b5c80e80444a124da335e8d4d208bffcf2737bjvr table[name] = value 21164b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 21264b5c80e80444a124da335e8d4d208bffcf2737bjvr assert table[name] == value, (table[name], value) 21364b5c80e80444a124da335e8d4d208bffcf2737bjvr def __findTable(self, name): 21464b5c80e80444a124da335e8d4d208bffcf2737bjvr for table in self.stack: 21564b5c80e80444a124da335e8d4d208bffcf2737bjvr if table.has_key(name): 21664b5c80e80444a124da335e8d4d208bffcf2737bjvr return table 21764b5c80e80444a124da335e8d4d208bffcf2737bjvr raise KeyError, name 21864b5c80e80444a124da335e8d4d208bffcf2737bjvr 21964b5c80e80444a124da335e8d4d208bffcf2737bjvr 220d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTable: 221d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 22264b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for all OpenType (sub)tables.""" 22364b5c80e80444a124da335e8d4d208bffcf2737bjvr 224d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 225d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters 226d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 227d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 228d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[name] 229d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 230d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def decompile(self, reader, font, tableStack=None): 231d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if tableStack is None: 232d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack = TableStack() 233d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = {} 234d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__rawTable = table # for debugging 235d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack.push(table) 236d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 237d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.name == "SubTable": 238d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = conv.getConverter(reader.tableType, 239d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table["LookupType"]) 240d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 241d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l = [] 242d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset): 243d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l.append(conv.read(reader, font, tableStack)) 244d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = l 245d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 246d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = conv.read(reader, font, tableStack) 247d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack.pop() 248d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.postRead(table, font) 249d4d151390d1288f8d2df30f6dfa26a309c7334dajvr del self.__rawTable # succeeded, get rid of debugging info 250d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 251d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def compile(self, writer, font, tableStack=None): 252d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if tableStack is None: 253d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack = TableStack() 254d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.preWrite(font) 255d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack.push(table) 256d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 257d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = table.get(conv.name) 258d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 259d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 26064b5c80e80444a124da335e8d4d208bffcf2737bjvr value = [] 261d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset) 262d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for item in value: 263d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv.write(writer, font, tableStack, item) 264d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif conv.isCount: 265d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Special-case Count values. 266d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Assumption: a Count field will *always* precede 267d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the actual array. 268d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We need a default value, as it may be set later by a nested 269d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # table. TableStack.storeValue() will then find it here. 270d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = None 271d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We add a reference: by the time the data is assembled 272d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the Count value will be filled in. 273d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeCountReference(table, conv.name) 274d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 275d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv.write(writer, font, tableStack, value) 276d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableStack.pop() 277d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 278d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def postRead(self, table, font): 279d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__dict__.update(table) 280d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 281d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def preWrite(self, font): 282d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__dict__.copy() 283d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, attrs=None): 285d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableName = self.__class__.__name__ 286d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 287d4d151390d1288f8d2df30f6dfa26a309c7334dajvr attrs = [] 288d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, "Format"): 28964b5c80e80444a124da335e8d4d208bffcf2737bjvr attrs = attrs + [("Format", self.Format)] 290d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(tableName, attrs) 291d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 292d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.toXML2(xmlWriter, font) 293d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(tableName) 294d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 295d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 296d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML2(self, xmlWriter, font): 297d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). 298d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # This is because in TTX our parent writes our main tag, and in otBase.py we 299d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # do it ourselves. I think I'm getting schizophrenic... 300d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 301d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(self, conv.name) 30264b5c80e80444a124da335e8d4d208bffcf2737bjvr if conv.repeat: 303d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(value)): 304d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = value[i] 30564b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, item, conv.name, 30664b5c80e80444a124da335e8d4d208bffcf2737bjvr [("index", i)]) 30764b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 30864b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, value, conv.name, []) 309d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 310d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def fromXML(self, (name, attrs, content), font): 311d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 312d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = self.getConverterByName(name) 313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 31464b5c80e80444a124da335e8d4d208bffcf2737bjvr## print self, name, attrs, content 315d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise # XXX on KeyError, raise nice error 316d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = conv.xmlRead(attrs, content, font) 317d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 318d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 31964b5c80e80444a124da335e8d4d208bffcf2737bjvr seq = getattr(self, conv.name) 320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except AttributeError: 321d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq = [] 32264b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, seq) 323d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq.append(value) 324d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 32564b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, value) 326d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 327d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __cmp__(self, other): 328d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # this is only for debugging, so it's ok to barf 329d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # when 'other' has no __dict__ or __class__ 330d4d151390d1288f8d2df30f6dfa26a309c7334dajvr rv = cmp(self.__class__, other.__class__) 331d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not rv: 332d4d151390d1288f8d2df30f6dfa26a309c7334dajvr rv = cmp(self.__dict__, other.__dict__) 333d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return rv 334d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 335d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return rv 336d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 337d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 338d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable): 339d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 34064b5c80e80444a124da335e8d4d208bffcf2737bjvr """Small specialization of BaseTable, for tables that have multiple 34164b5c80e80444a124da335e8d4d208bffcf2737bjvr formats, eg. CoverageFormat1 vs. CoverageFormat2.""" 34264b5c80e80444a124da335e8d4d208bffcf2737bjvr 343d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 344d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters[self.Format] 345d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 346d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 347d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[self.Format][name] 348d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 349d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def decompile(self, reader, font, tableStack=None): 350d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.Format = reader.readUShort() 351d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert self.Format <> 0, (self, reader.pos, len(reader.data)) 352d4d151390d1288f8d2df30f6dfa26a309c7334dajvr BaseTable.decompile(self, reader, font, tableStack) 353d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 354d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def compile(self, writer, font, tableStack=None): 355d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(self.Format) 356d4d151390d1288f8d2df30f6dfa26a309c7334dajvr BaseTable.compile(self, writer, font, tableStack) 357d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 358d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 35964b5c80e80444a124da335e8d4d208bffcf2737bjvr# 36064b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords 36164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 36264b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that 36364b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support 36464b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter... 36564b5c80e80444a124da335e8d4d208bffcf2737bjvr# 36664b5c80e80444a124da335e8d4d208bffcf2737bjvr 367d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [ 368d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# Mask Name isDevice signed 369d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0001, "XPlacement", 0, 1), 370d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0002, "YPlacement", 0, 1), 371d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0004, "XAdvance", 0, 1), 372d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0008, "YAdvance", 0, 1), 373d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0010, "XPlaDevice", 1, 0), 374d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0020, "YPlaDevice", 1, 0), 375d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0040, "XAdvDevice", 1, 0), 376d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0080, "YAdvDevice", 1, 0), 377d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# reserved: 378d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0100, "Reserved1", 0, 0), 379d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0200, "Reserved2", 0, 0), 380d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0400, "Reserved3", 0, 0), 381d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0800, "Reserved4", 0, 0), 382d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x1000, "Reserved5", 0, 0), 383d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x2000, "Reserved6", 0, 0), 384d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x4000, "Reserved7", 0, 0), 385d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x8000, "Reserved8", 0, 0), 386d4d151390d1288f8d2df30f6dfa26a309c7334dajvr] 387d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 388d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict(): 389d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d = {} 390d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 391d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d[name] = mask, isDevice, signed 392d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return d 393d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 394d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict() 395d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 396d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 397d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecordFactory: 398d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 39964b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a format code, this object convert ValueRecords.""" 40064b5c80e80444a124da335e8d4d208bffcf2737bjvr 401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def setFormat(self, valueFormat): 402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = [] 403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat & mask: 405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format.append((name, isDevice, signed)) 406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.format = format 407d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 408d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, reader, font): 409d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = self.format 410d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not format: 411d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueRecord = ValueRecord() 413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in format: 414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if signed: 415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readShort() 416d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 417d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readUShort() 418d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr import otTables 421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = reader.getSubReader(value) 422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.decompile(subReader, font) 424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = None 426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(valueRecord, name, value) 427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return valueRecord 428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 429d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, writer, font, valueRecord): 430d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in self.format: 431d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(valueRecord, name, 0) 432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.compile(subWriter, font) 437d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 438d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(0) 439d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif signed: 440d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 441d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 442d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 443d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 444d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 445d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord: 446d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 447d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # see ValueRecordFactory 448d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 449d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getFormat(self): 450d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = 0 451d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name in self.__dict__.keys(): 452d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = format | valueRecordFormatDict[name][0] 453d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 454d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 455d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, valueName, attrs=None): 456d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 457d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = [] 458d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 459d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = list(attrs) 460d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values 461d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 462d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems.append((name, getattr(self, name))) 463d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems = [] 464d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records 465d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 466d4d151390d1288f8d2df30f6dfa26a309c7334dajvr device = getattr(self, name) 467d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if device is not None: 468d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems.append((name, device)) 469d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceItems: 470d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(valueName, simpleItems) 471d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 472d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, deviceRecord in deviceItems: 473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceRecord is not None: 474d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceRecord.toXML(xmlWriter, font) 475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(valueName) 476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 478d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(valueName, simpleItems) 479d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 480d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 481d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def fromXML(self, (name, attrs, content), font): 482d4d151390d1288f8d2df30f6dfa26a309c7334dajvr import otTables 483d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for k, v in attrs.items(): 484d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, k, int(v)) 485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if type(element) <> TupleType: 487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, attrs, content = element 489d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 490d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for elem2 in content: 491d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if type(elem2) <> TupleType: 492d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 493d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.fromXML(elem2, font) 494d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, name, value) 495d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 496d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __cmp__(self, other): 497d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # this is only for debugging, so it's ok to barf 498d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # when 'other' has no __dict__ or __class__ 499d4d151390d1288f8d2df30f6dfa26a309c7334dajvr rv = cmp(self.__class__, other.__class__) 500d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not rv: 501d4d151390d1288f8d2df30f6dfa26a309c7334dajvr rv = cmp(self.__dict__, other.__dict__) 502d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return rv 503d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 504d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return rv 505d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 506