[feaLib] Implement required
qualifier on language
statements
This commit is contained in:
parent
a3783e1095
commit
a35291e8c1
@ -82,9 +82,9 @@ class LanguageStatement(Statement):
|
||||
self.required = required
|
||||
|
||||
def build(self, builder):
|
||||
# TODO(sascha): Handle required.
|
||||
builder.set_language(location=self.location, language=self.language,
|
||||
include_default=self.include_default)
|
||||
include_default=self.include_default,
|
||||
required=self.required)
|
||||
|
||||
|
||||
class LanguageSystemStatement(Statement):
|
||||
|
@ -24,7 +24,8 @@ class Builder(object):
|
||||
self.cur_lookup_name_ = None
|
||||
self.cur_feature_name_ = None
|
||||
self.lookups_ = []
|
||||
self.features_ = {} # ('latn', 'DEU', 'smcp') --> [LookupBuilder*]
|
||||
self.features_ = {} # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*]
|
||||
self.required_features_ = {} # ('latn', 'DEU ') --> 'scmp'
|
||||
|
||||
def build(self):
|
||||
parsetree = Parser(self.featurefile_path).parse()
|
||||
@ -86,7 +87,8 @@ class Builder(object):
|
||||
# Build a table for mapping (tag, lookup_indices) to feature_index.
|
||||
# For example, ('liga', (2,3,7)) --> 23.
|
||||
feature_indices = {}
|
||||
scripts = {} # 'cyrl' --> {'DEU': [23, 24]} for feature #23,24
|
||||
required_feature_indices = {} # ('latn', 'DEU') --> 23
|
||||
scripts = {} # 'latn' --> {'DEU': [23, 24]} for feature #23,24
|
||||
for key, lookups in sorted(self.features_.items()):
|
||||
script, lang, feature_tag = key
|
||||
# l.lookup_index will be None when a lookup is not needed
|
||||
@ -111,6 +113,8 @@ class Builder(object):
|
||||
feature_indices[feature_key] = feature_index
|
||||
scripts.setdefault(script, {}).setdefault(lang, []).append(
|
||||
feature_index)
|
||||
if self.required_features_.get((script, lang)) == feature_tag:
|
||||
required_feature_indices[(script, lang)] = feature_index
|
||||
|
||||
# Build ScriptList.
|
||||
for script, lang_features in sorted(scripts.items()):
|
||||
@ -123,9 +127,19 @@ class Builder(object):
|
||||
langrec = otTables.LangSysRecord()
|
||||
langrec.LangSys = otTables.LangSys()
|
||||
langrec.LangSys.LookupOrder = None
|
||||
langrec.LangSys.ReqFeatureIndex = 0xFFFF
|
||||
langrec.LangSys.FeatureCount = len(feature_indices)
|
||||
langrec.LangSys.FeatureIndex = feature_indices
|
||||
|
||||
req_feature_index = \
|
||||
required_feature_indices.get((script, lang))
|
||||
if req_feature_index is None:
|
||||
langrec.LangSys.ReqFeatureIndex = 0xFFFF
|
||||
else:
|
||||
langrec.LangSys.ReqFeatureIndex = req_feature_index
|
||||
|
||||
langrec.LangSys.FeatureIndex = [i for i in feature_indices
|
||||
if i != req_feature_index]
|
||||
langrec.LangSys.FeatureCount = \
|
||||
len(langrec.LangSys.FeatureIndex)
|
||||
|
||||
if lang == "dflt":
|
||||
srec.Script.DefaultLangSys = langrec.LangSys
|
||||
else:
|
||||
@ -187,7 +201,7 @@ class Builder(object):
|
||||
self.cur_lookup_name_ = None
|
||||
self.cur_lookup_ = None
|
||||
|
||||
def set_language(self, location, language, include_default):
|
||||
def set_language(self, location, language, include_default, required):
|
||||
assert(len(language) == 4)
|
||||
if self.cur_lookup_name_:
|
||||
raise FeatureLibError(
|
||||
@ -204,6 +218,16 @@ class Builder(object):
|
||||
langsys = set()
|
||||
langsys.add((self.script_, language))
|
||||
self.language_systems = frozenset(langsys)
|
||||
if required:
|
||||
key = (self.script_, language)
|
||||
if key in self.required_features_:
|
||||
raise FeatureLibError(
|
||||
"Language %s (script %s) has already "
|
||||
"specified feature %s as its required feature" % (
|
||||
language.strip(), self.script_.strip(),
|
||||
self.required_features_[key].strip()),
|
||||
location)
|
||||
self.required_features_[key] = self.cur_feature_name_
|
||||
|
||||
def set_script(self, location, script):
|
||||
if self.cur_lookup_name_:
|
||||
@ -217,7 +241,8 @@ class Builder(object):
|
||||
self.cur_lookup_ = None
|
||||
self.script_ = script
|
||||
self.lookup_flag_ = 0
|
||||
self.set_language(location, 'dflt', include_default=True)
|
||||
self.set_language(location, "dflt",
|
||||
include_default=True, required=False)
|
||||
|
||||
def add_alternate_substitution(self, location, glyph, from_class):
|
||||
lookup = self.get_lookup_(location, AlternateSubstBuilder)
|
||||
|
@ -174,10 +174,10 @@ class BuilderTest(unittest.TestCase):
|
||||
builder.start_feature(location=None, name='test')
|
||||
builder.set_script(location=None, script='cyrl')
|
||||
builder.set_language(location=None, language='RUS ',
|
||||
include_default=False)
|
||||
include_default=False, required=False)
|
||||
self.assertEqual(builder.language_systems, {('cyrl', 'RUS ')})
|
||||
builder.set_language(location=None, language='BGR ',
|
||||
include_default=True)
|
||||
include_default=True, required=False)
|
||||
self.assertEqual(builder.language_systems,
|
||||
{('latn', 'FRA '), ('cyrl', 'BGR ')})
|
||||
|
||||
@ -200,6 +200,28 @@ class BuilderTest(unittest.TestCase):
|
||||
"Language statements are not allowed within \"feature size\"",
|
||||
self.build, "feature size { language FRA; } size;")
|
||||
|
||||
def test_language_required(self):
|
||||
font = TTFont()
|
||||
addOpenTypeFeatures(self.getpath("language_required.fea"), font)
|
||||
self.expect_ttx(font, self.getpath("language_required.ttx"))
|
||||
|
||||
def test_language_required_duplicate(self):
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError,
|
||||
r"Language FRA \(script latn\) has already specified "
|
||||
"feature scmp as its required feature",
|
||||
self.build,
|
||||
"feature scmp {"
|
||||
" script latn;"
|
||||
" language FRA required;"
|
||||
" language DEU required;"
|
||||
" substitute [a-z] by [A.sc-Z.sc];"
|
||||
"} scmp;"
|
||||
"feature test {"
|
||||
" language FRA required;"
|
||||
" substitute [a-z] by [A.sc-Z.sc];"
|
||||
"} test;")
|
||||
|
||||
def test_lookup_already_defined(self):
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError,
|
||||
|
21
Lib/fontTools/feaLib/testdata/language_required.fea
vendored
Normal file
21
Lib/fontTools/feaLib/testdata/language_required.fea
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
languagesystem latn DEU;
|
||||
languagesystem latn FRA;
|
||||
languagesystem latn ITA;
|
||||
|
||||
feature hlig {
|
||||
script latn;
|
||||
language DEU exclude_dflt required;
|
||||
sub D E U by D_E_U;
|
||||
|
||||
language FRA exclude_dflt;
|
||||
sub F R A by D_E_U;
|
||||
} hlig;
|
||||
|
||||
feature liga {
|
||||
language ITA exclude_dflt required;
|
||||
sub I T A by I_T_A;
|
||||
} liga;
|
||||
|
||||
feature scmp {
|
||||
sub [a-z] by [A.sc-Z.sc];
|
||||
} scmp;
|
136
Lib/fontTools/feaLib/testdata/language_required.ttx
vendored
Normal file
136
Lib/fontTools/feaLib/testdata/language_required.ttx
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<GSUB>
|
||||
<GSUB>
|
||||
<Version value="1.0"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=1 -->
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="latn"/>
|
||||
<Script>
|
||||
<!-- LangSysCount=3 -->
|
||||
<LangSysRecord index="0">
|
||||
<LangSysTag value="DEU "/>
|
||||
<LangSys>
|
||||
<ReqFeatureIndex value="0"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="1"/>
|
||||
</LangSys>
|
||||
</LangSysRecord>
|
||||
<LangSysRecord index="1">
|
||||
<LangSysTag value="FRA "/>
|
||||
<LangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<!-- FeatureCount=2 -->
|
||||
<FeatureIndex index="0" value="2"/>
|
||||
<FeatureIndex index="1" value="1"/>
|
||||
</LangSys>
|
||||
</LangSysRecord>
|
||||
<LangSysRecord index="2">
|
||||
<LangSysTag value="ITA "/>
|
||||
<LangSys>
|
||||
<ReqFeatureIndex value="3"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="1"/>
|
||||
</LangSys>
|
||||
</LangSysRecord>
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=4 -->
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="hlig"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="0"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
<FeatureRecord index="1">
|
||||
<FeatureTag value="scmp"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="3"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
<FeatureRecord index="2">
|
||||
<FeatureTag value="hlig"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="1"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
<FeatureRecord index="3">
|
||||
<FeatureTag value="liga"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="2"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=4 -->
|
||||
<LigatureSubst index="0" Format="1">
|
||||
<LigatureSet glyph="D">
|
||||
<Ligature components="D,E,U" glyph="D_E_U"/>
|
||||
</LigatureSet>
|
||||
</LigatureSubst>
|
||||
<LigatureSubst index="1" Format="1">
|
||||
<LigatureSet glyph="F">
|
||||
<Ligature components="F,R,A" glyph="D_E_U"/>
|
||||
</LigatureSet>
|
||||
</LigatureSubst>
|
||||
<LigatureSubst index="2" Format="1">
|
||||
<LigatureSet glyph="I">
|
||||
<Ligature components="I,T,A" glyph="I_T_A"/>
|
||||
</LigatureSet>
|
||||
</LigatureSubst>
|
||||
<SingleSubst index="3">
|
||||
<Substitution in="a" out="A.sc"/>
|
||||
<Substitution in="b" out="B.sc"/>
|
||||
<Substitution in="c" out="C.sc"/>
|
||||
<Substitution in="d" out="D.sc"/>
|
||||
<Substitution in="e" out="E.sc"/>
|
||||
<Substitution in="f" out="F.sc"/>
|
||||
<Substitution in="g" out="G.sc"/>
|
||||
<Substitution in="h" out="H.sc"/>
|
||||
<Substitution in="i" out="I.sc"/>
|
||||
<Substitution in="j" out="J.sc"/>
|
||||
<Substitution in="k" out="K.sc"/>
|
||||
<Substitution in="l" out="L.sc"/>
|
||||
<Substitution in="m" out="M.sc"/>
|
||||
<Substitution in="n" out="N.sc"/>
|
||||
<Substitution in="o" out="O.sc"/>
|
||||
<Substitution in="p" out="P.sc"/>
|
||||
<Substitution in="q" out="Q.sc"/>
|
||||
<Substitution in="r" out="R.sc"/>
|
||||
<Substitution in="s" out="S.sc"/>
|
||||
<Substitution in="t" out="T.sc"/>
|
||||
<Substitution in="u" out="U.sc"/>
|
||||
<Substitution in="v" out="V.sc"/>
|
||||
<Substitution in="w" out="W.sc"/>
|
||||
<Substitution in="x" out="X.sc"/>
|
||||
<Substitution in="y" out="Y.sc"/>
|
||||
<Substitution in="z" out="Z.sc"/>
|
||||
</SingleSubst>
|
||||
</LookupList>
|
||||
</GSUB>
|
||||
</GSUB>
|
||||
|
||||
<GPOS>
|
||||
<GPOS>
|
||||
<Version value="1.0"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=0 -->
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=0 -->
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=0 -->
|
||||
</LookupList>
|
||||
</GPOS>
|
||||
</GPOS>
|
||||
|
||||
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user