From 996852d15ca3397439304280363231649c12ba1e Mon Sep 17 00:00:00 2001 From: Rob Coenen <753704+rcoenen@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:55:43 -0400 Subject: [PATCH] M4: hanging punctuation test holds for fonts that kern with x offsets On macOS without craft-fonts the default face is Helvetica Neue, whose kerning comes out of the shaper partly as an x offset (e.g. on the "e" of "here."), so a glyph's x + advance is not its pen end. The layout was right (the full stop's pen sits exactly on the edge); the test assumed no offsets. Each hanging line is now compared with the same line set flush: every glyph must move by exactly the hanging mark's advance, and the flush right line must end on the edge. Co-Authored-By: Claude Opus 5.5 --- crates/text/src/tail_tests.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/text/src/tail_tests.rs b/crates/text/src/tail_tests.rs index 9f771b81..890b254d 100644 --- a/crates/text/src/tail_tests.rs +++ b/crates/text/src/tail_tests.rs @@ -130,14 +130,23 @@ fn hanging_punctuation_outside_the_aligned_edge() { t.auto_resize = TextAutoResize::Height; let Some(plain) = laid(&t) else { return }; let open = plain.glyphs[0].advance; + // Kerning may draw a glyph off its pen position (an x offset, e.g. Helvetica Neue's "re"), so + // each hanging line is checked against the same line set flush: every glyph moves by exactly + // the hanging mark's advance, which puts the letters' pen edge on the box edge. + let moved = |a: &TextLayout, b: &TextLayout, by: f64| { + a.glyphs.len() == b.glyphs.len() && a.glyphs.iter().zip(&b.glyphs).all(|(a, b)| (a.x - b.x - by).abs() < EPS) + }; t.hanging_punctuation = true; let l = laid(&t).unwrap(); - assert!((l.glyphs[0].x + open).abs() < EPS && l.glyphs[1].x.abs() < EPS, "left: the quote hangs, Q starts the line"); + assert!(plain.glyphs[0].x.abs() < EPS && moved(&l, &plain, -open), "left: the quote hangs, Q starts the line"); t.align_h = TextAlignH::Right; + t.hanging_punctuation = false; + let flush = laid(&t).unwrap(); + t.hanging_punctuation = true; let l = laid(&t).unwrap(); - let last = l.glyphs.last().unwrap(); - let before = &l.glyphs[l.glyphs.len() - 2]; - assert!((before.x + before.advance - 400.0).abs() < EPS && last.x >= 400.0 - EPS, "right: the full stop hangs past the edge"); + let stop = l.glyphs.last().unwrap().advance; + let edge = flush.lines[0].x + flush.lines[0].width; + assert!((edge - 400.0).abs() < EPS && moved(&l, &flush, stop), "right: the full stop hangs past the edge"); t.align_h = TextAlignH::Center; let (a, b) = (laid(&t).unwrap(), { t.hanging_punctuation = false;