diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py index 166158b12..75e5c3389 100644 --- a/Lib/fontTools/feaLib/ast.py +++ b/Lib/fontTools/feaLib/ast.py @@ -610,3 +610,14 @@ class SizeParameters(Statement): def build(self, builder): builder.set_size_parameters(self.location, self.DesignSize, self.SubfamilyID, self.RangeStart, self.RangeEnd) + + +class BaseAxis(Statement): + def __init__(self, location, bases, scripts, vertical): + Statement.__init__(self, location) + self.bases = bases + self.scripts = scripts + self.vertical = vertical + + def build(self, builder): + builder.set_base_axis(self.bases, self.scripts, self.vertical) diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py index dbdbe14e4..4b2a08d35 100644 --- a/Lib/fontTools/feaLib/builder.py +++ b/Lib/fontTools/feaLib/builder.py @@ -44,6 +44,9 @@ class Builder(object): self.fontRevision_ = None # 2.71 # for table 'name' self.names_ = [] + # for table 'BASE' + self.base_horiz_axis_ = None + self.base_vert_axis_ = None # for table 'GDEF' self.attachPoints_ = {} # "a" --> {3, 7} self.ligCaretCoords_ = {} # "f_f_i" --> {300, 600} @@ -73,6 +76,11 @@ class Builder(object): self.font["GDEF"] = gdef elif "GDEF" in self.font: del self.font["GDEF"] + base = self.buildBASE() + if base: + self.font["BASE"] = base + elif "BASE" in self.font: + del self.font["BASE"] def get_chained_lookup_(self, location, builder_class): result = builder_class(self.font, location) @@ -204,6 +212,46 @@ class Builder(object): nameID = self.featureNames_ids_[tag] table.setName(string, nameID, platformID, platEncID, langID) + def buildBASE(self): + if not self.base_horiz_axis_ and not self.base_vert_axis_: + return None + base = otTables.BASE() + base.Version = 0x00010000 + base.HorizAxis = self.buildBASEAxis(self.base_horiz_axis_) + base.VertAxis = self.buildBASEAxis(self.base_vert_axis_) + + result = getTableClass("BASE")() + result.table = base + return result + + def buildBASEAxis(self, axis): + if not axis: + return + bases, scripts = axis + axis = otTables.Axis() + axis.BaseTagList = otTables.BaseTagList() + axis.BaseTagList.BaselineTag = bases + axis.BaseTagList.BaseTagCount = len(bases) + axis.BaseScriptList = otTables.BaseScriptList() + axis.BaseScriptList.BaseScriptRecord = [] + axis.BaseScriptList.BaseScriptCount = len(scripts) + for script in sorted(scripts): + record = otTables.BaseScriptRecord() + record.BaseScriptTag = script[0] + record.BaseScript = otTables.BaseScript() + record.BaseScript.BaseLangSysCount = 0 + record.BaseScript.BaseValues = otTables.BaseValues() + record.BaseScript.BaseValues.DefaultIndex = bases.index(script[1]) + record.BaseScript.BaseValues.BaseCoord = [] + record.BaseScript.BaseValues.BaseCoordCount = len(script[2]) + for c in script[2]: + coord = otTables.BaseCoord() + coord.Format = 1 + coord.Coordinate = c + record.BaseScript.BaseValues.BaseCoord.append(coord) + axis.BaseScriptList.BaseScriptRecord.append(record) + return axis + def buildGDEF(self): gdef = otTables.GDEF() gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_() @@ -566,6 +614,12 @@ class Builder(object): def add_featureName(self, location, tag): self.featureNames_.append(tag) + def set_base_axis(self, bases, scripts, vertical): + if vertical: + self.base_vert_axis_ = (bases, scripts) + else: + self.base_horiz_axis_ = (bases, scripts) + def set_size_parameters(self, location, DesignSize, SubfamilyID, RangeStart, RangeEnd): if self.cur_feature_name_ != 'size': diff --git a/Lib/fontTools/feaLib/builder_test.py b/Lib/fontTools/feaLib/builder_test.py index 829ab7112..5ba171656 100644 --- a/Lib/fontTools/feaLib/builder_test.py +++ b/Lib/fontTools/feaLib/builder_test.py @@ -57,7 +57,7 @@ class BuilderTest(unittest.TestCase): 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 spec8b spec8c - spec9b spec9c1 spec9c2 spec9c3 spec9e + spec9a spec9b spec9c1 spec9c2 spec9c3 spec9e bug453 bug463 bug501 bug502 bug505 bug506 bug509 bug512 name size size2 """.split() @@ -103,7 +103,8 @@ class BuilderTest(unittest.TestCase): def expect_ttx(self, font, expected_ttx): path = self.temp_path(suffix=".ttx") - font.saveXML(path, tables=['head', 'name', 'GDEF', 'GSUB', 'GPOS']) + font.saveXML(path, tables=['head', 'name', 'BASE', 'GDEF', 'GSUB', + 'GPOS']) actual = self.read_ttx(path) expected = self.read_ttx(expected_ttx) if actual != expected: diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py index a2205d990..ff5b30e70 100644 --- a/Lib/fontTools/feaLib/parser.py +++ b/Lib/fontTools/feaLib/parser.py @@ -695,6 +695,7 @@ class Parser(object): "GDEF": self.parse_table_GDEF_, "head": self.parse_table_head_, "name": self.parse_table_name_, + "BASE": self.parse_table_BASE_, }.get(name) if handler: handler(table) @@ -810,6 +811,52 @@ class Parser(object): return re.sub(r'\\[0-9a-zAZ]{4}', unescape, string) + def parse_table_BASE_(self, table): + statements = table.statements + while self.next_token_ != "}": + self.advance_lexer_() + if self.is_cur_keyword_("HorizAxis.BaseTagList"): + horiz_bases = self.parse_base_tag_list_() + elif self.is_cur_keyword_("HorizAxis.BaseScriptList"): + horiz_scripts = self.parse_base_script_list_(len(horiz_bases)) + statements.append( + ast.BaseAxis(self.cur_token_location_, horiz_bases, + horiz_scripts, False)) + elif self.is_cur_keyword_("VertAxis.BaseTagList"): + vert_bases = self.parse_base_tag_list_() + elif self.is_cur_keyword_("VertAxis.BaseScriptList"): + vert_scripts = self.parse_base_script_list_(len(vert_bases)) + statements.append( + ast.BaseAxis(self.cur_token_location_, vert_bases, + vert_scripts, True)) + elif self.cur_token_ == ";": + continue + + def parse_base_tag_list_(self): + assert self.cur_token_ in ("HorizAxis.BaseTagList", + "VertAxis.BaseTagList"), self.cur_token_ + bases = [] + while self.next_token_ != ";": + bases.append(self.expect_script_tag_()) + self.expect_symbol_(";") + return bases + + def parse_base_script_list_(self, count): + assert self.cur_token_ in ("HorizAxis.BaseScriptList", + "VertAxis.BaseScriptList"), self.cur_token_ + scripts = [(self.parse_base_script_record_(count))] + while self.next_token_ == ",": + self.expect_symbol_(",") + scripts.append(self.parse_base_script_record_(count)) + self.expect_symbol_(";") + return scripts + + def parse_base_script_record_(self, count): + script_tag = self.expect_script_tag_() + base_tag = self.expect_script_tag_() + coords = [self.expect_number_() for i in range(count)] + return script_tag, base_tag, coords + def parse_device_(self): result = None self.expect_symbol_("<") diff --git a/Lib/fontTools/feaLib/testdata/spec9a.fea b/Lib/fontTools/feaLib/testdata/spec9a.fea new file mode 100644 index 000000000..c6c1e199e --- /dev/null +++ b/Lib/fontTools/feaLib/testdata/spec9a.fea @@ -0,0 +1,6 @@ +table BASE { + HorizAxis.BaseTagList ideo romn; + HorizAxis.BaseScriptList latn romn -120 0, cyrl romn -120 0, + grek romn -120 0, hani ideo -120 0, + kana ideo -120 0, hang ideo -120 0; +} BASE; diff --git a/Lib/fontTools/feaLib/testdata/spec9a.ttx b/Lib/fontTools/feaLib/testdata/spec9a.ttx new file mode 100644 index 000000000..013626345 --- /dev/null +++ b/Lib/fontTools/feaLib/testdata/spec9a.ttx @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +