[feaLib] Implement the Attach statement
This commit is contained in:
parent
f8987125e7
commit
89ac1b2d0d
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
@ -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")
|
||||||
|
5
Lib/fontTools/feaLib/testdata/Attach.fea
vendored
Normal file
5
Lib/fontTools/feaLib/testdata/Attach.fea
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
table GDEF {
|
||||||
|
Attach [a e] 7;
|
||||||
|
Attach a 23;
|
||||||
|
Attach a 23;
|
||||||
|
} GDEF;
|
24
Lib/fontTools/feaLib/testdata/Attach.ttx
vendored
Normal file
24
Lib/fontTools/feaLib/testdata/Attach.ttx
vendored
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user