otBase.py revision 821572c9a92d338a7ecbb4261c08ce378eb5434d
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division 230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .DefaultTable import DefaultTable 4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct 5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6823f8cd15f16bb9dc3991c2672f16dd90579711bjvrclass OverflowErrorRecord: 7823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __init__(self, overflowTuple): 8823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.tableType = overflowTuple[0] 9823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.LookupListIndex = overflowTuple[1] 10823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.SubTableIndex = overflowTuple[2] 11823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.itemName = overflowTuple[3] 12823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.itemIndex = overflowTuple[4] 13823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 14823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __repr__(self): 15823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return str((self.tableType, "LookupIndex:", self.LookupListIndex, "SubTableIndex:", self.SubTableIndex, "ItemName:", self.itemName, "ItemIndex:", self.itemIndex)) 16823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 17823f8cd15f16bb9dc3991c2672f16dd90579711bjvrclass OTLOffsetOverflowError(Exception): 18823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __init__(self, overflowErrorRecord): 19823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.value = overflowErrorRecord 20823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 21823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __str__(self): 22823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return repr(self.value) 23823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 24d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 25d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass BaseTTXConverter(DefaultTable): 26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 273a6aa2359e179779c281e6d319cb9a1a851e6df1jvr """Generic base class for TTX table converters. It functions as an 283a6aa2359e179779c281e6d319cb9a1a851e6df1jvr adapter between the TTX (ttLib actually) table model and the model 293a6aa2359e179779c281e6d319cb9a1a851e6df1jvr we use for OpenType tables, which is necessarily subtly different. 303a6aa2359e179779c281e6d319cb9a1a851e6df1jvr """ 3164b5c80e80444a124da335e8d4d208bffcf2737bjvr 32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def decompile(self, data, font): 332b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 340585b64f70cca15aab3483f529c8faf1b7b825fdBehdad Esfahbod cachingStats = None if True else {} 3579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod class GlobalState: 3679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, tableType, cachingStats): 3779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.tableType = tableType 3879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.cachingStats = cachingStats 3979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod globalState = GlobalState(tableType=self.tableTag, 4079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod cachingStats=cachingStats) 4179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader = OTTableReader(data, globalState) 42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = getattr(otTables, self.tableTag) 43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = tableClass() 44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.decompile(reader, font) 450585b64f70cca15aab3483f529c8faf1b7b825fdBehdad Esfahbod if cachingStats: 46ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod stats = sorted([(v, k) for k, v in cachingStats.items()]) 47cfadfd0096d634f4505bca27d6926555d43305d4jvr stats.reverse() 483ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print("cachingsstats for ", self.tableTag) 49cfadfd0096d634f4505bca27d6926555d43305d4jvr for v, k in stats: 50cfadfd0096d634f4505bca27d6926555d43305d4jvr if v < 2: 51cfadfd0096d634f4505bca27d6926555d43305d4jvr break 523ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print(v, k) 533ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print("---", len(stats)) 54d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def compile(self, font): 56823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ Create a top-level OTFWriter for the GPOS/GSUB table. 57823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Call the compile method for the the table 581c734526926cad812f4f0e688eb2f68a33eda2f8jvr for each 'converter' record in the table converter list 591c734526926cad812f4f0e688eb2f68a33eda2f8jvr call converter's write method for each item in the value. 60823f8cd15f16bb9dc3991c2672f16dd90579711bjvr - For simple items, the write method adds a string to the 61823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer's self.items list. 621c734526926cad812f4f0e688eb2f68a33eda2f8jvr - For Struct/Table/Subtable items, it add first adds new writer to the 63823f8cd15f16bb9dc3991c2672f16dd90579711bjvr to the writer's self.items, then calls the item's compile method. 64823f8cd15f16bb9dc3991c2672f16dd90579711bjvr This creates a tree of writers, rooted at the GUSB/GPOS writer, with 65823f8cd15f16bb9dc3991c2672f16dd90579711bjvr each writer representing a table, and the writer.items list containing 66823f8cd15f16bb9dc3991c2672f16dd90579711bjvr the child data strings and writers. 671c734526926cad812f4f0e688eb2f68a33eda2f8jvr call the getAllData method 68823f8cd15f16bb9dc3991c2672f16dd90579711bjvr call _doneWriting, which removes duplicates 691c734526926cad812f4f0e688eb2f68a33eda2f8jvr call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables 701c734526926cad812f4f0e688eb2f68a33eda2f8jvr Traverse the flat list of tables, calling getDataLength on each to update their position 711c734526926cad812f4f0e688eb2f68a33eda2f8jvr Traverse the flat list of tables again, calling getData each get the data in the table, now that 721c734526926cad812f4f0e688eb2f68a33eda2f8jvr pos's and offset are known. 73823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 74823f8cd15f16bb9dc3991c2672f16dd90579711bjvr If a lookup subtable overflows an offset, we have to start all over. 75823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 7679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod class GlobalState: 7779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, tableType): 7879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.tableType = tableType 7979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod globalState = GlobalState(tableType=self.tableTag) 8079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer = OTTableWriter(globalState) 81823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.parent = None 82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.compile(writer, font) 83cfadfd0096d634f4505bca27d6926555d43305d4jvr return writer.getAllData() 84823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 85d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, writer, font): 86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.toXML2(writer, font) 87d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 883a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 892b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 90d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(self, "table"): 91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = getattr(otTables, self.tableTag) 92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = tableClass() 933a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod self.table.fromXML(name, attrs, content, font) 94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 95d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 963879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableReader(object): 977981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 9864b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to retrieve data from an OpenType table.""" 993879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbod 10079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState') 1017981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 10279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, data, globalState={}, localState=None, offset=0): 103d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.data = data 104d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.offset = offset 105d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = offset 10679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.globalState = globalState 10779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = localState 1087981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1090fac7fe32072e813d36882ba68af1227b6caff9bBehdad Esfahbod def getSubReader(self, offset): 110d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = self.offset + offset 11179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod cachingStats = self.globalState.cachingStats 11279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod if cachingStats is not None: 11379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod cachingStats[offset] = cachingStats.get(offset, 0) + 1 11479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.__class__(self.data, self.globalState, self.localState, offset) 1157981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readUShort(self): 117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 118d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 119e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">H", self.data[pos:newpos]) 120d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1227981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readShort(self): 124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 126e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">h", self.data[pos:newpos]) 127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readLong(self): 131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 133e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">l", self.data[pos:newpos]) 134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1367981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1379e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def readUInt24(self): 1389e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pos = self.pos 1399e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod newpos = pos + 3 140319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod value = (byteord(self.data[pos]) << 16) | (byteord(self.data[pos+1]) << 8) | byteord(self.data[pos+2]) 1419e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod value, = struct.unpack(">H", self.data[pos:newpos]) 1429e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod self.pos = newpos 1439e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod return value 1449e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 145823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def readULong(self): 146823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newpos = pos + 4 148823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value, = struct.unpack(">L", self.data[pos:newpos]) 149823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.pos = newpos 150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readTag(self): 153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 155960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod value = Tag(self.data[pos:newpos]) 156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(value) == 4 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1597981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 16079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 16179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 16279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 16379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 1647981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 16579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 16679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 1677981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1693879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object): 170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 17164b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to gather and assemble data for OpenType tables.""" 17264b5c80e80444a124da335e8d4d208bffcf2737bjvr 17379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, globalState, localState=None): 174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items = [] 175cfadfd0096d634f4505bca27d6926555d43305d4jvr self.pos = None 17679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.globalState = globalState 17779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = localState 1787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 17979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 18079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 18279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 1837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 18579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 1867981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1874105ca0b9515887a8a4cbb873c3de98009cbc633jvr # assembler interface 1884105ca0b9515887a8a4cbb873c3de98009cbc633jvr 1894105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getAllData(self): 1904105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble all data, including all subtables.""" 1914105ca0b9515887a8a4cbb873c3de98009cbc633jvr self._doneWriting() 192823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tables, extTables = self._gatherTables() 1934105ca0b9515887a8a4cbb873c3de98009cbc633jvr tables.reverse() 194823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables.reverse() 1954105ca0b9515887a8a4cbb873c3de98009cbc633jvr # Gather all data in two passes: the absolute positions of all 1964105ca0b9515887a8a4cbb873c3de98009cbc633jvr # subtable are needed before the actual data can be assembled. 1974105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = 0 1984105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 1994105ca0b9515887a8a4cbb873c3de98009cbc633jvr table.pos = pos 2004105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = pos + table.getDataLength() 201823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 202823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 203823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table.pos = pos 204823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = pos + table.getDataLength() 205823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 206823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2074105ca0b9515887a8a4cbb873c3de98009cbc633jvr data = [] 2084105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr tableData = table.getData() 2104105ca0b9515887a8a4cbb873c3de98009cbc633jvr data.append(tableData) 211823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 212823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 213823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tableData = table.getData() 214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr data.append(tableData) 215823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 216821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(data) 2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getDataLength(self): 2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Return the length of this table in bytes, without subtables.""" 2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = 0 2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr for item in self.items: 2224105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData") or hasattr(item, "getCountData"): 223e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 4 # sizeof(ULong) 225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 226823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 2 # sizeof(UShort) 2274105ca0b9515887a8a4cbb873c3de98009cbc633jvr else: 2284105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = l + len(item) 2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr return l 2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getData(self): 2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble the data for this writer/table, without subtables.""" 2334105ca0b9515887a8a4cbb873c3de98009cbc633jvr items = list(self.items) # make a shallow copy 234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(items) 236823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 2374105ca0b9515887a8a4cbb873c3de98009cbc633jvr item = items[i] 238823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2394105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData"): 240e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 241823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packULong(item.pos - pos) 242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 243823f8cd15f16bb9dc3991c2672f16dd90579711bjvr try: 244823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packUShort(item.pos - pos) 245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr except AssertionError: 246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # provide data to fix overflow problem. 24758acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod # If the overflow is to a lookup, or from a lookup to a subtable, 248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # just report the current item. 249823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name in [ 'LookupList', 'Lookup']: 250823f8cd15f16bb9dc3991c2672f16dd90579711bjvr overflowErrorRecord = self.getOverflowErrorRecord(item) 251823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 252823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # overflow is within a subTable. Life is more complicated. 253823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # If we split the sub-table just before the current item, we may still suffer overflow. 254823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # This is because duplicate table merging is done only within an Extension subTable tree; 255823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # when we split the subtable in two, some items may no longer be duplicates. 256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Get worst case by adding up all the item lengths, depth first traversal. 257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # and then report the first item that overflows a short. 258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getDeepItemLength(table): 259823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(table, "getDataLength"): 260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = 0 261823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for item in table.items: 262823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = len(table) 265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return length 266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 267823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = self.getDataLength() 268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast") and item.name == "Coverage": 269823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage is first in the item list, but last in the table list, 270823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # The original overflow is really in the item list. Skip the Coverage 271823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # table in the following test. 272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items = items[i+1:] 273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for j in range(len(items)): 275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = items[j] 276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if length > 65535: 278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr overflowErrorRecord = self.getOverflowErrorRecord(item) 280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 281823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 282cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise OTLOffsetOverflowError(overflowErrorRecord) 283823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 284821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(items) 285d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 286cfadfd0096d634f4505bca27d6926555d43305d4jvr def __hash__(self): 287cfadfd0096d634f4505bca27d6926555d43305d4jvr # only works after self._doneWriting() has been called 288cfadfd0096d634f4505bca27d6926555d43305d4jvr return hash(self.items) 289cfadfd0096d634f4505bca27d6926555d43305d4jvr 290b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 291b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 292b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 293b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.items == other.items 294cfadfd0096d634f4505bca27d6926555d43305d4jvr 295cfadfd0096d634f4505bca27d6926555d43305d4jvr def _doneWriting(self, internedTables=None): 296823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert CountData references to data string items 297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # collapse duplicate table references to a unique entry 298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For Extension Lookup types, we can 301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # eliminate duplicates only within the tree under the Extension Lookup, 302823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # as offsets may exceed 64K even between Extension LookupTable subtables. 303cfadfd0096d634f4505bca27d6926555d43305d4jvr if internedTables is None: 304cfadfd0096d634f4505bca27d6926555d43305d4jvr internedTables = {} 305cfadfd0096d634f4505bca27d6926555d43305d4jvr items = self.items 30697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(len(items))) 307823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 308823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 309823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 1 310823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 311823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 0 312823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = items[i] 314cfadfd0096d634f4505bca27d6926555d43305d4jvr if hasattr(item, "getCountData"): 315cfadfd0096d634f4505bca27d6926555d43305d4jvr items[i] = item.getCountData() 316cfadfd0096d634f4505bca27d6926555d43305d4jvr elif hasattr(item, "getData"): 317823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if newTree: 318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting() 319d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 320823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting(internedTables) 321bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item in internedTables: 322823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = item = internedTables[item] 323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr internedTables[item] = item 325cfadfd0096d634f4505bca27d6926555d43305d4jvr self.items = tuple(items) 326cfadfd0096d634f4505bca27d6926555d43305d4jvr 327823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def _gatherTables(self, tables=None, extTables=None, done=None): 328823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert table references in self.items tree to a flat 329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # list of tables in depth-first traversal order. 330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We do the traversal in reverse order at each level, in order to 332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # resolve duplicate references to be the last reference in the list of tables. 333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For extension lookups, duplicate references can be merged only within the 334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # writer tree under the extension lookup. 335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if tables is None: # init call for first time. 336cfadfd0096d634f4505bca27d6926555d43305d4jvr tables = [] 337823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables = [] 338cfadfd0096d634f4505bca27d6926555d43305d4jvr done = {} 339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr done[self] = 1 341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(self.items) 34397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(numItems)) 344823f8cd15f16bb9dc3991c2672f16dd90579711bjvr iRange.reverse() 345823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 1 348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 0 350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # add Coverage table if it is sorted last. 352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 0 353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast"): 354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Find coverage table 355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, "name") and (item.name == "Coverage"): 358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 1 359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 360bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item not in done: 361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr saveItem = None 367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 369cfadfd0096d634f4505bca27d6926555d43305d4jvr if not hasattr(item, "getData"): 370cfadfd0096d634f4505bca27d6926555d43305d4jvr continue 371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if sortCoverageLast and (i==1) and item.name == 'Coverage': 373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we've already 'gathered' it above 374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr continue 375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if appendExtensions: 377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables" 378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newDone = {} 379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(extTables, None, newDone) 380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 381bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif item not in done: 382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 383823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 388cfadfd0096d634f4505bca27d6926555d43305d4jvr tables.append(self) 389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return tables, extTables 390cfadfd0096d634f4505bca27d6926555d43305d4jvr 3914105ca0b9515887a8a4cbb873c3de98009cbc633jvr # interface for gathering data, as used by table.compile() 3928e48312f88adc41feb7c730154c6d058a010189djvr 3934105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getSubWriter(self): 39479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod subwriter = self.__class__(self.globalState, self.localState) 395823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subwriter.parent = {0:self} # because some subtables have idential values, we discard 396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the duplicates under the getAllData method. Hence some 397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # subtable writers can have more than one parent writer. 398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return subwriter 399d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 400d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeUShort(self, value): 401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert 0 <= value < 0x10000 402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">H", value)) 403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeShort(self, value): 405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">h", value)) 4069e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 4079e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def writeUInt24(self, value): 4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod assert 0 <= value < 0x1000000 409b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod self.items.append(''.join(bytechr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff))) 410d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 411d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeLong(self, value): 412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">l", value)) 413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 414823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeULong(self, value): 415823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(struct.pack(">L", value)) 416823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 417d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeTag(self, tag): 418960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod tag = Tag(tag).tobytes() 419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(tag) == 4 420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(tag) 421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeSubTable(self, subWriter): 423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(subWriter) 424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeCountReference(self, table, name): 4267981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod ref = CountReference(table, name) 4277981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod self.items.append(ref) 428ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod return ref 429d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 430d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeStruct(self, format, values): 43166214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod data = struct.pack(*(format,) + values) 432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(data) 433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 434823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeData(self, data): 435823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(data) 436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getOverflowErrorRecord(self, item): 438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = SubTableIndex = itemName = itemIndex = None 439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'LookupList': 440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = item.repeatIndex 441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'Lookup': 442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.repeatIndex 443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = item.repeatIndex 444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = item.name 446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, 'repeatIndex'): 447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemIndex = item.repeatIndex 448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'SubTable': 449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].repeatIndex 450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'ExtSubTable': 452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].parent[0].repeatIndex 453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.parent[0].repeatIndex 454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable. 455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = ".".join(self.name, item.name) 456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = self.parent[0] 457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr while p1 and p1.name not in ['ExtSubTable', 'SubTable']: 458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = ".".join(p1.name, item.name) 459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = p1.parent[0] 460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1: 461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1.name == 'ExtSubTable': 462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].parent[0].repeatIndex 463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.parent[0].repeatIndex 464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].repeatIndex 466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 46879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) ) 469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 470d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 471d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass CountReference: 472cfadfd0096d634f4505bca27d6926555d43305d4jvr """A reference to a Count value, not a count of references.""" 473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, table, name): 474d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = table 475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 4767981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod def setValue(self, value): 4777981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table = self.table 4787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod name = self.name 4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod if table[name] is None: 4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table[name] = value 4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod else: 4821f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod assert table[name] == value, (name, table[name], value) 483cfadfd0096d634f4505bca27d6926555d43305d4jvr def getCountData(self): 484d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return packUShort(self.table[self.name]) 485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 48764b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value): 488cfadfd0096d634f4505bca27d6926555d43305d4jvr assert 0 <= value < 0x10000, value 48964b5c80e80444a124da335e8d4d208bffcf2737bjvr return struct.pack(">H", value) 490d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 491d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 492823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value): 493ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr assert 0 <= value < 0x100000000, value 494823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return struct.pack(">L", value) 495823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 496823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 4975988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object): 498823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __init__(self): 499823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.compileStatus = 0 # 0 means table was created 500823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # 1 means the table.read() function was called by a table which is subject 501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to delayed compilation 502823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # 2 means that it was subject to delayed compilation, and 503823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # has been decompiled 504823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 505823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse = 0 506823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 507823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __getattr__(self, attr): 508823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we get here only when the table does not have the attribute. 509823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # This method ovveride exists so that we can try to de-compile 510823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # a table which is subject to delayed decompilation, and then try 511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to get the value again after decompilation. 512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse +=1 513823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.recurse > 2: 514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # shouldn't ever get here - we should only get to two levels of recursion. 515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # this guards against self.decompile NOT setting compileStatus to other than 1. 516cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise AttributeError(attr) 517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.compileStatus == 1: 518f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr val = getattr(self, attr) 520823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse -=1 521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return val 522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 523cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise AttributeError(attr) 524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 525823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 52664b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for all OpenType (sub)tables.""" 52764b5c80e80444a124da335e8d4d208bffcf2737bjvr 528d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 529d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters 530d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 531d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 532d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[name] 533d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 534078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def decompile(self, reader, font): 535823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.compileStatus = 2 # table has been decompiled. 536f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.readFormat(reader) 537d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = {} 538d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__rawTable = table # for debugging 53941caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod converters = self.getConverters() 54041caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod for conv in converters: 541d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.name == "SubTable": 54279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table["LookupType"]) 544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if conv.name == "ExtSubTable": 54579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table["ExtensionLookupType"]) 5479e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.name == "FeatureParams": 5489e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv = conv.getConverter(reader["FeatureTag"]) 549d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 550d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l = [] 551ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 552ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod countValue = table[conv.repeat] 553ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 554ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 55579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod countValue = reader[conv.repeat] 5566b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod for i in range(countValue + conv.aux): 557078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod l.append(conv.read(reader, font, table)) 558d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = l 559d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 5605b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 5615b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 562078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod table[conv.name] = conv.read(reader, font, table) 5639e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 56479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader[conv.name] = table[conv.name] 56541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 566d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.postRead(table, font) 56741caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 568d4d151390d1288f8d2df30f6dfa26a309c7334dajvr del self.__rawTable # succeeded, get rid of debugging info 569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 570f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod def ensureDecompiled(self): 571f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod if self.compileStatus != 1: 572f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod return 573f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod self.decompile(self.reader, self.font) 574f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod del self.reader, self.font 575f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 576078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def compile(self, writer, font): 5773ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod self.ensureDecompiled() 578d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.preWrite(font) 579823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, 'sortCoverageLast'): 581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.sortCoverageLast = 1 582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 583f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.writeFormat(writer) 584d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 585d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = table.get(conv.name) 586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 587d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 58864b5c80e80444a124da335e8d4d208bffcf2737bjvr value = [] 5896b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod countValue = len(value) - conv.aux 590ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 591ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = table[conv.repeat] 592ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.repeat] = None 593ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref.setValue(countValue) 594ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 595ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 59679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.repeat].setValue(countValue) 597823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(len(value)): 598078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value[i], i) 599d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif conv.isCount: 600d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Special-case Count values. 601d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Assumption: a Count field will *always* precede 602ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # the actual array(s). 603d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We need a default value, as it may be set later by a nested 60441caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod # table. We will later store it here. 605d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We add a reference: by the time the data is assembled 606d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the Count value will be filled in. 607ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = writer.writeCountReference(table, conv.name) 6089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 609ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.name] = None 61079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.name] = ref 611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 612ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.name] = ref 613d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 6145b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 6155b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 616078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value) 6179e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 6189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod writer[conv.name] = value 619d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 620f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 621f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 622f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 623f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 624f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 625f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 626d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def postRead(self, table, font): 627d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__dict__.update(table) 628d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def preWrite(self, font): 630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__dict__.copy() 631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, attrs=None): 633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableName = self.__class__.__name__ 634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr attrs = [] 636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, "Format"): 63764b5c80e80444a124da335e8d4d208bffcf2737bjvr attrs = attrs + [("Format", self.Format)] 638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(tableName, attrs) 639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.toXML2(xmlWriter, font) 641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(tableName) 642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 644d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML2(self, xmlWriter, font): 645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). 646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # This is because in TTX our parent writes our main tag, and in otBase.py we 647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # do it ourselves. I think I'm getting schizophrenic... 648d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 64964b5c80e80444a124da335e8d4d208bffcf2737bjvr if conv.repeat: 6505b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(value)): 652d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = value[i] 65364b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, item, conv.name, 65464b5c80e80444a124da335e8d4d208bffcf2737bjvr [("index", i)]) 65564b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 6565b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, vars(self)): 6575b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 6585b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 65964b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, value, conv.name, []) 660d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6613a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = self.getConverterByName(name) 664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 665d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise # XXX on KeyError, raise nice error 666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = conv.xmlRead(attrs, content, font) 667d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 66852966bb14409b558dc46b52d34953fbde7dc1bcajvr seq = getattr(self, conv.name, None) 66952966bb14409b558dc46b52d34953fbde7dc1bcajvr if seq is None: 670d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq = [] 67164b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, seq) 672d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq.append(value) 673d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 67464b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, value) 675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 676b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 677b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 678b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 67996b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 680f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 681b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod other.ensureDecompiled() 682f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 683b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 685d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 686d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable): 687d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 688cfadfd0096d634f4505bca27d6926555d43305d4jvr """Minor specialization of BaseTable, for tables that have multiple 68964b5c80e80444a124da335e8d4d208bffcf2737bjvr formats, eg. CoverageFormat1 vs. CoverageFormat2.""" 69064b5c80e80444a124da335e8d4d208bffcf2737bjvr 691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 692d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters[self.Format] 693d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 694d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[self.Format][name] 696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 697f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.Format = reader.readUShort() 699180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod assert self.Format != 0, (self, reader.pos, len(reader.data)) 700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 701f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(self.Format) 703d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 70564b5c80e80444a124da335e8d4d208bffcf2737bjvr# 70664b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords 70764b5c80e80444a124da335e8d4d208bffcf2737bjvr# 70864b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that 70964b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support 71064b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter... 71164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 71264b5c80e80444a124da335e8d4d208bffcf2737bjvr 713d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [ 714d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# Mask Name isDevice signed 715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0001, "XPlacement", 0, 1), 716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0002, "YPlacement", 0, 1), 717d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0004, "XAdvance", 0, 1), 718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0008, "YAdvance", 0, 1), 719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0010, "XPlaDevice", 1, 0), 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0020, "YPlaDevice", 1, 0), 721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0040, "XAdvDevice", 1, 0), 722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0080, "YAdvDevice", 1, 0), 723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# reserved: 724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0100, "Reserved1", 0, 0), 725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0200, "Reserved2", 0, 0), 726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0400, "Reserved3", 0, 0), 727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0800, "Reserved4", 0, 0), 728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x1000, "Reserved5", 0, 0), 729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x2000, "Reserved6", 0, 0), 730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x4000, "Reserved7", 0, 0), 731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x8000, "Reserved8", 0, 0), 732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr] 733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 734d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict(): 735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d = {} 736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d[name] = mask, isDevice, signed 738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return d 739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 740d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict() 741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 743d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecordFactory: 744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 74564b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a format code, this object convert ValueRecords.""" 746d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod 747601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod def __init__(self, valueFormat): 748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = [] 749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 750d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat & mask: 751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format.append((name, isDevice, signed)) 752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.format = format 753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, reader, font): 755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = self.format 756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not format: 757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueRecord = ValueRecord() 759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in format: 760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if signed: 761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readShort() 762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readUShort() 764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 7662b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = reader.getSubReader(value) 768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.decompile(subReader, font) 770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = None 772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(valueRecord, name, value) 773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return valueRecord 774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, writer, font, valueRecord): 776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in self.format: 777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(valueRecord, name, 0) 778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.compile(subWriter, font) 783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(0) 785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif signed: 786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 791d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord: 792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # see ValueRecordFactory 794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getFormat(self): 796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = 0 797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name in self.__dict__.keys(): 798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = format | valueRecordFormatDict[name][0] 799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, valueName, attrs=None): 802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = [] 804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = list(attrs) 806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values 807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems.append((name, getattr(self, name))) 809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems = [] 810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records 811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr device = getattr(self, name) 813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if device is not None: 814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems.append((name, device)) 815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceItems: 816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(valueName, simpleItems) 817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, deviceRecord in deviceItems: 819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceRecord is not None: 820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceRecord.toXML(xmlWriter, font) 821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(valueName) 822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(valueName, simpleItems) 825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8273a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 8282b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for k, v in attrs.items(): 830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, k, int(v)) 831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 832b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, attrs, content = element 835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 836d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for elem2 in content: 837b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(elem2, tuple): 838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 8393a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod name2, attrs2, content2 = elem2 8403a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod value.fromXML(name2, attrs2, content2, font) 841d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, name, value) 842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 843b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 844b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 845b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 846b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 847