Skip to content

Commit

Permalink
[featureWriters] Support insert marker in the middle of a feature block
Browse files Browse the repository at this point in the history
Split the feature block into two and insert the generated code in the
middle.

See #351 (comment)
  • Loading branch information
khaledhosny committed Sep 23, 2024
1 parent 590c8de commit ff3f47b
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 41 deletions.
18 changes: 7 additions & 11 deletions Lib/ufo2ft/featureWriters/baseFeatureWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
25 changes: 25 additions & 0 deletions tests/featureWriters/__snapshots__/kernFeatureWriter2_test.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 2 additions & 7 deletions tests/featureWriters/kernFeatureWriter2_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
50 changes: 34 additions & 16 deletions tests/featureWriters/kernFeatureWriter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2010,38 +2028,38 @@ 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;
pos @kern1.Telu.shatelugu.below @kern2.Telu.katelugu.below 20;
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;
Expand Down
57 changes: 50 additions & 7 deletions tests/featureWriters/markFeatureWriter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 <anchor 100 200> @MC_top;
markClass acutecomb <anchor 100 200> @MC_top;
feature mark {
lookup mark1 {
pos base a
<anchor 100 200> mark @MC_top;
} mark1;
#
} mark;
feature mark {
lookup mark2base {
pos base a
<anchor 100 200> mark @MC_top;
} mark2base;
lookup mark2liga {
pos ligature f_i
<anchor 100 500> mark @MC_top
ligComponent
<anchor 600 500> mark @MC_top;
} mark2liga;
} mark;
feature mark {
#
lookup mark2 {
pos base a
<anchor 150 250> mark @MC_top;
} mark2;
} mark;
feature mkmk {
lookup mark2mark_top {
@MFS_mark2mark_top = [acutecomb tildecomb];
lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
pos mark tildecomb
<anchor 100 300> mark @MC_top;
} mark2mark_top;
} mkmk;
"""
)

# test append mode ignores insert marker
generated = self.writeFeatures(testufo, mode="append")
Expand Down

0 comments on commit ff3f47b

Please sign in to comment.