11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 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) 8095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod overflowRecord = None 8195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod 8295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod while True: 8395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod try: 8495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod writer = OTTableWriter(globalState) 8595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod self.table.compile(writer, font) 8695f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod return writer.getAllData() 8795f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod 8895f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod except OTLOffsetOverflowError as e: 8995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod 9095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod if overflowRecord == e.value: 9195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod raise # Oh well... 9295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod 9395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod overflowRecord = e.value 9495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod print("Attempting to fix OTLOffsetOverflowError", e) 9595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod lastItem = overflowRecord 9695f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod 9795f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod ok = 0 9895f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod if overflowRecord.itemName is None: 9995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod from .otTables import fixLookupOverFlows 10095f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod ok = fixLookupOverFlows(font, overflowRecord) 10195f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod else: 10295f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod from .otTables import fixSubTableOverFlows 10395f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod ok = fixSubTableOverFlows(font, overflowRecord) 10495f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod if not ok: 10595f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod raise 106823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 107d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, writer, font): 108d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table.toXML2(writer, font) 109d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1103a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 1112b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 112d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(self, "table"): 113d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = getattr(otTables, self.tableTag) 114d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = tableClass() 1153a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod self.table.fromXML(name, attrs, content, font) 116d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 117d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1183879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableReader(object): 1197981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 12064b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to retrieve data from an OpenType table.""" 1213879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbod 12279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState') 1237981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 12479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, data, globalState={}, localState=None, offset=0): 125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.data = data 126d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.offset = offset 127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = offset 12879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.globalState = globalState 12979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = localState 1307981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1310fac7fe32072e813d36882ba68af1227b6caff9bBehdad Esfahbod def getSubReader(self, offset): 132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr offset = self.offset + offset 13379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod cachingStats = self.globalState.cachingStats 13479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod if cachingStats is not None: 13579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod cachingStats[offset] = cachingStats.get(offset, 0) + 1 13679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.__class__(self.data, self.globalState, self.localState, offset) 1377981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readUShort(self): 139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 141e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">H", self.data[pos:newpos]) 142d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1447981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readShort(self): 146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 2 148e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">h", self.data[pos:newpos]) 149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1517981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readLong(self): 153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 155e69caf8771a0dd675ca3c4490625dac963a6b65cjvr value, = struct.unpack(">l", self.data[pos:newpos]) 156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1587981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 1599e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def readUInt24(self): 1609e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pos = self.pos 1619e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod newpos = pos + 3 162c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod value, = struct.unpack(">l", b'\0'+self.data[pos:newpos]) 1639e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod self.pos = newpos 1649e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod return value 1659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 166823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def readULong(self): 167823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 168823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newpos = pos + 4 169823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value, = struct.unpack(">L", self.data[pos:newpos]) 170823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.pos = newpos 171823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 172823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readTag(self): 174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pos = self.pos 175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr newpos = pos + 4 176960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod value = Tag(self.data[pos:newpos]) 177d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(value) == 4 178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.pos = newpos 179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 1807981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 18179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 18279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 18379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 18479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 1857981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 18679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 18779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 1887981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 189d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1903879cf94396869f645ae4c34dcdafed40ccdcdd4Behdad Esfahbodclass OTTableWriter(object): 191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 19264b5c80e80444a124da335e8d4d208bffcf2737bjvr """Helper class to gather and assemble data for OpenType tables.""" 19364b5c80e80444a124da335e8d4d208bffcf2737bjvr 19479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __init__(self, globalState, localState=None): 195d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items = [] 196cfadfd0096d634f4505bca27d6926555d43305d4jvr self.pos = None 19779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.globalState = globalState 19879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = localState 19995f795f40a5d75c2c14c03c1b74cf1ee8816dbf8Behdad Esfahbod self.parent = None 2007981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 20179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __setitem__(self, name, value): 20279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state = self.localState.copy() if self.localState else dict() 20379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod state[name] = value 20479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.localState = state 2057981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 20679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod def __getitem__(self, name): 20779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return self.localState[name] 2087981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod 2094105ca0b9515887a8a4cbb873c3de98009cbc633jvr # assembler interface 2104105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2114105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getAllData(self): 2124105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble all data, including all subtables.""" 2134105ca0b9515887a8a4cbb873c3de98009cbc633jvr self._doneWriting() 214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tables, extTables = self._gatherTables() 2154105ca0b9515887a8a4cbb873c3de98009cbc633jvr tables.reverse() 216823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables.reverse() 2174105ca0b9515887a8a4cbb873c3de98009cbc633jvr # Gather all data in two passes: the absolute positions of all 2184105ca0b9515887a8a4cbb873c3de98009cbc633jvr # subtable are needed before the actual data can be assembled. 2194105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = 0 2204105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 2214105ca0b9515887a8a4cbb873c3de98009cbc633jvr table.pos = pos 2224105ca0b9515887a8a4cbb873c3de98009cbc633jvr pos = pos + table.getDataLength() 223823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table.pos = pos 226823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = pos + table.getDataLength() 227823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 228823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2294105ca0b9515887a8a4cbb873c3de98009cbc633jvr data = [] 2304105ca0b9515887a8a4cbb873c3de98009cbc633jvr for table in tables: 2314105ca0b9515887a8a4cbb873c3de98009cbc633jvr tableData = table.getData() 2324105ca0b9515887a8a4cbb873c3de98009cbc633jvr data.append(tableData) 233823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for table in extTables: 235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr tableData = table.getData() 236823f8cd15f16bb9dc3991c2672f16dd90579711bjvr data.append(tableData) 237823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 238821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(data) 2394105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2404105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getDataLength(self): 2414105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Return the length of this table in bytes, without subtables.""" 2424105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = 0 2434105ca0b9515887a8a4cbb873c3de98009cbc633jvr for item in self.items: 2444105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData") or hasattr(item, "getCountData"): 245e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 4 # sizeof(ULong) 247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 248823f8cd15f16bb9dc3991c2672f16dd90579711bjvr l = l + 2 # sizeof(UShort) 2494105ca0b9515887a8a4cbb873c3de98009cbc633jvr else: 2504105ca0b9515887a8a4cbb873c3de98009cbc633jvr l = l + len(item) 2514105ca0b9515887a8a4cbb873c3de98009cbc633jvr return l 2524105ca0b9515887a8a4cbb873c3de98009cbc633jvr 2534105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getData(self): 2544105ca0b9515887a8a4cbb873c3de98009cbc633jvr """Assemble the data for this writer/table, without subtables.""" 2554105ca0b9515887a8a4cbb873c3de98009cbc633jvr items = list(self.items) # make a shallow copy 256823f8cd15f16bb9dc3991c2672f16dd90579711bjvr pos = self.pos 257823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(items) 258823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 2594105ca0b9515887a8a4cbb873c3de98009cbc633jvr item = items[i] 260823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 2614105ca0b9515887a8a4cbb873c3de98009cbc633jvr if hasattr(item, "getData"): 262e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if item.longOffset: 263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packULong(item.pos - pos) 264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 265823f8cd15f16bb9dc3991c2672f16dd90579711bjvr try: 266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = packUShort(item.pos - pos) 2679e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod except struct.error: 268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # provide data to fix overflow problem. 26958acba2d0284ec63bf27c457fa261adf7774e996Behdad Esfahbod # If the overflow is to a lookup, or from a lookup to a subtable, 270ab0ca1bd6b495ea3b9523c74cc06ab957a10293cBehdad Esfahbod # just report the current item. Otherwise... 271ab0ca1bd6b495ea3b9523c74cc06ab957a10293cBehdad Esfahbod if self.name not in [ 'LookupList', 'Lookup']: 272823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # overflow is within a subTable. Life is more complicated. 273823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # If we split the sub-table just before the current item, we may still suffer overflow. 274823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # This is because duplicate table merging is done only within an Extension subTable tree; 275823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # when we split the subtable in two, some items may no longer be duplicates. 276823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Get worst case by adding up all the item lengths, depth first traversal. 277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # and then report the first item that overflows a short. 278823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getDeepItemLength(table): 279823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(table, "getDataLength"): 280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = 0 281823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for item in table.items: 282823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 283823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 284823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = len(table) 285823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return length 286823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 287823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = self.getDataLength() 288823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast") and item.name == "Coverage": 289823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage is first in the item list, but last in the table list, 290823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # The original overflow is really in the item list. Skip the Coverage 291823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # table in the following test. 292823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items = items[i+1:] 293823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 294823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for j in range(len(items)): 295823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = items[j] 296823f8cd15f16bb9dc3991c2672f16dd90579711bjvr length = length + getDeepItemLength(item) 297823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if length > 65535: 298823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 299823f8cd15f16bb9dc3991c2672f16dd90579711bjvr overflowErrorRecord = self.getOverflowErrorRecord(item) 300823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 301823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 302cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod raise OTLOffsetOverflowError(overflowErrorRecord) 303823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 304821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod return bytesjoin(items) 305d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 306cfadfd0096d634f4505bca27d6926555d43305d4jvr def __hash__(self): 307cfadfd0096d634f4505bca27d6926555d43305d4jvr # only works after self._doneWriting() has been called 308cfadfd0096d634f4505bca27d6926555d43305d4jvr return hash(self.items) 309cfadfd0096d634f4505bca27d6926555d43305d4jvr 3108ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 3118ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 312b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 313b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 314273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 315b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.items == other.items 316cfadfd0096d634f4505bca27d6926555d43305d4jvr 317cfadfd0096d634f4505bca27d6926555d43305d4jvr def _doneWriting(self, internedTables=None): 318823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert CountData references to data string items 319823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # collapse duplicate table references to a unique entry 320823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 321823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 322823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For Extension Lookup types, we can 323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # eliminate duplicates only within the tree under the Extension Lookup, 324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # as offsets may exceed 64K even between Extension LookupTable subtables. 325cfadfd0096d634f4505bca27d6926555d43305d4jvr if internedTables is None: 326cfadfd0096d634f4505bca27d6926555d43305d4jvr internedTables = {} 327cfadfd0096d634f4505bca27d6926555d43305d4jvr items = self.items 32897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(len(items))) 329823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 330823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 331823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 1 332823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 333823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newTree = 0 334823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 335d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = items[i] 336cfadfd0096d634f4505bca27d6926555d43305d4jvr if hasattr(item, "getCountData"): 337cfadfd0096d634f4505bca27d6926555d43305d4jvr items[i] = item.getCountData() 338cfadfd0096d634f4505bca27d6926555d43305d4jvr elif hasattr(item, "getData"): 339823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if newTree: 340823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting() 341d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._doneWriting(internedTables) 343dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod internedItem = internedTables.get(item) 344dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod if internedItem: 345dea08f2ba1c7d88465f81fbdd413bb82a8dbc5e4Behdad Esfahbod items[i] = item = internedItem 346823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 347823f8cd15f16bb9dc3991c2672f16dd90579711bjvr internedTables[item] = item 348cfadfd0096d634f4505bca27d6926555d43305d4jvr self.items = tuple(items) 349cfadfd0096d634f4505bca27d6926555d43305d4jvr 350823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def _gatherTables(self, tables=None, extTables=None, done=None): 351823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Convert table references in self.items tree to a flat 352823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # list of tables in depth-first traversal order. 353823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # "tables" are OTTableWriter objects. 354823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We do the traversal in reverse order at each level, in order to 355823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # resolve duplicate references to be the last reference in the list of tables. 356823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # For extension lookups, duplicate references can be merged only within the 357823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # writer tree under the extension lookup. 358823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if tables is None: # init call for first time. 359cfadfd0096d634f4505bca27d6926555d43305d4jvr tables = [] 360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extTables = [] 361cfadfd0096d634f4505bca27d6926555d43305d4jvr done = {} 362823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr done[self] = 1 364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 365823f8cd15f16bb9dc3991c2672f16dd90579711bjvr numItems = len(self.items) 36697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod iRange = list(range(numItems)) 367823f8cd15f16bb9dc3991c2672f16dd90579711bjvr iRange.reverse() 368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "Extension"): 370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 1 371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr appendExtensions = 0 373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 374823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # add Coverage table if it is sorted last. 375823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 0 376823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, "sortCoverageLast"): 377823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Find coverage table 378823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(numItems): 379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, "name") and (item.name == "Coverage"): 381823f8cd15f16bb9dc3991c2672f16dd90579711bjvr sortCoverageLast = 1 382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr break 383bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if item not in done: 384823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 385823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 386ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod # We're a new parent of item 387ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod pass 388823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in iRange: 390823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = self.items[i] 391cfadfd0096d634f4505bca27d6926555d43305d4jvr if not hasattr(item, "getData"): 392cfadfd0096d634f4505bca27d6926555d43305d4jvr continue 393823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 394823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if sortCoverageLast and (i==1) and item.name == 'Coverage': 395823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # we've already 'gathered' it above 396823f8cd15f16bb9dc3991c2672f16dd90579711bjvr continue 397823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 398823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if appendExtensions: 3999e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables" 400823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newDone = {} 401823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(extTables, None, newDone) 402823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 403bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif item not in done: 404823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item._gatherTables(tables, extTables, done) 405823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 406ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod # We're a new parent of item 407ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod pass 408823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 409823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 410cfadfd0096d634f4505bca27d6926555d43305d4jvr tables.append(self) 411823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return tables, extTables 412cfadfd0096d634f4505bca27d6926555d43305d4jvr 4134105ca0b9515887a8a4cbb873c3de98009cbc633jvr # interface for gathering data, as used by table.compile() 4148e48312f88adc41feb7c730154c6d058a010189djvr 4154105ca0b9515887a8a4cbb873c3de98009cbc633jvr def getSubWriter(self): 41679f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod subwriter = self.__class__(self.globalState, self.localState) 417ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod subwriter.parent = self # because some subtables have idential values, we discard 418ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod # the duplicates under the getAllData method. Hence some 419ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod # subtable writers can have more than one parent writer. 420ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod # But we just care about first one right now. 421823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return subwriter 422d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 423d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeUShort(self, value): 424d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert 0 <= value < 0x10000 425d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">H", value)) 426d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 427d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeShort(self, value): 428d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">h", value)) 4299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 4309e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def writeUInt24(self, value): 4319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod assert 0 <= value < 0x1000000 432c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod b = struct.pack(">L", value) 433c0a9d697f62545ddf48f7bff062302096328c0bcBehdad Esfahbod self.items.append(b[1:]) 434d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 435d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeLong(self, value): 436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(struct.pack(">l", value)) 437d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeULong(self, value): 439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(struct.pack(">L", value)) 440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 441d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeTag(self, tag): 442960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod tag = Tag(tag).tobytes() 443d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(tag) == 4 444d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(tag) 445d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 446d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeSubTable(self, subWriter): 447d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(subWriter) 448d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 449d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeCountReference(self, table, name): 4507981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod ref = CountReference(table, name) 4517981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod self.items.append(ref) 452ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod return ref 453d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 454d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeStruct(self, format, values): 45566214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod data = struct.pack(*(format,) + values) 456d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.items.append(data) 457d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 458823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def writeData(self, data): 459823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.items.append(data) 460d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def getOverflowErrorRecord(self, item): 462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = SubTableIndex = itemName = itemIndex = None 463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'LookupList': 464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = item.repeatIndex 465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'Lookup': 466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookupListIndex = self.repeatIndex 467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = item.repeatIndex 468823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemName = item.name 470823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(item, 'repeatIndex'): 471823f8cd15f16bb9dc3991c2672f16dd90579711bjvr itemIndex = item.repeatIndex 472823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if self.name == 'SubTable': 473ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod LookupListIndex = self.parent.repeatIndex 474823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTableIndex = self.repeatIndex 475823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif self.name == 'ExtSubTable': 476ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod LookupListIndex = self.parent.parent.repeatIndex 477ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod SubTableIndex = self.parent.repeatIndex 478823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable. 4799e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod itemName = ".".join([self.name, item.name]) 480ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod p1 = self.parent 481823f8cd15f16bb9dc3991c2672f16dd90579711bjvr while p1 and p1.name not in ['ExtSubTable', 'SubTable']: 4829e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod itemName = ".".join([p1.name, item.name]) 483ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod p1 = p1.parent 484823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1: 485823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if p1.name == 'ExtSubTable': 486ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod LookupListIndex = p1.parent.parent.repeatIndex 487ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod SubTableIndex = p1.parent.repeatIndex 488823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 489ee6340f4752e9bc286340399fbd2cc5a790d0574Behdad Esfahbod LookupListIndex = p1.parent.repeatIndex 4909e482332baf75b67058bcd9e2b0ae139fbbebc78Behdad Esfahbod SubTableIndex = p1.repeatIndex 491823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 49279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) ) 493823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 494d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 495e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass CountReference(object): 496cfadfd0096d634f4505bca27d6926555d43305d4jvr """A reference to a Count value, not a count of references.""" 497d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def __init__(self, table, name): 498d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.table = table 499d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 5007981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod def setValue(self, value): 5017981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table = self.table 5027981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod name = self.name 5037981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod if table[name] is None: 5047981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod table[name] = value 5057981704a0aade1831d36ece04e2faa02394e8305Behdad Esfahbod else: 5061f0eed84596e81650b9a8aabfc385bf8dc0c97a2Behdad Esfahbod assert table[name] == value, (name, table[name], value) 507cfadfd0096d634f4505bca27d6926555d43305d4jvr def getCountData(self): 508d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return packUShort(self.table[self.name]) 509d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 510d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 51164b5c80e80444a124da335e8d4d208bffcf2737bjvrdef packUShort(value): 51264b5c80e80444a124da335e8d4d208bffcf2737bjvr return struct.pack(">H", value) 513d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 514d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 515823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef packULong(value): 516ce47e0d978a7768bdc55e00c6ebd6b691e1eaeb3jvr assert 0 <= value < 0x100000000, value 517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return struct.pack(">L", value) 518823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 5205988cc32cbe04ed8ff6330bd6ede634b9c68faf0Behdad Esfahbodclass BaseTable(object): 521b4070bd6293998ac4bc6a68c1f774e9e9908c65bBehdad Esfahbod 522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr def __getattr__(self, attr): 523f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod reader = self.__dict__.get("reader") 524dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod if reader: 525dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.reader 526dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod font = self.font 527dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.font 528dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod self.decompile(reader, font) 529dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod return getattr(self, attr) 530dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod 531dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod raise AttributeError(attr) 532823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 53364b5c80e80444a124da335e8d4d208bffcf2737bjvr """Generic base class for all OpenType (sub)tables.""" 53464b5c80e80444a124da335e8d4d208bffcf2737bjvr 535d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 536d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters 537d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 538d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 539d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[name] 540d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 541078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def decompile(self, reader, font): 542f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.readFormat(reader) 543d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = {} 544d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__rawTable = table # for debugging 54541caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod converters = self.getConverters() 54641caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod for conv in converters: 547d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.name == "SubTable": 54879f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 549d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table["LookupType"]) 550823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if conv.name == "ExtSubTable": 55179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod conv = conv.getConverter(reader.globalState.tableType, 552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table["ExtensionLookupType"]) 5539e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.name == "FeatureParams": 5549e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv = conv.getConverter(reader["FeatureTag"]) 555d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 556d4d151390d1288f8d2df30f6dfa26a309c7334dajvr l = [] 557ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 558ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod countValue = table[conv.repeat] 559ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 560ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 56179f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod countValue = reader[conv.repeat] 5626b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod for i in range(countValue + conv.aux): 563078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod l.append(conv.read(reader, font, table)) 564d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table[conv.name] = l 565d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 5665b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 5675b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 568078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod table[conv.name] = conv.read(reader, font, table) 5699e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 57079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader[conv.name] = table[conv.name] 57141caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 572d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.postRead(table, font) 57341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod 574d4d151390d1288f8d2df30f6dfa26a309c7334dajvr del self.__rawTable # succeeded, get rid of debugging info 575823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 576f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod def ensureDecompiled(self): 577f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod reader = self.__dict__.get("reader") 578dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod if reader: 579dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.reader 580dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod font = self.font 581dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod del self.font 582dafdb2933a9d4ef7ef5365c5e8ecfba39b4d3c57Behdad Esfahbod self.decompile(reader, font) 583f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 584078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def compile(self, writer, font): 5853ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod self.ensureDecompiled() 586d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.preWrite(font) 587823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(self, 'sortCoverageLast'): 589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.sortCoverageLast = 1 590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 5915fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod if hasattr(self.__class__, 'LookupType'): 5925fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod writer['LookupType'].setValue(self.__class__.LookupType) 5935fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod 594f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr self.writeFormat(writer) 595d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 596d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = table.get(conv.name) 597d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 598d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 59964b5c80e80444a124da335e8d4d208bffcf2737bjvr value = [] 6006b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod countValue = len(value) - conv.aux 601ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod if conv.repeat in table: 6026bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod CountReference(table, conv.repeat).setValue(countValue) 603ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod else: 604ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # conv.repeat is a propagated count 60579f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.repeat].setValue(countValue) 606823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(len(value)): 607078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value[i], i) 608d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif conv.isCount: 609d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Special-case Count values. 610d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Assumption: a Count field will *always* precede 611ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod # the actual array(s). 612d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We need a default value, as it may be set later by a nested 61341caf2dea1e0c58c5ac763ad2575592df734b62cBehdad Esfahbod # table. We will later store it here. 614d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # We add a reference: by the time the data is assembled 615d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # the Count value will be filled in. 616ee27eb8517ee53594a6232d110e17403a37ff2d9Behdad Esfahbod ref = writer.writeCountReference(table, conv.name) 6176bfee2cde5b16f46a4fe91342b8d20db35cb8046Behdad Esfahbod table[conv.name] = None 6189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 61979f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[conv.name] = ref 6205fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod elif conv.isLookupType: 6215fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod ref = writer.writeCountReference(table, conv.name) 6225fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod table[conv.name] = None 6235fec22bfcd53fe9a10fa066e17d0a0d18cf2d74aBehdad Esfahbod writer['LookupType'] = ref 624d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 6255b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, table): 6265b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 627078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod conv.write(writer, font, table, value) 6289e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if conv.isPropagated: 6299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod writer[conv.name] = value 630d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 631f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 632f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 633f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 634f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 635f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr pass 636f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr 637d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def postRead(self, table, font): 638d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.__dict__.update(table) 639d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 640d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def preWrite(self, font): 641d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.__dict__.copy() 642d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 643d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod def toXML(self, xmlWriter, font, attrs=None, name=None): 644d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod tableName = name if name else self.__class__.__name__ 645d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 646d4d151390d1288f8d2df30f6dfa26a309c7334dajvr attrs = [] 647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, "Format"): 64864b5c80e80444a124da335e8d4d208bffcf2737bjvr attrs = attrs + [("Format", self.Format)] 649d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(tableName, attrs) 650d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.toXML2(xmlWriter, font) 652d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(tableName) 653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 655d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML2(self, xmlWriter, font): 656d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). 657d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # This is because in TTX our parent writes our main tag, and in otBase.py we 658d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # do it ourselves. I think I'm getting schizophrenic... 659d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for conv in self.getConverters(): 66064b5c80e80444a124da335e8d4d208bffcf2737bjvr if conv.repeat: 6615b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(len(value)): 663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr item = value[i] 66464b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, item, conv.name, 66564b5c80e80444a124da335e8d4d208bffcf2737bjvr [("index", i)]) 66664b5c80e80444a124da335e8d4d208bffcf2737bjvr else: 6675b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod if conv.aux and not eval(conv.aux, None, vars(self)): 6685b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod continue 6695b9cabcb9a83e2d95e43c4b6a113f7dc71169c7fBehdad Esfahbod value = getattr(self, conv.name) 67064b5c80e80444a124da335e8d4d208bffcf2737bjvr conv.xmlWrite(xmlWriter, font, value, conv.name, []) 671d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6723a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 673d4d151390d1288f8d2df30f6dfa26a309c7334dajvr try: 674d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv = self.getConverterByName(name) 675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr except KeyError: 676d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise # XXX on KeyError, raise nice error 677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = conv.xmlRead(attrs, content, font) 678d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if conv.repeat: 67952966bb14409b558dc46b52d34953fbde7dc1bcajvr seq = getattr(self, conv.name, None) 68052966bb14409b558dc46b52d34953fbde7dc1bcajvr if seq is None: 681d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq = [] 68264b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, seq) 683d4d151390d1288f8d2df30f6dfa26a309c7334dajvr seq.append(value) 684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 68564b5c80e80444a124da335e8d4d208bffcf2737bjvr setattr(self, conv.name, value) 686d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6878ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 6888ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 689b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 690b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 691273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 69296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 693f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod self.ensureDecompiled() 694b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod other.ensureDecompiled() 695f50d0dff869a8b74bcff028721204a284cadff12Behdad Esfahbod 696b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 699d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FormatSwitchingBaseTable(BaseTable): 700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 701cfadfd0096d634f4505bca27d6926555d43305d4jvr """Minor specialization of BaseTable, for tables that have multiple 70264b5c80e80444a124da335e8d4d208bffcf2737bjvr formats, eg. CoverageFormat1 vs. CoverageFormat2.""" 70364b5c80e80444a124da335e8d4d208bffcf2737bjvr 704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverters(self): 705d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.converters[self.Format] 706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverterByName(self, name): 708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return self.convertersByName[self.Format][name] 709d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 710f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def readFormat(self, reader): 711d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.Format = reader.readUShort() 712180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod assert self.Format != 0, (self, reader.pos, len(reader.data)) 713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 714f7ef96ccca73f8c5abfeb1cd110e3c9a314d06b4jvr def writeFormat(self, writer): 715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(self.Format) 716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 717d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod def toXML(self, xmlWriter, font, attrs=None, name=None): 718d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__) 719d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 72164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 72264b5c80e80444a124da335e8d4d208bffcf2737bjvr# Support for ValueRecords 72364b5c80e80444a124da335e8d4d208bffcf2737bjvr# 72464b5c80e80444a124da335e8d4d208bffcf2737bjvr# This data type is so different from all other OpenType data types that 72564b5c80e80444a124da335e8d4d208bffcf2737bjvr# it requires quite a bit of code for itself. It even has special support 72664b5c80e80444a124da335e8d4d208bffcf2737bjvr# in OTTableReader and OTTableWriter... 72764b5c80e80444a124da335e8d4d208bffcf2737bjvr# 72864b5c80e80444a124da335e8d4d208bffcf2737bjvr 729d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormat = [ 730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# Mask Name isDevice signed 731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0001, "XPlacement", 0, 1), 732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0002, "YPlacement", 0, 1), 733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0004, "XAdvance", 0, 1), 734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0008, "YAdvance", 0, 1), 735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0010, "XPlaDevice", 1, 0), 736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0020, "YPlaDevice", 1, 0), 737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0040, "XAdvDevice", 1, 0), 738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0080, "YAdvDevice", 1, 0), 739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr# reserved: 740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0100, "Reserved1", 0, 0), 741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0200, "Reserved2", 0, 0), 742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0400, "Reserved3", 0, 0), 743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x0800, "Reserved4", 0, 0), 744d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x1000, "Reserved5", 0, 0), 745d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x2000, "Reserved6", 0, 0), 746d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x4000, "Reserved7", 0, 0), 747d4d151390d1288f8d2df30f6dfa26a309c7334dajvr (0x8000, "Reserved8", 0, 0), 748d4d151390d1288f8d2df30f6dfa26a309c7334dajvr] 749d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 750d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildDict(): 751d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d = {} 752d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 753d4d151390d1288f8d2df30f6dfa26a309c7334dajvr d[name] = mask, isDevice, signed 754d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return d 755d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 756d4d151390d1288f8d2df30f6dfa26a309c7334dajvrvalueRecordFormatDict = _buildDict() 757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 758d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 759e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecordFactory(object): 760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 76164b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a format code, this object convert ValueRecords.""" 762d01c44a59bcf8f8e75e8606142da422319d281e2Behdad Esfahbod 763601bb94afca4892835922c35d26116aaecf90283Behdad Esfahbod def __init__(self, valueFormat): 764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = [] 765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, signed in valueRecordFormat: 766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if valueFormat & mask: 767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format.append((name, isDevice, signed)) 768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.format = format 769d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def readValueRecord(self, reader, font): 771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = self.format 772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not format: 773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 774d4d151390d1288f8d2df30f6dfa26a309c7334dajvr valueRecord = ValueRecord() 775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in format: 776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if signed: 777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readShort() 778d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 779d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = reader.readUShort() 780d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 781d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 7822b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 783d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subReader = reader.getSubReader(value) 784d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 785d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.decompile(subReader, font) 786d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 787d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = None 788d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(valueRecord, name, value) 789d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return valueRecord 790d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 791d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def writeValueRecord(self, writer, font, valueRecord): 792d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, isDevice, signed in self.format: 793d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(valueRecord, name, 0) 794d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if isDevice: 795d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value: 796d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 797d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 798d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.compile(subWriter, font) 799d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 800d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(0) 801d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif signed: 802d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 803d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 804d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 805d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 806d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 807e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ValueRecord(object): 808d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 809d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # see ValueRecordFactory 810d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 811d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getFormat(self): 812d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = 0 813d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name in self.__dict__.keys(): 814d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = format | valueRecordFormatDict[name][0] 815d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 816d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 817d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def toXML(self, xmlWriter, font, valueName, attrs=None): 818d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if attrs is None: 819d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = [] 820d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 821d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems = list(attrs) 822d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values 823d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 824d4d151390d1288f8d2df30f6dfa26a309c7334dajvr simpleItems.append((name, getattr(self, name))) 825d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems = [] 826d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records 827d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if hasattr(self, name): 828d4d151390d1288f8d2df30f6dfa26a309c7334dajvr device = getattr(self, name) 829d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if device is not None: 830d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceItems.append((name, device)) 831d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceItems: 832d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.begintag(valueName, simpleItems) 833d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 834d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, deviceRecord in deviceItems: 835d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if deviceRecord is not None: 836d4d151390d1288f8d2df30f6dfa26a309c7334dajvr deviceRecord.toXML(xmlWriter, font) 837d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.endtag(valueName) 838d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 839d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 840d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(valueName, simpleItems) 841d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 842d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8433a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 8442b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from . import otTables 845d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for k, v in attrs.items(): 846d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, k, int(v)) 847d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 848b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 849d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 850d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, attrs, content = element 851d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = getattr(otTables, name)() 852d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for elem2 in content: 853b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(elem2, tuple): 854d4d151390d1288f8d2df30f6dfa26a309c7334dajvr continue 8553a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod name2, attrs2, content2 = elem2 8563a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod value.fromXML(name2, attrs2, content2, font) 857d4d151390d1288f8d2df30f6dfa26a309c7334dajvr setattr(self, name, value) 858d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8598ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 8608ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 861b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 862b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod if type(self) != type(other): 863273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod return NotImplemented 864b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod return self.__dict__ == other.__dict__ 865