[feaLib] Implement the GlyphClassDef statement
This commit is contained in:
parent
c79e005d77
commit
5696b50fac
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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')
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
5
Lib/fontTools/feaLib/testdata/GlyphClassDef.fea
vendored
Normal file
5
Lib/fontTools/feaLib/testdata/GlyphClassDef.fea
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
table GDEF {
|
||||
GlyphClassDef [a], [b], [c], [d];
|
||||
GlyphClassDef [e], [f], [g], [h];
|
||||
GlyphClassDef [i], [j], [k], [l];
|
||||
} GDEF;
|
22
Lib/fontTools/feaLib/testdata/GlyphClassDef.ttx
vendored
Normal file
22
Lib/fontTools/feaLib/testdata/GlyphClassDef.ttx
vendored
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user