You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
font/shaper: Fix CoreText position.y for some scripts (#10179)
This PR simplifies and corrects the logic for placing a glyph
vertically, by using the `position.y` from `CoreText` directly, instead
of using an offset from the cell's starting `y`. The logic was incorrect
from the beginning, always treating the first glyph of a cell as being
at `y` of zero. We only need to be subtracting the cell's starting `x`
to align the glyphs to the cell grid.
Enabling the commented out logging, I found no instances of `position.y
differs from old offset.y` lines with `JetBrains Mono` with ligatures
turned on, but running
[ttylang](https://github.com/jacobsandlund/ttylang) (printing the
Universal Declaration of Human Rights in various languages) revealed 676
instances of this, with many only slightly off.
An example log from some Tai Tham text is the following, and this PR
adds a test based on this:
```
...pos=(0.00,-8.21) run_offset=(69.41,-8.21) cell_offset=(69.41,-8.21) old offset.y=0.00 cps = \u{1a49}\u{1a60} \u{1a3f}▸\u{1a69} \u{1a2f} → ᩉ᩠ᨿᩩᨯ
```
Browsers display this as:
ᩉ᩠ᨿᩩ
`main` is printing:
<img width="852" height="90" alt="CleanShot 2026-01-05 at 10 28 17@2x"
src="https://github.com/user-attachments/assets/c97b738c-8fe4-48b5-81f8-e0e79f1a9269"
/>
this PR prints:
<img width="958" height="90" alt="CleanShot 2026-01-05 at 10 29 07@2x"
src="https://github.com/user-attachments/assets/88fd26a7-8041-4b33-ab02-56f411204b04"
/>
Since this is a ligature of two different grapheme clusters, Ghostty
ends up subtracting too much of the `x` value with the `cell_offset.x`
(starting x), so neither of the screenshots above are correct, but the
second is closer and gets the `y` value right.
AI disclaimer: I didn't use AI for the code, but did ask it about this
Tai Tham text and why it wasn't a single grapheme cluster:
https://ampcode.com/threads/T-019b8ea2-1822-75bb-a8eb-55a9ddb9f7ea
log.warn("position.y differs from old offset.y: cluster={d} pos=({d:.2},{d:.2}) run_offset=({d:.2},{d:.2}) cell_offset=({d:.2},{d:.2}) old offset.y={d:.2} cps = {s}", .{
708
+
cell_offset.cluster,
709
+
x_offset,
710
+
position.y,
711
+
run_offset_x,
712
+
run_offset_y,
713
+
cell_offset.x,
714
+
cell_offset_y,
715
+
old_offset_y,
716
+
formatted_cps,
717
+
});
718
+
}
709
719
}
710
720
}
711
721
};
@@ -1522,6 +1532,66 @@ test "shape Tai Tham vowels (position differs from advance)" {
1522
1532
trytesting.expectEqual(@as(usize, 1), count);
1523
1533
}
1524
1534
1535
+
test"shape Tai Tham letters (position.y differs from advance)" {
1536
+
consttesting=std.testing;
1537
+
constalloc=testing.allocator;
1538
+
1539
+
// We need a font that supports Tai Tham for this to work, if we can't find
1540
+
// Noto Sans Tai Tham, which is a system font on macOS, we just skip the
1541
+
// test.
1542
+
vartestdata=testShaperWithDiscoveredFont(
1543
+
alloc,
1544
+
"Noto Sans Tai Tham",
1545
+
) catchreturnerror.SkipZigTest;
1546
+
defertestdata.deinit();
1547
+
1548
+
varbuf: [32]u8=undefined;
1549
+
varbuf_idx: usize=0;
1550
+
1551
+
// First grapheme cluster:
1552
+
buf_idx+=trystd.unicode.utf8Encode(0x1a49, buf[buf_idx..]); // HA
0 commit comments