fonttools/fw2ft.py
2015-10-17 03:43:31 -03:00

240 lines
6.4 KiB
Python
Executable File

#!/usr/bin/python
# FontWorker-to-FontTools for OpenType Layout tables
from __future__ import print_function, division, absolute_import
from fontTools import ttLib
from fontTools.ttLib.tables import otTables as ot
import re
debug = print
def skipUntil(lines, what):
for line in lines:
if line[0] == what:
return line
def readUntil(lines, what):
for line in lines:
if line[0] == what:
raise StopIteration
yield line
def parseScriptList(lines):
skipUntil(lines, 'script table begin')
self = ot.ScriptList()
self.ScriptRecord = []
for line in readUntil(lines, 'script table end'):
scriptTag, langSysTag, defaultFeature, features = line
debug("Adding script", scriptTag, "language-system", langSysTag)
langSys = ot.LangSys()
langSys.LookupOrder = None
# TODO The following two lines should use lazy feature name-to-index mapping
langSys.ReqFeatureIndex = int(defaultFeature) if defaultFeature else 0xFFFF
langSys.FeatureIndex = [int(f) for f in features.split(',')]
langSys.FeatureCount = len(langSys.FeatureIndex)
script = [s for s in self.ScriptRecord if s.ScriptTag == scriptTag]
if script:
script = script[0].Script
else:
scriptRec = ot.ScriptRecord()
scriptRec.ScriptTag = scriptTag
scriptRec.Script = ot.Script()
self.ScriptRecord.append(scriptRec)
script = scriptRec.Script
script.DefaultLangSys = None
script.LangSysRecord = []
script.LangSysCount = 0
if langSysTag == 'default':
script.DefaultLangSys = langSys
else:
langSysRec = ot.LangSysRecord()
langSysRec.LangSysTag = langSysTag + ' '*(4 - len(langSysTag))
langSysRec.LangSys = langSys
script.LangSysRecord.append(langSysRec)
script.LangSysCount = len(script.LangSysRecord)
self.ScriptCount = len(self.ScriptRecord)
# TODO sort scripts and langSys's?
return self
def parseFeatureList(lines):
skipUntil(lines, 'feature table begin')
self = ot.FeatureList()
self.FeatureRecord = []
for line in readUntil(lines, 'feature table end'):
idx, featureTag, lookups = line
assert int(idx) == len(self.FeatureRecord), "%d %d" % (idx, len(self.FeatureRecord))
featureRec = ot.FeatureRecord()
featureRec.FeatureTag = featureTag
featureRec.Feature = ot.Feature()
self.FeatureRecord.append(featureRec)
feature = featureRec.Feature
feature.FeatureParams = None
# TODO The following line should use lazy lookup name-to-index mapping
feature.LookupListIndex = [int(l) for l in lookups.split(',')]
feature.LookupCount = len(feature.LookupListIndex)
self.FeatureCount = len(self.FeatureRecord)
return self
def appendSingleSubst(self, line):
mapping = getattr(self, "mapping", None)
if mapping is None:
self.mapping = mapping = {}
mapping[line[0]] = line[1]
def appendMultiple(self, line):
debug(line)
raise NotImplementedError
def appendAlternate(self, line):
debug(line)
raise NotImplementedError
def appendLigature(self, line):
ligatures = getattr(self, "ligatures", None)
if ligatures is None:
self.ligatures = ligatures = {}
ligGlyph, firstGlyph = line[:2]
otherComponents = line[2:]
if firstGlyph not in ligatures:
ligatures[firstGlyph] = []
ligature = ot.Ligature()
ligature.Component = otherComponents
ligature.CompCount = len(ligature.Component) + 1
ligature.LigGlyph = ligGlyph
ligatures[firstGlyph].append(ligature)
def appendSinglePos(self, line):
debug(line)
raise NotImplementedError
def appendPair(self, line):
raise NotImplementedError
def appendCursive(self, line):
debug(line)
raise NotImplementedError
def appendMarkToSomething(self, line):
debug(line)
raise NotImplementedError
def appendMarkToLigature(self, line):
debug(line)
raise NotImplementedError
def appendContext(self, line):
debug(line)
raise NotImplementedError
def appendChained(self, line):
debug(line)
raise NotImplementedError
def parseLookupList(lines, tableTag):
self = ot.LookupList()
self.Lookup = []
while True:
line = skipUntil(lines, 'lookup')
if line is None: break
_, idx, typ = line
assert int(idx) == len(self.Lookup), "%d %d" % (idx, len(self.Lookup))
lookup = ot.Lookup()
self.Lookup.append(lookup)
lookup.LookupFlags = 0
lookup.LookupType, append = {
'GSUB': {
'single': (1, appendSingleSubst),
'multiple': (2, appendMultiple),
'alternate': (3, appendAlternate),
'ligature': (4, appendLigature),
'context': (5, appendContext),
'chained': (6, appendChained),
},
'GPOS': {
'single': (1, appendSinglePos),
'pair': (2, appendPair),
'kernset': (2, appendPair),
'cursive': (3, appendCursive),
'mark to base': (4, appendMarkToSomething),
'mark to ligature':(5, appendMarkToLigature),
'mark to mark': (6, appendMarkToSomething),
'context': (7, appendContext),
'chained': (8, appendChained),
},
}[tableTag][typ]
subtable = ot.lookupTypes[tableTag][lookup.LookupType]()
subtable.LookupType = lookup.LookupType
for line in readUntil(lines, 'lookup end'):
flag = {
'RightToLeft': 0x0001,
'IgnoreBaseGlyphs': 0x0002,
'IgnoreLigatures': 0x0004,
'IgnoreMarks': 0x0008,
}.get(line[0])
if flag:
assert line[1] in ['yes', 'no'], line[1]
if line[1] == 'yes':
lookup.LookupFlags |= flag
continue
if line[0] == 'MarkAttachmentType':
lookup.LookupFlags |= int(line[1]) << 8
continue
if len(line) > 1 or line[0] != '':
append(subtable, line)
lookup.SubTable = [subtable]
lookup.SubTableCount = len(lookup.SubTable)
self.LookupCount = len(self.Lookup)
return self
def parseGSUB(lines):
debug("Parsing GSUB")
self = ot.GSUB()
self.ScriptList = parseScriptList(lines)
self.FeatureList = parseFeatureList(lines)
self.LookupList = parseLookupList(lines, 'GSUB')
return self
def parseGPOS(lines):
debug("Parsing GPOS")
self = ot.GPOS()
# TODO parse EM?
self.ScriptList = parseScriptList(lines)
self.FeatureList = parseFeatureList(lines)
self.LookupList = parseLookupList(lines, 'GPOS')
return self
def parseGDEF(lines):
debug("Parsing GDEF TODO")
return None
def compile(s):
lines = (line.split('\t') for line in re.split('\r?\n?', s))
line = next(lines)
assert line[0][:9] == 'FontDame ', line
assert line[0][13:] == ' table', line
tableTag = line[0][9:13]
container = ttLib.getTableClass(tableTag)()
table = {'GSUB': parseGSUB,
'GPOS': parseGPOS,
'GDEF': parseGDEF,
}[tableTag](lines)
container.table = table
return container
if __name__ == '__main__':
import sys
for f in sys.argv[1:]:
debug("Processing", f)
compile(open(f).read())