From 243a1e630536e2a95a2b09e78d56adbe50cc28ce Mon Sep 17 00:00:00 2001 From: Jonas Wagner Date: Thu, 19 Dec 2024 00:55:08 +0100 Subject: [PATCH] estimate text layout using an offscreen canvas --- plotters/Cargo.toml | 33 +++++++++++++++++++------------- plotters/src/style/font/web.rs | 35 ++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/plotters/Cargo.toml b/plotters/Cargo.toml index d6ecbe10..c6b818fc 100644 --- a/plotters/Cargo.toml +++ b/plotters/Cargo.toml @@ -58,26 +58,33 @@ version = "0.2.89" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies.web-sys] version = "0.3.66" features = [ - "Document", - "DomRect", - "Element", - "HtmlElement", - "Node", - "Window", - "HtmlCanvasElement", - "CanvasRenderingContext2d", + "Document", + "DomRect", + "Element", + "HtmlElement", + "Node", + "Window", + "HtmlCanvasElement", + "CanvasRenderingContext2d", + "OffscreenCanvas", + "OffscreenCanvasRenderingContext2d", + "TextMetrics", ] [features] default = [ - "bitmap_backend", "bitmap_encoder", "bitmap_gif", + "bitmap_backend", + "bitmap_encoder", + "bitmap_gif", "svg_backend", "chrono", "ttf", "image", - "deprecated_items", "all_series", "all_elements", + "deprecated_items", + "all_series", + "all_elements", "full_palette", - "colormaps" + "colormaps", ] all_series = ["area_series", "line_series", "point_series", "surface_series"] all_elements = ["errorbar", "candlestick", "boxplot", "histogram"] @@ -117,7 +124,8 @@ datetime = ["chrono"] serialization = ["serde"] evcxr = ["svg_backend"] evcxr_bitmap = ["evcxr", "bitmap_backend", "plotters-svg/bitmap_encoder"] -deprecated_items = [] # Keep some of the deprecated items for backward compatibility +deprecated_items = [ +] # Keep some of the deprecated items for backward compatibility [dev-dependencies] itertools = "0.10.0" @@ -143,4 +151,3 @@ path = "benches/main.rs" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "doc_cfg"] - diff --git a/plotters/src/style/font/web.rs b/plotters/src/style/font/web.rs index 7af99965..add97e8d 100644 --- a/plotters/src/style/font/web.rs +++ b/plotters/src/style/font/web.rs @@ -1,6 +1,6 @@ use super::{FontData, FontFamily, FontStyle, LayoutBox}; use wasm_bindgen::JsCast; -use web_sys::{window, HtmlElement}; +use web_sys::{window, HtmlElement, OffscreenCanvas, OffscreenCanvasRenderingContext2d}; #[derive(Debug, Clone)] pub enum FontError { @@ -29,18 +29,25 @@ impl FontData for FontDataInternal { )) } fn estimate_layout(&self, size: f64, text: &str) -> Result { - let window = window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); - let span = document.create_element("span").unwrap(); - span.set_text_content(Some(text)); - span.set_attribute("style", &format!("display: inline-block; font-family:{}; font-style:{}; font-size: {}px; position: fixed; top: 100%", self.0, self.1, size)).unwrap(); - let span = span.into(); - body.append_with_node_1(&span).unwrap(); - let elem = JsCast::dyn_into::(span).unwrap(); - let height = elem.offset_height() as i32; - let width = elem.offset_width() as i32; - elem.remove(); - Ok(((0, 0), (width, height))) + let canvas = OffscreenCanvas::new(0, 0).expect("offscreen canvas"); + let context = canvas + .get_context("2d") + .expect("getContext") + .expect("context for 2d not null") + .dyn_into::() + .expect("cast"); + context.set_font("sans-serif"); + context.set_font(&format!( + "{} {}px {}", + self.1.as_str(), + size, + self.0.as_str(), + )); + let metrics = context + .measure_text(text) + .expect("measure_text to return metrics"); + let width = metrics.width(); + let height = metrics.font_bounding_box_ascent() + metrics.font_bounding_box_descent(); + Ok(((0, 0), (width as i32, height as i32))) } }