[feaLib] Support size feature

This commit is contained in:
Khaled Hosny 2016-03-18 13:18:31 +04:00
parent 6e291cf705
commit 40be0e6f3a
10 changed files with 232 additions and 6 deletions

View File

@ -596,3 +596,17 @@ class FeatureNameStatement(NameRecord):
def build(self, builder):
NameRecord.build(self, builder)
builder.add_featureName(self.location, self.nameID)
class SizeParameters(Statement):
def __init__(self, location, DesignSize, SubfamilyID, RangeStart,
RangeEnd):
Statement.__init__(self, location)
self.DesignSize = DesignSize
self.SubfamilyID = SubfamilyID
self.RangeStart = RangeStart
self.RangeEnd = RangeEnd
def build(self, builder):
builder.set_size_parameters(self.location, self.DesignSize,
self.SubfamilyID, self.RangeStart, self.RangeEnd)

View File

@ -38,6 +38,8 @@ class Builder(object):
# for 'featureNames'
self.featureNames_ = []
self.featureNames_ids_ = {}
# for feature 'size'
self.size_parameters_ = None
# for table 'head'
self.fontRevision_ = None # 2.71
# for table 'name'
@ -169,7 +171,15 @@ class Builder(object):
def buildFeatureParams(self, tag):
params = None
if tag in self.featureNames_:
if tag == "size":
params = otTables.FeatureParamsSize()
params.DesignSize, params.SubfamilyID, params.RangeStart, \
params.RangeEnd = self.size_parameters_
if tag in self.featureNames_ids_:
params.SubfamilyNameID = self.featureNames_ids_[tag]
else:
params.SubfamilyNameID = 0
elif tag in self.featureNames_:
assert tag in self.featureNames_ids_
params = otTables.FeatureParamsStylisticSet()
params.Version = 0
@ -293,7 +303,9 @@ class Builder(object):
# rules will have no lookup_index while building GPOS tables.
lookup_indices = tuple([l.lookup_index for l in lookups
if l.lookup_index is not None])
if len(lookup_indices) == 0:
size_feature = (tag == "GPOS" and feature_tag == "size")
if len(lookup_indices) == 0 and not size_feature:
continue
feature_key = (feature_tag, lookup_indices)
@ -554,6 +566,17 @@ class Builder(object):
def add_featureName(self, location, tag):
self.featureNames_.append(tag)
def set_size_parameters(self, location, DesignSize, SubfamilyID,
RangeStart, RangeEnd):
if self.cur_feature_name_ != 'size':
raise FeatureLibError(
"Parameters statements are not allowed "
"within \"feature %s\"" % self.cur_feature_name_, location)
self.size_parameters_ = [DesignSize, SubfamilyID, RangeStart, RangeEnd]
for script, lang in self.language_systems:
key = (script, lang, self.cur_feature_name_)
self.features_.setdefault(key, [])
def add_ligature_subst(self, location,
prefix, glyphs, suffix, replacement, forceChain):
if prefix or suffix or forceChain:

View File

@ -56,10 +56,10 @@ class BuilderTest(unittest.TestCase):
spec4h1 spec5d1 spec5d2 spec5fi1 spec5fi2 spec5fi3 spec5fi4
spec5f_ii_1 spec5f_ii_2 spec5f_ii_3 spec5f_ii_4
spec5h1 spec6b_ii spec6d2 spec6e spec6f
spec6h_ii spec6h_iii_1 spec6h_iii_3d spec8a spec8c
spec6h_ii spec6h_iii_1 spec6h_iii_3d spec8a spec8b spec8c
spec9b spec9c1 spec9c2 spec9c3 spec9e
bug453 bug463 bug501 bug502 bug505 bug506 bug509 bug512
name
name size size2
""".split()
def __init__(self, methodName):

View File

@ -663,6 +663,29 @@ class Parser(object):
self.expect_symbol_(";")
return ast.SubtableStatement(location)
def parse_size_parameters_(self):
assert self.is_cur_keyword_("parameters")
location = self.cur_token_location_
DesignSize = self.expect_decipoint_()
SubfamilyID = self.expect_number_()
RangeStart = 0
RangeEnd = 0
if self.next_token_type_ in (Lexer.NUMBER, Lexer.FLOAT) or \
SubfamilyID != 0:
RangeStart = self.expect_decipoint_()
RangeEnd = self.expect_decipoint_()
self.expect_symbol_(";")
return ast.SizeParameters(location, DesignSize, SubfamilyID,
RangeStart, RangeEnd)
def parse_size_menuname_(self):
assert self.is_cur_keyword_("sizemenuname")
location = self.cur_token_location_
platformID, platEncID, langID, string = self.parse_name_()
return ast.FeatureNameStatement(location, "size", platformID,
platEncID, langID, string)
def parse_table_(self):
assert self.is_cur_keyword_("table")
location, name = self.cur_token_location_, self.expect_tag_()
@ -889,13 +912,15 @@ class Parser(object):
if tag in ["ss%02d" % i for i in range(1, 20+1)]:
stylisticset = tag
size_feature = (tag == "size")
use_extension = False
if self.next_token_ == "useExtension":
self.expect_keyword_("useExtension")
use_extension = True
block = ast.FeatureBlock(location, tag, use_extension)
self.parse_block_(block, vertical, stylisticset)
self.parse_block_(block, vertical, stylisticset, size_feature)
return block
def parse_feature_reference_(self):
@ -938,7 +963,8 @@ class Parser(object):
location)
return ast.FontRevisionStatement(location, version)
def parse_block_(self, block, vertical, stylisticset=None):
def parse_block_(self, block, vertical, stylisticset=None,
size_feature=False):
self.expect_symbol_("{")
for symtab in self.symbol_tables_:
symtab.enter_scope()
@ -978,6 +1004,10 @@ class Parser(object):
statements.append(self.parse_valuerecord_definition_(vertical))
elif stylisticset and self.is_cur_keyword_("featureNames"):
statements.extend(self.parse_featureNames_(stylisticset))
elif size_feature and self.is_cur_keyword_("parameters"):
statements.append(self.parse_size_parameters_())
elif size_feature and self.is_cur_keyword_("sizemenuname"):
statements.append(self.parse_size_menuname_())
elif self.cur_token_ == ";":
continue
else:
@ -1097,6 +1127,15 @@ class Parser(object):
raise FeatureLibError("Expected a floating-point number",
self.cur_token_location_)
def expect_decipoint_(self):
if self.next_token_type_ == Lexer.FLOAT:
return self.expect_float_()
elif self.next_token_type_ is Lexer.NUMBER:
return self.expect_number_() / 10
else:
raise FeatureLibError("Expected an integer or floating-point number",
self.cur_token_location_)
def expect_string_(self):
self.advance_lexer_()
if self.cur_token_type_ is Lexer.STRING:

View File

@ -0,0 +1,3 @@
feature size {
parameters 10.0 0 0 0;
} size;

41
Lib/fontTools/feaLib/testdata/size.ttx vendored Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GPOS>
<Version value="1.0"/>
<ScriptList>
<!-- ScriptCount=1 -->
<ScriptRecord index="0">
<ScriptTag value="DFLT"/>
<Script>
<DefaultLangSys>
<ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 -->
<FeatureIndex index="0" value="0"/>
</DefaultLangSys>
<!-- LangSysCount=0 -->
</Script>
</ScriptRecord>
</ScriptList>
<FeatureList>
<!-- FeatureCount=1 -->
<FeatureRecord index="0">
<FeatureTag value="size"/>
<Feature>
<FeatureParamsSize>
<DesignSize value="10.0"/>
<SubfamilyID value="0"/>
<SubfamilyNameID value="0"/>
<RangeStart value="0.0"/>
<RangeEnd value="0.0"/>
</FeatureParamsSize>
<!-- LookupCount=0 -->
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=0 -->
</LookupList>
</GPOS>
</ttFont>

View File

@ -0,0 +1,3 @@
feature size {
parameters 10.0 0;
} size;

41
Lib/fontTools/feaLib/testdata/size2.ttx vendored Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GPOS>
<Version value="1.0"/>
<ScriptList>
<!-- ScriptCount=1 -->
<ScriptRecord index="0">
<ScriptTag value="DFLT"/>
<Script>
<DefaultLangSys>
<ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 -->
<FeatureIndex index="0" value="0"/>
</DefaultLangSys>
<!-- LangSysCount=0 -->
</Script>
</ScriptRecord>
</ScriptList>
<FeatureList>
<!-- FeatureCount=1 -->
<FeatureRecord index="0">
<FeatureTag value="size"/>
<Feature>
<FeatureParamsSize>
<DesignSize value="10.0"/>
<SubfamilyID value="0"/>
<SubfamilyNameID value="0"/>
<RangeStart value="0"/>
<RangeEnd value="0"/>
</FeatureParamsSize>
<!-- LookupCount=0 -->
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=0 -->
</LookupList>
</GPOS>
</ttFont>

View File

@ -0,0 +1,9 @@
feature size {
parameters 100 # design size (decipoints)
3 # subfamily identifier
80 # range start (exclusive, decipoints)
139; # range end (inclusive, decipoints)
sizemenuname "Win MinionPro Size Name";
sizemenuname 1 "Mac MinionPro Size Name";
sizemenuname 1 21 0 "Mac MinionPro Size Name";
} size;

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<name>
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
Win MinionPro Size Name
</namerecord>
<namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
Mac MinionPro Size Name
</namerecord>
<namerecord nameID="256" platformID="1" platEncID="21" langID="0x0" unicode="True">
Mac MinionPro Size Name
</namerecord>
</name>
<GPOS>
<Version value="1.0"/>
<ScriptList>
<!-- ScriptCount=1 -->
<ScriptRecord index="0">
<ScriptTag value="DFLT"/>
<Script>
<DefaultLangSys>
<ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 -->
<FeatureIndex index="0" value="0"/>
</DefaultLangSys>
<!-- LangSysCount=0 -->
</Script>
</ScriptRecord>
</ScriptList>
<FeatureList>
<!-- FeatureCount=1 -->
<FeatureRecord index="0">
<FeatureTag value="size"/>
<Feature>
<FeatureParamsSize>
<DesignSize value="10.0"/>
<SubfamilyID value="3"/>
<SubfamilyNameID value="256"/>
<RangeStart value="8.0"/>
<RangeEnd value="13.9"/>
</FeatureParamsSize>
<!-- LookupCount=0 -->
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=0 -->
</LookupList>
</GPOS>
</ttFont>