diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py index 69fa7e171..b1429d594 100644 --- a/Lib/fontTools/feaLib/ast.py +++ b/Lib/fontTools/feaLib/ast.py @@ -195,6 +195,16 @@ class AnchorDefinition(Statement): 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): def __init__(self, location, prefix, glyphs, suffix, lookups): Statement.__init__(self, location) diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py index bb51765e9..bf76c0071 100644 --- a/Lib/fontTools/feaLib/builder.py +++ b/Lib/fontTools/feaLib/builder.py @@ -28,6 +28,7 @@ class Builder(object): self.cur_feature_name_ = None self.lookups_ = [] self.features_ = {} # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*] + self.attachPoints_ = {} # "a" --> {3, 7} self.ligatureCaretByIndex_ = {} # "f_f_i" --> {3, 7} self.ligatureCaretByPos_ = {} # "f_f_i" --> {300, 600} self.parseTree = None @@ -95,8 +96,8 @@ class Builder(object): gdef = otTables.GDEF() gdef.Version = 1.0 gdef.GlyphClassDef = None - gdef.AttachList = None - gdef.LigCaretList = self.makeLigCaretList_() + gdef.AttachList = self.makeGDEFAttachList_() + gdef.LigCaretList = self.makeGDEFLigCaretList_() inferredGlyphClass = {} for lookup in self.lookups_: @@ -140,15 +141,31 @@ class Builder(object): coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID) m.Coverage.append(coverage) - if (gdef.GlyphClassDef is None and - gdef.LigCaretList is None and - gdef.MarkAttachClassDef is None): + if any((gdef.GlyphClassDef, gdef.AttachList, + gdef.LigCaretList, gdef.MarkAttachClassDef)): + result = getTableClass("GDEF")() + result.table = gdef + return result + else: 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 - def makeLigCaretList_(self): + def makeGDEFLigCaretList_(self): glyphs = set(self.ligatureCaretByPos_.keys()) glyphs.update(self.ligatureCaretByIndex_.keys()) glyphs = sorted(glyphs, key=self.font.getGlyphID) @@ -425,6 +442,10 @@ class Builder(object): lookup_builders.append(None) 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): lookup = self.get_lookup_(location, ChainContextPosBuilder) lookup.rules.append((prefix, glyphs, suffix, diff --git a/Lib/fontTools/feaLib/builder_test.py b/Lib/fontTools/feaLib/builder_test.py index 7d3e60467..375d8d3b8 100644 --- a/Lib/fontTools/feaLib/builder_test.py +++ b/Lib/fontTools/feaLib/builder_test.py @@ -146,7 +146,7 @@ class BuilderTest(unittest.TestCase): self.build, "feature test { pos A 123; pos A 456; } test;") def test_constructs(self): - for name in ("enum markClass language_required " + for name in ("Attach enum markClass language_required " "LigatureCaretByIndex LigatureCaretByPos " "lookup lookupflag").split(): font = makeTTFont() diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py index 660fcf0a8..41403c919 100644 --- a/Lib/fontTools/feaLib/parser.py +++ b/Lib/fontTools/feaLib/parser.py @@ -116,6 +116,16 @@ class Parser(object): self.anchors_.define(name, 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): assert self.cur_token_ in {"enumerate", "enum"} self.advance_lexer_() @@ -608,13 +618,16 @@ class Parser(object): statements = table.statements while self.next_token_ != "}": 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_()) elif self.is_cur_keyword_("LigatureCaretByPos"): statements.append(self.parse_ligatureCaretByPos_()) else: raise FeatureLibError( - "Expected LigatureCaretByIndex or LigatureCaretByPos", + "Expected Attach, LigatureCaretByIndex, " + "or LigatureCaretByPos", self.cur_token_location_) def parse_device_(self): diff --git a/Lib/fontTools/feaLib/parser_test.py b/Lib/fontTools/feaLib/parser_test.py index a88724b36..e2285a211 100644 --- a/Lib/fontTools/feaLib/parser_test.py +++ b/Lib/fontTools/feaLib/parser_test.py @@ -115,6 +115,13 @@ class ParserTest(unittest.TestCase): self.assertEqual(foo.y, 456) 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): [liga] = self.parse("feature liga {} liga;").statements self.assertEqual(liga.name, "liga") diff --git a/Lib/fontTools/feaLib/testdata/Attach.fea b/Lib/fontTools/feaLib/testdata/Attach.fea new file mode 100644 index 000000000..86266eeec --- /dev/null +++ b/Lib/fontTools/feaLib/testdata/Attach.fea @@ -0,0 +1,5 @@ +table GDEF { + Attach [a e] 7; + Attach a 23; + Attach a 23; +} GDEF; diff --git a/Lib/fontTools/feaLib/testdata/Attach.ttx b/Lib/fontTools/feaLib/testdata/Attach.ttx new file mode 100644 index 000000000..0e8362f25 --- /dev/null +++ b/Lib/fontTools/feaLib/testdata/Attach.ttx @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +