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
# 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):
def __init__(self, tag):
@ -28,11 +34,11 @@ class table_C_F_F_(DefaultTable.DefaultTable):
self.cff.fonts[self.cff.fontNames[0]].setGlyphOrder(glyphOrder)
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
DefaultTable.DefaultTable.toXML(self, writer, otFont)
else:
self.cff.toXML(writer, progress)
#def fromXML(self, (name, attrs, content), otFont):
# xxx

View File

@ -10,17 +10,39 @@ class table_G_P_O_S_(otCommon.base_GPOS_GSUB):
class SinglePos:
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
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
class PairPos:
@ -43,11 +65,8 @@ class PairPos:
self.pairs = pairs = {}
for i in range(reader.readUShort()):
firstGlyphName = glyphNames[i]
offset = reader.readOffset()
setData = reader.getSubString(offset)
set = PairSet()
set.decompile(setData, otFont, valueFactory1, valueFactory2)
pairs[firstGlyphName] = set.values
set = reader.readTable(PairSet, otFont, valueFactory1, valueFactory2)
pairs[firstGlyphName] = set.getValues()
def decompileFormat2(self, reader, otFont):
coverage = reader.readTable(otCommon.CoverageTable, otFont)
@ -62,14 +81,48 @@ class PairPos:
for i in range(class1Count):
row = {}
for j in range(class2Count):
value1 = valueFactory1.getValueRecord(reader)
value2 = valueFactory2.getValueRecord(reader)
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, 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
def toXML(self, xmlWriter, otFont):
@ -86,38 +139,56 @@ class PairPos:
pairs.sort()
for firstGlyph, secondGlyphs in pairs:
for secondGlyph, value1, value2 in secondGlyphs:
#XXXXXXXXX
xmlWriter.begintag("Pair", first=firstGlyph, second=secondGlyph)
xmlWriter.newline()
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("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
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()
self.values = values = []
for j in range(pairValueCount):
secondGlyphID = reader.readUShort()
secondGlyphName = otFont.getGlyphName(secondGlyphID)
value1 = valueFactory1.getValueRecord(reader)
value2 = valueFactory2.getValueRecord(reader)
value1 = self.valueFactory1.readValueRecord(reader, otFont)
value2 = self.valueFactory2.readValueRecord(reader, otFont)
values.append((secondGlyphName, value1, value2))
def compile(self, otFont):
xxx
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)
#
# ------------------
@ -126,81 +197,180 @@ class PairSet:
class CursivePos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
class MarkBasePos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
class MarkLigPos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
class MarkMarkPos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
class ContextPos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
class ChainContextPos:
def decompile(self, reader, otFont):
pass
xxx
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
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,
@ -212,83 +382,3 @@ lookupTypeClasses = {
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]
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
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()
@ -52,7 +76,7 @@ class SingleSubst:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
class MultipleSubst:
@ -67,9 +91,9 @@ class MultipleSubst:
self.substitutions = substitutions = {}
for i in range(sequenceCount):
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
def toXML(self, xmlWriter, otFont):
@ -83,12 +107,15 @@ class MultipleSubst:
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, otFont):
def compile(self, writer, otFont):
xxx
@ -102,16 +129,30 @@ class AlternateSubst:
coverage = reader.readTable(otCommon.CoverageTable, otFont)
glyphNames = coverage.getGlyphNames()
alternateSetCount = reader.readUShort()
self.alternateSet = alternateSet = {}
self.alternateSets = alternateSets = {}
for i in range(alternateSetCount):
set = reader.readTable(AlternateSet, otFont)
alternateSet[glyphNames[i]] = set.glyphs
alternateSets[glyphNames[i]] = set.getGlyphs()
def compile(self, otFont):
xxx
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.alternateSet.items()
alternates = self.alternateSets.items()
alternates.sort()
for input, substList in alternates:
xmlWriter.begintag("AlternateSet", [("in", input)])
@ -125,22 +166,31 @@ class AlternateSubst:
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, otFont):
xxx
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):
format = reader.readUShort()
if format <> 1:
self.format = reader.readUShort()
if self.format <> 1:
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)
glyphNames = coverage.getGlyphNames()
ligSetCount = reader.readUShort()
@ -148,11 +198,33 @@ class LigatureSubst:
for i in range(ligSetCount):
firstGlyph = glyphNames[i]
ligSet = reader.readTable(LigatureSet, otFont)
for ligatureGlyph, components in ligSet.ligatures:
ligatures.append(((firstGlyph,) + tuple(components)), ligatureGlyph)
for components, ligatureGlyph in ligSet.getLigatures():
ligatures.append((((firstGlyph,) + tuple(components)), ligatureGlyph))
def compile(self, otFont):
xxx
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
@ -163,19 +235,39 @@ class LigatureSubst:
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.ligatureGlyph, lig.components))
ligatures.append(lig.get())
def compile(self, otFont):
xxx
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()
@ -183,8 +275,12 @@ class Ligature:
for i in range(compCount-1):
components.append(otFont.getGlyphName(reader.readUShort()))
def compile(self, otFont):
xxx
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:
@ -219,11 +315,11 @@ class ContextSubst:
lookupRecord.decompile(reader, otFont)
substitutions.append((coverage[i].getGlyphNames(), lookupRecord))
def compile(self, otFont):
def compile(self, writer, otFont):
xxx
def toXML(self, xmlWriter, otFont):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
@ -242,19 +338,63 @@ class ChainContextSubst:
raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format
def decompileFormat1(self, reader, otFont):
pass
XXX
def decompileFormat2(self, reader, otFont):
pass
XXX
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):
xxx
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 = 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):
xmlWriter.comment("XXX")
xmlWriter.comment("NotImplemented")
xmlWriter.newline()
@ -278,6 +418,7 @@ class SubstLookupRecord:
self.sequenceIndex = reader.readUShort()
self.lookupListIndex = reader.readUShort()
def compile(self, otFont):
xxx
def compile(self, writer, otFont):
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 string
import DefaultTable
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):
@ -13,8 +19,7 @@ class base_GPOS_GSUB(DefaultTable.DefaultTable):
version = 0x00010000
def decompile(self, data, otFont):
self.data = data # while work is in progress, dump as hex
return
#self.data = data # handy for debugging
reader = OTTableReader(data)
self.version = reader.readLong()
if self.version <> 0x00010000:
@ -25,10 +30,12 @@ class base_GPOS_GSUB(DefaultTable.DefaultTable):
self.lookupList = reader.readTable(LookupList, otFont, self.tableTag)
def compile(self, otFont):
return self.data
class Dummy:
writer = OTTableWriter()
writer.writeLong(self.version)
writer.writeTable(self.scriptList, otFont)
writer.writeTable(self.featureList, otFont)
writer.writeTable(self.lookupList, otFont)
return writer.getData()
def toXML(self, xmlWriter, otFont):
names = [("ScriptList", "scriptList"),
@ -45,9 +52,13 @@ class Dummy:
xmlWriter.newline()
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
#
@ -61,8 +72,9 @@ class ScriptList:
scriptCount = reader.readUShort()
self.scripts = reader.readTagList(scriptCount, Script, otFont)
def compile(self, otFont):
XXXXXX
def compile(self, writer, otFont):
writer.writeUShort(len(self.scripts))
writer.writeTagList(self.scripts, otFont)
def toXML(self, xmlWriter, otFont):
for tag, script in self.scripts:
@ -73,19 +85,20 @@ class ScriptList:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
class Script:
def decompile(self, reader, otFont):
self.defaultLangSystem = None
self.defaultLangSystem = reader.readTable(LanguageSystem, otFont)
langSysCount = reader.readUShort()
self.languageSystems = reader.readTagList(langSysCount, LanguageSystem, otFont)
def compile(self, otFont):
XXXXX
def compile(self, writer, otFont):
writer.writeTable(self.defaultLangSystem, otFont)
writer.writeUShort(len(self.languageSystems))
writer.writeTagList(self.languageSystems, otFont)
def toXML(self, xmlWriter, otFont):
xmlWriter.begintag("DefaultLanguageSystem")
@ -109,8 +122,11 @@ class LanguageSystem:
featureCount = reader.readUShort()
self.featureIndex = reader.readUShortArray(featureCount)
def compile(self, otFont):
xxx
def compile(self, writer, otFont):
writer.writeUShort(self.lookupOrder)
writer.writeUShort(self.reqFeatureIndex)
writer.writeUShort(len(self.featureIndex))
writer.writeUShortArray(self.featureIndex)
def toXML(self, xmlWriter, otFont):
xmlWriter.simpletag("LookupOrder", value=self.lookupOrder)
@ -135,8 +151,9 @@ class FeatureList:
featureCount = reader.readUShort()
self.features = reader.readTagList(featureCount, Feature, otFont)
def compile(self, otFont):
XXXXX
def compile(self, writer, otFont):
writer.writeUShort(len(self.features))
writer.writeTagList(self.features, otFont)
def toXML(self, xmlWriter, otFont):
for index in range(len(self.features)):
@ -148,7 +165,7 @@ class FeatureList:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
class Feature:
@ -158,8 +175,10 @@ class Feature:
lookupCount = reader.readUShort()
self.lookupListIndex = reader.readUShortArray(lookupCount)
def compile(self, otFont):
XXXXX
def compile(self, writer, otFont):
writer.writeUShort(self.featureParams)
writer.writeUShort(len(self.lookupListIndex))
writer.writeUShortArray(self.lookupListIndex)
def toXML(self, xmlWriter, otFont):
xmlWriter.simpletag("FeatureParams", value=hex(self.featureParams))
@ -169,7 +188,7 @@ class Feature:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
#
@ -183,12 +202,11 @@ class LookupList:
def decompile(self, reader, otFont):
lookupCount = reader.readUShort()
self.lookup = lookup = []
for i in range(lookupCount):
lookup.append(reader.readTable(LookupTable, otFont, self.parentTag))
self.lookup = reader.readTableArray(lookupCount, LookupTable, otFont, self.parentTag)
def compile(self, otFont):
XXXXX
def compile(self, writer, otFont):
writer.writeUShort(len(self.lookup))
writer.writeTableArray(self.lookup, otFont)
def toXML(self, xmlWriter, otFont):
for i in range(len(self.lookup)):
@ -202,7 +220,7 @@ class LookupList:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
class LookupTable:
@ -215,13 +233,14 @@ class LookupTable:
self.lookupType = reader.readUShort()
self.lookupFlag = reader.readUShort()
subTableCount = reader.readUShort()
self.subTables = subTables = []
lookupTypeClass = parentTable.getLookupTypeClass(self.lookupType)
for i in range(subTableCount):
subTables.append(reader.readTable(lookupTypeClass, otFont))
self.subTables = reader.readTableArray(subTableCount, lookupTypeClass, otFont)
def compile(self, otFont):
XXXXXX
def compile(self, writer, otFont):
writer.writeUShort(self.lookupType)
writer.writeUShort(self.lookupFlag)
writer.writeUShort(len(self.subTables))
writer.writeTableArray(self.subTables, otFont)
def __repr__(self):
if not hasattr(self, "lookupTypeName"):
@ -241,7 +260,7 @@ class LookupTable:
xmlWriter.newline()
def fromXML(self, (name, attrs, content), otFont):
xxx
raise NotImplementedError
#
@ -256,6 +275,14 @@ class CoverageTable:
def getGlyphNames(self):
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):
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):
glyphIDs.append(glyphID)
def compile(self, otFont):
# brute force ;-)
data1 = self.compileFormat1(otFont)
data2 = self.compileFormat2(otFont)
if len(data1) <= len(data2):
format = 1
reader = data1
def compile(self, writer, otFont):
# figure out which format is more compact, doing so by brute force...
# compile format 1
writer1 = OTTableWriter()
writer1.writeUShort(1)
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:
format = 2
reader = data2
return struct.pack(">H", format) + reader
writer.writeRaw(data2)
def compileFormat1(self, otFont):
xxxxx
glyphIDs = map(otFont.getGlyphID, self.glyphNames)
data = pack(">H", len(glyphIDs))
pack = struct.pack
for glyphID in glyphIDs:
data = data + pack(">H", glyphID)
return data
def compileFormat1(self, writer, otFont):
writer.writeUShort(len(self.glyphIDs))
writer.writeUShortArray(self.glyphIDs)
def compileFormat2(self, otFont):
xxxxx
glyphIDs = map(otFont.getGlyphID, self.glyphNames)
def compileFormat2(self, writer, otFont):
ranges = []
lastID = startID = glyphIDs[0]
lastID = startID = self.glyphIDs[0]
startCoverageIndex = 0
glyphCount = len(glyphIDs)
glyphCount = len(self.glyphIDs)
for i in range(1, glyphCount+1):
if i == glyphCount:
glyphID = 0x1ffff # arbitrary, but larger than 0x10000
else:
glyphID = glyphIDs[i]
glyphID = self.glyphIDs[i]
if glyphID <> (lastID + 1):
ranges.append((startID, lastID, startCoverageIndex))
startCoverageIndex = i
startID = glyphID
lastID = glyphID
ranges.sort() # sort by startID
rangeData = ""
writer.writeUShort(len(ranges))
for startID, endID, startCoverageIndex in ranges:
rangeData = rangeData + struct.pack(">HHH", startID, endID, startCoverageIndex)
return pack(">H", len(ranges)) + rangeData
writer.writeUShort(startID)
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:
@ -374,7 +418,8 @@ class ClassDefinitionTable:
glyphName = otFont.getGlyphName(glyphID)
classDefs.append((glyphName, classValue))
def compile(self, otFont):
def compile(self, writer, otFont):
XXX
# brute force again
data1 = self.compileFormat1(otFont)
data2 = self.compileFormat2(otFont)
@ -387,6 +432,7 @@ class ClassDefinitionTable:
return struct.pack(">H", format) + data
def compileFormat1(self, otFont):
XXX
items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
(getGlyphID(glyphName), classValue), self.glyphs.items())
items.sort()
@ -402,6 +448,7 @@ class ClassDefinitionTable:
return struct.pack(">H", endGlyphID - startGlyphID + 1) + data
def compileFormat2(self, otFont):
XXX
items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
(getGlyphID(glyphName), classValue), self.glyphs.items())
items.sort()
@ -436,11 +483,9 @@ class ClassDefinitionTable:
class DeviceTable:
def decompile(self, reader, otFont):
xxxxxx
self.startSize = unpack_uint16(reader[:2])
endSize = unpack_uint16(reader[2:4])
deltaFormat = unpack_uint16(reader[4:6])
reader = reader[6:]
self.startSize = reader.readUShort()
endSize = reader.readUShort()
deltaFormat = reader.readUShort()
if deltaFormat == 1:
bits = 2
elif deltaFormat == 2:
@ -457,7 +502,7 @@ class DeviceTable:
shift = 1 << bits
for i in range(0, deltaCount, numCount):
offset = 2*i/numCount
chunk = unpack_uint16(reader[offset:offset+2])
chunk = reader.readUShort()
deltas = []
for j in range(numCount):
delta = chunk & mask
@ -469,7 +514,9 @@ class DeviceTable:
deltaValues = deltaValues + deltas
self.deltaValues = deltaValues[:deltaCount]
def compile(self, otFont):
def compile(self, writer, otFont):
raise NotImplementedError
# XXX
deltaValues = self.deltaValues
startSize = self.startSize
endSize = startSize + len(deltaValues) - 1
@ -515,6 +562,15 @@ class OTTableReader:
self.offset = 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):
pos = self.pos
newpos = pos + 2
@ -549,14 +605,12 @@ class OTTableReader:
def readUShortArray(self, count):
return self.readArray(count, "H")
readOffsetArray = readUShortArray
def readShortArray(self, count):
return self.readArray(count, "h")
def readArray(self, count, format):
assert format in "Hh"
from array import array
assert format in "Hh"
pos = self.pos
newpos = pos + 2 * count
a = array(format)
@ -566,15 +620,6 @@ class OTTableReader:
self.pos = newpos
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):
list = []
for i in range(count):
@ -608,3 +653,86 @@ class OTTableReader:
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)