otBase.py revision dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division 230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .DefaultTable import DefaultTable 4d4d151390d1288f8d2df30f6dfa26a309c7334dajvrimport struct 5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass OverflowErrorRecord(object): 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 {} 35e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod class GlobalState(object): 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 """ 76e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod class GlobalState(object): 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 140c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod value, = struct.unpack(">l", b'\0'+self.data[pos:newpos]) 1419e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod self.pos = newpos 1429e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod return value 1439e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 144823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def readULong(self): 145823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 146823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newpos = pos + 4 147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value, = struct.unpack(">L", self.data[pos:newpos]) 148823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.pos = newpos 149823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readTag(self): 152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 154960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod value = Tag(self.data[pos:newpos]) 155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(value) == 4 156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1587981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 15979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 16079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 16179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 16279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 1637981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 16479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 16579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 1667981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1683879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object): 169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 17064b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to gather and assemble data for OpenType tables.""" 17164b5c80e80444a124da335e8d4d208bffcf2737bjvr 17279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, globalState, localState=None): 173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items = [] 174cfadfd0096d634f4505bca27d6926555d43305d4jvr self.pos = None 17579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.globalState = globalState 17679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = localState 1777981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 17879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 17979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 18079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 1827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 18379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 1857981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1864105ca0b9515887a8a4cbb873c3de98009cbc633jvr # assembler interface 1874105ca0b9515887a8a4cbb873c3de98009cbc633jvr 1884105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getAllData(self): 1894105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble all data, including all subtables.""" 1904105ca0b9515887a8a4cbb873c3de98009cbc633jvr self._doneWriting() 191823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tables, extTables = self._gatherTables() 1924105ca0b9515887a8a4cbb873c3de98009cbc633jvr tables.reverse() 193823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables.reverse() 1944105ca0b9515887a8a4cbb873c3de98009cbc633jvr # Gather all data in two passes: the absolute positions of all 1954105ca0b9515887a8a4cbb873c3de98009cbc633jvr # subtable are needed before the actual data can be assembled. 1964105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = 0 1974105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 1984105ca0b9515887a8a4cbb873c3de98009cbc633jvr table.pos = pos 1994105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = pos + table.getDataLength() 200823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 201823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 202823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table.pos = pos 203823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = pos + table.getDataLength() 204823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 205823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2064105ca0b9515887a8a4cbb873c3de98009cbc633jvr data = [] 2074105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 2084105ca0b9515887a8a4cbb873c3de98009cbc633jvr tableData = table.getData() 2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr data.append(tableData) 210823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 211823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 212823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tableData = table.getData() 213823f8cd15f16bb9dc3991c2672f16dd90579711bjvr data.append(tableData) 214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 215821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(data) 2164105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getDataLength(self): 2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Return the length of this table in bytes, without subtables.""" 2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = 0 2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr for item in self.items: 2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData") or hasattr(item, "getCountData"): 222e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 223823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 4 # sizeof(ULong) 224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 2 # sizeof(UShort) 2264105ca0b9515887a8a4cbb873c3de98009cbc633jvr else: 2274105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = l + len(item) 2284105ca0b9515887a8a4cbb873c3de98009cbc633jvr return l 2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getData(self): 2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble the data for this writer/table, without subtables.""" 2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr items = list(self.items) # make a shallow copy 233823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(items) 235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 2364105ca0b9515887a8a4cbb873c3de98009cbc633jvr item = items[i] 237823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2384105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData"): 239e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 240823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packULong(item.pos - pos) 241823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr try: 243823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packUShort(item.pos - pos) 2449e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod except struct.error: 245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # provide data to fix overflow problem. 24658acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod # If the overflow is to a lookup, or from a lookup to a subtable, 247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # just report the current item. 248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name in [ 'LookupList', 'Lookup']: 249823f8cd15f16bb9dc3991c2672f16dd90579711bjvr overflowErrorRecord = self.getOverflowErrorRecord(item) 250823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 251823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # overflow is within a subTable. Life is more complicated. 252823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # If we split the sub-table just before the current item, we may still suffer overflow. 253823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # This is because duplicate table merging is done only within an Extension subTable tree; 254823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # when we split the subtable in two, some items may no longer be duplicates. 255823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Get worst case by adding up all the item lengths, depth first traversal. 256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # and then report the first item that overflows a short. 257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getDeepItemLength(table): 258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(table, "getDataLength"): 259823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = 0 260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for item in table.items: 261823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 262823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = len(table) 264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return length 265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = self.getDataLength() 267823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast") and item.name == "Coverage": 268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage is first in the item list, but last in the table list, 269823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # The original overflow is really in the item list. Skip the Coverage 270823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # table in the following test. 271823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items = items[i+1:] 272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for j in range(len(items)): 274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = items[j] 275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if length > 65535: 277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr overflowErrorRecord = self.getOverflowErrorRecord(item) 279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 281cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise OTLOffsetOverflowError(overflowErrorRecord) 282823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 283821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(items) 284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 285cfadfd0096d634f4505bca27d6926555d43305d4jvr def __hash__(self): 286cfadfd0096d634f4505bca27d6926555d43305d4jvr # only works after self._doneWriting() has been called 287cfadfd0096d634f4505bca27d6926555d43305d4jvr return hash(self.items) 288cfadfd0096d634f4505bca27d6926555d43305d4jvr 2898ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 2908ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 291b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 292b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 293273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 294b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.items == other.items 295cfadfd0096d634f4505bca27d6926555d43305d4jvr 296cfadfd0096d634f4505bca27d6926555d43305d4jvr def _doneWriting(self, internedTables=None): 297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert CountData references to data string items 298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # collapse duplicate table references to a unique entry 299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For Extension Lookup types, we can 302823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # eliminate duplicates only within the tree under the Extension Lookup, 303823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # as offsets may exceed 64K even between Extension LookupTable subtables. 304cfadfd0096d634f4505bca27d6926555d43305d4jvr if internedTables is None: 305cfadfd0096d634f4505bca27d6926555d43305d4jvr internedTables = {} 306cfadfd0096d634f4505bca27d6926555d43305d4jvr items = self.items 30797dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(len(items))) 308823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 309823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 310823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 1 311823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 312823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 0 313823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 314d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = items[i] 315cfadfd0096d634f4505bca27d6926555d43305d4jvr if hasattr(item, "getCountData"): 316cfadfd0096d634f4505bca27d6926555d43305d4jvr items[i] = item.getCountData() 317cfadfd0096d634f4505bca27d6926555d43305d4jvr elif hasattr(item, "getData"): 318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if newTree: 319823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting() 320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 321823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting(internedTables) 322dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod internedItem = internedTables.get(item) 323dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod if internedItem: 324dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod items[i] = item = internedItem 325823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 326823f8cd15f16bb9dc3991c2672f16dd90579711bjvr internedTables[item] = item 327cfadfd0096d634f4505bca27d6926555d43305d4jvr self.items = tuple(items) 328cfadfd0096d634f4505bca27d6926555d43305d4jvr 329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def _gatherTables(self, tables=None, extTables=None, done=None): 330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert table references in self.items tree to a flat 331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # list of tables in depth-first traversal order. 332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We do the traversal in reverse order at each level, in order to 334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # resolve duplicate references to be the last reference in the list of tables. 335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For extension lookups, duplicate references can be merged only within the 336823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # writer tree under the extension lookup. 337823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if tables is None: # init call for first time. 338cfadfd0096d634f4505bca27d6926555d43305d4jvr tables = [] 339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables = [] 340cfadfd0096d634f4505bca27d6926555d43305d4jvr done = {} 341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr done[self] = 1 343823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 344823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(self.items) 34597dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(numItems)) 346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr iRange.reverse() 347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 1 350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 0 352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # add Coverage table if it is sorted last. 354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 0 355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast"): 356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Find coverage table 357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, "name") and (item.name == "Coverage"): 360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 1 361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 362bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item not in done: 363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr saveItem = None 369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 371cfadfd0096d634f4505bca27d6926555d43305d4jvr if not hasattr(item, "getData"): 372cfadfd0096d634f4505bca27d6926555d43305d4jvr continue 373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if sortCoverageLast and (i==1) and item.name == 'Coverage': 375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we've already 'gathered' it above 376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr continue 377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if appendExtensions: 3799e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables" 380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newDone = {} 381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(extTables, None, newDone) 382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 383bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif item not in done: 384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 390cfadfd0096d634f4505bca27d6926555d43305d4jvr tables.append(self) 391823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return tables, extTables 392cfadfd0096d634f4505bca27d6926555d43305d4jvr 3934105ca0b9515887a8a4cbb873c3de98009cbc633jvr # interface for gathering data, as used by table.compile() 3948e48312f88adc41feb7c730154c6d058a010189djvr 3954105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getSubWriter(self): 39679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod subwriter = self.__class__(self.globalState, self.localState) 397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subwriter.parent = {0:self} # because some subtables have idential values, we discard 398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the duplicates under the getAllData method. Hence some 399823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # subtable writers can have more than one parent writer. 400823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return subwriter 401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeUShort(self, value): 403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert 0 <= value < 0x10000 404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">H", value)) 405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeShort(self, value): 407d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">h", value)) 4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 4099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def writeUInt24(self, value): 4109e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod assert 0 <= value < 0x1000000 411c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod b = struct.pack(">L", value) 412c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod self.items.append(b[1:]) 413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeLong(self, value): 415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">l", value)) 416d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 417823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeULong(self, value): 418823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(struct.pack(">L", value)) 419823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 420d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeTag(self, tag): 421960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod tag = Tag(tag).tobytes() 422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(tag) == 4 423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(tag) 424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeSubTable(self, subWriter): 426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(subWriter) 427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeCountReference(self, table, name): 4297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod ref = CountReference(table, name) 4307981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod self.items.append(ref) 431ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod return ref 432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 433d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeStruct(self, format, values): 43466214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod data = struct.pack(*(format,) + values) 435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(data) 436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeData(self, data): 438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(data) 439d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getOverflowErrorRecord(self, item): 441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = SubTableIndex = itemName = itemIndex = None 442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'LookupList': 443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = item.repeatIndex 444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'Lookup': 445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.repeatIndex 446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = item.repeatIndex 447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = item.name 449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, 'repeatIndex'): 450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemIndex = item.repeatIndex 451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'SubTable': 452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].repeatIndex 453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'ExtSubTable': 455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].parent[0].repeatIndex 456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.parent[0].repeatIndex 457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable. 4589e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod itemName = ".".join([self.name, item.name]) 459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = self.parent[0] 460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr while p1 and p1.name not in ['ExtSubTable', 'SubTable']: 4619e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod itemName = ".".join([p1.name, item.name]) 462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = p1.parent[0] 463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1: 464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1.name == 'ExtSubTable': 4659e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod LookupListIndex = p1.parent[0].parent[0].repeatIndex 4669e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod SubTableIndex = p1.parent[0].repeatIndex 467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 4689e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod LookupListIndex = p1.parent[0].repeatIndex 4699e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod SubTableIndex = p1.repeatIndex 470823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 47179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) ) 472823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 473d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 474e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object): 475cfadfd0096d634f4505bca27d6926555d43305d4jvr """A reference to a Count value, not a count of references.""" 476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, table, name): 477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = table 478d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod def setValue(self, value): 4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table = self.table 4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod name = self.name 4827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod if table[name] is None: 4837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table[name] = value 4847981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod else: 4851f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod assert table[name] == value, (name, table[name], value) 486cfadfd0096d634f4505bca27d6926555d43305d4jvr def getCountData(self): 487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return packUShort(self.table[self.name]) 488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 489d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 49064b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value): 49164b5c80e80444a124da335e8d4d208bffcf2737bjvr return struct.pack(">H", value) 492d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 493d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 494823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value): 495ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr assert 0 <= value < 0x100000000, value 496823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return struct.pack(">L", value) 497823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 498823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 4995988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object): 500b4070bd6293998ac4bc6a68c1f774e9e9908c65bBehdad Esfahbod 501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __getattr__(self, attr): 502f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod reader = self.__dict__.get("reader") 503dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod if reader: 504dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.reader 505dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod font = self.font 506dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.font 507dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod self.decompile(reader, font) 508dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod return getattr(self, attr) 509dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod 510dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod raise AttributeError(attr) 511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 51364b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for all OpenType (sub)tables.""" 51464b5c80e80444a124da335e8d4d208bffcf2737bjvr 515d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 516d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters 517d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 518d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 519d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[name] 520d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 521078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def decompile(self, reader, font): 522f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.readFormat(reader) 523d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = {} 524d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__rawTable = table # for debugging 52541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod converters = self.getConverters() 52641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod for conv in converters: 527d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.name == "SubTable": 52879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 529d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table["LookupType"]) 530823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if conv.name == "ExtSubTable": 53179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 532823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table["ExtensionLookupType"]) 5339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.name == "FeatureParams": 5349e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv = conv.getConverter(reader["FeatureTag"]) 535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 536d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l = [] 537ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 538ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod countValue = table[conv.repeat] 539ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 540ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 54179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod countValue = reader[conv.repeat] 5426b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod for i in range(countValue + conv.aux): 543078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod l.append(conv.read(reader, font, table)) 544d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = l 545d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 5465b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 5475b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 548078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod table[conv.name] = conv.read(reader, font, table) 5499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 55079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader[conv.name] = table[conv.name] 55141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 552d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.postRead(table, font) 55341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 554d4d151390d1288f8d2df30f6dfa26a309c7334dajvr del self.__rawTable # succeeded, get rid of debugging info 555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 556f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod def ensureDecompiled(self): 557f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod reader = self.__dict__.get("reader") 558dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod if reader: 559dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.reader 560dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod font = self.font 561dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.font 562dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod self.decompile(reader, font) 563f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 564078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def compile(self, writer, font): 5653ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod self.ensureDecompiled() 566d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.preWrite(font) 567823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 568823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, 'sortCoverageLast'): 569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.sortCoverageLast = 1 570823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 5715fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod if hasattr(self.__class__, 'LookupType'): 5725fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod writer['LookupType'].setValue(self.__class__.LookupType) 5735fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod 574f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.writeFormat(writer) 575d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 576d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = table.get(conv.name) 577d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 578d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 57964b5c80e80444a124da335e8d4d208bffcf2737bjvr value = [] 5806b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod countValue = len(value) - conv.aux 581ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 5826bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod CountReference(table, conv.repeat).setValue(countValue) 583ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 584ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 58579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.repeat].setValue(countValue) 586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(len(value)): 587078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value[i], i) 588d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif conv.isCount: 589d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Special-case Count values. 590d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Assumption: a Count field will *always* precede 591ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # the actual array(s). 592d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We need a default value, as it may be set later by a nested 59341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod # table. We will later store it here. 594d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We add a reference: by the time the data is assembled 595d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the Count value will be filled in. 596ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = writer.writeCountReference(table, conv.name) 5976bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod table[conv.name] = None 5989e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 59979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.name] = ref 6005fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod elif conv.isLookupType: 6015fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod ref = writer.writeCountReference(table, conv.name) 6025fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod table[conv.name] = None 6035fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod writer['LookupType'] = ref 604d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 6055b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 6065b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 607078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value) 6089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 6099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod writer[conv.name] = value 610d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 611f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 612f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 613f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 614f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 615f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 616f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 617d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def postRead(self, table, font): 618d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__dict__.update(table) 619d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 620d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def preWrite(self, font): 621d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__dict__.copy() 622d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 623d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod def toXML(self, xmlWriter, font, attrs=None, name=None): 624d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod tableName = name if name else self.__class__.__name__ 625d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 626d4d151390d1288f8d2df30f6dfa26a309c7334dajvr attrs = [] 627d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, "Format"): 62864b5c80e80444a124da335e8d4d208bffcf2737bjvr attrs = attrs + [("Format", self.Format)] 629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(tableName, attrs) 630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.toXML2(xmlWriter, font) 632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(tableName) 633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML2(self, xmlWriter, font): 636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). 637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # This is because in TTX our parent writes our main tag, and in otBase.py we 638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # do it ourselves. I think I'm getting schizophrenic... 639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 64064b5c80e80444a124da335e8d4d208bffcf2737bjvr if conv.repeat: 6415b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(value)): 643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = value[i] 64464b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, item, conv.name, 64564b5c80e80444a124da335e8d4d208bffcf2737bjvr [("index", i)]) 64664b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 6475b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, vars(self)): 6485b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 6495b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 65064b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, value, conv.name, []) 651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6523a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = self.getConverterByName(name) 655d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 656d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise # XXX on KeyError, raise nice error 657d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = conv.xmlRead(attrs, content, font) 658d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 65952966bb14409b558dc46b52d34953fbde7dc1bcajvr seq = getattr(self, conv.name, None) 66052966bb14409b558dc46b52d34953fbde7dc1bcajvr if seq is None: 661d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq = [] 66264b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, seq) 663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq.append(value) 664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 66564b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, value) 666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6678ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 6688ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 669b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 670b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 671273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 67296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 673f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 674b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod other.ensureDecompiled() 675f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 676b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 678d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 679d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable): 680d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 681cfadfd0096d634f4505bca27d6926555d43305d4jvr """Minor specialization of BaseTable, for tables that have multiple 68264b5c80e80444a124da335e8d4d208bffcf2737bjvr formats, eg. CoverageFormat1 vs. CoverageFormat2.""" 68364b5c80e80444a124da335e8d4d208bffcf2737bjvr 684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 685d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters[self.Format] 686d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 687d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 688d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[self.Format][name] 689d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 690f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.Format = reader.readUShort() 692180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod assert self.Format != 0, (self, reader.pos, len(reader.data)) 693d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 694f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(self.Format) 696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 697d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod def toXML(self, xmlWriter, font, attrs=None, name=None): 698d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__) 699d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod 700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 70164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 70264b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords 70364b5c80e80444a124da335e8d4d208bffcf2737bjvr# 70464b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that 70564b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support 70664b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter... 70764b5c80e80444a124da335e8d4d208bffcf2737bjvr# 70864b5c80e80444a124da335e8d4d208bffcf2737bjvr 709d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [ 710d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# Mask Name isDevice signed 711d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0001, "XPlacement", 0, 1), 712d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0002, "YPlacement", 0, 1), 713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0004, "XAdvance", 0, 1), 714d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0008, "YAdvance", 0, 1), 715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0010, "XPlaDevice", 1, 0), 716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0020, "YPlaDevice", 1, 0), 717d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0040, "XAdvDevice", 1, 0), 718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0080, "YAdvDevice", 1, 0), 719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# reserved: 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0100, "Reserved1", 0, 0), 721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0200, "Reserved2", 0, 0), 722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0400, "Reserved3", 0, 0), 723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0800, "Reserved4", 0, 0), 724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x1000, "Reserved5", 0, 0), 725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x2000, "Reserved6", 0, 0), 726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x4000, "Reserved7", 0, 0), 727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x8000, "Reserved8", 0, 0), 728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr] 729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 730d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict(): 731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d = {} 732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d[name] = mask, isDevice, signed 734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return d 735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 736d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict() 737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 739e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object): 740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 74164b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a format code, this object convert ValueRecords.""" 742d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod 743601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod def __init__(self, valueFormat): 744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = [] 745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat & mask: 747d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format.append((name, isDevice, signed)) 748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.format = format 749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 750d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, reader, font): 751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = self.format 752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not format: 753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueRecord = ValueRecord() 755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in format: 756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if signed: 757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readShort() 758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readUShort() 760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 7622b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = reader.getSubReader(value) 764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.decompile(subReader, font) 766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = None 768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(valueRecord, name, value) 769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return valueRecord 770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, writer, font, valueRecord): 772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in self.format: 773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(valueRecord, name, 0) 774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.compile(subWriter, font) 779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(0) 781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif signed: 782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 787e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object): 788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # see ValueRecordFactory 790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getFormat(self): 792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = 0 793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name in self.__dict__.keys(): 794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = format | valueRecordFormatDict[name][0] 795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, valueName, attrs=None): 798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = [] 800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = list(attrs) 802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values 803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems.append((name, getattr(self, name))) 805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems = [] 806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records 807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr device = getattr(self, name) 809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if device is not None: 810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems.append((name, device)) 811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceItems: 812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(valueName, simpleItems) 813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, deviceRecord in deviceItems: 815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceRecord is not None: 816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceRecord.toXML(xmlWriter, font) 817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(valueName) 818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(valueName, simpleItems) 821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8233a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 8242b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for k, v in attrs.items(): 826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, k, int(v)) 827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 828b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, attrs, content = element 831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 832d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for elem2 in content: 833b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(elem2, tuple): 834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 8353a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod name2, attrs2, content2 = elem2 8363a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod value.fromXML(name2, attrs2, content2, font) 837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, name, value) 838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8398ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 8408ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 841b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 842b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 843273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 844b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 845