[feaLib] Implement the Attach statement

This commit is contained in:
Sascha Brawer 2016-01-08 08:32:47 +01:00
parent f8987125e7
commit 89ac1b2d0d
7 changed files with 91 additions and 11 deletions

View File

@ -195,6 +195,16 @@ class AnchorDefinition(Statement):
self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint
class AttachStatement(Statement):
def __init__(self, location, glyphs, contourPoints):
Statement.__init__(self, location)
self.glyphs, self.contourPoints = (glyphs, contourPoints)
def build(self, builder):
glyphs = self.glyphs.glyphSet()
builder.add_attach_points(self.location, glyphs, self.contourPoints)
class ChainContextPosStatement(Statement): class ChainContextPosStatement(Statement):
def __init__(self, location, prefix, glyphs, suffix, lookups): def __init__(self, location, prefix, glyphs, suffix, lookups):
Statement.__init__(self, location) Statement.__init__(self, location)

View File

@ -28,6 +28,7 @@ class Builder(object):
self.cur_feature_name_ = None self.cur_feature_name_ = None
self.lookups_ = [] self.lookups_ = []
self.features_ = {} # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*] self.features_ = {} # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*]
self.attachPoints_ = {} # "a" --> {3, 7}
self.ligatureCaretByIndex_ = {} # "f_f_i" --> {3, 7} self.ligatureCaretByIndex_ = {} # "f_f_i" --> {3, 7}
self.ligatureCaretByPos_ = {} # "f_f_i" --> {300, 600} self.ligatureCaretByPos_ = {} # "f_f_i" --> {300, 600}
self.parseTree = None self.parseTree = None
@ -95,8 +96,8 @@ class Builder(object):
gdef = otTables.GDEF() gdef = otTables.GDEF()
gdef.Version = 1.0 gdef.Version = 1.0
gdef.GlyphClassDef = None gdef.GlyphClassDef = None
gdef.AttachList = None gdef.AttachList = self.makeGDEFAttachList_()
gdef.LigCaretList = self.makeLigCaretList_() gdef.LigCaretList = self.makeGDEFLigCaretList_()
inferredGlyphClass = {} inferredGlyphClass = {}
for lookup in self.lookups_: for lookup in self.lookups_:
@ -140,15 +141,31 @@ class Builder(object):
coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID) coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID)
m.Coverage.append(coverage) m.Coverage.append(coverage)
if (gdef.GlyphClassDef is None and if any((gdef.GlyphClassDef, gdef.AttachList,
gdef.LigCaretList is None and gdef.LigCaretList, gdef.MarkAttachClassDef)):
gdef.MarkAttachClassDef is None): result = getTableClass("GDEF")()
result.table = gdef
return result
else:
return None return None
result = getTableClass("GDEF")()
result.table = gdef def makeGDEFAttachList_(self):
glyphs = sorted(self.attachPoints_.keys(), key=self.font.getGlyphID)
if not glyphs:
return None
result = otTables.AttachList()
result.Coverage = otTables.Coverage()
result.Coverage.glyphs = glyphs
result.GlyphCount = len(glyphs)
result.AttachPoint = []
for glyph in glyphs:
pt = otTables.AttachPoint()
pt.PointIndex = sorted(self.attachPoints_[glyph])
pt.PointCount = len(pt.PointIndex)
result.AttachPoint.append(pt)
return result return result
def makeLigCaretList_(self): def makeGDEFLigCaretList_(self):
glyphs = set(self.ligatureCaretByPos_.keys()) glyphs = set(self.ligatureCaretByPos_.keys())
glyphs.update(self.ligatureCaretByIndex_.keys()) glyphs.update(self.ligatureCaretByIndex_.keys())
glyphs = sorted(glyphs, key=self.font.getGlyphID) glyphs = sorted(glyphs, key=self.font.getGlyphID)
@ -425,6 +442,10 @@ class Builder(object):
lookup_builders.append(None) lookup_builders.append(None)
return lookup_builders return lookup_builders
def add_attach_points(self, location, glyphs, contourPoints):
for glyph in glyphs:
self.attachPoints_.setdefault(glyph, set()).update(contourPoints)
def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups): def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups):
lookup = self.get_lookup_(location, ChainContextPosBuilder) lookup = self.get_lookup_(location, ChainContextPosBuilder)
lookup.rules.append((prefix, glyphs, suffix, lookup.rules.append((prefix, glyphs, suffix,

View File

@ -146,7 +146,7 @@ class BuilderTest(unittest.TestCase):
self.build, "feature test { pos A 123; pos A 456; } test;") self.build, "feature test { pos A 123; pos A 456; } test;")
def test_constructs(self): def test_constructs(self):
for name in ("enum markClass language_required " for name in ("Attach enum markClass language_required "
"LigatureCaretByIndex LigatureCaretByPos " "LigatureCaretByIndex LigatureCaretByPos "
"lookup lookupflag").split(): "lookup lookupflag").split():
font = makeTTFont() font = makeTTFont()

View File

@ -116,6 +116,16 @@ class Parser(object):
self.anchors_.define(name, anchordef) self.anchors_.define(name, anchordef)
return anchordef return anchordef
def parse_attach_(self):
assert self.is_cur_keyword_("Attach")
location = self.cur_token_location_
glyphs = self.parse_glyphclass_(accept_glyphname=True)
contourPoints = {self.expect_number_()}
while self.next_token_ != ";":
contourPoints.add(self.expect_number_())
self.expect_symbol_(";")
return ast.AttachStatement(location, glyphs, contourPoints)
def parse_enumerate_(self, vertical): def parse_enumerate_(self, vertical):
assert self.cur_token_ in {"enumerate", "enum"} assert self.cur_token_ in {"enumerate", "enum"}
self.advance_lexer_() self.advance_lexer_()
@ -608,13 +618,16 @@ class Parser(object):
statements = table.statements statements = table.statements
while self.next_token_ != "}": while self.next_token_ != "}":
self.advance_lexer_() self.advance_lexer_()
if self.is_cur_keyword_("LigatureCaretByIndex"): if self.is_cur_keyword_("Attach"):
statements.append(self.parse_attach_())
elif self.is_cur_keyword_("LigatureCaretByIndex"):
statements.append(self.parse_ligatureCaretByIndex_()) statements.append(self.parse_ligatureCaretByIndex_())
elif self.is_cur_keyword_("LigatureCaretByPos"): elif self.is_cur_keyword_("LigatureCaretByPos"):
statements.append(self.parse_ligatureCaretByPos_()) statements.append(self.parse_ligatureCaretByPos_())
else: else:
raise FeatureLibError( raise FeatureLibError(
"Expected LigatureCaretByIndex or LigatureCaretByPos", "Expected Attach, LigatureCaretByIndex, "
"or LigatureCaretByPos",
self.cur_token_location_) self.cur_token_location_)
def parse_device_(self): def parse_device_(self):

View File

@ -115,6 +115,13 @@ class ParserTest(unittest.TestCase):
self.assertEqual(foo.y, 456) self.assertEqual(foo.y, 456)
self.assertEqual(foo.contourpoint, 5) self.assertEqual(foo.contourpoint, 5)
def test_attach(self):
doc = self.parse("table GDEF {Attach [a e] 2;} GDEF;")
s = doc.statements[0].statements[0]
self.assertIsInstance(s, ast.AttachStatement)
self.assertEqual(glyphstr([s.glyphs]), "[a e]")
self.assertEqual(s.contourPoints, {2})
def test_feature_block(self): def test_feature_block(self):
[liga] = self.parse("feature liga {} liga;").statements [liga] = self.parse("feature liga {} liga;").statements
self.assertEqual(liga.name, "liga") self.assertEqual(liga.name, "liga")

View File

@ -0,0 +1,5 @@
table GDEF {
Attach [a e] 7;
Attach a 23;
Attach a 23;
} GDEF;

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GDEF>
<Version value="1.0"/>
<AttachList>
<Coverage>
<Glyph value="a"/>
<Glyph value="e"/>
</Coverage>
<!-- GlyphCount=2 -->
<AttachPoint index="0">
<!-- PointCount=2 -->
<PointIndex index="0" value="7"/>
<PointIndex index="1" value="23"/>
</AttachPoint>
<AttachPoint index="1">
<!-- PointCount=1 -->
<PointIndex index="0" value="7"/>
</AttachPoint>
</AttachList>
</GDEF>
</ttFont>