Keep descenders of a text whole and frame it at its drawn size
A text was drawn into a texture as tall as Pixi measures the string "|ÉqÅM". Whatever reached further, like the "g" of Manrope (0.015 em below the "q"), was cut off at the bottom. The blue frame around a text was only estimated from the font size and the number of letters, so it was far too wide for most texts. - textPicture.ts takes the height of a line from the font itself (fontBoundingBoxAscent and fontBoundingBoxDescent, as a browser does). Pixi draws with a margin, and the picture is cut to that box afterwards. Where Pixi's own box is larger, Pixi's box wins. - Both raster paths (linear and nearest) use the cut. - The height no longer depends on the letters typed, so a text does not jump while it is edited. - bounds.ts asks a measurer that textPicture.ts registers (setTextMeasure). Selection, picking in a running game, framing the camera, the drop preview and culling all get the measured size. The footprint functions take the font file for this. - Unit tests in node have no canvas and keep the estimate (estimatedTextSize). - Texts get taller: Source Sans 3 at 48 px is 68 instead of 55 px, Manrope is 1.366 em instead of 1.25 em. The glyphs stay in place, the centre moves by less than half a pixel. A shader on a text gets a taller UV rectangle. Tests: - Gate on melektaus-atx green: 307 files, 4762 tests. - 5 new unit tests for textBox, textCut and the measurer hook. - e2e 30: 26 checks, 30f-iii new. The texture is 68 high, the font box is 68, the frame equals the drawn picture (237x68). - Counter test: with Pixi's old box the texture is 55 high and 30f-iii is red. - Not checked in the browser: the Manrope caption of the user and texts with several lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9445f39c8d
commit
d4ff652293
12 files changed
+284
-47
No files matched your search
@@ -218,6 +218,26 @@ export async function font(t) {
|
||||
JSON.stringify({ plain: plain.width, wide: wide?.width, tight: tight?.width }),
|
||||
);
|
||||
|
||||
// 30f-iii. A text is as tall as its face's own line box, not as Pixi's "|ÉqÅM" reaches, so a
|
||||
// tail like Manrope's "g" stays whole – and the frame around it is the drawn picture.
|
||||
const upright = await waitFor(info, (value) => value?.width === plain.width);
|
||||
const lineBox = await evaluate(`(() => {
|
||||
const context = new OffscreenCanvas(1, 1).getContext('2d');
|
||||
context.font = '48px ' + ${JSON.stringify(upright?.family ?? '')};
|
||||
const metrics = context.measureText('M');
|
||||
return metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
|
||||
})()`);
|
||||
const framed = await json(`__gm.objectRect(${JSON.stringify(TEXT_OBJECT)})`);
|
||||
const frameScale = framed ? framed.w / upright.width : 0;
|
||||
check(
|
||||
'a text is as tall as its face’s line box, with no glyph cut at its top or bottom, and framed as drawn',
|
||||
Math.abs(upright?.height - lineBox) <= 1 &&
|
||||
upright?.edgeInk.top === 0 &&
|
||||
upright?.edgeInk.bottom === 0 &&
|
||||
Math.abs(framed?.h / frameScale - upright?.height) <= 1,
|
||||
JSON.stringify({ upright, lineBox, framed }),
|
||||
);
|
||||
|
||||
// 30g. Dropping a font file onto the editor uploads it and makes an asset named after the file.
|
||||
const dropped = await evaluate(`
|
||||
(async () => {
|
||||
|
||||
Reference in new issue
Block a user