Skip to content

Commit

Permalink
propagate_anchors: fix handling of contextual anchors
Browse files Browse the repository at this point in the history
- Don’t propagate contextual anchors, GlyphsApp does not propagate them.
- When copying anchors, copy also userData, otherwise the context of
  contextual anchors of any processed glyph will be dropped.

This is a regression from #1011
  • Loading branch information
khaledhosny committed Oct 5, 2024
1 parent 664779c commit e524564
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
10 changes: 9 additions & 1 deletion Lib/glyphsLib/builder/transformations/propagate_anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ def anchors_traversing_components(
component_transform = Transform(*component.transform)
xscale, yscale = get_xy_rotation(component_transform)
for anchor in anchors:
# skip contextual anchors
if anchor.name.startswith("*") and "GPOS_Context" in anchor.userData:
continue
new_has_underscore = anchor.name.startswith("_")
if (component_idx > 0 or has_underscore) and new_has_underscore:
continue
Expand Down Expand Up @@ -299,6 +302,7 @@ def origin_adjusted_anchors(anchors: list[GSAnchor]) -> Iterable[GSAnchor]:
GSAnchor(
name=a.name,
position=Point(a.position.x - origin.x, a.position.y - origin.y),
userData=a.userData,
)
for a in anchors
if a.name != "*origin"
Expand Down Expand Up @@ -398,7 +402,11 @@ def get_component_layer_anchors(
if layer_anchors is not None:
# return a copy as they may be modified in place
layer_anchors = [
GSAnchor(name=a.name, position=Point(a.position.x, a.position.y))
GSAnchor(
name=a.name,
position=Point(a.position.x, a.position.y),
userData=a.userData,
)
for a in layer_anchors
]
return layer_anchors
Expand Down
4 changes: 3 additions & 1 deletion Lib/glyphsLib/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2787,13 +2787,15 @@ def _serialize_to_plist(self, writer):
_parent = None
_defaultsForName = {"position": Point(0, 0)}

def __init__(self, name=None, position=None):
def __init__(self, name=None, position=None, userData=None):
self.name = "" if name is None else name
self._userData = None
if position is None:
self.position = copy.deepcopy(self._defaultsForName["position"])
else:
self.position = position
if userData is not None:
self.userData = userData

def __repr__(self):
return '<{} "{}" x={:.1f} y={:.1f}>'.format(
Expand Down
63 changes: 61 additions & 2 deletions tests/builder/transformations/propagate_anchors_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,13 @@ def add_component_anchor(self, name: str) -> Self:
component.anchor = name
return self

def add_anchor(self, name: str, pos: tuple[float, float]) -> Self:
anchor = GSAnchor(name, Point(*pos))
def add_anchor(
self,
name: str,
pos: tuple[float, float],
userData: dict | None = None,
) -> Self:
anchor = GSAnchor(name, Point(*pos), userData=userData)
self.current_layer.anchors.append(anchor)
return self

Expand Down Expand Up @@ -193,6 +198,7 @@ def assert_anchors(actual, expected):
for a, e in zip(actual, expected):
assert a.name == e[0]
assert a.position == Point(*e[1])
assert dict(a.userData) == (e[2] if len(e) > 2 else {})


def test_no_components_anchors_are_unchanged():
Expand Down Expand Up @@ -611,6 +617,59 @@ def test_origin_anchor():
)


def test_contextual_anchors():
glyphs = (
GlyphSetBuilder()
.add_glyph(
"behDotless-ar.init",
lambda glyph: (
glyph.add_anchor("bottom", (50, 0))
.add_anchor(
"*bottom",
(95, 0),
userData={"GPOS_Context": "* behDotless-ar.medi"},
)
.add_anchor("top", (35, 229))
),
)
.add_glyph(
"behDotless-ar.medi",
lambda glyph: (
glyph.add_component("behDotless-ar.init", (0, 0)).add_anchor(
"*bottom",
(95, 0),
userData={"GPOS_Context": "* behDotless-ar.fina"},
)
),
)
.add_glyph(
"behDotless-ar.fina",
lambda glyph: glyph.add_component("behDotless-ar.init", (0, 0)),
)
.build()
)
propagate_all_anchors_impl(glyphs)

new_glyph = glyphs["behDotless-ar.medi"]
assert_anchors(
new_glyph.layers[0].anchors,
[
("bottom", (50, 0)),
("top", (35, 229)),
("*bottom", (95, 0), {"GPOS_Context": "* behDotless-ar.fina"}),
],
)

new_glyph = glyphs["behDotless-ar.fina"]
assert_anchors(
new_glyph.layers[0].anchors,
[
("bottom", (50, 0)),
("top", (35, 229)),
],
)


def test_invert_names_on_rotation():
# derived from the observed behaviour of glyphs 3.2.2 (3259)
glyphs = (
Expand Down

0 comments on commit e524564

Please sign in to comment.