From a9a7d33c2ded8a3f811c81a342159ffdd2c81ba3 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Fri, 19 Jun 2026 08:13:40 +0800 Subject: [PATCH] fix(web): measure canvas text with draw style --- crates/op-host-web/src/canvaskit.rs | 19 +++++++++++-- crates/op-host-web/src/op_ck_bridge.js | 6 +++- crates/op-host-web/tests/paint_order.rs | 38 +++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/crates/op-host-web/src/canvaskit.rs b/crates/op-host-web/src/canvaskit.rs index 63c7ca3f1..dae999668 100644 --- a/crates/op-host-web/src/canvaskit.rs +++ b/crates/op-host-web/src/canvaskit.rs @@ -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) { diff --git a/crates/op-host-web/src/op_ck_bridge.js b/crates/op-host-web/src/op_ck_bridge.js index 592088155..e9588cbfc 100644 --- a/crates/op-host-web/src/op_ck_bridge.js +++ b/crates/op-host-web/src/op_ck_bridge.js @@ -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(); } diff --git a/crates/op-host-web/tests/paint_order.rs b/crates/op-host-web/tests/paint_order.rs index 72383f86e..3504e1d91 100644 --- a/crates/op-host-web/tests/paint_order.rs +++ b/crates/op-host-web/tests/paint_order.rs @@ -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),