fix(web): measure canvas text with draw style

This commit is contained in:
Kayshen-X 2026-06-19 08:13:40 +08:00
parent 242031c9b2
commit a9a7d33c2d
3 changed files with 57 additions and 6 deletions

View file

@ -212,6 +212,8 @@ extern "C" {
);
#[wasm_bindgen(method, js_name = measureText)]
fn measure_text(this: &OpCk, t: &str, sz: f32) -> f32;
#[wasm_bindgen(method, js_name = measureTextStyled)]
fn measure_text_styled(this: &OpCk, t: &str, sz: f32, weight: i32, italic: bool) -> f32;
#[wasm_bindgen(method, js_name = registerSystemFont)]
fn register_system_font(this: &OpCk, family: &str, bytes: &[u8]) -> bool;
#[wasm_bindgen(method, js_name = clipRect)]
@ -541,10 +543,21 @@ impl RenderBackend for CanvasKitBackend {
}
}
fn measure_text(&mut self, text: &str, font_size: f32) -> f32 {
self.ck.measure_text(text, font_size)
self.ck.measure_text_styled(text, font_size, 400, false)
}
fn measure_text_weighted(&mut self, text: &str, font_size: f32, _weight: u16) -> f32 {
self.ck.measure_text(text, font_size)
fn measure_text_weighted(&mut self, text: &str, font_size: f32, weight: u16) -> f32 {
self.ck
.measure_text_styled(text, font_size, i32::from(weight), false)
}
fn measure_text_styled(
&mut self,
text: &str,
font_size: f32,
weight: u16,
italic: bool,
) -> f32 {
self.ck
.measure_text_styled(text, font_size, weight as i32, italic)
}
fn clip_rect(&mut self, rect: Rect) {

View file

@ -396,13 +396,17 @@ export async function opCkInit(canvasId) {
p.delete();
},
measureText(t, sz) {
return this.measureTextStyled(t, sz, 400, false);
},
measureTextStyled(t, sz, weight, italic) {
let w = 0;
for (const seg of segments(t)) {
if (shouldUseBrowserTextFallback(seg.text, seg.emoji)) {
w += browserTextMeasure(seg.text, sz);
w += browserTextMeasure(seg.text, sz, weight, italic);
continue;
}
const f = new CK.Font(tfFor(seg.text, seg.emoji), sz);
if (italic && !seg.emoji) f.setSkewX(-0.25);
w += runWidth(f, seg.text);
f.delete();
}

View file

@ -131,7 +131,7 @@ fn canvaskit_text_defaults_to_browser_system_font_fallback() {
"const segments = (t)",
"const shouldUseBrowserTextFallback = (_t, _emojiRun) => Boolean(browserTextCtx);",
"drawBrowserText(seg.text, cx, y, sz, weight, italic, r, g, b, a)",
"browserTextMeasure(seg.text, sz)",
"browserTextMeasure(seg.text, sz, weight, italic)",
"CK.Typeface.GetDefault()",
] {
assert!(
@ -177,6 +177,40 @@ fn canvaskit_browser_text_fallback_does_not_require_canvas_paint() {
);
}
#[test]
fn canvaskit_text_measurement_uses_draw_text_style() {
let bridge = std::fs::read_to_string(format!(
"{}/src/op_ck_bridge.js",
env!("CARGO_MANIFEST_DIR")
))
.expect("CanvasKit bridge source is readable");
let backend =
std::fs::read_to_string(format!("{}/src/canvaskit.rs", env!("CARGO_MANIFEST_DIR")))
.expect("CanvasKit backend source is readable");
for marker in [
"measureTextStyled(t, sz, weight, italic)",
"browserTextMeasure(seg.text, sz, weight, italic)",
] {
assert!(
bridge.contains(marker),
"CanvasKit bridge must preserve `{marker}` so measured text matches drawn text"
);
}
for marker in [
"fn measure_text_styled(",
"weight: u16,",
"italic: bool,",
".measure_text_styled(text, font_size, weight as i32, italic)",
] {
assert!(
backend.contains(marker),
"CanvasKit backend must preserve `{marker}` so web layout measures with draw-time text style"
);
}
}
#[test]
fn canvaskit_paint_style_is_guarded_before_set_style() {
let source = std::fs::read_to_string(format!(
@ -243,7 +277,7 @@ fn canvaskit_bridge_uses_browser_text_fallback_when_local_font_api_is_unavailabl
"const shouldUseBrowserTextFallback",
"const drawBrowserText",
"CK.MakeImageFromCanvasImageSource",
"browserTextMeasure(seg.text, sz)",
"browserTextMeasure(seg.text, sz, weight, italic)",
] {
assert!(
source.contains(marker),