[feaLib] report error with multiple runs of marked ' glyphs

As Martin Hosken reported in https://github.com/fonttools/fonttools/pull/1096,
feaLib currently incorrectly handles the case where a marked input
glyph sequence in a contextual chaining sub/pos rule is split into
multiple runs, rather than being a single continuous run of ' marked
glyphs.

The consensus there was to raise a syntax error like makeotf instead of
second-guessing and silently fixing it like fontforge does.
This commit is contained in:
Cosimo Lupo 2017-11-21 11:42:28 +01:00
parent 943b63c7d2
commit 4dfea00356
2 changed files with 44 additions and 0 deletions

View File

@ -340,6 +340,14 @@ class Parser(object):
self.expect_symbol_("'")
hasMarks = marked = True
if marked:
if suffix:
# makeotf also reports this as an error, while FontForge
# silently inserts ' in all the intervening glyphs.
# https://github.com/fonttools/fonttools/pull/1096
raise FeatureLibError(
"Unsupported contextual target sequence: at most "
"one run of marked (') glyph/class names allowed",
self.cur_token_location_)
glyphs.append(gc)
elif glyphs:
suffix.append(gc)

View File

@ -1220,6 +1220,42 @@ class ParserTest(unittest.TestCase):
self.assertEqual(sub.glyph, "f_f_i")
self.assertEqual(sub.replacement, ("f", "f", "i"))
def test_split_marked_glyphs_runs(self):
self.assertRaisesRegex(
FeatureLibError,
"Unsupported contextual target sequence",
self.parse, "feature test{"
" ignore pos a' x x A';"
"} test;")
self.assertRaisesRegex(
FeatureLibError,
"Unsupported contextual target sequence",
self.parse, "lookup shift {"
" pos a <0 -10 0 0>;"
" pos A <0 10 0 0>;"
"} shift;"
"feature test {"
" sub a' lookup shift x x A' lookup shift;"
"} test;")
self.assertRaisesRegex(
FeatureLibError,
"Unsupported contextual target sequence",
self.parse, "feature test {"
" ignore sub a' x x A';"
"} test;")
self.assertRaisesRegex(
FeatureLibError,
"Unsupported contextual target sequence",
self.parse, "lookup upper {"
" sub a by A;"
"} upper;"
"lookup lower {"
" sub A by a;"
"} lower;"
"feature test {"
" sub a' lookup upper x x A' lookup lower;"
"} test;")
def test_substitute_from(self): # GSUB LookupType 3
doc = self.parse("feature test {"
" substitute a from [a.1 a.2 a.3];"