From ff3f47bea9fe8751c9f61fcb8da08c132ddd9ca1 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Mon, 23 Sep 2024 11:42:34 +0300 Subject: [PATCH] [featureWriters] Support insert marker in the middle of a feature block Split the feature block into two and insert the generated code in the middle. See https://github.com/googlefonts/ufo2ft/issues/351#issuecomment-765294436 --- .../featureWriters/baseFeatureWriter.py | 18 +++--- .../kernFeatureWriter2_test.ambr | 25 ++++++++ .../featureWriters/kernFeatureWriter2_test.py | 9 +-- .../featureWriters/kernFeatureWriter_test.py | 50 ++++++++++------ .../featureWriters/markFeatureWriter_test.py | 57 ++++++++++++++++--- 5 files changed, 118 insertions(+), 41 deletions(-) diff --git a/Lib/ufo2ft/featureWriters/baseFeatureWriter.py b/Lib/ufo2ft/featureWriters/baseFeatureWriter.py index 5916a0f3..de31a191 100644 --- a/Lib/ufo2ft/featureWriters/baseFeatureWriter.py +++ b/Lib/ufo2ft/featureWriters/baseFeatureWriter.py @@ -6,7 +6,6 @@ from fontTools.feaLib.variableScalar import VariableScalar from fontTools.misc.fixedTools import otRound -from ufo2ft.errors import InvalidFeaturesData from ufo2ft.featureWriters import ast from ufo2ft.util import ( OpenTypeCategories, @@ -218,17 +217,14 @@ def _insert( # insertFeatureMarker is in the middle of a feature block # preceded and followed by statements that are not comments - # - # Glyphs3 can insert a feature block when rules are before - # and after the insert marker. - # See - # https://github.com/googlefonts/ufo2ft/issues/351#issuecomment-765294436 - # This is currently not supported. else: - raise InvalidFeaturesData( - "Insert marker has rules before and after, feature " - f"{block.name} cannot be inserted. This is not supported." - ) + index = statements.index(block) + 1 + # Split statements after the insertFeatureMarker into a new block + afterBlock = ast.FeatureBlock(block.name) + afterBlock.statements = block.statements[markerIndex:] + statements.insert(index, afterBlock) + # And remove them from the original block + block.statements = block.statements[:markerIndex] statements.insert(index, feature) indices.append(index) diff --git a/tests/featureWriters/__snapshots__/kernFeatureWriter2_test.ambr b/tests/featureWriters/__snapshots__/kernFeatureWriter2_test.ambr index def5d3d7..dfb691e8 100644 --- a/tests/featureWriters/__snapshots__/kernFeatureWriter2_test.ambr +++ b/tests/featureWriters/__snapshots__/kernFeatureWriter2_test.ambr @@ -423,6 +423,31 @@ ''' # --- # name: test_insert_comment_middle + ''' + feature kern { + pos one four' -50 six; + # + } kern; + + lookup kern_dflt { + lookupflag IgnoreMarks; + pos seven six 25; + } kern_dflt; + + feature kern { + script DFLT; + language dflt; + lookup kern_dflt; + } kern; + + feature kern { + # + pos one six' -50 six; + } kern; + + ''' +# --- +# name: test_insert_comment_middle.1 ''' lookup kern_dflt { lookupflag IgnoreMarks; diff --git a/tests/featureWriters/kernFeatureWriter2_test.py b/tests/featureWriters/kernFeatureWriter2_test.py index 61c3a52b..510b7749 100644 --- a/tests/featureWriters/kernFeatureWriter2_test.py +++ b/tests/featureWriters/kernFeatureWriter2_test.py @@ -8,7 +8,6 @@ from syrupy.types import SnapshotIndex from ufo2ft.constants import UNICODE_SCRIPT_ALIASES -from ufo2ft.errors import InvalidFeaturesData from ufo2ft.featureCompiler import FeatureCompiler, parseLayoutFeatures from ufo2ft.featureWriters.kernFeatureWriter2 import KernFeatureWriter from ufo2ft.util import DFLT_SCRIPTS, unicodeScriptExtensions @@ -348,12 +347,8 @@ def test_insert_comment_middle(snapshot, FontClass): writer = KernFeatureWriter() feaFile = parseLayoutFeatures(ufo) - with pytest.raises( - InvalidFeaturesData, - match="Insert marker has rules before and after, feature kern " - "cannot be inserted.", - ): - writer.write(ufo, feaFile) + writer.write(ufo, feaFile) + assert str(feaFile) == snapshot # test append mode ignores insert marker generated = KernFeatureWriterTest.writeFeatures(ufo, mode="append") diff --git a/tests/featureWriters/kernFeatureWriter_test.py b/tests/featureWriters/kernFeatureWriter_test.py index a4caa618..f55ec5eb 100644 --- a/tests/featureWriters/kernFeatureWriter_test.py +++ b/tests/featureWriters/kernFeatureWriter_test.py @@ -6,7 +6,6 @@ from fontTools import unicodedata from ufo2ft.constants import UNICODE_SCRIPT_ALIASES -from ufo2ft.errors import InvalidFeaturesData from ufo2ft.featureCompiler import FeatureCompiler, parseLayoutFeatures from ufo2ft.featureWriters import KernFeatureWriter, ast from ufo2ft.util import DFLT_SCRIPTS, unicodeScriptExtensions @@ -170,7 +169,7 @@ def test_mark_to_base_kern(self, FontClass): language dflt; lookup kern_Latn; lookup kern_Latn_marks; - + script latn; language dflt; lookup kern_Latn; @@ -191,7 +190,7 @@ def test_mark_to_base_kern(self, FontClass): script DFLT; language dflt; lookup kern_Latn; - + script latn; language dflt; lookup kern_Latn; @@ -503,12 +502,31 @@ def test_insert_comment_middle(self, FontClass): writer = KernFeatureWriter() feaFile = parseLayoutFeatures(ufo) - with pytest.raises( - InvalidFeaturesData, - match="Insert marker has rules before and after, feature kern " - "cannot be inserted.", - ): - writer.write(ufo, feaFile) + writer.write(ufo, feaFile) + assert str(feaFile) == dedent( + """\ + feature kern { + pos one four' -50 six; + # + } kern; + + lookup kern_Default { + lookupflag IgnoreMarks; + pos seven six 25; + } kern_Default; + + feature kern { + script DFLT; + language dflt; + lookup kern_Default; + } kern; + + feature kern { + # + pos one six' -50 six; + } kern; + """ + ) # test append mode ignores insert marker generated = self.writeFeatures(ufo, mode="append") @@ -1087,7 +1105,7 @@ def test_kern_independent_of_languagesystem(self, FontClass): script DFLT; language dflt; lookup kern_Latn; - + script arab; language dflt; lookup kern_Arab; @@ -2010,7 +2028,7 @@ def test_defining_classdefs(FontClass): @kern1.Telu.ssatelugu.alt = [ssa-telugu.alt]; @kern2.Telu.katelugu.below = [ka-telugu.below]; @kern2.Telu.rVocalicMatratelugu = [rVocalicMatra-telugu]; - + lookup kern_Telu { lookupflag IgnoreMarks; enum pos @kern1.Telu.ssatelugu.alt sha-telugu.below 150; @@ -2018,30 +2036,30 @@ def test_defining_classdefs(FontClass): pos @kern1.Default.ssatelugu.alt @kern2.Telu.katelugu.below 60; pos @kern1.Telu.ssatelugu.alt @kern2.Telu.katelugu.below 60; } kern_Telu; - + lookup kern_Telu_marks { pos @kern1.Default.ssatelugu.alt @kern2.Telu.rVocalicMatratelugu 180; pos @kern1.Telu.ssatelugu.alt @kern2.Telu.rVocalicMatratelugu 180; } kern_Telu_marks; - + lookup kern_Default { lookupflag IgnoreMarks; enum pos @kern1.Default.ssatelugu.alt sha-telugu.below 150; } kern_Default; - + feature kern { script DFLT; language dflt; lookup kern_Default; } kern; - + feature dist { script tel2; language dflt; lookup kern_Default; lookup kern_Telu; lookup kern_Telu_marks; - + script telu; language dflt; lookup kern_Default; diff --git a/tests/featureWriters/markFeatureWriter_test.py b/tests/featureWriters/markFeatureWriter_test.py index e00daa45..a504f60e 100644 --- a/tests/featureWriters/markFeatureWriter_test.py +++ b/tests/featureWriters/markFeatureWriter_test.py @@ -6,7 +6,6 @@ import pytest from ufo2ft.constants import OBJECT_LIBS_KEY -from ufo2ft.errors import InvalidFeaturesData from ufo2ft.featureCompiler import FeatureCompiler, parseLayoutFeatures from ufo2ft.featureWriters import ast from ufo2ft.featureWriters.markFeatureWriter import ( @@ -479,12 +478,56 @@ def test_insert_comment_middle(self, testufo): ) feaFile = parseLayoutFeatures(testufo) - with pytest.raises( - InvalidFeaturesData, - match="Insert marker has rules before and after, feature mark " - "cannot be inserted.", - ): - writer.write(testufo, feaFile) + writer.write(testufo, feaFile) + assert str(feaFile) == dedent( + """\ + markClass tildecomb @MC_top; + + markClass acutecomb @MC_top; + feature mark { + lookup mark1 { + pos base a + mark @MC_top; + } mark1; + + # + } mark; + + feature mark { + lookup mark2base { + pos base a + mark @MC_top; + } mark2base; + + lookup mark2liga { + pos ligature f_i + mark @MC_top + ligComponent + mark @MC_top; + } mark2liga; + + } mark; + + feature mark { + # + lookup mark2 { + pos base a + mark @MC_top; + } mark2; + + } mark; + + feature mkmk { + lookup mark2mark_top { + @MFS_mark2mark_top = [acutecomb tildecomb]; + lookupflag UseMarkFilteringSet @MFS_mark2mark_top; + pos mark tildecomb + mark @MC_top; + } mark2mark_top; + + } mkmk; + """ + ) # test append mode ignores insert marker generated = self.writeFeatures(testufo, mode="append")