[feaLib] Implement the GlyphClassDef statement

This commit is contained in:
Sascha Brawer 2016-01-08 19:06:52 +01:00
parent c79e005d77
commit 5696b50fac
7 changed files with 77 additions and 21 deletions

View File

@ -136,12 +136,12 @@ class GlyphClassDefStatement(Statement):
self.componentGlyphs = componentGlyphs
def build(self, builder):
base = self.baseGlyphs.glyphSet() if self.baseGlyphs else None
mark = self.markGlyphs.glyphSet() if self.markGlyphs else None
liga = self.ligatureGlyphs.glyphSet() if self.ligatureGlyphs else None
base = self.baseGlyphs.glyphSet() if self.baseGlyphs else set()
liga = self.ligatureGlyphs.glyphSet() if self.ligatureGlyphs else set()
mark = self.markGlyphs.glyphSet() if self.markGlyphs else set()
comp = (self.componentGlyphs.glyphSet()
if self.componentGlyphs else None)
builder.add_glyphClassDef(self.location, base, mark, liga, comp)
if self.componentGlyphs else set())
builder.add_glyphClassDef(self.location, base, liga, mark, comp)
# While glyph classes can be defined only once, the feature file format

View File

@ -33,6 +33,7 @@ class Builder(object):
self.ligatureCaretByPos_ = {} # "f_f_i" --> {300, 600}
self.parseTree = None
self.required_features_ = {} # ('latn', 'DEU ') --> 'scmp'
self.glyphClassDefs_ = {} # "fi" --> (2, (file, line, column))
self.markAttach_ = {} # "acute" --> (4, (file, line, column))
self.markAttachClassID_ = {} # frozenset({"acute", "grave"}) --> 4
self.markFilterSets_ = {} # frozenset({"acute", "grave"}) --> 4
@ -141,9 +142,13 @@ class Builder(object):
(glyph, name1, name2), markClassDef.location)
marks[glyph] = markClass
inferredGlyphClass[glyph] = 3
if inferredGlyphClass:
if self.glyphClassDefs_:
classes = {g: c for (g, (c, _)) in self.glyphClassDefs_.items()}
else:
classes = inferredGlyphClass
if classes:
result = otTables.GlyphClassDef()
result.classDefs = inferredGlyphClass
result.classDefs = classes
return result
else:
return None
@ -600,10 +605,25 @@ class Builder(object):
location)
lookup.mapping[glyph] = valuerecord
def add_glyphClassDef(self, location, baseGlyphs, markGlyphs,
ligatureGlyphs, componentGlyphs):
# TODO: Not yet implemented.
pass
def setGlyphClass_(self, location, glyph, glyphClass):
oldClass, oldLocation = self.glyphClassDefs_.get(glyph, (None, None))
if oldClass and oldClass != glyphClass:
raise FeatureLibError(
"Glyph %s was assigned to a different class at %s:%s:%s" %
(glyph, oldLocation[0], oldLocation[1], oldLocation[2]),
location)
self.glyphClassDefs_[glyph] = (glyphClass, location)
def add_glyphClassDef(self, location, baseGlyphs, ligatureGlyphs,
markGlyphs, componentGlyphs):
for glyph in baseGlyphs:
self.setGlyphClass_(location, glyph, 1)
for glyph in ligatureGlyphs:
self.setGlyphClass_(location, glyph, 2)
for glyph in markGlyphs:
self.setGlyphClass_(location, glyph, 3)
for glyph in componentGlyphs:
self.setGlyphClass_(location, glyph, 4)
def add_ligatureCaretByIndex_(self, location, glyphs, carets):
for glyph in glyphs:

View File

@ -147,7 +147,7 @@ class BuilderTest(unittest.TestCase):
def test_constructs(self):
for name in ("Attach enum markClass language_required "
"LigatureCaretByIndex LigatureCaretByPos "
"GlyphClassDef LigatureCaretByIndex LigatureCaretByPos "
"lookup lookupflag").split():
font = makeTTFont()
addOpenTypeFeatures(self.getpath("%s.fea" % name), font)
@ -172,6 +172,15 @@ class BuilderTest(unittest.TestCase):
addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font)
self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
def test_GlyphClassDef_conflictingClasses(self):
self.assertRaisesRegex(
FeatureLibError, "Glyph X was assigned to a different class",
self.build,
"table GDEF {"
" GlyphClassDef [a b], [X], , ;"
" GlyphClassDef [a b X], , , ;"
"} GDEF;")
def test_languagesystem(self):
builder = Builder(None, makeTTFont())
builder.add_language_system(None, 'latn', 'FRA')

View File

@ -132,7 +132,7 @@ class Parser(object):
return self.parse_position_(enumerated=True, vertical=vertical)
def parse_GlyphClassDef_(self):
"""Parses 'GlyphClassDef @BASE, @MARKS, @LIGATURES, @COMPONENTS;'"""
"""Parses 'GlyphClassDef @BASE, @LIGATURES, @MARKS, @COMPONENTS;'"""
assert self.is_cur_keyword_("GlyphClassDef")
location = self.cur_token_location_
if self.next_token_ != ",":
@ -140,16 +140,16 @@ class Parser(object):
else:
baseGlyphs = None
self.expect_symbol_(",")
if self.next_token_ != ",":
markGlyphs = self.parse_glyphclass_(accept_glyphname=False)
else:
markGlyphs = None
self.expect_symbol_(",")
if self.next_token_ != ",":
ligatureGlyphs = self.parse_glyphclass_(accept_glyphname=False)
else:
ligatureGlyphs = None
self.expect_symbol_(",")
if self.next_token_ != ",":
markGlyphs = self.parse_glyphclass_(accept_glyphname=False)
else:
markGlyphs = None
self.expect_symbol_(",")
if self.next_token_ != ";":
componentGlyphs = self.parse_glyphclass_(accept_glyphname=False)
else:

View File

@ -232,20 +232,20 @@ class ParserTest(unittest.TestCase):
self.assertEqual(smcp.statements[0].glyphs, {"a", "b", "s"})
def test_GlyphClassDef(self):
doc = self.parse("table GDEF {GlyphClassDef [b],[m],[l],[C c];} GDEF;")
doc = self.parse("table GDEF {GlyphClassDef [b],[l],[m],[C c];} GDEF;")
s = doc.statements[0].statements[0]
self.assertIsInstance(s, ast.GlyphClassDefStatement)
self.assertEqual(glyphstr([s.baseGlyphs]), "b")
self.assertEqual(glyphstr([s.markGlyphs]), "m")
self.assertEqual(glyphstr([s.ligatureGlyphs]), "l")
self.assertEqual(glyphstr([s.markGlyphs]), "m")
self.assertEqual(glyphstr([s.componentGlyphs]), "[C c]")
def test_GlyphClassDef_noCLassesSpecified(self):
doc = self.parse("table GDEF {GlyphClassDef ,,,;} GDEF;")
s = doc.statements[0].statements[0]
self.assertIsNone(s.baseGlyphs)
self.assertIsNone(s.markGlyphs)
self.assertIsNone(s.ligatureGlyphs)
self.assertIsNone(s.markGlyphs)
self.assertIsNone(s.componentGlyphs)
def test_ignore_sub(self):

View File

@ -0,0 +1,5 @@
table GDEF {
GlyphClassDef [a], [b], [c], [d];
GlyphClassDef [e], [f], [g], [h];
GlyphClassDef [i], [j], [k], [l];
} GDEF;

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont>
<GDEF>
<Version value="1.0"/>
<GlyphClassDef>
<ClassDef glyph="a" class="1"/>
<ClassDef glyph="b" class="2"/>
<ClassDef glyph="c" class="3"/>
<ClassDef glyph="d" class="4"/>
<ClassDef glyph="e" class="1"/>
<ClassDef glyph="f" class="2"/>
<ClassDef glyph="g" class="3"/>
<ClassDef glyph="h" class="4"/>
<ClassDef glyph="i" class="1"/>
<ClassDef glyph="j" class="2"/>
<ClassDef glyph="k" class="3"/>
<ClassDef glyph="l" class="4"/>
</GlyphClassDef>
</GDEF>
</ttFont>