From d4d151390d1288f8d2df30f6dfa26a309c7334da Mon Sep 17 00:00:00 2001 From: jvr Date: Sat, 11 May 2002 00:59:27 +0000 Subject: [PATCH] Completely revamped OT support; this time it works and is complete. XML output is not yet as pretty as can be. git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@208 4cde692c-a291-49d1-8350-778aa11640f8 --- Lib/fontTools/ttLib/tables/B_A_S_E_.py | 5 + Lib/fontTools/ttLib/tables/G_D_E_F_.py | 5 + Lib/fontTools/ttLib/tables/G_P_O_S_.py | 385 +--------- Lib/fontTools/ttLib/tables/G_S_U_B_.py | 468 +----------- Lib/fontTools/ttLib/tables/J_S_T_F_.py | 5 + Lib/fontTools/ttLib/tables/otBase.py | 479 +++++++++++++ Lib/fontTools/ttLib/tables/otConverters.py | 265 +++++++ Lib/fontTools/ttLib/tables/otData.py | 791 +++++++++++++++++++++ Lib/fontTools/ttLib/tables/otTables.py | 111 +++ 9 files changed, 1667 insertions(+), 847 deletions(-) create mode 100644 Lib/fontTools/ttLib/tables/B_A_S_E_.py create mode 100644 Lib/fontTools/ttLib/tables/G_D_E_F_.py create mode 100644 Lib/fontTools/ttLib/tables/J_S_T_F_.py create mode 100644 Lib/fontTools/ttLib/tables/otBase.py create mode 100644 Lib/fontTools/ttLib/tables/otConverters.py create mode 100644 Lib/fontTools/ttLib/tables/otData.py create mode 100644 Lib/fontTools/ttLib/tables/otTables.py diff --git a/Lib/fontTools/ttLib/tables/B_A_S_E_.py b/Lib/fontTools/ttLib/tables/B_A_S_E_.py new file mode 100644 index 000000000..53975c883 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/B_A_S_E_.py @@ -0,0 +1,5 @@ +from otBase import BaseTTXConverter + + +class table_B_A_S_E_(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/G_D_E_F_.py b/Lib/fontTools/ttLib/tables/G_D_E_F_.py new file mode 100644 index 000000000..d763a5d74 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/G_D_E_F_.py @@ -0,0 +1,5 @@ +from otBase import BaseTTXConverter + + +class table_G_D_E_F_(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py index 82d5104d4..52f44d48e 100644 --- a/Lib/fontTools/ttLib/tables/G_P_O_S_.py +++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py @@ -1,384 +1,5 @@ -import otCommon +from otBase import BaseTTXConverter -class table_G_P_O_S_(otCommon.base_GPOS_GSUB): - - def getLookupTypeClass(self, lookupType): - return lookupTypeClasses[lookupType] - - -class SinglePos: - - def decompile(self, reader, otFont): - self.format = reader.readUShort() - if self.format == 1: - self.decompileFormat1(reader, otFont) - elif self.format == 2: - self.decompileFormat2(reader, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown SinglePos format: %d" % self.format - - def decompileFormat1(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - valueFactory = ValueRecordFactory(reader.readUShort()) - self.coverage = coverage.getGlyphNames() - self.value = valueFactory.readValueRecord(reader, otFont) - - def decompileFormat2(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - valueFactory = ValueRecordFactory(reader.readUShort()) - valueCount = reader.readUShort() - glyphNames = coverage.getGlyphNames() - self.pos = pos = {} - for i in range(valueCount): - pos[glyphNames[i]] = valueFactory.readValueRecord(reader, otFont) - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - def fromXML(self, (name, attrs, content), otFont): - raise NotImplementedError - - -class PairPos: - - def decompile(self, reader, otFont): - self.format = reader.readUShort() - if self.format == 1: - self.decompileFormat1(reader, otFont) - elif self.format == 2: - self.decompileFormat2(reader, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format - - def decompileFormat1(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphNames = coverage.glyphNames - valueFactory1 = ValueRecordFactory(reader.readUShort()) - valueFactory2 = ValueRecordFactory(reader.readUShort()) - self.pairs = pairs = {} - for i in range(reader.readUShort()): - firstGlyphName = glyphNames[i] - set = reader.readTable(PairSet, otFont, valueFactory1, valueFactory2) - pairs[firstGlyphName] = set.getValues() - - def decompileFormat2(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphNames = coverage.glyphNames - valueFactory1 = ValueRecordFactory(reader.readUShort()) - valueFactory2 = ValueRecordFactory(reader.readUShort()) - self.classDef1 = reader.readTable(otCommon.ClassDefinitionTable, otFont) - self.classDef2 = reader.readTable(otCommon.ClassDefinitionTable, otFont) - class1Count = reader.readUShort() - class2Count = reader.readUShort() - self.pairs = pairs = {} # sparse matrix - for i in range(class1Count): - row = {} - for j in range(class2Count): - value1 = valueFactory1.readValueRecord(reader, otFont) - value2 = valueFactory2.readValueRecord(reader, otFont) - if value1 or value2: - row[j] = (value1, value2) - if row: - pairs[i] = row - - def compile(self, writer, otFont): - if self.format == 1: - self.compileFormat1(writer, otFont) - elif self.format == 2: - self.compileFormat2(writer, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format - - def compileFormat1(self, writer, otFont): - pairs = self.pairs - glyphNames = pairs.keys() - coverage = otCommon.CoverageTable() - glyphNames = coverage.setGlyphNames(glyphNames, otFont) - writer.writeTable(coverage, otFont) - # dumb approach: just take the first pair and grab the value. - dummy, sample1, sample2 = pairs[pairs.keys()[0]][0] - valueFormat1 = valueFormat2 = 0 - if sample1: - valueFormat1 = sample1.getFormat() - if sample2: - valueFormat2 = sample2.getFormat() - writer.writeUShort(valueFormat1) - writer.writeUShort(valueFormat2) - - valueFactory1 = ValueRecordFactory(valueFormat1) - valueFactory2 = ValueRecordFactory(valueFormat2) - - writer.writeUShort(len(pairs)) - for glyphName in glyphNames: - set = PairSet(valueFactory1, valueFactory2) - set.setValues(pairs[glyphName]) - writer.writeTable(set, otFont) - - def compileFormat2(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - if self.format == 1: - self.toXMLFormat1(xmlWriter, otFont) - elif self.format == 2: - self.toXMLFormat2(xmlWriter, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format - - def toXMLFormat1(self, xmlWriter, otFont): - pairs = self.pairs.items() - pairs.sort() - for firstGlyph, secondGlyphs in pairs: - for secondGlyph, value1, value2 in secondGlyphs: - xmlWriter.begintag("Pair", pair=firstGlyph+","+secondGlyph) - if value1: - value1.toXML(xmlWriter, otFont) - else: - xmlWriter.simpletag("Value") - if value2: - value2.toXML(xmlWriter, otFont) - #else: # the second value can be omitted - # xmlWriter.simpletag("Value") - xmlWriter.endtag("Pair") - xmlWriter.newline() - - def toXMLFormat2(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - def fromXML(self, (name, attrs, content), otFont): - raise NotImplementedError - - -class PairSet: - - def __init__(self, valueFactory1=None, valueFactory2=None): - self.valueFactory1 = valueFactory1 - self.valueFactory2 = valueFactory2 - - def getValues(self): - return self.values - - def setValues(self, values): - self.values = values - - def decompile(self, reader, otFont): - pairValueCount = reader.readUShort() - self.values = values = [] - for j in range(pairValueCount): - secondGlyphID = reader.readUShort() - secondGlyphName = otFont.getGlyphName(secondGlyphID) - value1 = self.valueFactory1.readValueRecord(reader, otFont) - value2 = self.valueFactory2.readValueRecord(reader, otFont) - values.append((secondGlyphName, value1, value2)) - - def compile(self, writer, otFont): - values = self.values - writer.writeUShort(len(values)) - for secondGlyphName, value1, value2 in values: - writer.writeUShort(otFont.getGlyphID(secondGlyphName)) - self.valueFactory1.writeValuerecord(value1, writer, otFont) - self.valueFactory2.writeValuerecord(value2, writer, otFont) - - -# -# ------------------ -# - -class CursivePos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class MarkBasePos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class MarkLigPos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class MarkMarkPos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class ContextPos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class ChainContextPos: - - def decompile(self, reader, otFont): - xxx - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -valueRecordFormat = [ -# Mask Name isDevice struct format char - (0x0001, "XPlacement", 0, "h"), - (0x0002, "YPlacement", 0, "h"), - (0x0004, "XAdvance", 0, "h"), - (0x0008, "YAdvance", 0, "h"), - (0x0010, "XPlaDevice", 1, "H"), - (0x0020, "YPlaDevice", 1, "H"), - (0x0040, "XAdvDevice", 1, "H"), - (0x0080, "YAdvDevice", 1, "H"), -# reserved: - (0x0100, "Reserved1", 0, "H"), - (0x0200, "Reserved2", 0, "H"), - (0x0400, "Reserved3", 0, "H"), - (0x0800, "Reserved4", 0, "H"), - (0x1000, "Reserved5", 0, "H"), - (0x2000, "Reserved6", 0, "H"), - (0x4000, "Reserved7", 0, "H"), - (0x8000, "Reserved8", 0, "H"), -] - -valueRecordFormatDict = {} -for mask, name, isDevice, format in valueRecordFormat: - valueRecordFormatDict[name] = mask, isDevice, format - - -class ValueRecordFactory: - - def __init__(self, valueFormat): - format = ">" - names = [] - for mask, name, isDevice, formatChar in valueRecordFormat: - if valueFormat & mask: - names.append((name, isDevice)) - format = format + formatChar - self.names, self.format = names, format - self.size = 2 * len(names) - - def readValueRecord(self, reader, otFont): - names = self.names - if not names: - return None - values = reader.readStruct(self.format, self.size) - values = map(int, values) - valueRecord = ValueRecord() - items = map(None, names, values) - for (name, isDevice), value in items: - if isDevice: - if value: - device = otCommon.DeviceTable() - device.decompile(reader.getSubString(value), otFont) - else: - device = None - setattr(valueRecord, name, device) - else: - setattr(valueRecord, name, value) - return valueRecord - - def writeValuerecord(self, valueRecord, writer, otFont): - values = [] - for (name, isDevice) in self.names: - if isDevice: - raise NotImplementedError - else: - values.append(valueRecord.__dict__.get(name, 0)) - writer.writeStruct(self.format, tuple(values)) - - -class ValueRecord: - # see ValueRecordFactory - - def getFormat(self): - format = 0 - for name in self.__dict__.keys(): - format = format | valueRecordFormatDict[name][0] - return format - - def toXML(self, xmlWriter, otFont): - simpleItems = [] - for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values - if hasattr(self, name): - simpleItems.append((name, getattr(self, name))) - deviceItems = [] - for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records - if hasattr(self, name): - deviceItems.append((name, getattr(self, name))) - if deviceItems: - xmlWriter.begintag("Value", simpleItems) - xmlWriter.newline() - for name, deviceRecord in deviceItems: - xxx - xmlWriter.endtag("Value") - else: - xmlWriter.simpletag("Value", simpleItems) - - def __repr__(self): - return "" - - -lookupTypeClasses = { - 1: SinglePos, - 2: PairPos, - 3: CursivePos, - 4: MarkBasePos, - 5: MarkLigPos, - 6: MarkMarkPos, - 7: ContextPos, - 8: ChainContextPos, -} - +class table_G_P_O_S_(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py index 70e6eafd4..a523c595f 100644 --- a/Lib/fontTools/ttLib/tables/G_S_U_B_.py +++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py @@ -1,467 +1,5 @@ -import otCommon +from otBase import BaseTTXConverter -class table_G_S_U_B_(otCommon.base_GPOS_GSUB): - - def getLookupTypeClass(self, lookupType): - return lookupTypeClasses[lookupType] - - -class SingleSubst: - - def decompile(self, reader, otFont): - self.format = reader.readUShort() - if self.format == 1: - self.decompileFormat1(reader, otFont) - elif self.format == 2: - self.decompileFormat2(reader, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown SingleSub format: %d" % self.format - - def decompileFormat1(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphIDs = coverage.getGlyphIDs() - glyphNames = coverage.getGlyphNames() - self.substitutions = substitutions = {} - deltaGlyphID = reader.readShort() - for i in range(len(glyphIDs)): - input = glyphNames[i] - output = otFont.getGlyphName(glyphIDs[i] + deltaGlyphID) - substitutions[input] = output - - def decompileFormat2(self, reader, otFont): - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphNames = coverage.getGlyphNames() - glyphCount = reader.readUShort() - self.substitutions = substitutions = {} - for i in range(glyphCount): - glyphID = reader.readUShort() - output = otFont.getGlyphName(glyphID) - input = glyphNames[i] - substitutions[input] = output - - def compile(self, writer, otFont): - writer.writeUShort(self.format) - if self.format == 1: - self.compileFormat1(writer, otFont) - elif self.format == 2: - self.compileFormat2(writer, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown SingleSub format: %d" % self.format - - def compileFormat1(self, writer, otFont): - xxx - - def compileFormat2(self, writer, otFont): - substitutions = self.substitutions - coverage = otCommon.CoverageTable() - glyphNames = substitutions.keys() - glyphNames = coverage.setGlyphNames(glyphNames, otFont) - - writer.writeTable(coverage, otFont) - writer.writeUShort(len(substitutions)) - - for i in range(len(substitutions)): - glyphName = glyphNames[i] - output = substitutions[glyphName] - writer.writeUShort(otFont.getGlyphID(output)) - - def toXML(self, xmlWriter, otFont): - substitutions = self.substitutions.items() - substitutions.sort() - for input, output in substitutions: - xmlWriter.simpletag("Subst", [("in", input), ("out", output)]) - xmlWriter.newline() - - def fromXML(self, (name, attrs, content), otFont): - raise NotImplementedError - - -class MultipleSubst: - - def decompile(self, reader, otFont): - format = reader.readUShort() - if format <> 1: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown MultipleSubst format: %d" % format - glyphNames = reader.readTable(otCommon.CoverageTable, otFont).getGlyphNames() - sequenceCount = reader.readUShort() - self.substitutions = substitutions = {} - for i in range(sequenceCount): - sequence = reader.readTable(Sequence, otFont) - substitutions[glyphNames[i]] = sequence.getGlyphs() - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - import string - items = self.substitutions.items() - items.sort() - for input, output in items: - xmlWriter.simpletag("Subst", [("in", input), ("out", string.join(output, ","))]) - xmlWriter.newline() - - -class Sequence: - - def getGlyphs(self): - return self.glyphs - - def decompile(self, reader, otFont): - self.glyphs = [] - for i in range(reader.readUShort()): - self.glyphs.append(otFont.getGlyphName(reader.readUShort())) - - def compile(self, writer, otFont): - xxx - - -class AlternateSubst: - - def decompile(self, reader, otFont): - format = reader.readUShort() - if format <> 1: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown AlternateSubst format: %d" % format - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphNames = coverage.getGlyphNames() - alternateSetCount = reader.readUShort() - self.alternateSets = alternateSets = {} - for i in range(alternateSetCount): - set = reader.readTable(AlternateSet, otFont) - alternateSets[glyphNames[i]] = set.getGlyphs() - - def compile(self, writer, otFont): - writer.writeUShort(1) # format = 1 - alternateSets = self.alternateSets - alternateSetCount = len(alternateSets) - glyphNames = alternateSets.keys() - coverage = otCommon.CoverageTable() - glyphNames = coverage.setGlyphNames(glyphNames, otFont) - - writer.writeTable(coverage, otFont) - writer.writeUShort(alternateSetCount) - - for i in range(alternateSetCount): - glyphName = glyphNames[i] - set = AlternateSet() - set.setGlyphs(alternateSets[glyphName]) - writer.writeTable(set, otFont) - - def toXML(self, xmlWriter, otFont): - alternates = self.alternateSets.items() - alternates.sort() - for input, substList in alternates: - xmlWriter.begintag("AlternateSet", [("in", input)]) - xmlWriter.newline() - for output in substList: - xmlWriter.simpletag("Subst", out=output) - xmlWriter.newline() - xmlWriter.endtag("AlternateSet") - xmlWriter.newline() - - -class AlternateSet: - - def getGlyphs(self): - return self.glyphs - - def setGlyphs(self, glyphs): - self.glyphs = glyphs - - def decompile(self, reader, otFont): - glyphCount = reader.readUShort() - glyphIDs = reader.readUShortArray(glyphCount) - self.glyphs = map(otFont.getGlyphName, glyphIDs) - - def compile(self, writer, otFont): - glyphs = self.glyphs - writer.writeUShort(len(glyphs)) - glyphIDs = map(otFont.getGlyphID, glyphs) - writer.writeUShortArray(glyphIDs) - - -class LigatureSubst: - - def decompile(self, reader, otFont): - self.format = reader.readUShort() - if self.format <> 1: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown LigatureSubst format: %d" % self.format - coverage = reader.readTable(otCommon.CoverageTable, otFont) - glyphNames = coverage.getGlyphNames() - ligSetCount = reader.readUShort() - self.ligatures = ligatures = [] - for i in range(ligSetCount): - firstGlyph = glyphNames[i] - ligSet = reader.readTable(LigatureSet, otFont) - for components, ligatureGlyph in ligSet.getLigatures(): - ligatures.append((((firstGlyph,) + tuple(components)), ligatureGlyph)) - - def compile(self, writer, otFont): - lastGlyph = None - sets = {} - currentSet = None - for input, output in self.ligatures: - firstGlyph = input[0] - if firstGlyph <> lastGlyph: - assert not sets.has_key(firstGlyph) - currentSet = LigatureSet() - sets[firstGlyph] = currentSet - lastGlyph = firstGlyph - currentSet.appendLigature(input[1:], output) - - glyphNames = sets.keys() - coverage = otCommon.CoverageTable() - glyphNames = coverage.setGlyphNames(glyphNames, otFont) - - writer.writeUShort(self.format) - writer.writeTable(coverage, otFont) - writer.writeUShort(len(sets)) - - for i in range(len(glyphNames)): - set = sets[glyphNames[i]] - writer.writeTable(set, otFont) - - def toXML(self, xmlWriter, otFont): - import string - for input, output in self.ligatures: - xmlWriter.simpletag("Subst", [("in", string.join(input, ",")), ("out", output)]) - xmlWriter.newline() - - -class LigatureSet: - - def __init__(self): - self.ligatures = [] - - def getLigatures(self): - return self.ligatures - - def appendLigature(self, components, ligatureGlyph): - self.ligatures.append((components, ligatureGlyph)) - - def decompile(self, reader, otFont): - ligatureCount = reader.readUShort() - self.ligatures = ligatures = [] - for i in range(ligatureCount): - lig = reader.readTable(Ligature, otFont) - ligatures.append(lig.get()) - - def compile(self, writer, otFont): - writer.writeUShort(len(self.ligatures)) - - for components, output in self.ligatures: - lig = Ligature() - lig.set(components, output) - writer.writeTable(lig, otFont) - - -class Ligature: - - def get(self): - return self.components, self.ligatureGlyph - - def set(self, components, ligatureGlyph): - self.components, self.ligatureGlyph = components, ligatureGlyph - - def decompile(self, reader, otFont): - self.ligatureGlyph = otFont.getGlyphName(reader.readUShort()) - compCount = reader.readUShort() - self.components = components = [] - for i in range(compCount-1): - components.append(otFont.getGlyphName(reader.readUShort())) - - def compile(self, writer, otFont): - ligGlyphID = otFont.getGlyphID(self.ligatureGlyph) - writer.writeUShort(ligGlyphID) - writer.writeUShort(len(self.components) + 1) - for compo in self.components: - writer.writeUShort(otFont.getGlyphID(compo)) - - -class ContextSubst: - - def decompile(self, reader, otFont): - format = reader.readUShort() - if format == 1: - self.decompileFormat1(reader, otFont) - elif format == 2: - self.decompileFormat2(reader, otFont) - elif format == 3: - self.decompileFormat3(reader, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown ContextSubst format: %d" % format - - def decompileFormat1(self, reader, otFont): - xxx - - def decompileFormat2(self, reader, otFont): - xxx - - def decompileFormat3(self, reader, otFont): - glyphCount = reader.readUShort() - substCount = reader.readUShort() - coverage = [] - for i in range(glyphCount): - coverage.append(reader.readTable(otCommon.CoverageTable, otFont)) - self.substitutions = substitutions = [] - for i in range(substCount): - lookupRecord = SubstLookupRecord() - lookupRecord.decompile(reader, otFont) - substitutions.append((coverage[i].getGlyphNames(), lookupRecord)) - - def compile(self, writer, otFont): - xxx - - def toXML(self, xmlWriter, otFont): - xmlWriter.comment("NotImplemented") - xmlWriter.newline() - - -class ChainContextSubst: - - def decompile(self, reader, otFont): - self.format = reader.readUShort() - if self.format == 1: - self.decompileFormat1(reader, otFont) - elif self.format == 2: - self.decompileFormat2(reader, otFont) - elif self.format == 3: - self.decompileFormat3(reader, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format - - def decompileFormat1(self, reader, otFont): - XXX - - def decompileFormat2(self, reader, otFont): - XXX - - def decompileFormat3(self, reader, otFont): - backtrackGlyphCount = reader.readUShort() - backtrackCoverage = reader.readTableArray(backtrackGlyphCount, otCommon.CoverageTable, otFont) - self.backtrack = otCommon.unpackCoverageArray(backtrackCoverage) - - inputGlyphCount = reader.readUShort() - inputCoverage = reader.readTableArray(inputGlyphCount, otCommon.CoverageTable, otFont) - self.input = otCommon.unpackCoverageArray(inputCoverage) - - lookaheadGlyphCount = reader.readUShort() - lookaheadCoverage = reader.readTableArray(lookaheadGlyphCount, otCommon.CoverageTable, otFont) - self.lookahead = otCommon.unpackCoverageArray(lookaheadCoverage) - - substCount = reader.readUShort() - self.substitutions = [] - for i in range(substCount): - lookupRecord = SubstLookupRecord() - lookupRecord.decompile(reader, otFont) - self.substitutions.append(lookupRecord) - - # print "XXX", [len(x) for x in self.backtrack], self.substitutions - - def compile(self, writer, otFont): - writer.writeUShort(self.format) - if self.format == 1: - self.compileFormat1(writer, otFont) - elif self.format == 2: - self.compileFormat2(writer, otFont) - elif self.format == 3: - self.compileFormat3(writer, otFont) - else: - from fontTools import ttLib - raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format - - def compileFormat1(self, writer, otFont): - XXX - - def compileFormat2(self, writer, otFont): - XXX - - def compileFormat3(self, writer, otFont): - writer.writeUShort(len(self.backtrack)) - backtrack = otCommon.buildCoverageArray(self.backtrack, otFont) - writer.writeTableArray(backtrack, otFont) - - writer.writeUShort(len(self.input)) - input = otCommon.buildCoverageArray(self.input, otFont) - writer.writeTableArray(input, otFont) - - writer.writeUShort(len(self.lookahead)) - lookahead = otCommon.buildCoverageArray(self.lookahead, otFont) - writer.writeTableArray(lookahead, otFont) - - writer.writeUShort(len(self.substitutions)) - for lookupRecord in self.substitutions: - lookupRecord.compile(writer, otFont) - - def toXML(self, xmlWriter, otFont): - # XXXX this is for format 3?! - xmlWriter.begintag("Backtrack") - xmlWriter.newline() - for g in self.backtrack: - xmlWriter.simpletag("glyph", values=",".join(g)) - xmlWriter.newline() - xmlWriter.endtag("Backtrack") - xmlWriter.newline() - - xmlWriter.begintag("Input") - xmlWriter.newline() - for g in self.input: - xmlWriter.simpletag("glyph", values=",".join(g)) - xmlWriter.newline() - xmlWriter.endtag("Input") - xmlWriter.newline() - - xmlWriter.begintag("Lookahead") - xmlWriter.newline() - for g in self.lookahead: - xmlWriter.simpletag("glyph", values=",".join(g)) - xmlWriter.newline() - xmlWriter.endtag("Lookahead") - xmlWriter.newline() - - xmlWriter.begintag("Subst") - xmlWriter.newline() - for subst in self.substitutions: - subst.toXML(xmlWriter, otFont) - xmlWriter.newline() - xmlWriter.endtag("Subst") - xmlWriter.newline() - - -lookupTypeClasses = { - 1: SingleSubst, - 2: MultipleSubst, - 3: AlternateSubst, - 4: LigatureSubst, - 5: ContextSubst, - 6: ChainContextSubst, -# 7: ExtensionSubst, # ugh... -} - - -# -# Shared classes -# - -class SubstLookupRecord: - - def decompile(self, reader, otFont): - self.sequenceIndex = reader.readUShort() - self.lookupListIndex = reader.readUShort() - - def compile(self, writer, otFont): - writer.writeUShort(self.sequenceIndex) - writer.writeUShort(self.lookupListIndex) - - def toXML(self, xmlWriter, otFont): - xmlWriter.simpletag("SubstLookupRecord", - [('sequenceIndex', self.sequenceIndex), - ('lookupListIndex', self.lookupListIndex)]) - +class table_G_S_U_B_(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/J_S_T_F_.py b/Lib/fontTools/ttLib/tables/J_S_T_F_.py new file mode 100644 index 000000000..8ff395efb --- /dev/null +++ b/Lib/fontTools/ttLib/tables/J_S_T_F_.py @@ -0,0 +1,5 @@ +from otBase import BaseTTXConverter + + +class table_J_S_T_F_(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py new file mode 100644 index 000000000..85ffcd7f9 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/otBase.py @@ -0,0 +1,479 @@ +from DefaultTable import DefaultTable +import otData +import struct +from types import TupleType + + +class BaseTTXConverter(DefaultTable): + + def decompile(self, data, font): + import otTables + reader = OTTableReader(data, self.tableTag) + tableClass = getattr(otTables, self.tableTag) + self.table = tableClass() + self.table.decompile(reader, font) + + def compile(self, font): + writer = OTTableWriter(self.tableTag) + self.table.compile(writer, font) + return writer.getData() + + def toXML(self, writer, font): + self.table.toXML2(writer, font) + + def fromXML(self, (name, attrs, content), font): + import otTables + if not hasattr(self, "table"): + tableClass = getattr(otTables, self.tableTag) + self.table = tableClass() + self.table.fromXML((name, attrs, content), font) + + +class OTTableReader: + + def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None): + self.data = data + self.offset = offset + self.pos = offset + self.tableType = tableType + if valueFormat is None: + valueFormat = (ValueRecordFactory(), ValueRecordFactory()) + self.valueFormat = valueFormat + self.cachingStats = cachingStats + + def getSubReader(self, offset): + offset = self.offset + offset + if self.cachingStats is not None: + try: + self.cachingStats[offset] = self.cachingStats[offset] + 1 + except KeyError: + self.cachingStats[offset] = 1 + + subReader = self.__class__(self.data, self.tableType, offset, + self.valueFormat, self.cachingStats) + return subReader + + def readUShort(self): + pos = self.pos + newpos = pos + 2 + value = struct.unpack(">H", self.data[pos:newpos])[0] + self.pos = newpos + return value + + def readShort(self): + pos = self.pos + newpos = pos + 2 + value = struct.unpack(">h", self.data[pos:newpos])[0] + self.pos = newpos + return value + + def readLong(self): + pos = self.pos + newpos = pos + 4 + value = struct.unpack(">l", self.data[pos:newpos])[0] + self.pos = newpos + return value + + def readTag(self): + pos = self.pos + newpos = pos + 4 + value = self.data[pos:newpos] + assert len(value) == 4 + self.pos = newpos + return value + + def readStruct(self, format, size=None): + if size is None: + size = struct.calcsize(format) + else: + assert size == struct.calcsize(format) + pos = self.pos + newpos = pos + size + values = struct.unpack(format, self.data[pos:newpos]) + self.pos = newpos + return values + + def setValueFormat(self, format, which): + self.valueFormat[which].setFormat(format) + + def readValueRecord(self, font, which): + return self.valueFormat[which].readValueRecord(self, font) + + +class OTTableWriter: + + def __init__(self, tableType, valueFormat=None): + self.items = [] + self.tableType = tableType + if valueFormat is None: + valueFormat = ValueRecordFactory(), ValueRecordFactory() + self.valueFormat = valueFormat + + def getSubWriter(self): + return self.__class__(self.tableType, self.valueFormat) + + def getData(self): + items = list(self.items) + offset = 0 + for item in items: + if hasattr(item, "getData") or hasattr(item, "getCount"): + offset = offset + 2 # sizeof(UShort) + else: + offset = offset + len(item) + subTables = [] + cache = {} + for i in range(len(items)): + item = items[i] + if hasattr(item, "getData"): + subTableData = item.getData() + if cache.has_key(subTableData): + items[i] = packUShort(cache[subTableData]) + else: + items[i] = packUShort(offset) + subTables.append(subTableData) + cache[subTableData] = offset + offset = offset + len(subTableData) + elif hasattr(item, "getCount"): + items[i] = item.getCount() + return "".join(items + subTables) + + def writeUShort(self, value): + assert 0 <= value < 0x10000 + self.items.append(struct.pack(">H", value)) + + def writeShort(self, value): + self.items.append(struct.pack(">h", value)) + + def writeLong(self, value): + self.items.append(struct.pack(">l", value)) + + def writeTag(self, tag): + assert len(tag) == 4 + self.items.append(tag) + + def writeSubTable(self, subWriter): + self.items.append(subWriter) + + def writeCountReference(self, table, name): + self.items.append(CountReference(table, name)) + + def writeStruct(self, format, values): + data = apply(struct.pack, (format,) + values) + self.items.append(data) + + def setValueFormat(self, format, which): + self.valueFormat[which].setFormat(format) + + def writeValueRecord(self, value, font, which): + return self.valueFormat[which].writeValueRecord(self, font, value) + + +class CountReference: + def __init__(self, table, name): + self.table = table + self.name = name + def getCount(self): + return packUShort(self.table[self.name]) + + +def packUShort(offset): + assert 0 <= offset < 0x10000 + return struct.pack(">H", offset) + + + +class BaseTable: + + def getConverters(self): + return self.converters + + def getConverterByName(self, name): + return self.convertersByName[name] + + def decompile(self, reader, font, tableStack=None): + if tableStack is None: + tableStack = TableStack() + table = {} + self.__rawTable = table # for debugging + tableStack.push(table) + for conv in self.getConverters(): + if conv.name == "SubTable": + conv = conv.getConverter(reader.tableType, + table["LookupType"]) + if conv.repeat: + l = [] + for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset): + l.append(conv.read(reader, font, tableStack)) + table[conv.name] = l + else: + table[conv.name] = conv.read(reader, font, tableStack) + tableStack.pop() + self.postRead(table, font) + del self.__rawTable # succeeded, get rid of debugging info + + def compile(self, writer, font, tableStack=None): + if tableStack is None: + tableStack = TableStack() + table = self.preWrite(font) + tableStack.push(table) + for conv in self.getConverters(): + value = table.get(conv.name) + if conv.repeat: + if value is None: + value = [] # XXXXXX + tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset) + for item in value: + conv.write(writer, font, tableStack, item) + elif conv.isCount: + # Special-case Count values. + # Assumption: a Count field will *always* precede + # the actual array. + # We need a default value, as it may be set later by a nested + # table. TableStack.storeValue() will then find it here. + table[conv.name] = None + # We add a reference: by the time the data is assembled + # the Count value will be filled in. + writer.writeCountReference(table, conv.name) + else: + conv.write(writer, font, tableStack, value) + tableStack.pop() + + def postRead(self, table, font): + self.__dict__.update(table) + + def preWrite(self, font): + return self.__dict__.copy() + + def toXML(self, xmlWriter, font, attrs=None): + tableName = self.__class__.__name__ + if attrs is None: + attrs = [] + if hasattr(self, "Format"): + attrs = attrs + [("Format", str(self.Format))] + xmlWriter.begintag(tableName, attrs) + xmlWriter.newline() + self.toXML2(xmlWriter, font) + xmlWriter.endtag(tableName) + xmlWriter.newline() + + def toXML2(self, xmlWriter, font): + # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB). + # This is because in TTX our parent writes our main tag, and in otBase.py we + # do it ourselves. I think I'm getting schizophrenic... + for conv in self.getConverters(): + value = getattr(self, conv.name) + if not conv.repeat: + conv.xmlWrite(xmlWriter, font, value, conv.name, []) + else: + for i in range(len(value)): + item = value[i] + conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)]) + + def fromXML(self, (name, attrs, content), font): + try: + conv = self.getConverterByName(name) + except KeyError: + print self, name, attrs, content + raise # XXX on KeyError, raise nice error + value = conv.xmlRead(attrs, content, font) + name = conv.name + if conv.repeat: + try: + seq = getattr(self, name) + except AttributeError: + seq = [] + setattr(self, name, seq) + seq.append(value) + else: + setattr(self, name, value) + + def __cmp__(self, other): + # this is only for debugging, so it's ok to barf + # when 'other' has no __dict__ or __class__ + rv = cmp(self.__class__, other.__class__) + if not rv: + rv = cmp(self.__dict__, other.__dict__) + return rv + else: + return rv + + +class FormatSwitchingBaseTable(BaseTable): + + def getConverters(self): + return self.converters[self.Format] + + def getConverterByName(self, name): + return self.convertersByName[self.Format][name] + + def decompile(self, reader, font, tableStack=None): + self.Format = reader.readUShort() + assert self.Format <> 0, (self, reader.pos, len(reader.data)) + BaseTable.decompile(self, reader, font, tableStack) + + def compile(self, writer, font, tableStack=None): + writer.writeUShort(self.Format) + BaseTable.compile(self, writer, font, tableStack) + + +valueRecordFormat = [ +# Mask Name isDevice signed + (0x0001, "XPlacement", 0, 1), + (0x0002, "YPlacement", 0, 1), + (0x0004, "XAdvance", 0, 1), + (0x0008, "YAdvance", 0, 1), + (0x0010, "XPlaDevice", 1, 0), + (0x0020, "YPlaDevice", 1, 0), + (0x0040, "XAdvDevice", 1, 0), + (0x0080, "YAdvDevice", 1, 0), +# reserved: + (0x0100, "Reserved1", 0, 0), + (0x0200, "Reserved2", 0, 0), + (0x0400, "Reserved3", 0, 0), + (0x0800, "Reserved4", 0, 0), + (0x1000, "Reserved5", 0, 0), + (0x2000, "Reserved6", 0, 0), + (0x4000, "Reserved7", 0, 0), + (0x8000, "Reserved8", 0, 0), +] + +def _buildDict(): + d = {} + for mask, name, isDevice, signed in valueRecordFormat: + d[name] = mask, isDevice, signed + return d + +valueRecordFormatDict = _buildDict() + + +class ValueRecordFactory: + + def setFormat(self, valueFormat): + format = [] + for mask, name, isDevice, signed in valueRecordFormat: + if valueFormat & mask: + format.append((name, isDevice, signed)) + self.format = format + + def readValueRecord(self, reader, font): + format = self.format + if not format: + return None + valueRecord = ValueRecord() + for name, isDevice, signed in format: + if signed: + value = reader.readShort() + else: + value = reader.readUShort() + if isDevice: + if value: + import otTables + subReader = reader.getSubReader(value) + value = getattr(otTables, name)() + value.decompile(subReader, font) + else: + value = None + setattr(valueRecord, name, value) + return valueRecord + + def writeValueRecord(self, writer, font, valueRecord): + for name, isDevice, signed in self.format: + value = getattr(valueRecord, name, 0) + if isDevice: + if value: + subWriter = writer.getSubWriter() + writer.writeSubTable(subWriter) + value.compile(subWriter, font) + else: + writer.writeUShort(0) + elif signed: + writer.writeShort(value) + else: + writer.writeUShort(value) + + +class ValueRecord: + + # see ValueRecordFactory + + def getFormat(self): + format = 0 + for name in self.__dict__.keys(): + format = format | valueRecordFormatDict[name][0] + return format + + def toXML(self, xmlWriter, font, valueName, attrs=None): + if attrs is None: + simpleItems = [] + else: + simpleItems = list(attrs) + for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values + if hasattr(self, name): + simpleItems.append((name, getattr(self, name))) + deviceItems = [] + for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records + if hasattr(self, name): + device = getattr(self, name) + if device is not None: + deviceItems.append((name, device)) + if deviceItems: + xmlWriter.begintag(valueName, simpleItems) + xmlWriter.newline() + for name, deviceRecord in deviceItems: + if deviceRecord is not None: + deviceRecord.toXML(xmlWriter, font) + xmlWriter.endtag(valueName) + xmlWriter.newline() + else: + xmlWriter.simpletag(valueName, simpleItems) + xmlWriter.newline() + + def fromXML(self, (name, attrs, content), font): + import otTables + for k, v in attrs.items(): + setattr(self, k, int(v)) + for element in content: + if type(element) <> TupleType: + continue + name, attrs, content = element + value = getattr(otTables, name)() + for elem2 in content: + if type(elem2) <> TupleType: + continue + value.fromXML(elem2, font) + setattr(self, name, value) + + def __cmp__(self, other): + # this is only for debugging, so it's ok to barf + # when 'other' has no __dict__ or __class__ + rv = cmp(self.__class__, other.__class__) + if not rv: + rv = cmp(self.__dict__, other.__dict__) + return rv + else: + return rv + + +class TableStack: + def __init__(self): + self.stack = [] + def push(self, table): + self.stack.insert(0, table) + def pop(self): + self.stack.pop(0) + def getTop(self): + return self.stack[0] + def getValue(self, name): + return self.__findTable(name)[name] + def storeValue(self, name, value): + table = self.__findTable(name) + if table[name] is None: + table[name] = value + else: + assert table[name] == value, (table[name], value) + def __findTable(self, name): + for table in self.stack: + if table.has_key(name): + return table + raise KeyError, name + diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py new file mode 100644 index 000000000..ab18b1077 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/otConverters.py @@ -0,0 +1,265 @@ +from types import TupleType +from fontTools.misc.textTools import safeEval + + +def buildConverterList(tableSpec, tableNamespace): + converters = [] + convertersByName = {} + for tp, name, repeat, repeatOffset, descr in tableSpec: + if name.startswith("ValueFormat"): + assert tp == "uint16" + converterClass = ValueFormat + elif name == "DeltaValue": + assert tp == "uint16" + converterClass = DeltaValue + elif name.endswith("Count"): + assert tp == "uint16" + converterClass = Count + elif name == "SubTable": + converterClass = SubTable + else: + converterClass = converterMapping[tp] + tableClass = tableNamespace.get(name) + conv = converterClass(name, repeat, repeatOffset, tableClass) + if name == "SubTable": + conv.lookupTypes = tableNamespace['lookupTypes'] + # also create reverse mapping + for t in conv.lookupTypes.values(): + for cls in t.values(): + convertersByName[cls.__name__] = Table("SubTable", repeat, repeatOffset, cls) + converters.append(conv) + assert not convertersByName.has_key(name) + convertersByName[name] = conv + return converters, convertersByName + + +class BaseConverter: + + def __init__(self, name, repeat, repeatOffset, tableClass): + self.name = name + self.repeat = repeat + self.repeatOffset = repeatOffset + self.tableClass = tableClass + self.isCount = name.endswith("Count") + + def read(self, reader, font, tableStack): + raise NotImplementedError, self + + def write(self, writer, font, tableStack, value): + raise NotImplementedError, self + + def xmlWrite(self, xmlWriter, font, value, name, attrs): + raise NotImplementedError, self + + def xmlRead(self, attrs, content, font): + raise NotImplementedError, self + + +class SimpleValue(BaseConverter): + def xmlWrite(self, xmlWriter, font, value, name, attrs): + xmlWriter.simpletag(name, attrs + [("value", value)]) + xmlWriter.newline() + def xmlRead(self, attrs, content, font): + return attrs["value"] + +class IntValue(SimpleValue): + def xmlRead(self, attrs, content, font): + return int(attrs["value"]) + +class Long(IntValue): + def read(self, reader, font, tableStack): + return reader.readLong() + def write(self, writer, font, tableStack, value): + writer.writeLong(value) + +class Short(IntValue): + def read(self, reader, font, tableStack): + return reader.readShort() + def write(self, writer, font, tableStack, value): + writer.writeShort(value) + +class UShort(IntValue): + def read(self, reader, font, tableStack): + return reader.readUShort() + def write(self, writer, font, tableStack, value): + writer.writeUShort(value) + +class Count(Short): + def xmlWrite(self, xmlWriter, font, value, name, attrs): + xmlWriter.comment("%s=%s" % (name, value)) + xmlWriter.newline() + +class Tag(SimpleValue): + def read(self, reader, font, tableStack): + return reader.readTag() + def write(self, writer, font, tableStack, value): + writer.writeTag(value) + +class GlyphID(SimpleValue): + def read(self, reader, font, tableStack): + return font.getGlyphName(reader.readUShort()) + def write(self, writer, font, tableStack, value): + writer.writeUShort(font.getGlyphID(value)) + +class Struct(BaseConverter): + + def read(self, reader, font, tableStack): + table = self.tableClass() + table.decompile(reader, font, tableStack) + return table + + def write(self, writer, font, tableStack, value): + value.compile(writer, font, tableStack) + + def xmlWrite(self, xmlWriter, font, value, name, attrs): + if value is None: + pass # NULL table, ignore + else: + value.toXML(xmlWriter, font, attrs) + + def xmlRead(self, attrs, content, font): + table = self.tableClass() + Format = attrs.get("Format") + if Format is not None: + table.Format = int(Format) + for element in content: + if type(element) <> TupleType: + continue + name, attrs, content = element + table.fromXML((name, attrs, content), font) + return table + + +class Table(Struct): + + def read(self, reader, font, tableStack): + offset = reader.readUShort() + if offset == 0: + return None + if offset <= 3: + # XXX hack to work around buggy pala.ttf + print "*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \ + % (offset, self.tableClass.__name__) + return None + subReader = reader.getSubReader(offset) + table = self.tableClass() + table.decompile(subReader, font, tableStack) + return table + + def write(self, writer, font, tableStack, value): + if value is None: + writer.writeUShort(0) + else: + subWriter = writer.getSubWriter() + writer.writeSubTable(subWriter) + value.compile(subWriter, font, tableStack) + + +class SubTable(Table): + def getConverter(self, tableType, lookupType): + lookupTypes = self.lookupTypes[tableType] + tableClass = lookupTypes[lookupType] + return Table(self.name, self.repeat, self.repeatOffset, tableClass) + + +class ValueFormat(IntValue): + def __init__(self, name, repeat, repeatOffset, tableClass): + BaseConverter.__init__(self, name, repeat, repeatOffset, tableClass) + self.which = name[-1] == "2" + def read(self, reader, font, tableStack): + format = reader.readUShort() + reader.setValueFormat(format, self.which) + return format + def write(self, writer, font, tableStack, format): + writer.writeUShort(format) + writer.setValueFormat(format, self.which) + +class ValueRecord(ValueFormat): + def read(self, reader, font, tableStack): + return reader.readValueRecord(font, self.which) + def write(self, writer, font, tableStack, value): + writer.writeValueRecord(value, font, self.which) + def xmlWrite(self, xmlWriter, font, value, name, attrs): + if value is None: + pass # NULL table, ignore + else: + value.toXML(xmlWriter, font, self.name, attrs) + def xmlRead(self, attrs, content, font): + from otBase import ValueRecord + value = ValueRecord() + value.fromXML((None, attrs, content), font) + return value + + +class DeltaValue(BaseConverter): + + def read(self, reader, font, tableStack): + table = tableStack.getTop() + StartSize = table["StartSize"] + EndSize = table["EndSize"] + DeltaFormat = table["DeltaFormat"] + assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat" + nItems = EndSize - StartSize + 1 + nBits = 1 << DeltaFormat + minusOffset = 1 << nBits + mask = (1 << nBits) - 1 + signMask = 1 << (nBits - 1) + + DeltaValue = [] + tmp, shift = 0, 0 + for i in range(nItems): + if shift == 0: + tmp, shift = reader.readUShort(), 16 + shift = shift - nBits + value = (tmp >> shift) & mask + if value & signMask: + value = value - minusOffset + DeltaValue.append(value) + return DeltaValue + + def write(self, writer, font, tableStack, value): + table = tableStack.getTop() + StartSize = table["StartSize"] + EndSize = table["EndSize"] + DeltaFormat = table["DeltaFormat"] + DeltaValue = table["DeltaValue"] + assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat" + nItems = EndSize - StartSize + 1 + nBits = 1 << DeltaFormat + assert len(DeltaValue) == nItems + mask = (1 << nBits) - 1 + + tmp, shift = 0, 16 + for value in DeltaValue: + shift = shift - nBits + tmp = tmp | ((value & mask) << shift) + if shift == 0: + writer.writeUShort(tmp) + tmp, shift = 0, 16 + if shift <> 16: + writer.writeUShort(tmp) + + def xmlWrite(self, xmlWriter, font, value, name, attrs): + xmlWriter.simpletag(name, attrs + [("value", value)]) + xmlWriter.newline() + + def xmlRead(self, attrs, content, font): + return safeEval(attrs["value"]) + + +converterMapping = { + # type class + "int16": Short, + "uint16": UShort, + "ULONG": Long, + "Tag": Tag, + "GlyphID": GlyphID, + "struct": Struct, + "Offset": Table, + "ValueRecord": ValueRecord, +} + +# equivalents: +converterMapping["USHORT"] = converterMapping["uint16"] +converterMapping["Fixed"] = converterMapping["fixed32"] = converterMapping["ULONG"] + diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py new file mode 100644 index 000000000..f863a6616 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -0,0 +1,791 @@ +otData = [ + + # + # common (generated from chapter2.htm) + # + + ('ScriptList', [ + ('uint16', 'ScriptCount', None, None, 'Number of ScriptRecords'), + ('struct', 'ScriptRecord', 'ScriptCount', 0, 'Array of ScriptRecords -listed alphabetically by ScriptTag'), + ]), + + ('ScriptRecord', [ + ('Tag', 'ScriptTag', None, None, '4-byte ScriptTag identifier'), + ('Offset', 'Script', None, None, 'Offset to Script table-from beginning of ScriptList'), + ]), + + ('Script', [ + ('Offset', 'DefaultLangSys', None, None, 'Offset to DefaultLangSys table-from beginning of Script table-may be NULL'), + ('uint16', 'LangSysCount', None, None, 'Number of LangSysRecords for this script-excluding the DefaultLangSys'), + ('struct', 'LangSysRecord', 'LangSysCount', 0, 'Array of LangSysRecords-listed alphabetically by LangSysTag'), + ]), + + ('LangSysRecord', [ + ('Tag', 'LangSysTag', None, None, '4-byte LangSysTag identifier'), + ('Offset', 'LangSys', None, None, 'Offset to LangSys table-from beginning of Script table'), + ]), + + ('LangSys', [ + ('Offset', 'LookupOrder', None, None, '= NULL (reserved for an offset to a reordering table)'), + ('uint16', 'ReqFeatureIndex', None, None, 'Index of a feature required for this language system- if no required features = 0xFFFF'), + ('uint16', 'FeatureCount', None, None, 'Number of FeatureIndex values for this language system-excludes the required feature'), + ('uint16', 'FeatureIndex', 'FeatureCount', 0, 'Array of indices into the FeatureList-in arbitrary order'), + ]), + + ('FeatureList', [ + ('uint16', 'FeatureCount', None, None, 'Number of FeatureRecords in this table'), + ('struct', 'FeatureRecord', 'FeatureCount', 0, 'Array of FeatureRecords-zero-based (first feature has FeatureIndex = 0)-listed alphabetically by FeatureTag'), + ]), + + ('FeatureRecord', [ + ('Tag', 'FeatureTag', None, None, '4-byte feature identification tag'), + ('Offset', 'Feature', None, None, 'Offset to Feature table-from beginning of FeatureList'), + ]), + + ('Feature', [ + ('Offset', 'FeatureParams', None, None, '= NULL (reserved for offset to FeatureParams)'), + ('uint16', 'LookupCount', None, None, 'Number of LookupList indices for this feature'), + ('uint16', 'LookupListIndex', 'LookupCount', 0, 'Array of LookupList indices for this feature -zero-based (first lookup is LookupListIndex = 0)'), + ]), + + ('LookupList', [ + ('uint16', 'LookupCount', None, None, 'Number of lookups in this table'), + ('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'), + ]), + + ('Lookup', [ + ('uint16', 'LookupType', None, None, 'Different enumerations for GSUB and GPOS'), + ('uint16', 'LookupFlag', None, None, 'Lookup qualifiers'), + ('uint16', 'SubTableCount', None, None, 'Number of SubTables for this lookup'), + ('Offset', 'SubTable', 'SubTableCount', 0, 'Array of offsets to SubTables-from beginning of Lookup table'), + ]), + + ('CoverageFormat1', [ + ('uint16', 'CoverageFormat', None, None, 'Format identifier-format = 1'), + ('uint16', 'GlyphCount', None, None, 'Number of glyphs in the GlyphArray'), + ('GlyphID', 'GlyphArray', 'GlyphCount', 0, 'Array of GlyphIDs-in numerical order'), + ]), + + ('CoverageFormat2', [ + ('uint16', 'CoverageFormat', None, None, 'Format identifier-format = 2'), + ('uint16', 'RangeCount', None, None, 'Number of RangeRecords'), + ('struct', 'RangeRecord', 'RangeCount', 0, 'Array of glyph ranges-ordered by Start GlyphID'), + ]), + + ('RangeRecord', [ + ('GlyphID', 'Start', None, None, 'First GlyphID in the range'), + ('GlyphID', 'End', None, None, 'Last GlyphID in the range'), + ('uint16', 'StartCoverageIndex', None, None, 'Coverage Index of first GlyphID in range'), + ]), + + ('ClassDefFormat1', [ + ('uint16', 'ClassFormat', None, None, 'Format identifier-format = 1'), + ('GlyphID', 'StartGlyph', None, None, 'First GlyphID of the ClassValueArray'), + ('uint16', 'GlyphCount', None, None, 'Size of the ClassValueArray'), + ('uint16', 'ClassValueArray', 'GlyphCount', 0, 'Array of Class Values-one per GlyphID'), + ]), + + ('ClassDefFormat2', [ + ('uint16', 'ClassFormat', None, None, 'Format identifier-format = 2'), + ('uint16', 'ClassRangeCount', None, None, 'Number of ClassRangeRecords'), + ('struct', 'ClassRangeRecord', 'ClassRangeCount', 0, 'Array of ClassRangeRecords-ordered by Start GlyphID'), + ]), + + ('ClassRangeRecord', [ + ('GlyphID', 'Start', None, None, 'First GlyphID in the range'), + ('GlyphID', 'End', None, None, 'Last GlyphID in the range'), + ('uint16', 'Class', None, None, 'Applied to all glyphs in the range'), + ]), + + ('Device', [ + ('uint16', 'StartSize', None, None, 'Smallest size to correct-in ppem'), + ('uint16', 'EndSize', None, None, 'Largest size to correct-in ppem'), + ('uint16', 'DeltaFormat', None, None, 'Format of DeltaValue array data: 1, 2, or 3'), + ('uint16', 'DeltaValue', '', 0, 'Array of compressed data'), + ]), + + + # + # gpos (generated from gpos.htm) + # + + ('GPOS', [ + ('Fixed', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'), + ('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GPOS table'), + ('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GPOS table'), + ('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GPOS table'), + ]), + + ('SinglePosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of SinglePos subtable'), + ('uint16', 'ValueFormat', None, None, 'Defines the types of data in the ValueRecord'), + ('ValueRecord', 'Value', None, None, 'Defines positioning value(s)-applied to all glyphs in the Coverage table'), + ]), + + ('SinglePosFormat2', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of SinglePos subtable'), + ('uint16', 'ValueFormat', None, None, 'Defines the types of data in the ValueRecord'), + ('uint16', 'ValueCount', None, None, 'Number of ValueRecords'), + ('ValueRecord', 'Value', 'ValueCount', 0, 'Array of ValueRecords-positioning values applied to glyphs'), + ]), + + ('PairPosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of PairPos subtable-only the first glyph in each pair'), + ('uint16', 'ValueFormat1', None, None, 'Defines the types of data in ValueRecord1-for the first glyph in the pair -may be zero (0)'), + ('uint16', 'ValueFormat2', None, None, 'Defines the types of data in ValueRecord2-for the second glyph in the pair -may be zero (0)'), + ('uint16', 'PairSetCount', None, None, 'Number of PairSet tables'), + ('Offset', 'PairSet', 'PairSetCount', 0, 'Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index'), + ]), + + ('PairSet', [ + ('uint16', 'PairValueCount', None, None, 'Number of PairValueRecords'), + ('struct', 'PairValueRecord', 'PairValueCount', 0, 'Array of PairValueRecords-ordered by GlyphID of the second glyph'), + ]), + + ('PairValueRecord', [ + ('GlyphID', 'SecondGlyph', None, None, 'GlyphID of second glyph in the pair-first glyph is listed in the Coverage table'), + ('ValueRecord', 'Value1', None, None, 'Positioning data for the first glyph in the pair'), + ('ValueRecord', 'Value2', None, None, 'Positioning data for the second glyph in the pair'), + ]), + + ('PairPosFormat2', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of PairPos subtable-for the first glyph of the pair'), + ('uint16', 'ValueFormat1', None, None, 'ValueRecord definition-for the first glyph of the pair-may be zero (0)'), + ('uint16', 'ValueFormat2', None, None, 'ValueRecord definition-for the second glyph of the pair-may be zero (0)'), + ('Offset', 'ClassDef1', None, None, 'Offset to ClassDef table-from beginning of PairPos subtable-for the first glyph of the pair'), + ('Offset', 'ClassDef2', None, None, 'Offset to ClassDef table-from beginning of PairPos subtable-for the second glyph of the pair'), + ('uint16', 'Class1Count', None, None, 'Number of classes in ClassDef1 table-includes Class0'), + ('uint16', 'Class2Count', None, None, 'Number of classes in ClassDef2 table-includes Class0'), + ('struct', 'Class1Record', 'Class1Count', 0, 'Array of Class1 records-ordered by Class1'), + ]), + + ('Class1Record', [ + ('struct', 'Class2Record', 'Class2Count', 0, 'Array of Class2 records-ordered by Class2'), + ]), + + ('Class2Record', [ + ('ValueRecord', 'Value1', None, None, 'Positioning for first glyph-empty if ValueFormat1 = 0'), + ('ValueRecord', 'Value2', None, None, 'Positioning for second glyph-empty if ValueFormat2 = 0'), + ]), + + ('CursivePosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of CursivePos subtable'), + ('uint16', 'EntryExitCount', None, None, 'Number of EntryExit records'), + ('struct', 'EntryExitRecord', 'EntryExitCount', 0, 'Array of EntryExit records-in Coverage Index order'), + ]), + + ('EntryExitRecord', [ + ('Offset', 'EntryAnchor', None, None, 'Offset to EntryAnchor table-from beginning of CursivePos subtable-may be NULL'), + ('Offset', 'ExitAnchor', None, None, 'Offset to ExitAnchor table-from beginning of CursivePos subtable-may be NULL'), + ]), + + ('MarkBasePosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'MarkCoverage', None, None, 'Offset to MarkCoverage table-from beginning of MarkBasePos subtable'), + ('Offset', 'BaseCoverage', None, None, 'Offset to BaseCoverage table-from beginning of MarkBasePos subtable'), + ('uint16', 'ClassCount', None, None, 'Number of classes defined for marks'), + ('Offset', 'MarkArray', None, None, 'Offset to MarkArray table-from beginning of MarkBasePos subtable'), + ('Offset', 'BaseArray', None, None, 'Offset to BaseArray table-from beginning of MarkBasePos subtable'), + ]), + + ('BaseArray', [ + ('uint16', 'BaseCount', None, None, 'Number of BaseRecords'), + ('struct', 'BaseRecord', 'BaseCount', 0, 'Array of BaseRecords-in order of BaseCoverage Index'), + ]), + + ('BaseRecord', [ + ('Offset', 'BaseAnchor', 'ClassCount', 0, 'Array of offsets (one per class) to Anchor tables-from beginning of BaseArray table-ordered by class-zero-based'), + ]), + + ('MarkLigPosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'MarkCoverage', None, None, 'Offset to Mark Coverage table-from beginning of MarkLigPos subtable'), + ('Offset', 'LigatureCoverage', None, None, 'Offset to Ligature Coverage table-from beginning of MarkLigPos subtable'), + ('uint16', 'ClassCount', None, None, 'Number of defined mark classes'), + ('Offset', 'MarkArray', None, None, 'Offset to MarkArray table-from beginning of MarkLigPos subtable'), + ('Offset', 'LigatureArray', None, None, 'Offset to LigatureArray table-from beginning of MarkLigPos subtable'), + ]), + + ('LigatureArray', [ + ('uint16', 'LigatureCount', None, None, 'Number of LigatureAttach table offsets'), + ('Offset', 'LigatureAttach', 'LigatureCount', 0, 'Array of offsets to LigatureAttach tables-from beginning of LigatureArray table-ordered by LigatureCoverage Index'), + ]), + + ('LigatureAttach', [ + ('uint16', 'ComponentCount', None, None, 'Number of ComponentRecords in this ligature'), + ('struct', 'ComponentRecord', 'ComponentCount', 0, 'Array of Component records-ordered in writing direction'), + ]), + + ('ComponentRecord', [ + ('Offset', 'LigatureAnchor', 'ClassCount', 0, 'Array of offsets (one per class) to Anchor tables-from beginning of LigatureAttach table-ordered by class-NULL if a component does not have an attachment for a class-zero-based array'), + ]), + + ('MarkMarkPosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Mark1Coverage', None, None, 'Offset to Combining Mark Coverage table-from beginning of MarkMarkPos subtable'), + ('Offset', 'Mark2Coverage', None, None, 'Offset to Base Mark Coverage table-from beginning of MarkMarkPos subtable'), + ('uint16', 'ClassCount', None, None, 'Number of Combining Mark classes defined'), + ('Offset', 'Mark1Array', None, None, 'Offset to MarkArray table for Mark1-from beginning of MarkMarkPos subtable'), + ('Offset', 'Mark2Array', None, None, 'Offset to Mark2Array table for Mark2-from beginning of MarkMarkPos subtable'), + ]), + + ('Mark2Array', [ + ('uint16', 'Mark2Count', None, None, 'Number of Mark2 records'), + ('struct', 'Mark2Record', 'Mark2Count', 0, 'Array of Mark2 records-in Coverage order'), + ]), + + ('Mark2Record', [ + ('Offset', 'Mark2Anchor', 'ClassCount', 0, 'Array of offsets (one per class) to Anchor tables-from beginning of Mark2Array table-zero-based array'), + ]), + + ('PosLookupRecord', [ + ('uint16', 'SequenceIndex', None, None, 'Index to input glyph sequence-first glyph = 0'), + ('uint16', 'LookupListIndex', None, None, 'Lookup to apply to that position-zero-based'), + ]), + + ('ContextPosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of ContextPos subtable'), + ('uint16', 'PosRuleSetCount', None, None, 'Number of PosRuleSet tables'), + ('Offset', 'PosRuleSet', 'PosRuleSetCount', 0, 'Array of offsets to PosRuleSet tables-from beginning of ContextPos subtable-ordered by Coverage Index'), + ]), + + ('PosRuleSet', [ + ('uint16', 'PosRuleCount', None, None, 'Number of PosRule tables'), + ('Offset', 'PosRule', 'PosRuleCount', 0, 'Array of offsets to PosRule tables-from beginning of PosRuleSet-ordered by preference'), + ]), + + ('PosRule', [ + ('uint16', 'GlyphCount', None, None, 'Number of glyphs in the Input glyph sequence'), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('GlyphID', 'Input', 'GlyphCount', -1, 'Array of input GlyphIDs-starting with the second glyph'), + ('struct', 'PosLookupRecord', 'PosCount', 0, 'Array of positioning lookups-in design order'), + ]), + + ('ContextPosFormat2', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of ContextPos subtable'), + ('Offset', 'ClassDef', None, None, 'Offset to ClassDef table-from beginning of ContextPos subtable'), + ('uint16', 'PosClassSetCnt', None, None, 'Number of PosClassSet tables'), + ('Offset', 'PosClassSet', 'PosClassSetCnt', 0, 'Array of offsets to PosClassSet tables-from beginning of ContextPos subtable-ordered by class-may be NULL'), + ]), + + ('PosClassSet', [ + ('uint16', 'PosClassRuleCnt', None, None, 'Number of PosClassRule tables'), + ('Offset', 'PosClassRule', 'PosClassRuleCnt', 0, 'Array of offsets to PosClassRule tables-from beginning of PosClassSet-ordered by preference'), + ]), + + ('PosClassRule', [ + ('uint16', 'GlyphCount', None, None, 'Number of glyphs to be matched'), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('uint16', 'Class', 'GlyphCount', -1, 'Array of classes-beginning with the second class-to be matched to the input glyph sequence'), + ('struct', 'PosLookupRecord', 'PosCount', 0, 'Array of positioning lookups-in design order'), + ]), + + ('ContextPosFormat3', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 3'), + ('uint16', 'GlyphCount', None, None, 'Number of glyphs in the input sequence'), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('Offset', 'Coverage', 'GlyphCount', 0, 'Array of offsets to Coverage tables-from beginning of ContextPos subtable'), + ('struct', 'PosLookupRecord', 'PosCount', 0, 'Array of positioning lookups-in design order'), + ]), + + ('ChainContextPosFormat1', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of ContextPos subtable'), + ('uint16', 'ChainPosRuleSetCount', None, None, 'Number of ChainPosRuleSet tables'), + ('Offset', 'ChainPosRuleSet', 'ChainPosRuleSetCount', 0, 'Array of offsets to ChainPosRuleSet tables-from beginning of ContextPos subtable-ordered by Coverage Index'), + ]), + + ('ChainPosRuleSet', [ + ('uint16', 'ChainPosRuleCount', None, None, 'Number of ChainPosRule tables'), + ('Offset', 'ChainPosRule', 'ChainPosRuleCount', 0, 'Array of offsets to ChainPosRule tables-from beginning of ChainPosRuleSet-ordered by preference'), + ]), + + ('ChainPosRule', [ + ('uint16', 'BacktrackGlyphCount', None, None, 'Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)'), + ('GlyphID', 'Backtrack', 'BacktrackGlyphCount', 0, "Array of backtracking GlyphID's (to be matched before the input sequence)"), + ('uint16', 'InputGlyphCount', None, None, 'Total number of glyphs in the input sequence (includes the first glyph)'), + ('GlyphID', 'Input', 'InputGlyphCount', -1, 'Array of input GlyphIDs (start with second glyph)'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)'), + ('GlyphID', 'LookAhead', 'LookAheadGlyphCount', 0, "Array of lookahead GlyphID's (to be matched after the input sequence)"), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('struct', 'PosLookupRecord', 'PosCount', 0, 'Array of PosLookupRecords (in design order)'), + ]), + + ('ChainContextPosFormat2', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of ChainContextPos subtable'), + ('Offset', 'BacktrackClassDef', None, None, 'Offset to ClassDef table containing backtrack sequence context-from beginning of ChainContextPos subtable'), + ('Offset', 'InputClassDef', None, None, 'Offset to ClassDef table containing input sequence context-from beginning of ChainContextPos subtable'), + ('Offset', 'LookaheadClassDef', None, None, 'Offset to ClassDef table containing lookahead sequence context-from beginning of ChainContextPos subtable'), + ('uint16', 'ChainPosClassSetCnt', None, None, 'Number of ChainPosClassSet tables'), + ('Offset', 'ChainPosClassSet', 'ChainPosClassSetCnt', 0, 'Array of offsets to ChainPosClassSet tables-from beginning of ChainContextPos subtable-ordered by input class-may be NULL'), + ]), + + ('ChainPosClassSet', [ + ('uint16', 'ChainPosClassRuleCnt', None, None, 'Number of ChainPosClassRule tables'), + ('Offset', 'ChainPosClassRule', 'ChainPosClassRuleCnt', 0, 'Array of offsets to ChainPosClassRule tables-from beginning of ChainPosClassSet-ordered by preference'), + ]), + + ('ChainPosClassRule', [ + ('uint16', 'BacktrackGlyphCount', None, None, 'Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)'), + ('uint16', 'Backtrack', 'BacktrackGlyphCount', 0, 'Array of backtracking classes(to be matched before the input sequence)'), + ('uint16', 'InputGlyphCount', None, None, 'Total number of classes in the input sequence (includes the first class)'), + ('uint16', 'Input', 'InputGlyphCount', -1, 'Array of input classes(start with second class; to be matched with the input glyph sequence)'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Total number of classes in the look ahead sequence (number of classes to be matched after the input sequence)'), + ('uint16', 'LookAhead', 'LookAheadGlyphCount', 0, 'Array of lookahead classes(to be matched after the input sequence)'), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('struct', 'PosLookupRecord', 'ChainPosCount', 0, 'Array of PosLookupRecords (in design order)'), + ]), + + ('ChainContextPosFormat3', [ + ('uint16', 'PosFormat', None, None, 'Format identifier-format = 3'), + ('uint16', 'BacktrackGlyphCount', None, None, 'Number of glyphs in the backtracking sequence'), + ('Offset', 'BacktrackCoverage', 'BacktrackGlyphCount', 0, 'Array of offsets to coverage tables in backtracking sequence, in glyph sequence order'), + ('uint16', 'InputGlyphCount', None, None, 'Number of glyphs in input sequence'), + ('Offset', 'InputCoverage', 'InputGlyphCount', 0, 'Array of offsets to coverage tables in input sequence, in glyph sequence order'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Number of glyphs in lookahead sequence'), + ('Offset', 'LookaheadCoverage', 'LookaheadGlyphCount', 0, 'Array of offsets to coverage tables in lookahead sequence, in glyph sequence order'), + ('uint16', 'PosCount', None, None, 'Number of PosLookupRecords'), + ('struct', 'PosLookupRecord', 'PosCount', 0, 'Array of PosLookupRecords,in design order'), + ]), + + ('ExtensionPosFormat1', [ + ('USHORT', 'PosFormat', None, None, 'Format identifier. Set to 1.'), + ('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'), + ('ULONG', 'ExtensionOffset', None, None, 'Offset to the extension subtable, of lookup type ExtensionLookupType, relative to the start of the ExtensionPosFormat1 subtable.'), + ]), + + ('ValueRecord', [ + ('int16', 'XPlacement', None, None, 'Horizontal adjustment for placement-in design units'), + ('int16', 'YPlacement', None, None, 'Vertical adjustment for placement-in design units'), + ('int16', 'XAdvance', None, None, 'Horizontal adjustment for advance-in design units (only used for horizontal writing)'), + ('int16', 'YAdvance', None, None, 'Vertical adjustment for advance-in design units (only used for vertical writing)'), + ('Offset', 'XPlaDevice', None, None, 'Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)'), + ('Offset', 'YPlaDevice', None, None, 'Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)'), + ('Offset', 'XAdvDevice', None, None, 'Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)'), + ('Offset', 'YAdvDevice', None, None, 'Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)'), + ]), + + ('AnchorFormat1', [ + ('uint16', 'AnchorFormat', None, None, 'Format identifier-format = 1'), + ('int16', 'XCoordinate', None, None, 'Horizontal value-in design units'), + ('int16', 'YCoordinate', None, None, 'Vertical value-in design units'), + ]), + + ('AnchorFormat2', [ + ('uint16', 'AnchorFormat', None, None, 'Format identifier-format = 2'), + ('int16', 'XCoordinate', None, None, 'Horizontal value-in design units'), + ('int16', 'YCoordinate', None, None, 'Vertical value-in design units'), + ('uint16', 'AnchorPoint', None, None, 'Index to glyph contour point'), + ]), + + ('AnchorFormat3', [ + ('uint16', 'AnchorFormat', None, None, 'Format identifier-format = 3'), + ('int16', 'XCoordinate', None, None, 'Horizontal value-in design units'), + ('int16', 'YCoordinate', None, None, 'Vertical value-in design units'), + ('Offset', 'XDeviceTable', None, None, 'Offset to Device table for X coordinate- from beginning of Anchor table (may be NULL)'), + ('Offset', 'YDeviceTable', None, None, 'Offset to Device table for Y coordinate- from beginning of Anchor table (may be NULL)'), + ]), + + ('MarkArray', [ + ('uint16', 'MarkCount', None, None, 'Number of MarkRecords'), + ('struct', 'MarkRecord', 'MarkCount', 0, 'Array of MarkRecords-in Coverage order'), + ]), + + ('MarkRecord', [ + ('uint16', 'Class', None, None, 'Class defined for this mark'), + ('Offset', 'MarkAnchor', None, None, 'Offset to Anchor table-from beginning of MarkArray table'), + ]), + + + # + # gsub (generated from gsub.htm) + # + + ('GSUB', [ + ('Fixed', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'), + ('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GSUB table'), + ('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GSUB table'), + ('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GSUB table'), + ]), + + ('SingleSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('int16', 'DeltaGlyphID', None, None, 'Add to original GlyphID to get substitute GlyphID'), + ]), + + ('SingleSubstFormat2', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'GlyphCount', None, None, 'Number of GlyphIDs in the Substitute array'), + ('GlyphID', 'Substitute', 'GlyphCount', 0, 'Array of substitute GlyphIDs-ordered by Coverage Index'), + ]), + + ('MultipleSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'SequenceCount', None, None, 'Number of Sequence table offsets in the Sequence array'), + ('Offset', 'Sequence', 'SequenceCount', 0, 'Array of offsets to Sequence tables-from beginning of Substitution table-ordered by Coverage Index'), + ]), + + ('Sequence', [ + ('uint16', 'GlyphCount', None, None, 'Number of GlyphIDs in the Substitute array. This should always be greater than 0.'), + ('GlyphID', 'Substitute', 'GlyphCount', 0, 'String of GlyphIDs to substitute'), + ]), + + ('AlternateSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'AlternateSetCount', None, None, 'Number of AlternateSet tables'), + ('Offset', 'AlternateSet', 'AlternateSetCount', 0, 'Array of offsets to AlternateSet tables-from beginning of Substitution table-ordered by Coverage Index'), + ]), + + ('AlternateSet', [ + ('uint16', 'GlyphCount', None, None, 'Number of GlyphIDs in the Alternate array'), + ('GlyphID', 'Alternate', 'GlyphCount', 0, 'Array of alternate GlyphIDs-in arbitrary order'), + ]), + + ('LigatureSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'LigSetCount', None, None, 'Number of LigatureSet tables'), + ('Offset', 'LigatureSet', 'LigSetCount', 0, 'Array of offsets to LigatureSet tables-from beginning of Substitution table-ordered by Coverage Index'), + ]), + + ('LigatureSet', [ + ('uint16', 'LigatureCount', None, None, 'Number of Ligature tables'), + ('Offset', 'Ligature', 'LigatureCount', 0, 'Array of offsets to Ligature tables-from beginning of LigatureSet table-ordered by preference'), + ]), + + ('Ligature', [ + ('GlyphID', 'LigGlyph', None, None, 'GlyphID of ligature to substitute'), + ('uint16', 'CompCount', None, None, 'Number of components in the ligature'), + ('GlyphID', 'Component', 'CompCount', -1, 'Array of component GlyphIDs-start with the second component-ordered in writing direction'), + ]), + + ('SubstLookupRecord', [ + ('uint16', 'SequenceIndex', None, None, 'Index into current glyph sequence-first glyph = 0'), + ('uint16', 'LookupListIndex', None, None, 'Lookup to apply to that position-zero-based'), + ]), + + ('ContextSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'SubRuleSetCount', None, None, 'Number of SubRuleSet tables-must equal GlyphCount in Coverage table'), + ('Offset', 'SubRuleSet', 'SubRuleSetCount', 0, 'Array of offsets to SubRuleSet tables-from beginning of Substitution table-ordered by Coverage Index'), + ]), + + ('SubRuleSet', [ + ('uint16', 'SubRuleCount', None, None, 'Number of SubRule tables'), + ('Offset', 'SubRule', 'SubRuleCount', 0, 'Array of offsets to SubRule tables-from beginning of SubRuleSet table-ordered by preference'), + ]), + + ('SubRule', [ + ('uint16', 'GlyphCount', None, None, 'Total number of glyphs in input glyph sequence-includes the first glyph'), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('GlyphID', 'Input', 'GlyphCount', -1, 'Array of input GlyphIDs-start with second glyph'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of SubstLookupRecords-in design order'), + ]), + + ('ContextSubstFormat2', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('Offset', 'ClassDef', None, None, 'Offset to glyph ClassDef table-from beginning of Substitution table'), + ('uint16', 'SubClassSetCnt', None, None, 'Number of SubClassSet tables'), + ('Offset', 'SubClassSet', 'SubClassSetCnt', 0, 'Array of offsets to SubClassSet tables-from beginning of Substitution table-ordered by class-may be NULL'), + ]), + + ('SubClassSet', [ + ('uint16', 'SubClassRuleCnt', None, None, 'Number of SubClassRule tables'), + ('Offset', 'SubClassRule', 'SubClassRuleCount', 0, 'Array of offsets to SubClassRule tables-from beginning of SubClassSet-ordered by preference'), + ]), + + ('SubClassRule', [ + ('uint16', 'GlyphCount', None, None, 'Total number of classes specified for the context in the rule-includes the first class'), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('uint16', 'Class', 'GlyphCount', -1, 'Array of classes-beginning with the second class-to be matched to the input glyph class sequence'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of Substitution lookups-in design order'), + ]), + + ('ContextSubstFormat3', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 3'), + ('uint16', 'GlyphCount', None, None, 'Number of glyphs in the input glyph sequence'), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('Offset', 'Coverage', 'GlyphCount', 0, 'Array of offsets to Coverage table-from beginning of Substitution table-in glyph sequence order'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of SubstLookupRecords-in design order'), + ]), + + ('ChainContextSubstFormat1', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('uint16', 'ChainSubRuleSetCount', None, None, 'Number of ChainSubRuleSet tables-must equal GlyphCount in Coverage table'), + ('Offset', 'ChainSubRuleSet', 'ChainSubRuleSetCount', 0, 'Array of offsets to ChainSubRuleSet tables-from beginning of Substitution table-ordered by Coverage Index'), + ]), + + ('ChainSubRuleSet', [ + ('uint16', 'ChainSubRuleCount', None, None, 'Number of ChainSubRule tables'), + ('Offset', 'ChainSubRule', 'ChainSubRuleCount', 0, 'Array of offsets to ChainSubRule tables-from beginning of ChainSubRuleSet table-ordered by preference'), + ]), + + ('ChainSubRule', [ + ('uint16', 'BacktrackGlyphCount', None, None, 'Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)'), + ('GlyphID', 'Backtrack', 'BacktrackGlyphCount', 0, "Array of backtracking GlyphID's (to be matched before the input sequence)"), + ('uint16', 'InputGlyphCount', None, None, 'Total number of glyphs in the input sequence (includes the first glyph)'), + ('GlyphID', 'Input', 'InputGlyphCount', -1, 'Array of input GlyphIDs (start with second glyph)'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)'), + ('GlyphID', 'Lookahead', 'LookAheadGlyphCount', 0, "Array of lookahead GlyphID's (to be matched after the input sequence)"), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of SubstLookupRecords (in design order)'), + ]), + + ('ChainContextSubstFormat2', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 2'), + ('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'), + ('Offset', 'BacktrackClassDef', None, None, 'Offset to glyph ClassDef table containing backtrack sequence data-from beginning of Substitution table'), + ('Offset', 'InputClassDef', None, None, 'Offset to glyph ClassDef table containing input sequence data-from beginning of Substitution table'), + ('Offset', 'LookaheadClassDef', None, None, 'Offset to glyph ClassDef table containing lookahead sequence data-from beginning of Substitution table'), + ('uint16', 'ChainSubClassSetCnt', None, None, 'Number of ChainSubClassSet tables'), + ('Offset', 'ChainSubClassSet', 'ChainSubClassSetCnt', 0, 'Array of offsets to ChainSubClassSet tables-from beginning of Substitution table-ordered by input class-may be NULL'), + ]), + + ('ChainSubClassSet', [ + ('uint16', 'ChainSubClassRuleCnt', None, None, 'Number of ChainSubClassRule tables'), + ('Offset', 'ChainSubClassRule', 'ChainSubClassRuleCount', 0, 'Array of offsets to ChainSubClassRule tables-from beginning of ChainSubClassSet-ordered by preference'), + ]), + + ('ChainSubClassRule', [ + ('uint16', 'BacktrackGlyphCount', None, None, 'Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)'), + ('uint16', 'Backtrack', 'BacktrackGlyphCount', 0, 'Array of backtracking classes(to be matched before the input sequence)'), + ('uint16', 'InputGlyphCount', None, None, 'Total number of classes in the input sequence (includes the first class)'), + ('uint16', 'Input', 'InputGlyphCount', -1, 'Array of input classes(start with second class; to be matched with the input glyph sequence)'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Total number of classes in the look ahead sequence (number of classes to be matched after the input sequence)'), + ('uint16', 'LookAhead', 'LookAheadGlyphCount', 0, 'Array of lookahead classes(to be matched after the input sequence)'), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of SubstLookupRecords (in design order)'), + ]), + + ('ChainContextSubstFormat3', [ + ('uint16', 'SubstFormat', None, None, 'Format identifier-format = 3'), + ('uint16', 'BacktrackGlyphCount', None, None, 'Number of glyphs in the backtracking sequence'), + ('Offset', 'BacktrackCoverage', 'BacktrackGlyphCount', 0, 'Array of offsets to coverage tables in backtracking sequence, in glyph sequence order'), + ('uint16', 'InputGlyphCount', None, None, 'Number of glyphs in input sequence'), + ('Offset', 'InputCoverage', 'InputGlyphCount', 0, 'Array of offsets to coverage tables in input sequence, in glyph sequence order'), + ('uint16', 'LookaheadGlyphCount', None, None, 'Number of glyphs in lookahead sequence'), + ('Offset', 'LookaheadCoverage', 'LookaheadGlyphCount', 0, 'Array of offsets to coverage tables in lookahead sequence, in glyph sequence order'), + ('uint16', 'SubstCount', None, None, 'Number of SubstLookupRecords'), + ('struct', 'SubstLookupRecord', 'SubstCount', 0, 'Array of SubstLookupRecords, in design order'), + ]), + + ('ExtensionSubstFormat1', [ + ('USHORT', 'SubstFormat', None, None, 'Format identifier. Set to 1.'), + ('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'), + ('ULONG', 'ExtensionOffset', None, None, 'Offset to the extension subtable, of lookup type ExtensionLookupType, relative to the start of the ExtensionSubstFormat1 subtable.'), + ]), + + + # + # gdef (generated from gdef.htm) + # + + ('GDEF', [ + ('Fixed', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'), + ('Offset', 'GlyphClassDef', None, None, 'Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL)'), + ('Offset', 'AttachList', None, None, 'Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL)'), + ('Offset', 'LigCaretList', None, None, 'Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL)'), + ('Offset', 'MarkAttachClassDef', None, None, 'Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL)'), + ]), + + ('AttachList', [ + ('Offset', 'Coverage', None, None, 'Offset to Coverage table - from beginning of AttachList table'), + ('uint16', 'GlyphCount', None, None, 'Number of glyphs with attachment points'), + ('Offset', 'AttachPoint', 'GlyphCount', 0, 'Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order'), + ]), + + ('AttachPoint', [ + ('uint16', 'PointCount', None, None, 'Number of attachment points on this glyph'), + ('uint16', 'PointIndex', 'PointCount', 0, 'Array of contour point indices -in increasing numerical order'), + ]), + + ('LigCaretList', [ + ('Offset', 'Coverage', None, None, 'Offset to Coverage table - from beginning of LigCaretList table'), + ('uint16', 'LigGlyphCount', None, None, 'Number of ligature glyphs'), + ('Offset', 'LigGlyph', 'LigGlyphCount', 0, 'Array of offsets to LigGlyph tables-from beginning of LigCaretList table-in Coverage Index order'), + ]), + + ('LigGlyph', [ + ('uint16', 'CaretCount', None, None, 'Number of CaretValues for this ligature (components - 1)'), + ('Offset', 'CaretValue', 'CaretCount', 0, 'Array of offsets to CaretValue tables-from beginning of LigGlyph table-in increasing coordinate order'), + ]), + + ('CaretValueFormat1', [ + ('uint16', 'CaretValueFormat', None, None, 'Format identifier-format = 1'), + ('int16', 'Coordinate', None, None, 'X or Y value, in design units'), + ]), + + ('CaretValueFormat2', [ + ('uint16', 'CaretValueFormat', None, None, 'Format identifier-format = 2'), + ('uint16', 'CaretValuePoint', None, None, 'Contour point index on glyph'), + ]), + + ('CaretValueFormat3', [ + ('uint16', 'CaretValueFormat', None, None, 'Format identifier-format = 3'), + ('int16', 'Coordinate', None, None, 'X or Y value, in design units'), + ('Offset', 'DeviceTable', None, None, 'Offset to Device table for X or Y value-from beginning of CaretValue table'), + ]), + + + # + # base (generated from base.htm) + # + + ('BASE', [ + ('fixed32', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'), + ('Offset', 'HorizAxis', None, None, 'Offset to horizontal Axis table-from beginning of BASE table-may be NULL'), + ('Offset', 'VertAxis', None, None, 'Offset to vertical Axis table-from beginning of BASE table-may be NULL'), + ]), + + ('Axis', [ + ('Offset', 'BaseTagList', None, None, 'Offset to BaseTagList table-from beginning of Axis table-may be NULL'), + ('Offset', 'BaseScriptList', None, None, 'Offset to BaseScriptList table-from beginning of Axis table'), + ]), + + ('BaseTagList', [ + ('uint16', 'BaseTagCount', None, None, 'Number of baseline identification tags in this text direction-may be zero (0)'), + ('Tag', 'BaselineTag', 'BaseTagCount', 0, 'Array of 4-byte baseline identification tags-must be in alphabetical order'), + ]), + + ('BaseScriptList', [ + ('uint16', 'BaseScriptCount', None, None, 'Number of BaseScriptRecords defined'), + ('struct', 'BaseScriptRecord', 'BaseScriptCount', 0, 'Array of BaseScriptRecords-in alphabetical order by BaseScriptTag'), + ]), + + ('BaseScriptRecord', [ + ('Tag', 'BaseScriptTag', None, None, '4-byte script identification tag'), + ('Offset', 'BaseScript', None, None, 'Offset to BaseScript table-from beginning of BaseScriptList'), + ]), + + ('BaseScript', [ + ('Offset', 'BaseValues', None, None, 'Offset to BaseValues table-from beginning of BaseScript table-may be NULL'), + ('Offset', 'DefaultMinMax', None, None, 'Offset to MinMax table- from beginning of BaseScript table-may be NULL'), + ('uint16', 'BaseLangSysCount', None, None, 'Number of BaseLangSysRecords defined-may be zero (0)'), + ('struct', 'BaseLangSysRecord', 'BaseLangSysCount', 0, 'Array of BaseLangSysRecords-in alphabetical order by BaseLangSysTag'), + ]), + + ('BaseLangSysRecord', [ + ('Tag', 'BaseLangSysTag', None, None, '4-byte language system identification tag'), + ('Offset', 'MinMax', None, None, 'Offset to MinMax table-from beginning of BaseScript table'), + ]), + + ('BaseValues', [ + ('uint16', 'DefaultIndex', None, None, 'Index number of default baseline for this script-equals index position of baseline tag in BaselineArray of the BaseTagList'), + ('uint16', 'BaseCoordCount', None, None, 'Number of BaseCoord tables defined-should equal BaseTagCount in the BaseTagList'), + ('Offset', 'BaseCoord', 'BaseCoordCount', 0, 'Array of offsets to BaseCoord-from beginning of BaseValues table-order matches BaselineTag array in the BaseTagList'), + ]), + + ('MinMax', [ + ('Offset', 'MinCoord', None, None, 'Offset to BaseCoord table-defines minimum extent value-from the beginning of MinMax table-may be NULL'), + ('Offset', 'MaxCoord', None, None, 'Offset to BaseCoord table-defines maximum extent value-from the beginning of MinMax table-may be NULL'), + ('uint16', 'FeatMinMaxCount', None, None, 'Number of FeatMinMaxRecords-may be zero (0)'), + ('struct', 'FeatMinMaxRecord', 'FeatMinMaxCount', 0, 'Array of FeatMinMaxRecords-in alphabetical order, by FeatureTableTag'), + ]), + + ('FeatMinMaxRecord', [ + ('Tag', 'FeatureTableTag', None, None, '4-byte feature identification tag-must match FeatureTag in FeatureList'), + ('Offset', 'MinCoord', None, None, 'Offset to BaseCoord table-defines minimum extent value-from beginning of MinMax table-may be NULL'), + ('Offset', 'MaxCoord', None, None, 'Offset to BaseCoord table-defines maximum extent value-from beginning of MinMax table-may be NULL'), + ]), + + ('BaseCoordFormat1', [ + ('uint16', 'BaseCoordFormat', None, None, 'Format identifier-format = 1'), + ('int16', 'Coordinate', None, None, 'X or Y value, in design units'), + ]), + + ('BaseCoordFormat2', [ + ('uint16', 'BaseCoordFormat', None, None, 'Format identifier-format = 2'), + ('int16', 'Coordinate', None, None, 'X or Y value, in design units'), + ('GlyphID', 'ReferenceGlyph', None, None, 'GlyphID of control glyph'), + ('uint16', 'BaseCoordPoint', None, None, 'Index of contour point on the ReferenceGlyph'), + ]), + + ('BaseCoordFormat3', [ + ('uint16', 'BaseCoordFormat', None, None, 'Format identifier-format = 3'), + ('int16', 'Coordinate', None, None, 'X or Y value, in design units'), + ('Offset', 'DeviceTable', None, None, 'Offset to Device table for X or Y value'), + ]), + + + # + # jstf (generated from jstf.htm) + # + + ('JSTF', [ + ('fixed32', 'Version', None, None, 'Version of the JSTF table-initially set to 0x00010000'), + ('uint16', 'JstfScriptCount', None, None, 'Number of JstfScriptRecords in this table'), + ('struct', 'JstfScriptRecord', 'JstfScriptCount', 0, 'Array of JstfScriptRecords-in alphabetical order, by JstfScriptTag'), + ]), + + ('JstfScriptRecord', [ + ('Tag', 'JstfScriptTag', None, None, '4-byte JstfScript identification'), + ('Offset', 'JstfScript', None, None, 'Offset to JstfScript table-from beginning of JSTF Header'), + ]), + + ('JstfScript', [ + ('Offset', 'ExtenderGlyph', None, None, 'Offset to ExtenderGlyph table-from beginning of JstfScript table-may be NULL'), + ('Offset', 'DefJstfLangSys', None, None, 'Offset to Default JstfLangSys table-from beginning of JstfScript table-may be NULL'), + ('uint16', 'JstfLangSysCount', None, None, 'Number of JstfLangSysRecords in this table- may be zero (0)'), + ('struct', 'JstfLangSysRecord', 'JstfLangSysCount', 0, 'Array of JstfLangSysRecords-in alphabetical order, by JstfLangSysTag'), + ]), + + ('JstfLangSysRecord', [ + ('Tag', 'JstfLangSysTag', None, None, '4-byte JstfLangSys identifier'), + ('Offset', 'JstfLangSys', None, None, 'Offset to JstfLangSys table-from beginning of JstfScript table'), + ]), + + ('ExtenderGlyph', [ + ('uint16', 'GlyphCount', None, None, 'Number of Extender Glyphs in this script'), + ('GlyphID', 'ExtenderGlyph', 'GlyphCount', 0, 'GlyphIDs-in increasing numerical order'), + ]), + + ('JstfLangSys', [ + ('uint16', 'JstfPriorityCnt', None, None, 'Number of JstfPriority tables'), + ('Offset', 'JstfPriority', 'JstfPriorityCnt', 0, 'Array of offsets to JstfPriority tables-from beginning of JstfLangSys table-in priority order'), + ]), + + ('JstfPriority', [ + ('Offset', 'ShrinkageEnableGSUB', None, None, 'Offset to Shrinkage Enable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ShrinkageDisableGSUB', None, None, 'Offset to Shrinkage Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ShrinkageEnableGPOS', None, None, 'Offset to Shrinkage Enable JstfGPOSModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ShrinkageDisableGPOS', None, None, 'Offset to Shrinkage Disable JstfGPOSModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ShrinkageJstfMax', None, None, 'Offset to Shrinkage JstfMax table-from beginning of JstfPriority table -may be NULL'), + ('Offset', 'ExtensionEnableGSUB', None, None, 'Offset to Extension Enable JstfGSUBModList table-may be NULL'), + ('Offset', 'ExtensionDisableGSUB', None, None, 'Offset to Extension Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ExtensionEnableGPOS', None, None, 'Offset to Extension Enable JstfGSUBModList table-may be NULL'), + ('Offset', 'ExtensionDisableGPOS', None, None, 'Offset to Extension Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL'), + ('Offset', 'ExtensionJstfMax', None, None, 'Offset to Extension JstfMax table-from beginning of JstfPriority table -may be NULL'), + ]), + + ('JstfGSUBModList', [ + ('uint16', 'LookupCount', None, None, 'Number of lookups for this modification'), + ('uint16', 'GSUBLookupIndex', 'LookupCount', 0, 'Array of LookupIndex identifiers in GSUB-in increasing numerical order'), + ]), + + ('JstfGPOSModList', [ + ('uint16', 'LookupCount', None, None, 'Number of lookups for this modification'), + ('uint16', 'GPOSLookupIndex', 'LookupCount', 0, 'Array of LookupIndex identifiers in GPOS-in increasing numerical order'), + ]), + + ('JstfMax', [ + ('uint16', 'LookupCount', None, None, 'Number of lookup Indices for this modification'), + ('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to GPOS-type lookup tables-from beginning of JstfMax table-in design order'), + ]), + +] + diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py new file mode 100644 index 000000000..24a5440e7 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -0,0 +1,111 @@ +"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various +OpenType subtables. + +Most are constructed upon import from data in otData.py. Most smartness is contained +in otBase.BaseTable. +""" + +from otBase import BaseTable, FormatSwitchingBaseTable + + +class LookupOrder(BaseTable): + """Dummy class; this table isn't defined, but is used, and is always NULL.""" + +class FeatureParams(BaseTable): + """Dummy class; this table isn't defined, but is used, and is always NULL.""" + + +_equivalents = [ + ('MarkArray', ("Mark1Array",)), + ('LangSys', ('DefaultLangSys',)), + ('Coverage', ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage', + 'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage', + 'LookaheadCoverage')), + ('ClassDef', ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef', + 'LookaheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef')), + ('Anchor', ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor', + 'Mark2Anchor', 'MarkAnchor')), + ('Device', ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice', + 'XDeviceTable', 'YDeviceTable', 'DeviceTable')), + ('Axis', ('HorizAxis', 'VertAxis',)), + ('MinMax', ('DefaultMinMax',)), + ('BaseCoord', ('MinCoord', 'MaxCoord',)), + ('JstfLangSys', ('DefJstfLangSys',)), + ('JstfGSUBModList', ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB', + 'ExtensionDisableGSUB',)), + ('JstfGPOSModList', ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS', + 'ExtensionDisableGPOS',)), + ('JstfMax', ('ShrinkageJstfMax', 'ExtensionJstfMax',)), +] + + +def _buildClasses(): + import new, re + from otData import otData + + formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") + namespace = globals() + + # populate module with classes + for name, table in otData: + baseClass = BaseTable + m = formatPat.match(name) + if m: + # XxxFormatN subtable, we only add the "base" table + name = m.group(1) + baseClass = FormatSwitchingBaseTable + if not namespace.has_key(name): + cls = new.classobj(name, (baseClass,), {}) + namespace[name] = cls + + for base, alts in _equivalents: + base = namespace[base] + for alt in alts: + namespace[alt] = new.classobj(alt, (base,), {}) + + global lookupTypes + lookupTypes = { + 'GSUB': { + 1: SingleSubst, + 2: MultipleSubst, + 3: AlternateSubst, + 4: LigatureSubst, + 5: ContextSubst, + 6: ChainContextSubst, + 7: ExtensionSubst, + }, + 'GPOS': { + 1: SinglePos, + 2: PairPos, + 3: CursivePos, + 4: MarkBasePos, + 5: MarkLigPos, + 6: MarkMarkPos, + 7: ContextPos, + 8: ChainContextPos, + 9: ExtensionPos, + }, + } + lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS + + # add converters to classes + from otConverters import buildConverterList + for name, table in otData: + m = formatPat.match(name) + if m: + # XxxFormatN subtable, add converter to "base" table + name, format = m.groups() + format = int(format) + cls = namespace[name] + if not hasattr(cls, "converters"): + cls.converters = {} + cls.convertersByName = {} + converters, convertersByName = buildConverterList(table[1:], namespace) + cls.converters[format] = converters + cls.convertersByName[format] = convertersByName + else: + cls = namespace[name] + cls.converters, cls.convertersByName = buildConverterList(table, namespace) + + +_buildClasses()