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
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 0000000..53975c8
--- /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 0000000..d763a5d
--- /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 82d5104..52f44d4 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 "<ValueRecord>"
-
-
-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 70e6eaf..a523c59 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 0000000..8ff395e
--- /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 0000000..85ffcd7
--- /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 0000000..ab18b10
--- /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 0000000..f863a66
--- /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 0000000..24a5440
--- /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()