[feaLib] Support BASE table

Just the parts documented a currently implemented by Adobe’s
implementation.
This commit is contained in:
Khaled Hosny 2016-03-19 02:47:04 +04:00
parent 40be0e6f3a
commit 1ac37d7d00
6 changed files with 235 additions and 2 deletions

View File

@ -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)

View File

@ -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':

View File

@ -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:

View File

@ -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_("<")

View File

@ -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;

114
Lib/fontTools/feaLib/testdata/spec9a.ttx vendored Normal file
View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<BASE>
<Version value="1.0"/>
<HorizAxis>
<BaseTagList>
<!-- BaseTagCount=2 -->
<BaselineTag index="0" value="ideo"/>
<BaselineTag index="1" value="romn"/>
</BaseTagList>
<BaseScriptList>
<!-- BaseScriptCount=6 -->
<BaseScriptRecord index="0">
<BaseScriptTag value="cyrl"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="1"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
<BaseScriptRecord index="1">
<BaseScriptTag value="grek"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="1"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
<BaseScriptRecord index="2">
<BaseScriptTag value="hang"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="0"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
<BaseScriptRecord index="3">
<BaseScriptTag value="hani"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="0"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
<BaseScriptRecord index="4">
<BaseScriptTag value="kana"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="0"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
<BaseScriptRecord index="5">
<BaseScriptTag value="latn"/>
<BaseScript>
<BaseValues>
<DefaultIndex value="1"/>
<!-- BaseCoordCount=2 -->
<BaseCoord index="0" Format="1">
<Coordinate value="-120"/>
</BaseCoord>
<BaseCoord index="1" Format="1">
<Coordinate value="0"/>
</BaseCoord>
</BaseValues>
<!-- BaseLangSysCount=0 -->
</BaseScript>
</BaseScriptRecord>
</BaseScriptList>
</HorizAxis>
</BASE>
</ttFont>