otConverters.py revision 2b06aaa2a6bcd363c25fb0c43f6bb906906594bd
1d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom types import TupleType 2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom fontTools.misc.textTools import safeEval 32b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .otBase import ValueRecordFactory 4d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 664b5c80e80444a124da335e8d4d208bffcf2737bjvrdef buildConverters(tableSpec, tableNamespace): 764b5c80e80444a124da335e8d4d208bffcf2737bjvr """Given a table spec from otData.py, build a converter object for each 864b5c80e80444a124da335e8d4d208bffcf2737bjvr field of the table. This is called for each table in otData.py, and 964b5c80e80444a124da335e8d4d208bffcf2737bjvr the results are assigned to the corresponding class in otTables.py.""" 10d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converters = [] 11d4d151390d1288f8d2df30f6dfa26a309c7334dajvr convertersByName = {} 126b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod for tp, name, repeat, aux, descr in tableSpec: 13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if name.startswith("ValueFormat"): 14d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert tp == "uint16" 15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converterClass = ValueFormat 16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif name.endswith("Count"): 17d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert tp == "uint16" 18d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converterClass = Count 19d4d151390d1288f8d2df30f6dfa26a309c7334dajvr elif name == "SubTable": 20d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converterClass = SubTable 21823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif name == "ExtSubTable": 22823f8cd15f16bb9dc3991c2672f16dd90579711bjvr converterClass = ExtSubTable 239e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod elif name == "FeatureParams": 249e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod converterClass = FeatureParams 25d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 26d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converterClass = converterMapping[tp] 27d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tableClass = tableNamespace.get(name) 286b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod conv = converterClass(name, repeat, aux, tableClass) 29823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if name in ["SubTable", "ExtSubTable"]: 30d4d151390d1288f8d2df30f6dfa26a309c7334dajvr conv.lookupTypes = tableNamespace['lookupTypes'] 31d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # also create reverse mapping 32d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for t in conv.lookupTypes.values(): 33d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for cls in t.values(): 346b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod convertersByName[cls.__name__] = Table(name, repeat, aux, cls) 359e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod if name == "FeatureParams": 369e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv.featureParamTypes = tableNamespace['featureParamTypes'] 379e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod conv.defaultFeatureParams = tableNamespace['FeatureParams'] 389e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod for cls in conv.featureParamTypes.values(): 399e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod convertersByName[cls.__name__] = Table(name, repeat, aux, cls) 40d4d151390d1288f8d2df30f6dfa26a309c7334dajvr converters.append(conv) 41bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod assert name not in convertersByName 42d4d151390d1288f8d2df30f6dfa26a309c7334dajvr convertersByName[name] = conv 43d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return converters, convertersByName 44d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 45d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 46d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass BaseConverter(object): 47d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4864b5c80e80444a124da335e8d4d208bffcf2737bjvr """Base class for converter objects. Apart from the constructor, this 4964b5c80e80444a124da335e8d4d208bffcf2737bjvr is an abstract class.""" 5064b5c80e80444a124da335e8d4d208bffcf2737bjvr 516b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod def __init__(self, name, repeat, aux, tableClass): 52d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.name = name 53d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.repeat = repeat 546b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod self.aux = aux 55d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.tableClass = tableClass 56d4d151390d1288f8d2df30f6dfa26a309c7334dajvr self.isCount = name.endswith("Count") 579e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag"] 58d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 59078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 6064b5c80e80444a124da335e8d4d208bffcf2737bjvr """Read a value from the reader.""" 61d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise NotImplementedError, self 62d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 63078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 6464b5c80e80444a124da335e8d4d208bffcf2737bjvr """Write a value to the writer.""" 65d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise NotImplementedError, self 66d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6764b5c80e80444a124da335e8d4d208bffcf2737bjvr def xmlRead(self, attrs, content, font): 6864b5c80e80444a124da335e8d4d208bffcf2737bjvr """Read a value from XML.""" 69d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise NotImplementedError, self 70d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7164b5c80e80444a124da335e8d4d208bffcf2737bjvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 7264b5c80e80444a124da335e8d4d208bffcf2737bjvr """Write a value to XML.""" 73d4d151390d1288f8d2df30f6dfa26a309c7334dajvr raise NotImplementedError, self 74d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 75d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 76d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass SimpleValue(BaseConverter): 77d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 78d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(name, attrs + [("value", value)]) 79d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 80d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlRead(self, attrs, content, font): 81d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return attrs["value"] 82d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 83d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass IntValue(SimpleValue): 84d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlRead(self, attrs, content, font): 85ffc3cfeed12e67b383ed5f540d05cab01b33bb77Behdad Esfahbod return int(attrs["value"], 0) 86d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 87d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Long(IntValue): 88078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 89d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return reader.readLong() 90078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 91d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeLong(value) 92d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 93a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbodclass Version(BaseConverter): 94078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 95a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = reader.readLong() 96a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod assert (value >> 16) == 1, "Unsupported version 0x%08x" % value 97a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod return float(value) / 0x10000 98078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 99a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod if value < 0x10000: 100a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value *= 0x10000 101a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = int(round(value)) 102a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod assert (value >> 16) == 1, "Unsupported version 0x%08x" % value 103a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod writer.writeLong(value) 1047007a0854548f744c44bb9c35380efcba7894bfbjvr def xmlRead(self, attrs, content, font): 105a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = attrs["value"] 106a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = float(int(value, 0)) if value.startswith("0") else float(value) 107a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod if value >= 0x10000: 108a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = float(value) / 0x10000 109a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod return value 110a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod def xmlWrite(self, xmlWriter, font, value, name, attrs): 111a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod if value >= 0x10000: 112a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = float(value) / 0x10000 113a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod if value % 1 != 0: 114a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod # Write as hex 115a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod value = "0x%08x" % (int(round(value * 0x10000))) 116a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod xmlWriter.simpletag(name, attrs + [("value", value)]) 117a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod xmlWriter.newline() 118b776a882ee55fff8c39543f352c528bb5d338177jvr 119d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Short(IntValue): 120078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 121d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return reader.readShort() 122078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 123d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeShort(value) 124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 125d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass UShort(IntValue): 126078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return reader.readUShort() 128078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(value) 130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass UInt24(IntValue): 1329e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def read(self, reader, font, tableDict): 1339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod return reader.readUInt24() 1349e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 1359e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod writer.writeUInt24(value) 1369e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 137d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Count(Short): 138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.comment("%s=%s" % (name, value)) 140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 142d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Tag(SimpleValue): 143078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return reader.readTag() 145078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 146d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeTag(value) 147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 148d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass GlyphID(SimpleValue): 149078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value = reader.readUShort() 151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value = font.getGlyphName(value) 152823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 153823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 154078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 155823f8cd15f16bb9dc3991c2672f16dd90579711bjvr value = font.getGlyphID(value) 156823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.writeUShort(value) 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1583ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbodclass FloatValue(SimpleValue): 1593ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod def xmlRead(self, attrs, content, font): 1603ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod return float(attrs["value"]) 1613ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod 1623ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbodclass DeciPoints(FloatValue): 1633ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod def read(self, reader, font, tableDict): 1643ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod value = reader.readUShort() 1653ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod return value / 10. 1663ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod 1673ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 1683ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod writer.writeUShort(int(round(value * 10))) 169b776a882ee55fff8c39543f352c528bb5d338177jvr 170d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Struct(BaseConverter): 171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 172078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.tableClass() 174078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod table.decompile(reader, font) 175d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return table 176d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 177078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 178078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod value.compile(writer, font) 179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 180d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 182cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod if attrs: 183cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod # If there are attributes (probably index), then 184cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod # don't drop this even if it's NULL. It will mess 185cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod # up the array indices of the containing element. 186cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod xmlWriter.simpletag(name, attrs + [("empty", True)]) 187cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod xmlWriter.newline() 188cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod else: 189cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod pass # NULL table, ignore 190d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 191d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.toXML(xmlWriter, font, attrs) 192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlRead(self, attrs, content, font): 194d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.tableClass() 195cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod if attrs.get("empty"): 196cb4adf682848103b25f955d19a892fb33fbde9e3Behdad Esfahbod return None 197d4d151390d1288f8d2df30f6dfa26a309c7334dajvr Format = attrs.get("Format") 198d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if Format is not None: 199d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table.Format = int(Format) 200d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for element in content: 20152966bb14409b558dc46b52d34953fbde7dc1bcajvr if type(element) == TupleType: 20252966bb14409b558dc46b52d34953fbde7dc1bcajvr name, attrs, content = element 20352966bb14409b558dc46b52d34953fbde7dc1bcajvr table.fromXML((name, attrs, content), font) 20452966bb14409b558dc46b52d34953fbde7dc1bcajvr else: 20552966bb14409b558dc46b52d34953fbde7dc1bcajvr pass 206d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return table 207d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 208d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 209d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass Table(Struct): 210fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod 211d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod longOffset = False 212d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod 213fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod def readOffset(self, reader): 214fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod return reader.readUShort() 215fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod 216fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod def writeNullOffset(self, writer): 217e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod if self.longOffset: 218fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod writer.writeULong(0) 219fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod else: 220fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod writer.writeUShort(0) 221d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2227ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod def read(self, reader, font, tableDict): 223fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod offset = self.readOffset(reader) 224d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if offset == 0: 225d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 226d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if offset <= 3: 227d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XXX hack to work around buggy pala.ttf 228d4d151390d1288f8d2df30f6dfa26a309c7334dajvr print "*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \ 229d4d151390d1288f8d2df30f6dfa26a309c7334dajvr % (offset, self.tableClass.__name__) 230d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return None 231d4d151390d1288f8d2df30f6dfa26a309c7334dajvr table = self.tableClass() 232f4e61ae637f3d12b4121c1264560390b430d294fBehdad Esfahbod table.reader = reader.getSubReader(offset) 233c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod table.font = font 234c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod table.compileStatus = 1 2357ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod if not font.lazy: 236c491f45eff9a884e69a44cbf9ae69288be9478b5Behdad Esfahbod table.ensureDecompiled() 237d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return table 238d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 239078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 240d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 241fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod self.writeNullOffset(writer) 242d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 243d4d151390d1288f8d2df30f6dfa26a309c7334dajvr subWriter = writer.getSubWriter() 244e0c2e8e2af82f79c25026bb8e603ca5bd560c552Behdad Esfahbod subWriter.longOffset = self.longOffset 245823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subWriter.name = self.name 246823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if repeatIndex is not None: 247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subWriter.repeatIndex = repeatIndex 248d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeSubTable(subWriter) 249078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod value.compile(subWriter, font) 250d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 251d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass LTable(Table): 252d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod 253d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod longOffset = True 254d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod 255d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod def readOffset(self, reader): 256d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod return reader.readULong() 257d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod 258d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod 259d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass SubTable(Table): 260d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def getConverter(self, tableType, lookupType): 261fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod tableClass = self.lookupTypes[tableType][lookupType] 2626b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod return self.__class__(self.name, self.repeat, self.aux, tableClass) 263823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 264823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 265d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbodclass ExtSubTable(LTable, SubTable): 266823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 267078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 268823f8cd15f16bb9dc3991c2672f16dd90579711bjvr writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer. 269fe67e3121e421e8f146c7b4df8b7188c620d1e5aBehdad Esfahbod Table.write(self, writer, font, tableDict, value, repeatIndex) 270d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2719e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParams(Table): 2729e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def getConverter(self, featureTag): 2739e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams) 2749e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod return self.__class__(self.name, self.repeat, self.aux, tableClass) 2759e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 276d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 277d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueFormat(IntValue): 2786b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod def __init__(self, name, repeat, aux, tableClass): 2796b6e9fae23499de4d17372d0bd7ce4a8f194b4d7Behdad Esfahbod BaseConverter.__init__(self, name, repeat, aux, tableClass) 28079f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1") 281078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 282d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = reader.readUShort() 28379f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod reader[self.which] = ValueRecordFactory(format) 284d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return format 285078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, format, repeatIndex=None): 286d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(format) 28779f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[self.which] = ValueRecordFactory(format) 288d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 289b776a882ee55fff8c39543f352c528bb5d338177jvr 290d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass ValueRecord(ValueFormat): 291078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 29279f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod return reader[self.which].readValueRecord(reader, font) 293078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 29479f734414c2a8d6851fb1b3ec69287ff0e0077b9Behdad Esfahbod writer[self.which].writeValueRecord(writer, font, value) 295d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 296d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value is None: 297d4d151390d1288f8d2df30f6dfa26a309c7334dajvr pass # NULL table, ignore 298d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 299d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.toXML(xmlWriter, font, self.name, attrs) 300d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlRead(self, attrs, content, font): 3012b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from .otBase import ValueRecord 302d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = ValueRecord() 303d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value.fromXML((None, attrs, content), font) 304d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return value 305d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 306d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 307d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass DeltaValue(BaseConverter): 308d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 309078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def read(self, reader, font, tableDict): 3102edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod StartSize = tableDict["StartSize"] 3112edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod EndSize = tableDict["EndSize"] 3122edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod DeltaFormat = tableDict["DeltaFormat"] 313d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat" 314d4d151390d1288f8d2df30f6dfa26a309c7334dajvr nItems = EndSize - StartSize + 1 315d4d151390d1288f8d2df30f6dfa26a309c7334dajvr nBits = 1 << DeltaFormat 316d4d151390d1288f8d2df30f6dfa26a309c7334dajvr minusOffset = 1 << nBits 317d4d151390d1288f8d2df30f6dfa26a309c7334dajvr mask = (1 << nBits) - 1 318d4d151390d1288f8d2df30f6dfa26a309c7334dajvr signMask = 1 << (nBits - 1) 319d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 320d4d151390d1288f8d2df30f6dfa26a309c7334dajvr DeltaValue = [] 321d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tmp, shift = 0, 0 322d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for i in range(nItems): 323d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if shift == 0: 324d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tmp, shift = reader.readUShort(), 16 325d4d151390d1288f8d2df30f6dfa26a309c7334dajvr shift = shift - nBits 326d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = (tmp >> shift) & mask 327d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if value & signMask: 328d4d151390d1288f8d2df30f6dfa26a309c7334dajvr value = value - minusOffset 329d4d151390d1288f8d2df30f6dfa26a309c7334dajvr DeltaValue.append(value) 330d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return DeltaValue 331d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 332078b36325d7f64ae989e7db22e4910bf6550562dBehdad Esfahbod def write(self, writer, font, tableDict, value, repeatIndex=None): 3332edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod StartSize = tableDict["StartSize"] 3342edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod EndSize = tableDict["EndSize"] 3352edc2da303716b9e401fd8dc808f54456d8137f2Behdad Esfahbod DeltaFormat = tableDict["DeltaFormat"] 3366f9b64f2966d18cccad3fcf6e03f44f208dbc2a4Behdad Esfahbod DeltaValue = value 337d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat" 338d4d151390d1288f8d2df30f6dfa26a309c7334dajvr nItems = EndSize - StartSize + 1 339d4d151390d1288f8d2df30f6dfa26a309c7334dajvr nBits = 1 << DeltaFormat 340d4d151390d1288f8d2df30f6dfa26a309c7334dajvr assert len(DeltaValue) == nItems 341d4d151390d1288f8d2df30f6dfa26a309c7334dajvr mask = (1 << nBits) - 1 342d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 343d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tmp, shift = 0, 16 344d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for value in DeltaValue: 345d4d151390d1288f8d2df30f6dfa26a309c7334dajvr shift = shift - nBits 346d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tmp = tmp | ((value & mask) << shift) 347d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if shift == 0: 348d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(tmp) 349d4d151390d1288f8d2df30f6dfa26a309c7334dajvr tmp, shift = 0, 16 350d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if shift <> 16: 351d4d151390d1288f8d2df30f6dfa26a309c7334dajvr writer.writeUShort(tmp) 352d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 353d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlWrite(self, xmlWriter, font, value, name, attrs): 354d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.simpletag(name, attrs + [("value", value)]) 355d4d151390d1288f8d2df30f6dfa26a309c7334dajvr xmlWriter.newline() 356d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 357d4d151390d1288f8d2df30f6dfa26a309c7334dajvr def xmlRead(self, attrs, content, font): 358d4d151390d1288f8d2df30f6dfa26a309c7334dajvr return safeEval(attrs["value"]) 359d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 360d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 361d4d151390d1288f8d2df30f6dfa26a309c7334dajvrconverterMapping = { 362d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # type class 363d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "int16": Short, 364d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "uint16": UShort, 3659e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod "uint24": UInt24, 366a221a57ccdeefc752384778444ee737d4559a516Behdad Esfahbod "Version": Version, 367d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "Tag": Tag, 368d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "GlyphID": GlyphID, 3693ac9e63fce920ca3cf12f53e067641849c9cdbb4Behdad Esfahbod "DeciPoints": DeciPoints, 370d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "struct": Struct, 371d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "Offset": Table, 372d58c38dc3660f764b659ec13fcbc6e54c1ec2078Behdad Esfahbod "LOffset": LTable, 373d4d151390d1288f8d2df30f6dfa26a309c7334dajvr "ValueRecord": ValueRecord, 3747d130307e6e6094e40a841a3c41fc74b203b7e8eBehdad Esfahbod "DeltaValue": DeltaValue, 375d4d151390d1288f8d2df30f6dfa26a309c7334dajvr} 376d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 377