Work in progress on CFF, GPOS and GSUB. Since it's only partly working, it's diasabled by default.

git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@189 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
jvr 2002-05-03 17:01:25 +00:00
parent 2a9c630193
commit a36fd88a20
4 changed files with 604 additions and 239 deletions

View File

@ -2,6 +2,12 @@ import DefaultTable
from fontTools import cffLib from fontTools import cffLib
# temporary switch:
# - if true use possibly incomplete compile/decompile/toXML/fromXML implementation
# - if false use DefaultTable, ie. dump as hex.
TESTING_CFF = 0
class table_C_F_F_(DefaultTable.DefaultTable): class table_C_F_F_(DefaultTable.DefaultTable):
def __init__(self, tag): def __init__(self, tag):
@ -28,11 +34,11 @@ class table_C_F_F_(DefaultTable.DefaultTable):
self.cff.fonts[self.cff.fontNames[0]].setGlyphOrder(glyphOrder) self.cff.fonts[self.cff.fontNames[0]].setGlyphOrder(glyphOrder)
def toXML(self, writer, otFont, progress=None): def toXML(self, writer, otFont, progress=None):
if "disableCFFdump": if TESTING_CFF:
self.cff.toXML(writer, progress)
else:
# dump as hex as long as we can't compile # dump as hex as long as we can't compile
DefaultTable.DefaultTable.toXML(self, writer, otFont) DefaultTable.DefaultTable.toXML(self, writer, otFont)
else:
self.cff.toXML(writer, progress)
#def fromXML(self, (name, attrs, content), otFont): #def fromXML(self, (name, attrs, content), otFont):
# xxx # xxx

View File

@ -10,17 +10,39 @@ class table_G_P_O_S_(otCommon.base_GPOS_GSUB):
class SinglePos: class SinglePos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass 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 compile(self, otFont): 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 xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class PairPos: class PairPos:
@ -43,11 +65,8 @@ class PairPos:
self.pairs = pairs = {} self.pairs = pairs = {}
for i in range(reader.readUShort()): for i in range(reader.readUShort()):
firstGlyphName = glyphNames[i] firstGlyphName = glyphNames[i]
offset = reader.readOffset() set = reader.readTable(PairSet, otFont, valueFactory1, valueFactory2)
setData = reader.getSubString(offset) pairs[firstGlyphName] = set.getValues()
set = PairSet()
set.decompile(setData, otFont, valueFactory1, valueFactory2)
pairs[firstGlyphName] = set.values
def decompileFormat2(self, reader, otFont): def decompileFormat2(self, reader, otFont):
coverage = reader.readTable(otCommon.CoverageTable, otFont) coverage = reader.readTable(otCommon.CoverageTable, otFont)
@ -62,14 +81,48 @@ class PairPos:
for i in range(class1Count): for i in range(class1Count):
row = {} row = {}
for j in range(class2Count): for j in range(class2Count):
value1 = valueFactory1.getValueRecord(reader) value1 = valueFactory1.readValueRecord(reader, otFont)
value2 = valueFactory2.getValueRecord(reader) value2 = valueFactory2.readValueRecord(reader, otFont)
if value1 or value2: if value1 or value2:
row[j] = (value1, value2) row[j] = (value1, value2)
if row: if row:
pairs[i] = row pairs[i] = row
def compile(self, otFont): 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 xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
@ -86,38 +139,56 @@ class PairPos:
pairs.sort() pairs.sort()
for firstGlyph, secondGlyphs in pairs: for firstGlyph, secondGlyphs in pairs:
for secondGlyph, value1, value2 in secondGlyphs: for secondGlyph, value1, value2 in secondGlyphs:
#XXXXXXXXX xmlWriter.begintag("Pair", pair=firstGlyph+","+secondGlyph)
xmlWriter.begintag("Pair", first=firstGlyph, second=secondGlyph)
xmlWriter.newline()
if value1: if value1:
value1.toXML(xmlWriter, otFont) value1.toXML(xmlWriter, otFont)
else:
xmlWriter.simpletag("Value")
if value2: if value2:
value2.toXML(xmlWriter, otFont) value2.toXML(xmlWriter, otFont)
#else: # the second value can be omitted
# xmlWriter.simpletag("Value")
xmlWriter.endtag("Pair") xmlWriter.endtag("Pair")
xmlWriter.newline() xmlWriter.newline()
def toXMLFormat2(self, xmlWriter, otFont): def toXMLFormat2(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class PairSet: class PairSet:
def decompile(self, reader, otFont, valueFactory1, valueFactory2): 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() pairValueCount = reader.readUShort()
self.values = values = [] self.values = values = []
for j in range(pairValueCount): for j in range(pairValueCount):
secondGlyphID = reader.readUShort() secondGlyphID = reader.readUShort()
secondGlyphName = otFont.getGlyphName(secondGlyphID) secondGlyphName = otFont.getGlyphName(secondGlyphID)
value1 = valueFactory1.getValueRecord(reader) value1 = self.valueFactory1.readValueRecord(reader, otFont)
value2 = valueFactory2.getValueRecord(reader) value2 = self.valueFactory2.readValueRecord(reader, otFont)
values.append((secondGlyphName, value1, value2)) values.append((secondGlyphName, value1, value2))
def compile(self, otFont): def compile(self, writer, otFont):
xxx 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)
# #
# ------------------ # ------------------
@ -126,81 +197,180 @@ class PairSet:
class CursivePos: class CursivePos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
class MarkBasePos: class MarkBasePos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
class MarkLigPos: class MarkLigPos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
class MarkMarkPos: class MarkMarkPos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
class ContextPos: class ContextPos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
class ChainContextPos: class ChainContextPos:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
pass xxx
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() 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 = { lookupTypeClasses = {
1: SinglePos, 1: SinglePos,
2: PairPos, 2: PairPos,
@ -212,83 +382,3 @@ lookupTypeClasses = {
8: ChainContextPos, 8: ChainContextPos,
} }
valueRecordFormat = [
# Mask Name struct format char
(0x0001, "XPlacement", "h"),
(0x0002, "YPlacement", "h"),
(0x0004, "XAdvance", "h"),
(0x0008, "YAdvance", "h"),
(0x0010, "XPlaDevice", "H"),
(0x0020, "YPlaDevice", "H"),
(0x0040, "XAdvDevice", "H"),
(0x0080, "YAdvDevice", "H"),
# reserved:
(0x0100, "Reserved1", "H"),
(0x0200, "Reserved2", "H"),
(0x0400, "Reserved3", "H"),
(0x0800, "Reserved4", "H"),
(0x1000, "Reserved5", "H"),
(0x2000, "Reserved6", "H"),
(0x4000, "Reserved7", "H"),
(0x8000, "Reserved8", "H"),
]
class ValueRecordFactory:
def __init__(self, valueFormat):
format = ">"
names = []
for mask, name, formatChar in valueRecordFormat:
if valueFormat & mask:
names.append(name)
format = format + formatChar
self.names, self.format = names, format
self.size = 2 * len(names)
def getValueRecord(self, reader):
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, value in items:
setattr(valueRecord, name, value)
return valueRecord
class ValueRecord:
# see ValueRecordFactory
def __nonzero__(self):
for value in self.__dict__.values():
if value:
return 1
return 0
def toXML(self, xmlWriter, otFont):
simpleItems = []
for mask, name, format in valueRecordFormat[:4]: # "simple" values
if hasattr(self, name):
simpleItems.append((name, getattr(self, name)))
deviceItems = []
for mask, name, format in valueRecordFormat[4:8]: # device records
if hasattr(self, name):
deviceItems.append((name, getattr(self, name)))
if deviceItems:
xmlWriter.begintag("ValueRecord", simpleItems)
xmlWriter.newline()
for name, deviceRecord in deviceItems:
xxx
xmlWriter.endtag("ValueRecord")
xmlWriter.newline()
else:
xmlWriter.simpletag("ValueRecord", simpleItems)
xmlWriter.newline()
def __repr__(self):
return "<ValueRecord>"

View File

@ -41,9 +41,33 @@ class SingleSubst:
input = glyphNames[i] input = glyphNames[i]
substitutions[input] = output substitutions[input] = output
def compile(self, otFont): 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 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): def toXML(self, xmlWriter, otFont):
substitutions = self.substitutions.items() substitutions = self.substitutions.items()
substitutions.sort() substitutions.sort()
@ -52,7 +76,7 @@ class SingleSubst:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class MultipleSubst: class MultipleSubst:
@ -67,9 +91,9 @@ class MultipleSubst:
self.substitutions = substitutions = {} self.substitutions = substitutions = {}
for i in range(sequenceCount): for i in range(sequenceCount):
sequence = reader.readTable(Sequence, otFont) sequence = reader.readTable(Sequence, otFont)
substitutions[glyphNames[i]] = sequence.glyphs substitutions[glyphNames[i]] = sequence.getGlyphs()
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
@ -83,12 +107,15 @@ class MultipleSubst:
class Sequence: class Sequence:
def getGlyphs(self):
return self.glyphs
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
self.glyphs = [] self.glyphs = []
for i in range(reader.readUShort()): for i in range(reader.readUShort()):
self.glyphs.append(otFont.getGlyphName(reader.readUShort())) self.glyphs.append(otFont.getGlyphName(reader.readUShort()))
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
@ -102,16 +129,30 @@ class AlternateSubst:
coverage = reader.readTable(otCommon.CoverageTable, otFont) coverage = reader.readTable(otCommon.CoverageTable, otFont)
glyphNames = coverage.getGlyphNames() glyphNames = coverage.getGlyphNames()
alternateSetCount = reader.readUShort() alternateSetCount = reader.readUShort()
self.alternateSet = alternateSet = {} self.alternateSets = alternateSets = {}
for i in range(alternateSetCount): for i in range(alternateSetCount):
set = reader.readTable(AlternateSet, otFont) set = reader.readTable(AlternateSet, otFont)
alternateSet[glyphNames[i]] = set.glyphs alternateSets[glyphNames[i]] = set.getGlyphs()
def compile(self, otFont): def compile(self, writer, otFont):
xxx 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): def toXML(self, xmlWriter, otFont):
alternates = self.alternateSet.items() alternates = self.alternateSets.items()
alternates.sort() alternates.sort()
for input, substList in alternates: for input, substList in alternates:
xmlWriter.begintag("AlternateSet", [("in", input)]) xmlWriter.begintag("AlternateSet", [("in", input)])
@ -125,22 +166,31 @@ class AlternateSubst:
class AlternateSet: class AlternateSet:
def getGlyphs(self):
return self.glyphs
def setGlyphs(self, glyphs):
self.glyphs = glyphs
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
glyphCount = reader.readUShort() glyphCount = reader.readUShort()
glyphIDs = reader.readUShortArray(glyphCount) glyphIDs = reader.readUShortArray(glyphCount)
self.glyphs = map(otFont.getGlyphName, glyphIDs) self.glyphs = map(otFont.getGlyphName, glyphIDs)
def compile(self, otFont): def compile(self, writer, otFont):
xxx glyphs = self.glyphs
writer.writeUShort(len(glyphs))
glyphIDs = map(otFont.getGlyphID, glyphs)
writer.writeUShortArray(glyphIDs)
class LigatureSubst: class LigatureSubst:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
format = reader.readUShort() self.format = reader.readUShort()
if format <> 1: if self.format <> 1:
from fontTools import ttLib from fontTools import ttLib
raise ttLib.TTLibError, "unknown LigatureSubst format: %d" % format raise ttLib.TTLibError, "unknown LigatureSubst format: %d" % self.format
coverage = reader.readTable(otCommon.CoverageTable, otFont) coverage = reader.readTable(otCommon.CoverageTable, otFont)
glyphNames = coverage.getGlyphNames() glyphNames = coverage.getGlyphNames()
ligSetCount = reader.readUShort() ligSetCount = reader.readUShort()
@ -148,11 +198,33 @@ class LigatureSubst:
for i in range(ligSetCount): for i in range(ligSetCount):
firstGlyph = glyphNames[i] firstGlyph = glyphNames[i]
ligSet = reader.readTable(LigatureSet, otFont) ligSet = reader.readTable(LigatureSet, otFont)
for ligatureGlyph, components in ligSet.ligatures: for components, ligatureGlyph in ligSet.getLigatures():
ligatures.append(((firstGlyph,) + tuple(components)), ligatureGlyph) ligatures.append((((firstGlyph,) + tuple(components)), ligatureGlyph))
def compile(self, otFont): def compile(self, writer, otFont):
xxx 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): def toXML(self, xmlWriter, otFont):
import string import string
@ -163,19 +235,39 @@ class LigatureSubst:
class LigatureSet: 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): def decompile(self, reader, otFont):
ligatureCount = reader.readUShort() ligatureCount = reader.readUShort()
self.ligatures = ligatures = [] self.ligatures = ligatures = []
for i in range(ligatureCount): for i in range(ligatureCount):
lig = reader.readTable(Ligature, otFont) lig = reader.readTable(Ligature, otFont)
ligatures.append((lig.ligatureGlyph, lig.components)) ligatures.append(lig.get())
def compile(self, otFont): def compile(self, writer, otFont):
xxx writer.writeUShort(len(self.ligatures))
for components, output in self.ligatures:
lig = Ligature()
lig.set(components, output)
writer.writeTable(lig, otFont)
class Ligature: 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): def decompile(self, reader, otFont):
self.ligatureGlyph = otFont.getGlyphName(reader.readUShort()) self.ligatureGlyph = otFont.getGlyphName(reader.readUShort())
compCount = reader.readUShort() compCount = reader.readUShort()
@ -183,8 +275,12 @@ class Ligature:
for i in range(compCount-1): for i in range(compCount-1):
components.append(otFont.getGlyphName(reader.readUShort())) components.append(otFont.getGlyphName(reader.readUShort()))
def compile(self, otFont): def compile(self, writer, otFont):
xxx 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: class ContextSubst:
@ -219,11 +315,11 @@ class ContextSubst:
lookupRecord.decompile(reader, otFont) lookupRecord.decompile(reader, otFont)
substitutions.append((coverage[i].getGlyphNames(), lookupRecord)) substitutions.append((coverage[i].getGlyphNames(), lookupRecord))
def compile(self, otFont): def compile(self, writer, otFont):
xxx xxx
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
@ -242,19 +338,63 @@ class ChainContextSubst:
raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format
def decompileFormat1(self, reader, otFont): def decompileFormat1(self, reader, otFont):
pass XXX
def decompileFormat2(self, reader, otFont): def decompileFormat2(self, reader, otFont):
pass XXX
def decompileFormat3(self, reader, otFont): def decompileFormat3(self, reader, otFont):
pass backtrackGlyphCount = reader.readUShort()
backtrackCoverage = reader.readTableArray(backtrackGlyphCount, otCommon.CoverageTable, otFont)
self.backtrack = otCommon.unpackCoverageArray(backtrackCoverage)
def compile(self, otFont): inputGlyphCount = reader.readUShort()
xxx 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 = reader.readTableArray(substCount, SubstLookupRecord, otFont)
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))
writer.writeTableArray(self.substitutions, otFont)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX") xmlWriter.comment("NotImplemented")
xmlWriter.newline() xmlWriter.newline()
@ -278,6 +418,7 @@ class SubstLookupRecord:
self.sequenceIndex = reader.readUShort() self.sequenceIndex = reader.readUShort()
self.lookupListIndex = reader.readUShort() self.lookupListIndex = reader.readUShort()
def compile(self, otFont): def compile(self, writer, otFont):
xxx writer.writeUShort(self.sequenceIndex)
writer.writeUShort(self.lookupListIndex)

View File

@ -1,10 +1,16 @@
"""ttLib.tables.otCommon.py -- Various data structures used by various OpenType tables. """fontTools.ttLib.tables.otCommon.py -- Various data structures used
""" by various OpenType tables."""
import struct, sstruct import struct, sstruct
import string
import DefaultTable import DefaultTable
from fontTools import ttLib from fontTools import ttLib
# temporary switch:
# - if true use possibly incomplete compile/decompile/toXML/fromXML implementation
# - if false use DefaultTable, ie. dump as hex.
TESTING_OT = 0
class base_GPOS_GSUB(DefaultTable.DefaultTable): class base_GPOS_GSUB(DefaultTable.DefaultTable):
@ -13,8 +19,7 @@ class base_GPOS_GSUB(DefaultTable.DefaultTable):
version = 0x00010000 version = 0x00010000
def decompile(self, data, otFont): def decompile(self, data, otFont):
self.data = data # while work is in progress, dump as hex #self.data = data # handy for debugging
return
reader = OTTableReader(data) reader = OTTableReader(data)
self.version = reader.readLong() self.version = reader.readLong()
if self.version <> 0x00010000: if self.version <> 0x00010000:
@ -25,11 +30,13 @@ class base_GPOS_GSUB(DefaultTable.DefaultTable):
self.lookupList = reader.readTable(LookupList, otFont, self.tableTag) self.lookupList = reader.readTable(LookupList, otFont, self.tableTag)
def compile(self, otFont): def compile(self, otFont):
return self.data writer = OTTableWriter()
writer.writeLong(self.version)
writer.writeTable(self.scriptList, otFont)
writer.writeTable(self.featureList, otFont)
writer.writeTable(self.lookupList, otFont)
return writer.getData()
class Dummy:
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
names = [("ScriptList", "scriptList"), names = [("ScriptList", "scriptList"),
("FeatureList", "featureList"), ("FeatureList", "featureList"),
@ -45,9 +52,13 @@ class Dummy:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
if not TESTING_OT:
# disable the GPOS/GSUB code, dump as hex.
base_GPOS_GSUB = DefaultTable.DefaultTable
# #
# Script List and subtables # Script List and subtables
# #
@ -61,8 +72,9 @@ class ScriptList:
scriptCount = reader.readUShort() scriptCount = reader.readUShort()
self.scripts = reader.readTagList(scriptCount, Script, otFont) self.scripts = reader.readTagList(scriptCount, Script, otFont)
def compile(self, otFont): def compile(self, writer, otFont):
XXXXXX writer.writeUShort(len(self.scripts))
writer.writeTagList(self.scripts, otFont)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
for tag, script in self.scripts: for tag, script in self.scripts:
@ -73,19 +85,20 @@ class ScriptList:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class Script: class Script:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
self.defaultLangSystem = None
self.defaultLangSystem = reader.readTable(LanguageSystem, otFont) self.defaultLangSystem = reader.readTable(LanguageSystem, otFont)
langSysCount = reader.readUShort() langSysCount = reader.readUShort()
self.languageSystems = reader.readTagList(langSysCount, LanguageSystem, otFont) self.languageSystems = reader.readTagList(langSysCount, LanguageSystem, otFont)
def compile(self, otFont): def compile(self, writer, otFont):
XXXXX writer.writeTable(self.defaultLangSystem, otFont)
writer.writeUShort(len(self.languageSystems))
writer.writeTagList(self.languageSystems, otFont)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.begintag("DefaultLanguageSystem") xmlWriter.begintag("DefaultLanguageSystem")
@ -109,8 +122,11 @@ class LanguageSystem:
featureCount = reader.readUShort() featureCount = reader.readUShort()
self.featureIndex = reader.readUShortArray(featureCount) self.featureIndex = reader.readUShortArray(featureCount)
def compile(self, otFont): def compile(self, writer, otFont):
xxx writer.writeUShort(self.lookupOrder)
writer.writeUShort(self.reqFeatureIndex)
writer.writeUShort(len(self.featureIndex))
writer.writeUShortArray(self.featureIndex)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.simpletag("LookupOrder", value=self.lookupOrder) xmlWriter.simpletag("LookupOrder", value=self.lookupOrder)
@ -135,8 +151,9 @@ class FeatureList:
featureCount = reader.readUShort() featureCount = reader.readUShort()
self.features = reader.readTagList(featureCount, Feature, otFont) self.features = reader.readTagList(featureCount, Feature, otFont)
def compile(self, otFont): def compile(self, writer, otFont):
XXXXX writer.writeUShort(len(self.features))
writer.writeTagList(self.features, otFont)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
for index in range(len(self.features)): for index in range(len(self.features)):
@ -148,7 +165,7 @@ class FeatureList:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class Feature: class Feature:
@ -158,8 +175,10 @@ class Feature:
lookupCount = reader.readUShort() lookupCount = reader.readUShort()
self.lookupListIndex = reader.readUShortArray(lookupCount) self.lookupListIndex = reader.readUShortArray(lookupCount)
def compile(self, otFont): def compile(self, writer, otFont):
XXXXX writer.writeUShort(self.featureParams)
writer.writeUShort(len(self.lookupListIndex))
writer.writeUShortArray(self.lookupListIndex)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
xmlWriter.simpletag("FeatureParams", value=hex(self.featureParams)) xmlWriter.simpletag("FeatureParams", value=hex(self.featureParams))
@ -169,7 +188,7 @@ class Feature:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
# #
@ -183,12 +202,11 @@ class LookupList:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
lookupCount = reader.readUShort() lookupCount = reader.readUShort()
self.lookup = lookup = [] self.lookup = reader.readTableArray(lookupCount, LookupTable, otFont, self.parentTag)
for i in range(lookupCount):
lookup.append(reader.readTable(LookupTable, otFont, self.parentTag))
def compile(self, otFont): def compile(self, writer, otFont):
XXXXX writer.writeUShort(len(self.lookup))
writer.writeTableArray(self.lookup, otFont)
def toXML(self, xmlWriter, otFont): def toXML(self, xmlWriter, otFont):
for i in range(len(self.lookup)): for i in range(len(self.lookup)):
@ -202,7 +220,7 @@ class LookupList:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
class LookupTable: class LookupTable:
@ -215,13 +233,14 @@ class LookupTable:
self.lookupType = reader.readUShort() self.lookupType = reader.readUShort()
self.lookupFlag = reader.readUShort() self.lookupFlag = reader.readUShort()
subTableCount = reader.readUShort() subTableCount = reader.readUShort()
self.subTables = subTables = []
lookupTypeClass = parentTable.getLookupTypeClass(self.lookupType) lookupTypeClass = parentTable.getLookupTypeClass(self.lookupType)
for i in range(subTableCount): self.subTables = reader.readTableArray(subTableCount, lookupTypeClass, otFont)
subTables.append(reader.readTable(lookupTypeClass, otFont))
def compile(self, otFont): def compile(self, writer, otFont):
XXXXXX writer.writeUShort(self.lookupType)
writer.writeUShort(self.lookupFlag)
writer.writeUShort(len(self.subTables))
writer.writeTableArray(self.subTables, otFont)
def __repr__(self): def __repr__(self):
if not hasattr(self, "lookupTypeName"): if not hasattr(self, "lookupTypeName"):
@ -241,7 +260,7 @@ class LookupTable:
xmlWriter.newline() xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont): def fromXML(self, (name, attrs, content), otFont):
xxx raise NotImplementedError
# #
@ -256,6 +275,14 @@ class CoverageTable:
def getGlyphNames(self): def getGlyphNames(self):
return self.glyphNames return self.glyphNames
def setGlyphNames(self, glyphNames, otFont):
glyphIDs = map(otFont.getGlyphID, glyphNames)
glyphIDs = map(None, glyphIDs, glyphNames)
glyphIDs.sort()
self.glyphNames = map(lambda (x, y): y, glyphIDs)
self.glyphIDs = map(lambda (x, y): x, glyphIDs)
return self.glyphNames
def makeGlyphNames(self, otFont): def makeGlyphNames(self, otFont):
self.glyphNames = map(lambda i, o=otFont.getGlyphOrder(): o[i], self.glyphIDs) self.glyphNames = map(lambda i, o=otFont.getGlyphOrder(): o[i], self.glyphIDs)
@ -286,49 +313,66 @@ class CoverageTable:
for glyphID in range(startID, endID + 1): for glyphID in range(startID, endID + 1):
glyphIDs.append(glyphID) glyphIDs.append(glyphID)
def compile(self, otFont): def compile(self, writer, otFont):
# brute force ;-) # figure out which format is more compact, doing so by brute force...
data1 = self.compileFormat1(otFont)
data2 = self.compileFormat2(otFont) # compile format 1
if len(data1) <= len(data2): writer1 = OTTableWriter()
format = 1 writer1.writeUShort(1)
reader = data1 self.compileFormat1(writer1, otFont)
data1 = writer1.getData()
# compile format 2
writer2 = OTTableWriter()
writer2.writeUShort(2)
self.compileFormat2(writer2, otFont)
data2 = writer2.getData()
if len(data1) < len(data2):
writer.writeRaw(data1)
else: else:
format = 2 writer.writeRaw(data2)
reader = data2
return struct.pack(">H", format) + reader
def compileFormat1(self, otFont): def compileFormat1(self, writer, otFont):
xxxxx writer.writeUShort(len(self.glyphIDs))
glyphIDs = map(otFont.getGlyphID, self.glyphNames) writer.writeUShortArray(self.glyphIDs)
data = pack(">H", len(glyphIDs))
pack = struct.pack
for glyphID in glyphIDs:
data = data + pack(">H", glyphID)
return data
def compileFormat2(self, otFont): def compileFormat2(self, writer, otFont):
xxxxx
glyphIDs = map(otFont.getGlyphID, self.glyphNames)
ranges = [] ranges = []
lastID = startID = glyphIDs[0] lastID = startID = self.glyphIDs[0]
startCoverageIndex = 0 startCoverageIndex = 0
glyphCount = len(glyphIDs) glyphCount = len(self.glyphIDs)
for i in range(1, glyphCount+1): for i in range(1, glyphCount+1):
if i == glyphCount: if i == glyphCount:
glyphID = 0x1ffff # arbitrary, but larger than 0x10000 glyphID = 0x1ffff # arbitrary, but larger than 0x10000
else: else:
glyphID = glyphIDs[i] glyphID = self.glyphIDs[i]
if glyphID <> (lastID + 1): if glyphID <> (lastID + 1):
ranges.append((startID, lastID, startCoverageIndex)) ranges.append((startID, lastID, startCoverageIndex))
startCoverageIndex = i startCoverageIndex = i
startID = glyphID startID = glyphID
lastID = glyphID lastID = glyphID
ranges.sort() # sort by startID ranges.sort() # sort by startID
rangeData = "" writer.writeUShort(len(ranges))
for startID, endID, startCoverageIndex in ranges: for startID, endID, startCoverageIndex in ranges:
rangeData = rangeData + struct.pack(">HHH", startID, endID, startCoverageIndex) writer.writeUShort(startID)
return pack(">H", len(ranges)) + rangeData writer.writeUShort(endID)
writer.writeUShort(startCoverageIndex)
def unpackCoverageArray(coverageArray):
coverageArray = coverageArray[:]
for i in range(len(coverageArray)):
coverageArray[i] = coverageArray[i].getGlyphNames()
return coverageArray
def buildCoverageArray(coverageArray, otFont):
coverageArray = coverageArray[:]
for i in range(len(coverageArray)):
coverage = CoverageTable()
coverage.setGlyphNames(coverageArray[i], otFont)
coverageArray[i] = coverage
return coverageArray
class ClassDefinitionTable: class ClassDefinitionTable:
@ -374,7 +418,8 @@ class ClassDefinitionTable:
glyphName = otFont.getGlyphName(glyphID) glyphName = otFont.getGlyphName(glyphID)
classDefs.append((glyphName, classValue)) classDefs.append((glyphName, classValue))
def compile(self, otFont): def compile(self, writer, otFont):
XXX
# brute force again # brute force again
data1 = self.compileFormat1(otFont) data1 = self.compileFormat1(otFont)
data2 = self.compileFormat2(otFont) data2 = self.compileFormat2(otFont)
@ -387,6 +432,7 @@ class ClassDefinitionTable:
return struct.pack(">H", format) + data return struct.pack(">H", format) + data
def compileFormat1(self, otFont): def compileFormat1(self, otFont):
XXX
items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID: items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
(getGlyphID(glyphName), classValue), self.glyphs.items()) (getGlyphID(glyphName), classValue), self.glyphs.items())
items.sort() items.sort()
@ -402,6 +448,7 @@ class ClassDefinitionTable:
return struct.pack(">H", endGlyphID - startGlyphID + 1) + data return struct.pack(">H", endGlyphID - startGlyphID + 1) + data
def compileFormat2(self, otFont): def compileFormat2(self, otFont):
XXX
items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID: items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
(getGlyphID(glyphName), classValue), self.glyphs.items()) (getGlyphID(glyphName), classValue), self.glyphs.items())
items.sort() items.sort()
@ -436,11 +483,9 @@ class ClassDefinitionTable:
class DeviceTable: class DeviceTable:
def decompile(self, reader, otFont): def decompile(self, reader, otFont):
xxxxxx self.startSize = reader.readUShort()
self.startSize = unpack_uint16(reader[:2]) endSize = reader.readUShort()
endSize = unpack_uint16(reader[2:4]) deltaFormat = reader.readUShort()
deltaFormat = unpack_uint16(reader[4:6])
reader = reader[6:]
if deltaFormat == 1: if deltaFormat == 1:
bits = 2 bits = 2
elif deltaFormat == 2: elif deltaFormat == 2:
@ -457,7 +502,7 @@ class DeviceTable:
shift = 1 << bits shift = 1 << bits
for i in range(0, deltaCount, numCount): for i in range(0, deltaCount, numCount):
offset = 2*i/numCount offset = 2*i/numCount
chunk = unpack_uint16(reader[offset:offset+2]) chunk = reader.readUShort()
deltas = [] deltas = []
for j in range(numCount): for j in range(numCount):
delta = chunk & mask delta = chunk & mask
@ -469,7 +514,9 @@ class DeviceTable:
deltaValues = deltaValues + deltas deltaValues = deltaValues + deltas
self.deltaValues = deltaValues[:deltaCount] self.deltaValues = deltaValues[:deltaCount]
def compile(self, otFont): def compile(self, writer, otFont):
raise NotImplementedError
# XXX
deltaValues = self.deltaValues deltaValues = self.deltaValues
startSize = self.startSize startSize = self.startSize
endSize = startSize + len(deltaValues) - 1 endSize = startSize + len(deltaValues) - 1
@ -515,6 +562,15 @@ class OTTableReader:
self.offset = offset self.offset = offset
self.pos = offset self.pos = offset
def readTable(self, tableClass, otFont, *args):
offset = self.readOffset()
if offset == 0:
return None
newReader = self.getSubString(offset)
table = apply(tableClass, args)
table.decompile(newReader, otFont)
return table
def readUShort(self): def readUShort(self):
pos = self.pos pos = self.pos
newpos = pos + 2 newpos = pos + 2
@ -549,14 +605,12 @@ class OTTableReader:
def readUShortArray(self, count): def readUShortArray(self, count):
return self.readArray(count, "H") return self.readArray(count, "H")
readOffsetArray = readUShortArray
def readShortArray(self, count): def readShortArray(self, count):
return self.readArray(count, "h") return self.readArray(count, "h")
def readArray(self, count, format): def readArray(self, count, format):
assert format in "Hh"
from array import array from array import array
assert format in "Hh"
pos = self.pos pos = self.pos
newpos = pos + 2 * count newpos = pos + 2 * count
a = array(format) a = array(format)
@ -566,15 +620,6 @@ class OTTableReader:
self.pos = newpos self.pos = newpos
return a.tolist() return a.tolist()
def readTable(self, tableClass, otFont, *args):
offset = self.readOffset()
if offset == 0:
return None
newReader = self.getSubString(offset)
table = apply(tableClass, args)
table.decompile(newReader, otFont)
return table
def readTableArray(self, count, tableClass, otFont, *args): def readTableArray(self, count, tableClass, otFont, *args):
list = [] list = []
for i in range(count): for i in range(count):
@ -608,3 +653,86 @@ class OTTableReader:
self.pos = self.pos + n self.pos = self.pos + n
class OTTableWriter:
def __init__(self):
self.items = []
def getData(self):
items = self.items[:]
offset = 0
for item in items:
if hasattr(item, "getData"):
offset = offset + 2 # sizeof(Offset)
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] = packOffset(cache[subTableData])
else:
items[i] = packOffset(offset)
subTables.append(subTableData)
cache[subTableData] = offset
offset = offset + len(subTableData)
return string.join(items, "") + string.join(subTables, "")
def writeTable(self, subTable, otFont):
if subTable is None:
self.writeUShort(0)
else:
subWriter = self.__class__()
self.items.append(subWriter)
subTable.compile(subWriter, otFont)
def writeUShort(self, value):
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 writeUShortArray(self, array):
return self.writeArray(array, "H")
def writeShortArray(self, array):
return self.writeArray(array, "h")
def writeArray(self, list, format):
from array import array
assert format in "Hh"
a = array(format, list)
if ttLib.endian <> 'big':
a.byteswap()
self.items.append(a.tostring())
def writeTableArray(self, list, otFont):
for subTable in list:
self.writeTable(subTable, otFont)
def writeTagList(self, list, otFont):
for tag, subTable in list:
self.writeTag(tag)
self.writeTable(subTable, otFont)
def writeStruct(self, format, values):
data = apply(struct.pack, (format,) + values)
self.items.append(data)
def writeRaw(self, data):
self.items.append(data)
def packOffset(offset):
return struct.pack(">H", offset)