Fix for #2293: allow more than one lookahead glyph/class in contextual positioning with "value at end" (#2294)

* add test that fails for #2293

* fixing #2293: rewrite of contextual positioning logic, ensure len(suffix) > 1 yields the correct result; checking more edge cases and raising errors inspired by makeotf

* test error cases

* only check when we actually have a value

* catch one more case that makeotf errors on and we didn't
This commit is contained in:
Just van Rossum 2021-05-08 09:22:30 +02:00 committed by GitHub
parent d601951eab
commit 9825ab0977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 8 deletions

View File

@ -473,14 +473,38 @@ class Parser(object):
assert lookups == []
return ([], prefix, [None] * len(prefix), values, [], hasMarks)
else:
assert not any(values[: len(prefix)]), values
format1 = values[len(prefix) :][: len(glyphs)]
format2 = values[(len(prefix) + len(glyphs)) :][: len(suffix)]
values = (
format2
if format2 and isinstance(format2[0], self.ast.ValueRecord)
else format1
)
if any(values[: len(prefix)]):
raise FeatureLibError(
"Positioning cannot be applied in the bactrack glyph sequence, "
"before the marked glyph sequence.",
self.cur_token_location_
)
marked_values = values[len(prefix) : len(prefix) + len(glyphs)]
if any(marked_values):
if any(values[len(prefix) + len(glyphs) :]):
raise FeatureLibError(
"Positioning values are allowed only in the marked glyph "
"sequence, or after the final glyph node when only one glyph "
"node is marked.",
self.cur_token_location_
)
values = marked_values
elif values and values[-1]:
if len(glyphs) > 1 or any(values[:-1]):
raise FeatureLibError(
"Positioning values are allowed only in the marked glyph "
"sequence, or after the final glyph node when only one glyph "
"node is marked.",
self.cur_token_location_
)
values = values[-1:]
elif any(values):
raise FeatureLibError(
"Positioning values are allowed only in the marked glyph "
"sequence, or after the final glyph node when only one glyph "
"node is marked.",
self.cur_token_location_
)
return (prefix, glyphs, lookups, values, suffix, hasMarks)
def parse_chain_context_(self):

View File

@ -870,6 +870,41 @@ class ParserTest(unittest.TestCase):
self.assertEqual(glyphstr(pos.prefix), "[A B]")
self.assertEqual(glyphstr(pos.suffix), "comma")
def test_gpos_type_1_chained_special_kern_format_valuerecord_format_b_bug2293(self):
# https://github.com/fonttools/fonttools/issues/2293
doc = self.parse("feature kern {pos [A B] [T Y]' comma a <0 0 0 0>;} kern;")
pos = doc.statements[0].statements[0]
self.assertIsInstance(pos, ast.SinglePosStatement)
[(glyphs, value)] = pos.pos
self.assertEqual(glyphstr([glyphs]), "[T Y]")
self.assertEqual(value.asFea(), "<0 0 0 0>")
self.assertEqual(glyphstr(pos.prefix), "[A B]")
self.assertEqual(glyphstr(pos.suffix), "comma a")
def test_gpos_type_1_chained_exception1(self):
with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
doc = self.parse("feature kern {"
" pos [A B]' [T Y]' comma a <0 0 0 0>;"
"} kern;")
def test_gpos_type_1_chained_exception2(self):
with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
doc = self.parse("feature kern {"
" pos [A B]' <0 0 0 0> [T Y]' comma a <0 0 0 0>;"
"} kern;")
def test_gpos_type_1_chained_exception3(self):
with self.assertRaisesRegex(FeatureLibError, "Positioning cannot be applied"):
doc = self.parse("feature kern {"
" pos [A B] <0 0 0 0> [T Y]' comma a <0 0 0 0>;"
"} kern;")
def test_gpos_type_1_chained_exception4(self):
with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
doc = self.parse("feature kern {"
" pos a' b c 123 d;"
"} kern;")
def test_gpos_type_2_format_a(self):
doc = self.parse("feature kern {"
" pos [T V] -60 [a b c] <1 2 3 4>;"