summaryrefslogtreecommitdiffstats
path: root/vendor/plotters/src/style/font
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/plotters/src/style/font
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/plotters/src/style/font')
-rw-r--r--vendor/plotters/src/style/font/font_desc.rs220
-rw-r--r--vendor/plotters/src/style/font/mod.rs55
-rw-r--r--vendor/plotters/src/style/font/naive.rs40
-rw-r--r--vendor/plotters/src/style/font/ttf.rs321
-rw-r--r--vendor/plotters/src/style/font/web.rs46
5 files changed, 682 insertions, 0 deletions
diff --git a/vendor/plotters/src/style/font/font_desc.rs b/vendor/plotters/src/style/font/font_desc.rs
new file mode 100644
index 000000000..a101a5d8b
--- /dev/null
+++ b/vendor/plotters/src/style/font/font_desc.rs
@@ -0,0 +1,220 @@
+use super::{FontData, FontDataInternal};
+use crate::style::text_anchor::Pos;
+use crate::style::{Color, TextStyle};
+
+use std::convert::From;
+
+pub use plotters_backend::{FontFamily, FontStyle, FontTransform};
+
+/// The error type for the font implementation
+pub type FontError = <FontDataInternal as FontData>::ErrorType;
+
+/// The type we used to represent a result of any font operations
+pub type FontResult<T> = Result<T, FontError>;
+
+/// Describes a font
+#[derive(Clone)]
+pub struct FontDesc<'a> {
+ size: f64,
+ family: FontFamily<'a>,
+ data: FontResult<FontDataInternal>,
+ transform: FontTransform,
+ style: FontStyle,
+}
+
+impl<'a> FontDesc<'a> {
+ /// Create a new font
+ ///
+ /// - `family`: The font family name
+ /// - `size`: The size of the font
+ /// - `style`: The font variations
+ /// - **returns** The newly created font description
+ pub fn new(family: FontFamily<'a>, size: f64, style: FontStyle) -> Self {
+ Self {
+ size,
+ family,
+ data: FontDataInternal::new(family, style),
+ transform: FontTransform::None,
+ style,
+ }
+ }
+
+ /// Create a new font desc with the same font but different size
+ ///
+ /// - `size`: The new size to set
+ /// - **returns** The newly created font descriptor with a new size
+ pub fn resize(&self, size: f64) -> Self {
+ Self {
+ size,
+ family: self.family,
+ data: self.data.clone(),
+ transform: self.transform.clone(),
+ style: self.style,
+ }
+ }
+
+ /// Set the style of the font
+ ///
+ /// - `style`: The new style
+ /// - **returns** The new font description with this style applied
+ pub fn style(&self, style: FontStyle) -> Self {
+ Self {
+ size: self.size,
+ family: self.family,
+ data: self.data.clone(),
+ transform: self.transform.clone(),
+ style,
+ }
+ }
+
+ /// Set the font transformation
+ ///
+ /// - `trans`: The new transformation
+ /// - **returns** The new font description with this font transformation applied
+ pub fn transform(&self, trans: FontTransform) -> Self {
+ Self {
+ size: self.size,
+ family: self.family,
+ data: self.data.clone(),
+ transform: trans,
+ style: self.style,
+ }
+ }
+
+ /// Get the font transformation description
+ pub fn get_transform(&self) -> FontTransform {
+ self.transform.clone()
+ }
+
+ /** Returns a new text style object with the specified `color`.
+
+ # Example
+
+ ```
+ use plotters::prelude::*;
+ let text_style = ("sans-serif", 20).into_font().color(&RED);
+ let drawing_area = SVGBackend::new("font_desc_color.svg", (200, 100)).into_drawing_area();
+ drawing_area.fill(&WHITE).unwrap();
+ drawing_area.draw_text("This is a big red label", &text_style, (10, 50));
+ ```
+
+ The result is a text label colorized accordingly:
+
+ ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@f030ed3/apidoc/font_desc_color.svg)
+
+ # See also
+
+ [`IntoTextStyle::with_color()`](crate::style::IntoTextStyle::with_color)
+
+ [`IntoTextStyle::into_text_style()`](crate::style::IntoTextStyle::into_text_style) for a more succinct example
+
+ */
+ pub fn color<C: Color>(&self, color: &C) -> TextStyle<'a> {
+ TextStyle {
+ font: self.clone(),
+ color: color.to_backend_color(),
+ pos: Pos::default(),
+ }
+ }
+
+ /// Returns the font family
+ pub fn get_family(&self) -> FontFamily {
+ self.family
+ }
+
+ /// Get the name of the font
+ pub fn get_name(&self) -> &str {
+ self.family.as_str()
+ }
+
+ /// Get the name of the style
+ pub fn get_style(&self) -> FontStyle {
+ self.style
+ }
+
+ /// Get the size of font
+ pub fn get_size(&self) -> f64 {
+ self.size
+ }
+
+ /// Get the size of the text if rendered in this font
+ ///
+ /// For a TTF type, zero point of the layout box is the left most baseline char of the string
+ /// Thus the upper bound of the box is most likely be negative
+ pub fn layout_box(&self, text: &str) -> FontResult<((i32, i32), (i32, i32))> {
+ match &self.data {
+ Ok(ref font) => font.estimate_layout(self.size, text),
+ Err(e) => Err(e.clone()),
+ }
+ }
+
+ /// Get the size of the text if rendered in this font.
+ /// This is similar to `layout_box` function, but it apply the font transformation
+ /// and estimate the overall size of the font
+ pub fn box_size(&self, text: &str) -> FontResult<(u32, u32)> {
+ let ((min_x, min_y), (max_x, max_y)) = self.layout_box(text)?;
+ let (w, h) = self.get_transform().transform(max_x - min_x, max_y - min_y);
+ Ok((w.unsigned_abs(), h.unsigned_abs()))
+ }
+
+ /// Actually draws a font with a drawing function
+ pub fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>(
+ &self,
+ text: &str,
+ (x, y): (i32, i32),
+ draw: DrawFunc,
+ ) -> FontResult<Result<(), E>> {
+ match &self.data {
+ Ok(ref font) => font.draw((x, y), self.size, text, draw),
+ Err(e) => Err(e.clone()),
+ }
+ }
+}
+
+impl<'a> From<&'a str> for FontDesc<'a> {
+ fn from(from: &'a str) -> FontDesc<'a> {
+ FontDesc::new(from.into(), 12.0, FontStyle::Normal)
+ }
+}
+
+impl<'a> From<FontFamily<'a>> for FontDesc<'a> {
+ fn from(family: FontFamily<'a>) -> FontDesc<'a> {
+ FontDesc::new(family, 12.0, FontStyle::Normal)
+ }
+}
+
+impl<'a, T: Into<f64>> From<(FontFamily<'a>, T)> for FontDesc<'a> {
+ fn from((family, size): (FontFamily<'a>, T)) -> FontDesc<'a> {
+ FontDesc::new(family, size.into(), FontStyle::Normal)
+ }
+}
+
+impl<'a, T: Into<f64>> From<(&'a str, T)> for FontDesc<'a> {
+ fn from((typeface, size): (&'a str, T)) -> FontDesc<'a> {
+ FontDesc::new(typeface.into(), size.into(), FontStyle::Normal)
+ }
+}
+
+impl<'a, T: Into<f64>, S: Into<FontStyle>> From<(FontFamily<'a>, T, S)> for FontDesc<'a> {
+ fn from((family, size, style): (FontFamily<'a>, T, S)) -> FontDesc<'a> {
+ FontDesc::new(family, size.into(), style.into())
+ }
+}
+
+impl<'a, T: Into<f64>, S: Into<FontStyle>> From<(&'a str, T, S)> for FontDesc<'a> {
+ fn from((typeface, size, style): (&'a str, T, S)) -> FontDesc<'a> {
+ FontDesc::new(typeface.into(), size.into(), style.into())
+ }
+}
+
+/// The trait that allows some type turns into a font description
+pub trait IntoFont<'a> {
+ /// Make the font description from the source type
+ fn into_font(self) -> FontDesc<'a>;
+}
+
+impl<'a, T: Into<FontDesc<'a>>> IntoFont<'a> for T {
+ fn into_font(self) -> FontDesc<'a> {
+ self.into()
+ }
+}
diff --git a/vendor/plotters/src/style/font/mod.rs b/vendor/plotters/src/style/font/mod.rs
new file mode 100644
index 000000000..305978fd0
--- /dev/null
+++ b/vendor/plotters/src/style/font/mod.rs
@@ -0,0 +1,55 @@
+/// The implementation of an actual font implementation
+///
+/// This exists since for the image rendering task, we want to use
+/// the system font. But in wasm application, we want the browser
+/// to handle all the font issue.
+///
+/// Thus we need different mechanism for the font implementation
+
+#[cfg(all(
+ not(all(target_arch = "wasm32", not(target_os = "wasi"))),
+ feature = "ttf"
+))]
+mod ttf;
+#[cfg(all(
+ not(all(target_arch = "wasm32", not(target_os = "wasi"))),
+ feature = "ttf"
+))]
+use ttf::FontDataInternal;
+
+#[cfg(all(
+ not(all(target_arch = "wasm32", not(target_os = "wasi"))),
+ not(feature = "ttf")
+))]
+mod naive;
+#[cfg(all(
+ not(all(target_arch = "wasm32", not(target_os = "wasi"))),
+ not(feature = "ttf")
+))]
+use naive::FontDataInternal;
+
+#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
+mod web;
+#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
+use web::FontDataInternal;
+
+mod font_desc;
+pub use font_desc::*;
+
+/// Represents a box where a text label can be fit
+pub type LayoutBox = ((i32, i32), (i32, i32));
+
+pub trait FontData: Clone {
+ type ErrorType: Sized + std::error::Error + Clone;
+ fn new(family: FontFamily, style: FontStyle) -> Result<Self, Self::ErrorType>;
+ fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType>;
+ fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>(
+ &self,
+ _pos: (i32, i32),
+ _size: f64,
+ _text: &str,
+ _draw: DrawFunc,
+ ) -> Result<Result<(), E>, Self::ErrorType> {
+ panic!("The font implementation is unable to draw text");
+ }
+}
diff --git a/vendor/plotters/src/style/font/naive.rs b/vendor/plotters/src/style/font/naive.rs
new file mode 100644
index 000000000..99530401b
--- /dev/null
+++ b/vendor/plotters/src/style/font/naive.rs
@@ -0,0 +1,40 @@
+use super::{FontData, FontFamily, FontStyle, LayoutBox};
+
+#[derive(Debug, Clone)]
+pub struct FontError;
+
+impl std::fmt::Display for FontError {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(fmt, "General Error")?;
+ Ok(())
+ }
+}
+
+impl std::error::Error for FontError {}
+
+#[derive(Clone)]
+pub struct FontDataInternal(String, String);
+
+impl FontData for FontDataInternal {
+ type ErrorType = FontError;
+ fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> {
+ Ok(FontDataInternal(
+ family.as_str().into(),
+ style.as_str().into(),
+ ))
+ }
+
+ /// Note: This is only a crude estimatation, since for some backend such as SVG, we have no way to
+ /// know the real size of the text anyway. Thus using font-kit is an overkill and doesn't helps
+ /// the layout.
+ fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> {
+ let em = size / 1.24 / 1.24;
+ Ok((
+ (0, -em.round() as i32),
+ (
+ (em * 0.7 * text.len() as f64).round() as i32,
+ (em * 0.24).round() as i32,
+ ),
+ ))
+ }
+}
diff --git a/vendor/plotters/src/style/font/ttf.rs b/vendor/plotters/src/style/font/ttf.rs
new file mode 100644
index 000000000..e6feadc82
--- /dev/null
+++ b/vendor/plotters/src/style/font/ttf.rs
@@ -0,0 +1,321 @@
+use std::borrow::{Borrow, Cow};
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::i32;
+use std::sync::{Arc, RwLock};
+
+use lazy_static::lazy_static;
+
+use font_kit::{
+ canvas::{Canvas, Format, RasterizationOptions},
+ error::{FontLoadingError, GlyphLoadingError},
+ family_name::FamilyName,
+ font::Font,
+ handle::Handle,
+ hinting::HintingOptions,
+ properties::{Properties, Style, Weight},
+ source::SystemSource,
+};
+
+use ttf_parser::{Face, GlyphId};
+
+use pathfinder_geometry::transform2d::Transform2F;
+use pathfinder_geometry::vector::{Vector2F, Vector2I};
+
+use super::{FontData, FontFamily, FontStyle, LayoutBox};
+
+type FontResult<T> = Result<T, FontError>;
+
+#[derive(Debug, Clone)]
+pub enum FontError {
+ LockError,
+ NoSuchFont(String, String),
+ FontLoadError(Arc<FontLoadingError>),
+ GlyphError(Arc<GlyphLoadingError>),
+}
+
+impl std::fmt::Display for FontError {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ FontError::LockError => write!(fmt, "Could not lock mutex"),
+ FontError::NoSuchFont(family, style) => {
+ write!(fmt, "No such font: {} {}", family, style)
+ }
+ FontError::FontLoadError(e) => write!(fmt, "Font loading error {}", e),
+ FontError::GlyphError(e) => write!(fmt, "Glyph error {}", e),
+ }
+ }
+}
+
+impl std::error::Error for FontError {}
+
+lazy_static! {
+ static ref DATA_CACHE: RwLock<HashMap<String, FontResult<Handle>>> =
+ RwLock::new(HashMap::new());
+}
+
+thread_local! {
+ static FONT_SOURCE: SystemSource = SystemSource::new();
+ static FONT_OBJECT_CACHE: RefCell<HashMap<String, FontExt>> = RefCell::new(HashMap::new());
+}
+
+const PLACEHOLDER_CHAR: char = '�';
+
+#[derive(Clone)]
+struct FontExt {
+ inner: Font,
+ face: Option<Face<'static>>,
+}
+
+impl Drop for FontExt {
+ fn drop(&mut self) {
+ // We should make sure the face object dead first
+ self.face.take();
+ }
+}
+
+impl FontExt {
+ fn new(font: Font) -> Self {
+ let handle = font.handle();
+ let (data, idx) = match handle.as_ref() {
+ Some(Handle::Memory { bytes, font_index }) => (&bytes[..], *font_index),
+ _ => unreachable!(),
+ };
+ let face = unsafe {
+ std::mem::transmute::<_, Option<Face<'static>>>(
+ ttf_parser::Face::from_slice(data, idx).ok(),
+ )
+ };
+ Self { inner: font, face }
+ }
+
+ fn query_kerning_table(&self, prev: u32, next: u32) -> f32 {
+ if let Some(face) = self.face.as_ref() {
+ if let Some(kern) = face.tables().kern {
+ let kern = kern
+ .subtables
+ .into_iter()
+ .filter(|st| st.horizontal && !st.variable)
+ .filter_map(|st| st.glyphs_kerning(GlyphId(prev as u16), GlyphId(next as u16)))
+ .next()
+ .unwrap_or(0);
+ return kern as f32;
+ }
+ }
+ 0.0
+ }
+}
+
+impl std::ops::Deref for FontExt {
+ type Target = Font;
+ fn deref(&self) -> &Font {
+ &self.inner
+ }
+}
+
+/// Lazily load font data. Font type doesn't own actual data, which
+/// lives in the cache.
+fn load_font_data(face: FontFamily, style: FontStyle) -> FontResult<FontExt> {
+ let key = match style {
+ FontStyle::Normal => Cow::Borrowed(face.as_str()),
+ _ => Cow::Owned(format!("{}, {}", face.as_str(), style.as_str())),
+ };
+
+ // First, we try to find the font object for current thread
+ if let Some(font_object) = FONT_OBJECT_CACHE.with(|font_object_cache| {
+ font_object_cache
+ .borrow()
+ .get(Borrow::<str>::borrow(&key))
+ .map(Clone::clone)
+ }) {
+ return Ok(font_object);
+ }
+
+ // Then we need to check if the data cache contains the font data
+ let cache = DATA_CACHE.read().unwrap();
+ if let Some(data) = cache.get(Borrow::<str>::borrow(&key)) {
+ return data.clone().map(|handle| {
+ handle
+ .load()
+ .map(FontExt::new)
+ .map_err(|e| FontError::FontLoadError(Arc::new(e)))
+ })?;
+ }
+ drop(cache);
+
+ // Otherwise we should load from system
+ let mut properties = Properties::new();
+ match style {
+ FontStyle::Normal => properties.style(Style::Normal),
+ FontStyle::Italic => properties.style(Style::Italic),
+ FontStyle::Oblique => properties.style(Style::Oblique),
+ FontStyle::Bold => properties.weight(Weight::BOLD),
+ };
+
+ let family = match face {
+ FontFamily::Serif => FamilyName::Serif,
+ FontFamily::SansSerif => FamilyName::SansSerif,
+ FontFamily::Monospace => FamilyName::Monospace,
+ FontFamily::Name(name) => FamilyName::Title(name.to_owned()),
+ };
+
+ let make_not_found_error =
+ || FontError::NoSuchFont(face.as_str().to_owned(), style.as_str().to_owned());
+
+ if let Ok(handle) = FONT_SOURCE
+ .with(|source| source.select_best_match(&[family, FamilyName::SansSerif], &properties))
+ {
+ let font = handle
+ .load()
+ .map(FontExt::new)
+ .map_err(|e| FontError::FontLoadError(Arc::new(e)));
+ let (should_cache, data) = match font.as_ref().map(|f| f.handle()) {
+ Ok(None) => (false, Err(FontError::LockError)),
+ Ok(Some(handle)) => (true, Ok(handle)),
+ Err(e) => (true, Err(e.clone())),
+ };
+
+ if should_cache {
+ DATA_CACHE
+ .write()
+ .map_err(|_| FontError::LockError)?
+ .insert(key.clone().into_owned(), data);
+ }
+
+ if let Ok(font) = font.as_ref() {
+ FONT_OBJECT_CACHE.with(|font_object_cache| {
+ font_object_cache
+ .borrow_mut()
+ .insert(key.into_owned(), font.clone());
+ });
+ }
+
+ return font;
+ }
+ Err(make_not_found_error())
+}
+
+#[derive(Clone)]
+pub struct FontDataInternal(FontExt);
+
+impl FontData for FontDataInternal {
+ type ErrorType = FontError;
+
+ fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> {
+ Ok(FontDataInternal(load_font_data(family, style)?))
+ }
+
+ fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> {
+ let font = &self.0;
+ let pixel_per_em = size / 1.24;
+ let metrics = font.metrics();
+
+ let font = &self.0;
+
+ let mut x_in_unit = 0f32;
+
+ let mut prev = None;
+ let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR);
+
+ for c in text.chars() {
+ if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) {
+ if let Ok(size) = font.advance(glyph_id) {
+ x_in_unit += size.x();
+ }
+ if let Some(pc) = prev {
+ x_in_unit += font.query_kerning_table(pc, glyph_id);
+ }
+ prev = Some(glyph_id);
+ }
+ }
+
+ let x_pixels = x_in_unit * pixel_per_em as f32 / metrics.units_per_em as f32;
+
+ Ok(((0, 0), (x_pixels as i32, pixel_per_em as i32)))
+ }
+
+ fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>(
+ &self,
+ (base_x, mut base_y): (i32, i32),
+ size: f64,
+ text: &str,
+ mut draw: DrawFunc,
+ ) -> Result<Result<(), E>, Self::ErrorType> {
+ let em = (size / 1.24) as f32;
+
+ let mut x = base_x as f32;
+ let font = &self.0;
+ let metrics = font.metrics();
+
+ let canvas_size = size as usize;
+
+ base_y -= (0.24 * em) as i32;
+
+ let mut prev = None;
+ let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR);
+
+ let mut result = Ok(());
+
+ for c in text.chars() {
+ if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) {
+ if let Some(pc) = prev {
+ x += font.query_kerning_table(pc, glyph_id) * em / metrics.units_per_em as f32;
+ }
+
+ let mut canvas = Canvas::new(Vector2I::splat(canvas_size as i32), Format::A8);
+
+ result = font
+ .rasterize_glyph(
+ &mut canvas,
+ glyph_id,
+ em as f32,
+ Transform2F::from_translation(Vector2F::new(0.0, em as f32)),
+ HintingOptions::None,
+ RasterizationOptions::GrayscaleAa,
+ )
+ .map_err(|e| FontError::GlyphError(Arc::new(e)))
+ .and(result);
+
+ let base_x = x as i32;
+
+ for dy in 0..canvas_size {
+ for dx in 0..canvas_size {
+ let alpha = canvas.pixels[dy * canvas_size + dx] as f32 / 255.0;
+ if let Err(e) = draw(base_x + dx as i32, base_y + dy as i32, alpha) {
+ return Ok(Err(e));
+ }
+ }
+ }
+
+ x += font.advance(glyph_id).map(|size| size.x()).unwrap_or(0.0) * em
+ / metrics.units_per_em as f32;
+
+ prev = Some(glyph_id);
+ }
+ }
+ result?;
+ Ok(Ok(()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::*;
+
+ #[test]
+ fn test_font_cache() -> FontResult<()> {
+ // We cannot only check the size of font cache, because
+ // the test case may be run in parallel. Thus the font cache
+ // may contains other fonts.
+ let _a = load_font_data(FontFamily::Serif, FontStyle::Normal)?;
+ assert!(DATA_CACHE.read().unwrap().contains_key("serif"));
+
+ let _b = load_font_data(FontFamily::Serif, FontStyle::Normal)?;
+ assert!(DATA_CACHE.read().unwrap().contains_key("serif"));
+
+ // TODO: Check they are the same
+
+ return Ok(());
+ }
+}
diff --git a/vendor/plotters/src/style/font/web.rs b/vendor/plotters/src/style/font/web.rs
new file mode 100644
index 000000000..e70e7b1aa
--- /dev/null
+++ b/vendor/plotters/src/style/font/web.rs
@@ -0,0 +1,46 @@
+use super::{FontData, FontFamily, FontStyle, LayoutBox};
+use wasm_bindgen::JsCast;
+use web_sys::{window, HtmlElement};
+
+#[derive(Debug, Clone)]
+pub enum FontError {
+ UnknownError,
+}
+
+impl std::fmt::Display for FontError {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ _ => write!(fmt, "Unknown error"),
+ }
+ }
+}
+
+impl std::error::Error for FontError {}
+
+#[derive(Clone)]
+pub struct FontDataInternal(String, String);
+
+impl FontData for FontDataInternal {
+ type ErrorType = FontError;
+ fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> {
+ Ok(FontDataInternal(
+ family.as_str().into(),
+ style.as_str().into(),
+ ))
+ }
+ fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> {
+ 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-size: {}px; position: fixed; top: 100%", self.0, size)).unwrap();
+ let span = span.into();
+ body.append_with_node_1(&span).unwrap();
+ let elem = JsCast::dyn_into::<HtmlElement>(span).unwrap();
+ let height = elem.offset_height() as i32;
+ let width = elem.offset_width() as i32;
+ elem.remove();
+ Ok(((0, 0), (width, height)))
+ }
+}