LibWeb: Preserve non-ASCII characters in canvas text preparation

The whitespace-normalization loop in prepare_text() called
StringBuilder::append() on each code point, which resolves to the
`char` overload and truncates non-ASCII characters. measureText("ó")
therefore returned a width of 0, despite fillText painting the glyph.

Use append_code_point() instead, and add a regression test for both
precomposed and decomposed accented text.
This commit is contained in:
Aliaksandr Kalenik
2026-04-25 01:33:03 +02:00
committed by Andreas Kling
parent 792a8c3a9c
commit 4f54b16315
Notes: github-actions[bot] 2026-04-25 12:56:50 +00:00
3 changed files with 31 additions and 1 deletions

View File

@@ -785,7 +785,7 @@ CanvasRenderingContext2D::PreparedText CanvasRenderingContext2D::prepare_text(Ut
// 2. Replace all ASCII whitespace in text with U+0020 SPACE characters.
StringBuilder builder { StringBuilder::Mode::UTF16, text.length_in_code_units() };
for (auto c : text) {
builder.append(Infra::is_ascii_whitespace(c) ? ' ' : c);
builder.append_code_point(Infra::is_ascii_whitespace(c) ? ' ' : c);
}
auto replaced_text = builder.to_utf16_string();

View File

@@ -0,0 +1,3 @@
precomposed accent has positive width: true
precomposed and decomposed accent widths match: true
word width includes precomposed accent: true

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<style>
@font-face {
font-family: "LatoTest";
src: url("../../../Assets/Lato-Bold.ttf");
}
</style>
<span style="font-family: LatoTest; position: absolute; visibility: hidden">Kraków</span>
<script src="../include.js"></script>
<script>
promiseTest(async () => {
await document.fonts.ready;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "48px LatoTest";
const accented = ctx.measureText("ó");
const decomposed = ctx.measureText("o\u0301");
println(`precomposed accent has positive width: ${accented.width > 0}`);
println(`precomposed and decomposed accent widths match: ${Math.abs(accented.width - decomposed.width) < 1}`);
const text = "Kraków";
const metrics = ctx.measureText(text);
println(`word width includes precomposed accent: ${metrics.width > ctx.measureText("Krakw").width}`);
});
</script>