max / shop
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+29 insertions,
-18 deletions
| @@ -396,27 +396,38 @@ | |||
| 396 | 396 | push(slot, shaper, id, col, 0.0, 0.0, color); | |
| 397 | 397 | } | |
| 398 | 398 | CellText::Cluster(s) => { | |
| 399 | - | // Every glyph is drawn from the SAME cell origin, and the | |
| 400 | - | // pen deliberately does not advance between them. | |
| 399 | + | // The pen DOES advance across the cluster, and the marks | |
| 400 | + | // still land on the base. Measured on the bundled Iosevka: | |
| 401 | + | // a combining glyph carries a full cell of advance and its | |
| 402 | + | // ink hangs off the LEFT of its origin (acute -5px for a | |
| 403 | + | // 3px mark, diaeresis -7px for a 6px one). The font is | |
| 404 | + | // built for a pen that has already stepped past the base, | |
| 405 | + | // and the negative bearing is what pulls the mark back | |
| 406 | + | // over it, centred. | |
| 401 | 407 | // | |
| 402 | - | // A cluster is one grapheme by construction — a base and | |
| 403 | - | // the zero-width marks after it — so everything past the | |
| 404 | - | // first glyph is a mark belonging on top of that base, in | |
| 405 | - | // this cell. Walking the pen by each glyph's advance is | |
| 406 | - | // what a LINE of text wants and is wrong here: a monospace | |
| 407 | - | // font gives its combining glyphs a full cell of advance | |
| 408 | - | // (measured on the bundled Iosevka: the acute reports the | |
| 409 | - | // cell width, not zero), which would put every mark one | |
| 410 | - | // cell right, on top of the neighbour. | |
| 408 | + | // Anchoring every glyph at the cell origin instead — which | |
| 409 | + | // looks right, and which this did briefly — draws each mark | |
| 410 | + | // a whole cell to the LEFT, over the previous character. | |
| 411 | + | // Do not "fix" it back without looking at a window first. | |
| 411 | 412 | // | |
| 412 | - | // The shaper's own offsets are still honoured, so a font | |
| 413 | - | // positioning marks by GPOS gets what it asked for: those | |
| 414 | - | // offsets are relative to the cluster origin, which is | |
| 415 | - | // exactly what this anchors to. Where the shaper composes | |
| 416 | - | // the pair into one precomposed glyph instead (Iosevka | |
| 417 | - | // does for e + acute), there is nothing left to stack. | |
| 413 | + | // The two other shapes this can take are handled by the | |
| 414 | + | // same line: where the shaper composes the pair into one | |
| 415 | + | // precomposed glyph (Iosevka does for e + acute) there is | |
| 416 | + | // nothing after the base to place, and where a font | |
| 417 | + | // positions marks by GPOS the mark's advance is zero and | |
| 418 | + | // its offsets do the work. | |
| 419 | + | let mut pen_x = 0.0; | |
| 418 | 420 | for g in shaper.shape(&s) { | |
| 419 | - | push(slot, shaper, g.id, col, g.x_offset, g.y_offset, color); | |
| 421 | + | push( | |
| 422 | + | slot, | |
| 423 | + | shaper, | |
| 424 | + | g.id, | |
| 425 | + | col, | |
| 426 | + | pen_x + g.x_offset, | |
| 427 | + | g.y_offset, | |
| 428 | + | color, | |
| 429 | + | ); | |
| 430 | + | pen_x += g.advance; | |
| 420 | 431 | } | |
| 421 | 432 | } | |
| 422 | 433 | } |