otBase.py revision 8ea6439d3b66c5acc246261d761d4375bcb7cfab
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) 244823f8cd15f16bb9dc3991c2672f16dd90579711bjvr except AssertionError: 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): 293b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 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) 322bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item in internedTables: 323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = item = internedTables[item] 324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 325823f8cd15f16bb9dc3991c2672f16dd90579711bjvr internedTables[item] = item 326cfadfd0096d634f4505bca27d6926555d43305d4jvr self.items = tuple(items) 327cfadfd0096d634f4505bca27d6926555d43305d4jvr 328823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def _gatherTables(self, tables=None, extTables=None, done=None): 329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert table references in self.items tree to a flat 330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # list of tables in depth-first traversal order. 331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We do the traversal in reverse order at each level, in order to 333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # resolve duplicate references to be the last reference in the list of tables. 334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For extension lookups, duplicate references can be merged only within the 335823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # writer tree under the extension lookup. 336823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if tables is None: # init call for first time. 337cfadfd0096d634f4505bca27d6926555d43305d4jvr tables = [] 338823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables = [] 339cfadfd0096d634f4505bca27d6926555d43305d4jvr done = {} 340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr done[self] = 1 342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 343823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(self.items) 34497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(numItems)) 345823f8cd15f16bb9dc3991c2672f16dd90579711bjvr iRange.reverse() 346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 348823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 1 349823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 0 351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # add Coverage table if it is sorted last. 353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 0 354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast"): 355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Find coverage table 356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, "name") and (item.name == "Coverage"): 359823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 1 360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 361bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item not in done: 362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 366823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr saveItem = None 368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 370cfadfd0096d634f4505bca27d6926555d43305d4jvr if not hasattr(item, "getData"): 371cfadfd0096d634f4505bca27d6926555d43305d4jvr continue 372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if sortCoverageLast and (i==1) and item.name == 'Coverage': 374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we've already 'gathered' it above 375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr continue 376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if appendExtensions: 3789e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables" 379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newDone = {} 380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(extTables, None, newDone) 381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 382bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif item not in done: 383823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr index = max(item.parent.keys()) 386823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item.parent[index + 1] = self 387823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 389cfadfd0096d634f4505bca27d6926555d43305d4jvr tables.append(self) 390823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return tables, extTables 391cfadfd0096d634f4505bca27d6926555d43305d4jvr 3924105ca0b9515887a8a4cbb873c3de98009cbc633jvr # interface for gathering data, as used by table.compile() 3938e48312f88adc41feb7c730154c6d058a010189djvr 3944105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getSubWriter(self): 39579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod subwriter = self.__class__(self.globalState, self.localState) 396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subwriter.parent = {0:self} # because some subtables have idential values, we discard 397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the duplicates under the getAllData method. Hence some 398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # subtable writers can have more than one parent writer. 399823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return subwriter 400d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 401d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeUShort(self, value): 402d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert 0 <= value < 0x10000 403d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">H", value)) 404d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 405d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeShort(self, value): 406d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">h", value)) 4079e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 4089e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def writeUInt24(self, value): 4099e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod assert 0 <= value < 0x1000000 410c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod b = struct.pack(">L", value) 411c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod self.items.append(b[1:]) 412d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 413d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeLong(self, value): 414d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">l", value)) 415d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 416823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeULong(self, value): 417823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(struct.pack(">L", value)) 418823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 419d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeTag(self, tag): 420960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod tag = Tag(tag).tobytes() 421d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(tag) == 4 422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(tag) 423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeSubTable(self, subWriter): 425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(subWriter) 426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeCountReference(self, table, name): 4287981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod ref = CountReference(table, name) 4297981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod self.items.append(ref) 430ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod return ref 431d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 432d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeStruct(self, format, values): 43366214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod data = struct.pack(*(format,) + values) 434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(data) 435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 436823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeData(self, data): 437823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(data) 438d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getOverflowErrorRecord(self, item): 440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = SubTableIndex = itemName = itemIndex = None 441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'LookupList': 442823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = item.repeatIndex 443823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'Lookup': 444823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.repeatIndex 445823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = item.repeatIndex 446823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 447823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = item.name 448823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, 'repeatIndex'): 449823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemIndex = item.repeatIndex 450823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'SubTable': 451823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].repeatIndex 452823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 453823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'ExtSubTable': 454823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].parent[0].repeatIndex 455823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.parent[0].repeatIndex 456823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable. 457823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = ".".join(self.name, item.name) 458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = self.parent[0] 459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr while p1 and p1.name not in ['ExtSubTable', 'SubTable']: 460823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = ".".join(p1.name, item.name) 461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr p1 = p1.parent[0] 462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1: 463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1.name == 'ExtSubTable': 464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].parent[0].repeatIndex 465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.parent[0].repeatIndex 466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.parent[0].repeatIndex 468823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 47079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) ) 471823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 472d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 473e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object): 474cfadfd0096d634f4505bca27d6926555d43305d4jvr """A reference to a Count value, not a count of references.""" 475d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, table, name): 476d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = table 477d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 4787981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod def setValue(self, value): 4797981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table = self.table 4807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod name = self.name 4817981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod if table[name] is None: 4827981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table[name] = value 4837981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod else: 4841f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod assert table[name] == value, (name, table[name], value) 485cfadfd0096d634f4505bca27d6926555d43305d4jvr def getCountData(self): 486d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return packUShort(self.table[self.name]) 487d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 488d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 48964b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value): 490cfadfd0096d634f4505bca27d6926555d43305d4jvr assert 0 <= value < 0x10000, 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): 500823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __init__(self): 501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.compileStatus = 0 # 0 means table was created 502823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # 1 means the table.read() function was called by a table which is subject 503823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to delayed compilation 504823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # 2 means that it was subject to delayed compilation, and 505823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # has been decompiled 506823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 507823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse = 0 508823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 509823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __getattr__(self, attr): 510823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we get here only when the table does not have the attribute. 511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # This method ovveride exists so that we can try to de-compile 512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # a table which is subject to delayed decompilation, and then try 513823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to get the value again after decompilation. 514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse +=1 515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.recurse > 2: 516823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # shouldn't ever get here - we should only get to two levels of recursion. 517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # this guards against self.decompile NOT setting compileStatus to other than 1. 518cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise AttributeError(attr) 519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.compileStatus == 1: 520f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr val = getattr(self, attr) 522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.recurse -=1 523823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return val 524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 525cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise AttributeError(attr) 526823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 527823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 52864b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for all OpenType (sub)tables.""" 52964b5c80e80444a124da335e8d4d208bffcf2737bjvr 530d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 531d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters 532d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 533d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 534d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[name] 535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 536078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def decompile(self, reader, font): 537823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.compileStatus = 2 # table has been decompiled. 538f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.readFormat(reader) 539d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = {} 540d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__rawTable = table # for debugging 54141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod converters = self.getConverters() 54241caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod for conv in converters: 543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.name == "SubTable": 54479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 545d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table["LookupType"]) 546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if conv.name == "ExtSubTable": 54779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table["ExtensionLookupType"]) 5499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.name == "FeatureParams": 5509e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv = conv.getConverter(reader["FeatureTag"]) 551d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 552d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l = [] 553ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 554ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod countValue = table[conv.repeat] 555ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 556ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 55779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod countValue = reader[conv.repeat] 5586b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod for i in range(countValue + conv.aux): 559078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod l.append(conv.read(reader, font, table)) 560d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = l 561d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 5625b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 5635b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 564078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod table[conv.name] = conv.read(reader, font, table) 5659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 56679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader[conv.name] = table[conv.name] 56741caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 568d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.postRead(table, font) 56941caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 570d4d151390d1288f8d2df30f6dfa26a309c7334dajvr del self.__rawTable # succeeded, get rid of debugging info 571823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 572f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod def ensureDecompiled(self): 573f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod if self.compileStatus != 1: 574f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod return 575f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod self.decompile(self.reader, self.font) 576f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod del self.reader, self.font 577f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 578078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def compile(self, writer, font): 5793ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod self.ensureDecompiled() 580d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.preWrite(font) 581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, 'sortCoverageLast'): 583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.sortCoverageLast = 1 584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 585f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.writeFormat(writer) 586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 587d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = table.get(conv.name) 588d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 589d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 59064b5c80e80444a124da335e8d4d208bffcf2737bjvr value = [] 5916b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod countValue = len(value) - conv.aux 592ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 593ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = table[conv.repeat] 594ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.repeat] = None 595ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref.setValue(countValue) 596ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 597ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 59879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.repeat].setValue(countValue) 599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(len(value)): 600078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value[i], i) 601d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif conv.isCount: 602d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Special-case Count values. 603d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Assumption: a Count field will *always* precede 604ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # the actual array(s). 605d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We need a default value, as it may be set later by a nested 60641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod # table. We will later store it here. 607d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We add a reference: by the time the data is assembled 608d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the Count value will be filled in. 609ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = writer.writeCountReference(table, conv.name) 6109e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.name] = None 61279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.name] = ref 613ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 614ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod table[conv.name] = ref 615d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 6165b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 6175b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 618078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value) 6199e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 6209e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod writer[conv.name] = value 621d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 622f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 623f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 624f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 625f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 626f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 627f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 628d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def postRead(self, table, font): 629d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__dict__.update(table) 630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 631d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def preWrite(self, font): 632d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__dict__.copy() 633d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 634d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, attrs=None): 635d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableName = self.__class__.__name__ 636d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr attrs = [] 638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, "Format"): 63964b5c80e80444a124da335e8d4d208bffcf2737bjvr attrs = attrs + [("Format", self.Format)] 640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(tableName, attrs) 641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.toXML2(xmlWriter, font) 643d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(tableName) 644d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML2(self, xmlWriter, font): 647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). 648d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # This is because in TTX our parent writes our main tag, and in otBase.py we 649d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # do it ourselves. I think I'm getting schizophrenic... 650d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 65164b5c80e80444a124da335e8d4d208bffcf2737bjvr if conv.repeat: 6525b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(value)): 654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = value[i] 65564b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, item, conv.name, 65664b5c80e80444a124da335e8d4d208bffcf2737bjvr [("index", i)]) 65764b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 6585b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, vars(self)): 6595b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 6605b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 66164b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, value, conv.name, []) 662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6633a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 664d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 665d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = self.getConverterByName(name) 666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 667d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise # XXX on KeyError, raise nice error 668d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = conv.xmlRead(attrs, content, font) 669d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 67052966bb14409b558dc46b52d34953fbde7dc1bcajvr seq = getattr(self, conv.name, None) 67152966bb14409b558dc46b52d34953fbde7dc1bcajvr if seq is None: 672d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq = [] 67364b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, seq) 674d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq.append(value) 675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 67664b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, value) 677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6788ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 6798ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 680b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 681b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 682b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 68396b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 684f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 685b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod other.ensureDecompiled() 686f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 687b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 688d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 689d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 690d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable): 691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 692cfadfd0096d634f4505bca27d6926555d43305d4jvr """Minor specialization of BaseTable, for tables that have multiple 69364b5c80e80444a124da335e8d4d208bffcf2737bjvr formats, eg. CoverageFormat1 vs. CoverageFormat2.""" 69464b5c80e80444a124da335e8d4d208bffcf2737bjvr 695d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 696d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters[self.Format] 697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 699d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[self.Format][name] 700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 701f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.Format = reader.readUShort() 703180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod assert self.Format != 0, (self, reader.pos, len(reader.data)) 704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 705f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(self.Format) 707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 70964b5c80e80444a124da335e8d4d208bffcf2737bjvr# 71064b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords 71164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 71264b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that 71364b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support 71464b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter... 71564b5c80e80444a124da335e8d4d208bffcf2737bjvr# 71664b5c80e80444a124da335e8d4d208bffcf2737bjvr 717d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [ 718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# Mask Name isDevice signed 719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0001, "XPlacement", 0, 1), 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0002, "YPlacement", 0, 1), 721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0004, "XAdvance", 0, 1), 722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0008, "YAdvance", 0, 1), 723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0010, "XPlaDevice", 1, 0), 724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0020, "YPlaDevice", 1, 0), 725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0040, "XAdvDevice", 1, 0), 726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0080, "YAdvDevice", 1, 0), 727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# reserved: 728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0100, "Reserved1", 0, 0), 729d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0200, "Reserved2", 0, 0), 730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0400, "Reserved3", 0, 0), 731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0800, "Reserved4", 0, 0), 732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x1000, "Reserved5", 0, 0), 733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x2000, "Reserved6", 0, 0), 734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x4000, "Reserved7", 0, 0), 735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x8000, "Reserved8", 0, 0), 736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr] 737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 738d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict(): 739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d = {} 740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d[name] = mask, isDevice, signed 742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return d 743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 744d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict() 745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 747e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object): 748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 74964b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a format code, this object convert ValueRecords.""" 750d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod 751601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod def __init__(self, valueFormat): 752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = [] 753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat & mask: 755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format.append((name, isDevice, signed)) 756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.format = format 757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, reader, font): 759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = self.format 760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not format: 761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueRecord = ValueRecord() 763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in format: 764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if signed: 765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readShort() 766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readUShort() 768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 7702b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = reader.getSubReader(value) 772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.decompile(subReader, font) 774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = None 776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(valueRecord, name, value) 777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return valueRecord 778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, writer, font, valueRecord): 780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in self.format: 781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(valueRecord, name, 0) 782d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.compile(subWriter, font) 787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(0) 789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif signed: 790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 795e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object): 796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # see ValueRecordFactory 798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getFormat(self): 800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = 0 801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name in self.__dict__.keys(): 802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = format | valueRecordFormatDict[name][0] 803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, valueName, attrs=None): 806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 807d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = [] 808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = list(attrs) 810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values 811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems.append((name, getattr(self, name))) 813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems = [] 814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records 815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr device = getattr(self, name) 817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if device is not None: 818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems.append((name, device)) 819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceItems: 820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(valueName, simpleItems) 821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, deviceRecord in deviceItems: 823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceRecord is not None: 824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceRecord.toXML(xmlWriter, font) 825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(valueName) 826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 828d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(valueName, simpleItems) 829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8313a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 8322b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for k, v in attrs.items(): 834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, k, int(v)) 835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 836b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, attrs, content = element 839d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 840d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for elem2 in content: 841b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(elem2, tuple): 842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 8433a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod name2, attrs2, content2 = elem2 8443a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod value.fromXML(name2, attrs2, content2, font) 845d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, name, value) 846d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8478ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 8488ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 849b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 850b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 851b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod raise TypeError("unordered types %s() < %s()", type(self), type(other)) 852b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 853