Skip to content

Comments

Preserve original anchor names through propagation#1890

Merged
anthrotype merged 2 commits intomainfrom
fix-caret-anchor-name-roundtrip
Feb 13, 2026
Merged

Preserve original anchor names through propagation#1890
anthrotype merged 2 commits intomainfrom
fix-caret-anchor-name-roundtrip

Conversation

@anthrotype
Copy link
Member

@anthrotype anthrotype commented Feb 13, 2026

After #1832 (propagate anchors in IR), fonts with non-standard caret anchor naming lost caret positions. For example, ComforterBrush-Pro.glyphs defines two caret anchors on "ffi":

  • caret_1 at x=242
  • caret_1_ at x=588

AnchorKind::new("caret_1_") parses the suffix "1_", fails parse::<usize>(), and falls back to Caret(1), i.e. the same variant as caret_1. When to_name() method converts both back to strings during anchor propagation, both become "caret_1" and one overwrites the other in the IndexMap.

Before #1832 this didn't matter because fontbe consumed Vec<Anchor> directly without round-tripping through names. After the merge, propagation serializes anchors via AnchorKind::to_name() at

https://github.com/googlefonts/fontc/blob/ade0a9bc/fontir/src/propagate_anchors.rs#L87

... introducing the lossy conversion.

(fontmake/ufo2ft doesn't have this problem because it never parses caret indices, it just checks name.startswith("caret_") and collects positions.. apparently nobody cares about caret indices except me)

The fix is to add original_name: SmolStr field to fontir::ir::Anchor to preserve the original source name and use that instead of anchor.kind.to_name() when feeding anchors into propagation.

The existing to_name() is kept as a public method but documented as lossy.

The first commit adds the regression test that asserts both caret_1 and caret_1_ survive propagation (deliberately fails without the fix). It is followed by the actual fix.

Reproduces the ComforterBrush ffi/ffl issue: non-standard caret anchor
names like "caret_1_" are lost during propagation because
AnchorKind::to_name() maps both "caret_1" and "caret_1_" to "caret_1".

This test currently FAILS, demonstrating the bug.
@anthrotype
Copy link
Member Author

https://github.com/googlefonts/fontc/actions/runs/21998353880/job/63564211580?pr=1890#step:5:246

thread 'propagate_anchors::tests::caret_anchor_names_preserved_through_propagation' (4227) panicked at fontir/src/propagate_anchors.rs:2062:9:
assertion `left == right` failed
  left: ["caret_1", "top_1", "top_2", "top_3"]
 right: ["caret_1", "caret_1_", "top_1", "top_2", "top_3"]

Add `original_name: SmolStr` field to `Anchor` struct to preserve the
original source name. Use `anchor.original_name` instead of
`anchor.kind.to_name()` during propagation, avoiding the lossy
round-trip where non-standard caret names like "caret_1_" were mapped
to "caret_1" via AnchorKind.

This fixes the ComforterBrush regression where ffi/ffl ligatures lost
one of their two caret positions.
@anthrotype anthrotype force-pushed the fix-caret-anchor-name-roundtrip branch from 616368e to e8d4585 Compare February 13, 2026 18:48
@anthrotype anthrotype added this pull request to the merge queue Feb 13, 2026
Merged via the queue into main with commit 5d70744 Feb 13, 2026
13 checks passed
@anthrotype anthrotype deleted the fix-caret-anchor-name-roundtrip branch February 13, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants