import os
import textwrap
from xml.etree import ElementTree
from fontTools.ttLib import TTFont, newTable
from fontTools.misc.psCharStrings import T2CharString
from fontTools.ttLib.tables.otTables import (
GSUB,
ScriptList,
ScriptRecord,
Script,
DefaultLangSys,
FeatureList,
FeatureRecord,
Feature,
LookupList,
Lookup,
AlternateSubst,
SingleSubst,
)
# paths
directory = os.path.dirname(__file__)
shellSourcePath = os.path.join(directory, "gsubtest-shell.ttx" )
shellTempPath = os.path.join(directory, "gsubtest-shell.otf" )
featureList = os.path.join(directory, "gsubtest-features.txt" )
javascriptData = os.path.join(directory, "gsubtest-features.js" )
outputPath = os.path.join(os.path.dirname(directory), "gsubtest-lookup%d" )
baseCodepoint = 0xE000
# -------
# Features
# -------
f = open(featureList, "rb" )
text = f.read()
f.close()
mapping = []
for line in text.splitlines():
line = line.strip()
if not line:
continue
if line.startswith("#"):
continue
# parse
values = line.split("\t" )
tag = values.pop(0)
mapping.append(tag)
# --------
# Outlines
# --------
def addGlyphToCFF(
glyphName=None ,
program=None ,
private=None ,
globalSubrs=None ,
charStringsIndex=None ,
topDict=None ,
charStrings=None ,
):
charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)
charStringsIndex.append(charString)
glyphID = len(topDict.charset)
charStrings.charStrings[glyphName] = glyphID
topDict.charset.append(glyphName)
def makeLookup1():
# make a variation of the shell TTX data
f = open(shellSourcePath)
ttxData = f.read()
f.close()
ttxData = ttxData.replace("__familyName__" , "gsubtest-lookup1" )
tempShellSourcePath = shellSourcePath + ".temp"
f = open(tempShellSourcePath, "wb" )
f.write(ttxData)
f.close()
# compile the shell
shell = TTFont(sfntVersion="OTTO" )
shell.importXML(tempShellSourcePath)
shell.save(shellTempPath)
os.remove(tempShellSourcePath)
# load the shell
shell = TTFont(shellTempPath)
# grab the PASS and FAIL data
hmtx = shell["hmtx" ]
glyphSet = shell.getGlyphSet()
failGlyph = glyphSet["F" ]
failGlyph.decompile()
failGlyphProgram = list(failGlyph.program)
failGlyphMetrics = hmtx["F" ]
passGlyph = glyphSet["P" ]
passGlyph.decompile()
passGlyphProgram = list(passGlyph.program)
passGlyphMetrics = hmtx["P" ]
# grab some tables
hmtx = shell["hmtx" ]
cmap = shell["cmap" ]
# start the glyph order
existingGlyphs = [".notdef" , "space" , "F" , "P" ]
glyphOrder = list(existingGlyphs)
# start the CFF
cff = shell["CFF " ].cff
globalSubrs = cff.GlobalSubrs
topDict = cff.topDictIndex[0]
topDict.charset = existingGlyphs
private = topDict.Private
charStrings = topDict.CharStrings
charStringsIndex = charStrings.charStringsIndex
features = sorted(mapping)
# build the outline, hmtx and cmap data
cp = baseCodepoint
for index, tag in enumerate(features):
# tag.pass
glyphName = "%s.pass" % tag
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=passGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = passGlyphMetrics
for table in cmap.tables:
if table.format == 4:
table.cmap[cp] = glyphName
else :
raise NotImplementedError(
"Unsupported cmap table format: %d" % table.format
)
cp += 1
# tag.fail
glyphName = "%s.fail" % tag
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=failGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = failGlyphMetrics
for table in cmap.tables:
if table.format == 4:
table.cmap[cp] = glyphName
else :
raise NotImplementedError(
"Unsupported cmap table format: %d" % table.format
)
# bump this up so that the sequence is the same as the lookup 3 font
cp += 3
# set the glyph order
shell.setGlyphOrder(glyphOrder)
# start the GSUB
shell["GSUB" ] = newTable("GSUB" )
gsub = shell["GSUB" ].table = GSUB()
gsub.Version = 1.0
# make a list of all the features we will make
featureCount = len(features)
# set up the script list
scriptList = gsub.ScriptList = ScriptList()
scriptList.ScriptCount = 1
scriptList.ScriptRecord = []
scriptRecord = ScriptRecord()
scriptList.ScriptRecord.append(scriptRecord)
scriptRecord.ScriptTag = "DFLT"
script = scriptRecord.Script = Script()
defaultLangSys = script.DefaultLangSys = DefaultLangSys()
defaultLangSys.FeatureCount = featureCount
defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount)
defaultLangSys.ReqFeatureIndex = 65535
defaultLangSys.LookupOrder = None
script.LangSysCount = 0
script.LangSysRecord = []
# set up the feature list
featureList = gsub.FeatureList = FeatureList()
featureList.FeatureCount = featureCount
featureList.FeatureRecord = []
for index, tag in enumerate(features):
# feature record
featureRecord = FeatureRecord()
featureRecord.FeatureTag = tag
feature = featureRecord.Feature = Feature()
featureList.FeatureRecord.append(featureRecord)
# feature
feature.FeatureParams = None
feature.LookupCount = 1
feature.LookupListIndex = [index]
# write the lookups
lookupList = gsub.LookupList = LookupList()
lookupList.LookupCount = featureCount
lookupList.Lookup = []
for tag in features:
# lookup
lookup = Lookup()
lookup.LookupType = 1
lookup.LookupFlag = 0
lookup.SubTableCount = 1
lookup.SubTable = []
lookupList.Lookup.append(lookup)
# subtable
subtable = SingleSubst()
subtable.Format = 2
subtable.LookupType = 1
subtable.mapping = {
"%s.pass" % tag: "%s.fail" % tag,
"%s.fail" % tag: "%s.pass" % tag,
}
lookup.SubTable.append(subtable)
path = outputPath % 1 + ".otf"
if os.path.exists(path):
os.remove(path)
shell.save(path)
# get rid of the shell
if os.path.exists(shellTempPath):
os.remove(shellTempPath)
def makeLookup3():
# make a variation of the shell TTX data
f = open(shellSourcePath)
ttxData = f.read()
f.close()
ttxData = ttxData.replace("__familyName__" , "gsubtest-lookup3" )
tempShellSourcePath = shellSourcePath + ".temp"
f = open(tempShellSourcePath, "wb" )
f.write(ttxData)
f.close()
# compile the shell
shell = TTFont(sfntVersion="OTTO" )
shell.importXML(tempShellSourcePath)
shell.save(shellTempPath)
os.remove(tempShellSourcePath)
# load the shell
shell = TTFont(shellTempPath)
# grab the PASS and FAIL data
hmtx = shell["hmtx" ]
glyphSet = shell.getGlyphSet()
failGlyph = glyphSet["F" ]
failGlyph.decompile()
failGlyphProgram = list(failGlyph.program)
failGlyphMetrics = hmtx["F" ]
passGlyph = glyphSet["P" ]
passGlyph.decompile()
passGlyphProgram = list(passGlyph.program)
passGlyphMetrics = hmtx["P" ]
# grab some tables
hmtx = shell["hmtx" ]
cmap = shell["cmap" ]
# start the glyph order
existingGlyphs = [".notdef" , "space" , "F" , "P" ]
glyphOrder = list(existingGlyphs)
# start the CFF
cff = shell["CFF " ].cff
globalSubrs = cff.GlobalSubrs
topDict = cff.topDictIndex[0]
topDict.charset = existingGlyphs
private = topDict.Private
charStrings = topDict.CharStrings
charStringsIndex = charStrings.charStringsIndex
features = sorted(mapping)
# build the outline, hmtx and cmap data
cp = baseCodepoint
for index, tag in enumerate(features):
# tag.pass
glyphName = "%s.pass" % tag
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=passGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = passGlyphMetrics
# tag.fail
glyphName = "%s.fail" % tag
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=failGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = failGlyphMetrics
# tag.default
glyphName = "%s.default" % tag
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=passGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = passGlyphMetrics
for table in cmap.tables:
if table.format == 4:
table.cmap[cp] = glyphName
else :
raise NotImplementedError(
"Unsupported cmap table format: %d" % table.format
)
cp += 1
# tag.alt1,2,3
for i in range(1, 4):
glyphName = "%s.alt%d" % (tag, i)
glyphOrder.append(glyphName)
addGlyphToCFF(
glyphName=glyphName,
program=failGlyphProgram,
private=private,
globalSubrs=globalSubrs,
charStringsIndex=charStringsIndex,
topDict=topDict,
charStrings=charStrings,
)
hmtx[glyphName] = failGlyphMetrics
for table in cmap.tables:
if table.format == 4:
table.cmap[cp] = glyphName
else :
raise NotImplementedError(
"Unsupported cmap table format: %d" % table.format
)
cp += 1
# set the glyph order
shell.setGlyphOrder(glyphOrder)
# start the GSUB
shell["GSUB" ] = newTable("GSUB" )
gsub = shell["GSUB" ].table = GSUB()
gsub.Version = 1.0
# make a list of all the features we will make
featureCount = len(features)
# set up the script list
scriptList = gsub.ScriptList = ScriptList()
scriptList.ScriptCount = 1
scriptList.ScriptRecord = []
scriptRecord = ScriptRecord()
scriptList.ScriptRecord.append(scriptRecord)
scriptRecord.ScriptTag = "DFLT"
script = scriptRecord.Script = Script()
defaultLangSys = script.DefaultLangSys = DefaultLangSys()
defaultLangSys.FeatureCount = featureCount
defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount)
defaultLangSys.ReqFeatureIndex = 65535
defaultLangSys.LookupOrder = None
script.LangSysCount = 0
script.LangSysRecord = []
# set up the feature list
featureList = gsub.FeatureList = FeatureList()
featureList.FeatureCount = featureCount
featureList.FeatureRecord = []
for index, tag in enumerate(features):
# feature record
featureRecord = FeatureRecord()
featureRecord.FeatureTag = tag
feature = featureRecord.Feature = Feature()
featureList.FeatureRecord.append(featureRecord)
# feature
feature.FeatureParams = None
feature.LookupCount = 1
feature.LookupListIndex = [index]
# write the lookups
lookupList = gsub.LookupList = LookupList()
lookupList.LookupCount = featureCount
lookupList.Lookup = []
for tag in features:
# lookup
lookup = Lookup()
lookup.LookupType = 3
lookup.LookupFlag = 0
lookup.SubTableCount = 1
lookup.SubTable = []
lookupList.Lookup.append(lookup)
# subtable
subtable = AlternateSubst()
subtable.Format = 1
subtable.LookupType = 3
subtable.alternates = {
"%s.default" % tag: ["%s.fail" % tag, "%s.fail" % tag, "%s.fail" % tag],
"%s.alt1" % tag: ["%s.pass" % tag, "%s.fail" % tag, "%s.fail" % tag],
"%s.alt2" % tag: ["%s.fail" % tag, "%s.pass" % tag, "%s.fail" % tag],
"%s.alt3" % tag: ["%s.fail" % tag, "%s.fail" % tag, "%s.pass" % tag],
}
lookup.SubTable.append(subtable)
path = outputPath % 3 + ".otf"
if os.path.exists(path):
os.remove(path)
shell.save(path)
# get rid of the shell
if os.path.exists(shellTempPath):
os.remove(shellTempPath)
def makeJavascriptData():
features = sorted(mapping)
outStr = []
outStr.append("" )
outStr.append("/* This file is autogenerated by makegsubfonts.py */" )
outStr.append("" )
outStr.append("/* " )
outStr.append(" Features defined in gsubtest fonts with associated base" )
outStr.append(" codepoints for each feature:" )
outStr.append("" )
outStr.append(" cp = codepoint for feature featX" )
outStr.append("" )
outStr.append(" cp default PASS" )
outStr.append(" cp featX=1 FAIL" )
outStr.append(" cp featX=2 FAIL" )
outStr.append("" )
outStr.append(" cp+1 default FAIL" )
outStr.append(" cp+1 featX=1 PASS" )
outStr.append(" cp+1 featX=2 FAIL" )
outStr.append("" )
outStr.append(" cp+2 default FAIL" )
outStr.append(" cp+2 featX=1 FAIL" )
outStr.append(" cp+2 featX=2 PASS" )
outStr.append("" )
outStr.append("*/" )
outStr.append("" )
outStr.append("var gFeatures = {" )
cp = baseCodepoint
taglist = []
for tag in features:
taglist.append('"%s": 0x%x' % (tag, cp))
cp += 4
outStr.append(
textwrap.fill(", " .join(taglist), initial_indent=" " , subsequent_indent=" " )
)
outStr.append("};" )
outStr.append("" )
if os.path.exists(javascriptData):
os.remove(javascriptData)
f = open(javascriptData, "wb" )
f.write("\n" .join(outStr))
f.close()
# build fonts
print("Making lookup type 1 font..." )
makeLookup1()
print("Making lookup type 3 font..." )
makeLookup3()
# output javascript data
print("Making javascript data file..." )
makeJavascriptData()
Messung V0.5 C=90 H=65 G=78
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland