[feaLib] Basic test framework for building OpenType features

This commit is contained in:
Sascha Brawer 2015-09-04 15:06:11 +02:00
parent b54c04f1d4
commit df740092d9
5 changed files with 219 additions and 0 deletions

View File

@ -6,6 +6,9 @@ class FeatureFile(object):
def __init__(self):
self.statements = []
def build(self, builder):
pass
class FeatureBlock(object):
def __init__(self, location, name, use_extension):

View File

@ -0,0 +1,45 @@
from __future__ import print_function, division, absolute_import
from __future__ import unicode_literals
from fontTools.feaLib.error import FeatureLibError
from fontTools.feaLib.parser import Parser
from fontTools.ttLib.tables import otTables
def addOpenTypeFeatures(featurefile_path, font):
builder = Builder(featurefile_path, font)
builder.build()
class Builder(object):
def __init__(self, featurefile_path, font):
self.featurefile_path = featurefile_path
self.font = font
self.language_systems_ = []
def build(self):
self.gpos = self.font['GPOS'] = self.makeTable('GPOS')
self.gsub = self.font['GSUB'] = self.makeTable('GSUB')
parsetree = Parser(self.featurefile_path).parse()
parsetree.build(self)
def makeTable(self, tag):
table = getattr(otTables, tag, None)()
table.Version = 1.0
table.ScriptList = otTables.ScriptList()
table.ScriptList.ScriptCount = 0
table.ScriptList.ScriptRecord = []
table.FeatureList = otTables.FeatureList()
table.FeatureList.FeatureCount = 0
table.FeatureList.FeatureRecord = []
table.LookupList = otTables.LookupList()
table.LookupList.LookupCount = 0
table.LookupList.Lookup = []
return table
def add_language_system(self, location, script, language):
if script == "DFLT" and language == "dflt" and self.language_systems_:
# OpenType Feature File Specification, section 4.b.i
raise FeatureLibError(
'If "languagesystem DFLT dflt" is present, it must be '
'the first of the languagesystem statements', location)
self.language_systems_.append((script, language))

View File

@ -0,0 +1,73 @@
from __future__ import print_function, division, absolute_import
from __future__ import unicode_literals
from fontTools.feaLib.builder import addOpenTypeFeatures
from fontTools.ttLib import TTFont
import codecs
import difflib
import os
import shutil
import sys
import tempfile
import unittest
class BuilderTest(unittest.TestCase):
def __init__(self, methodName):
unittest.TestCase.__init__(self, methodName)
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
# and fires deprecation warnings if a program uses the old name.
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def setUp(self):
self.tempdir = None
self.num_tempfiles = 0
def tearDown(self):
if self.tempdir:
shutil.rmtree(self.tempdir)
@staticmethod
def getpath(testfile):
path, _ = os.path.split(__file__)
return os.path.join(path, "testdata", testfile)
def temp_path(self, suffix):
if not self.tempdir:
self.tempdir = tempfile.mkdtemp()
self.num_tempfiles += 1
return os.path.join(self.tempdir,
"tmp%d%s" % (self.num_tempfiles, suffix))
def read_ttx(self, path):
lines = []
with codecs.open(path, "r", "utf-8") as ttx:
for line in ttx.readlines():
# Elide ttFont attributes because ttLibVersion may change,
# and use os-native line separators so we can run difflib.
if line.startswith("<ttFont "):
lines.append("<ttFont>" + os.linesep)
else:
lines.append(line.rstrip() + os.linesep)
return lines
def expect_ttx(self, font, expected_ttx):
path = self.temp_path(suffix=".ttx")
font.saveXML(path, quiet=True, tables=['GSUB', 'GPOS'])
actual = self.read_ttx(path)
expected = self.read_ttx(expected_ttx)
if actual != expected:
for line in difflib.unified_diff(
expected, actual, fromfile=path, tofile=expected_ttx):
sys.stdout.write(line)
self.fail("TTX output is different from expected")
def test_spec4h1(self):
# OpenType Feature File specification, section 4.h, example 1.
font = TTFont()
addOpenTypeFeatures(self.getpath("spec4h1.fea"), font)
self.expect_ttx(font, self.getpath("spec4h1_expected.ttx"))
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,64 @@
# OpenType Feature File specification, section 4.h, example 1.
# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
languagesystem DFLT dflt;
languagesystem latn dflt;
languagesystem latn DEU;
languagesystem latn TRK;
languagesystem cyrl dflt;
feature smcp {
sub [a-z] by [A.sc-Z.sc];
# Since all the rules in this feature are of the same type, they
# will be grouped in a single lookup. Since no script or language
# keyword has been specified yet, the lookup will be registered
# for this feature under all the language systems.
} smcp;
feature liga {
sub f f by f_f;
sub f i by f_i;
sub f l by f_l;
# Since all the rules in this feature are of the same type, they
# will be grouped in a single lookup. Since no script or language
# keyword has been specified yet, the lookup will be registered
# for this feature under all the language systems.
script latn;
language dflt;
# lookupflag 0; (implicit)
sub c t by c_t;
sub c s by c_s;
# The rules above will be placed in a lookup that is registered
# for all the specified languages for the script latn, but not any
# other scripts.
language DEU;
# script latn; (stays the same)
# lookupflag 0; (stays the same)
sub c h by c_h;
sub c k by c_k;
# The rules above will be placed in a lookup that is registered
# only under the script latn, language DEU.
language TRK;
# This will inherit both the top level default rules - the rules
# defined before the first 'script' statement, and the
# script-level default rules for 'latn': all the lookups of this
# feature defined after the 'script latn' statement, and before
# the language DEU statement. If TRK were not named here, it
# would not inherit the default rules for the script latn.
} liga;
# TODO(sascha): Uncomment once we support 'pos' statements.
# feature kern {
# pos a y -150;
# # [more pos statements]
# # All the rules in this feature will be grouped in a single lookup
# # that is is registered under all the language systems.
# } kern;

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GSUB>
<GSUB>
<Version value="1.0"/>
<ScriptList>
<!-- ScriptCount=0 -->
</ScriptList>
<FeatureList>
<!-- FeatureCount=0 -->
</FeatureList>
<LookupList>
<!-- LookupCount=0 -->
</LookupList>
</GSUB>
</GSUB>
<GPOS>
<GPOS>
<Version value="1.0"/>
<ScriptList>
<!-- ScriptCount=0 -->
</ScriptList>
<FeatureList>
<!-- FeatureCount=0 -->
</FeatureList>
<LookupList>
<!-- LookupCount=0 -->
</LookupList>
</GPOS>
</GPOS>
</ttFont>