From 0d0ac986b7637185c22920b2f9c14afdb09aa940 Mon Sep 17 00:00:00 2001 From: kaiakz Date: Sat, 15 Feb 2020 20:52:51 +0800 Subject: [PATCH] Add a simple implementation of CanvasRenderingContext2d.fillText --- Cargo.lock | 9 ++- components/canvas/Cargo.toml | 1 + components/canvas/canvas_data.rs | 26 +++++- components/canvas/raqote_backend.rs | 81 +++++++++++++++++++ .../text-styles/canvas_text_font_001.htm.ini | 2 + 5 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 tests/wpt/metadata/2dcontext/text-styles/canvas_text_font_001.htm.ini diff --git a/Cargo.lock b/Cargo.lock index 60a8ffb17c7..e5ef56bb0de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -486,6 +486,7 @@ dependencies = [ "embedder_traits", "euclid", "fnv", + "font-kit", "gleam 0.9.2", "half", "ipc-channel", @@ -4406,8 +4407,8 @@ checksum = "dd5927936723a9e8b715d37d7e4b390455087c4bdf25b9f702309460577b14f9" [[package]] name = "raqote" -version = "0.7.8-alpha.0" -source = "git+https://github.com/jrmuizel/raqote#0cad3c338d9587bf0e9f6bc9e26112998767a1b5" +version = "0.7.15-alpha.0" +source = "git+https://github.com/jrmuizel/raqote#56fa45b39ec5dc6779bdc7c103394df5cf58d546" dependencies = [ "euclid", "font-kit", @@ -5799,9 +5800,9 @@ checksum = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b" [[package]] name = "sw-composite" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a36f1738c7e57fec506df8c94719b2210816ab9de4d3dadeb9efb6bc71f1d2" +checksum = "489500b88acdc4007684adf921506e84057e232a7f8b76a0caa288169ab39b94" [[package]] name = "swapper" diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 0917ad981a0..21f5a055cd3 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -44,3 +44,4 @@ webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]} surfman = { version = "0.1", features = ["sm-angle", "sm-osmesa"] } surfman-chains = "0.3" surfman-chains-api = "0.2" +font-kit = "0.5.0" diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index f291ce416e9..fa77b016dd0 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -266,6 +266,15 @@ pub trait GenericDrawTarget { ); fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); fn fill_rect(&mut self, rect: &Rect, pattern: Pattern, draw_options: Option<&DrawOptions>); + fn fill_text( + &mut self, + text: String, + x: f32, + y: f32, + max_width: Option, + pattern: Pattern, + draw_options: &DrawOptions, + ); fn get_format(&self) -> SurfaceFormat; fn get_size(&self) -> Size2D; fn get_transform(&self) -> Transform2D; @@ -458,10 +467,19 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text(&self, text: String, x: f64, y: f64, max_width: Option) { - error!( - "Unimplemented canvas2d.fillText. Values received: {}, {}, {}, {:?}.", - text, x, y, max_width + pub fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option) { + // If any of the arguments are infinite or NaN, then return. + if !x.is_finite() || !y.is_finite() { + return; + } + + self.drawtarget.fill_text( + text, + x as f32, + y as f32, + max_width, + self.state.fill_style.clone(), + &self.state.draw_options, ); } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 72eae8b40c8..6daa8712421 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -13,6 +13,9 @@ use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::Angle; +use font_kit::family_name::FamilyName; +use font_kit::properties::Properties; +use font_kit::source::SystemSource; use lyon_geom::Arc; use raqote::PathOp; use std::marker::PhantomData; @@ -539,6 +542,84 @@ impl GenericDrawTarget for raqote::DrawTarget { &DrawOptions::Raqote(draw_options), ); } + // TODO + // This should eventually use the same infrastructure as layout + // (i.e. layout should be updated to use font-kit as well). + // Need to implement .font . + fn fill_text( + &mut self, + text: String, + x: f32, + y: f32, + max_width: Option, + pattern: canvas_data::Pattern, + draw_options: &DrawOptions, + ) { + // Replace all ASCII whitespace in text with U+0020 SPACE characters. + fn replace_whitespace(text: String) -> String { + text.chars() + .map(|c| match c { + '\x09'..='\x0D' => '\x20', + _ => c, + }) + .collect() + } + + // Compute the width of the text + fn get_text_width(text: &str, font: &font_kit::font::Font) -> f64 { + let point_size = 24.; + let mut length = 0.; + for c in text.chars() { + let id = font.glyph_for_char(c).unwrap(); + length += (font.advance(id).unwrap() * point_size / 24. / 96.).x; + } + length as f64 + } + + let font = SystemSource::new() + .select_best_match(&[FamilyName::SansSerif], &Properties::new()) + .unwrap() + .load() + .unwrap(); + + // text preparation algorithm + let (scale_factor, replaced_text) = match max_width { + Some(value) => { + if value <= 0. || !value.is_finite() { + return; + } else { + let replaced_text = replace_whitespace(text); + let text_width = get_text_width(&replaced_text, &font); + if value > text_width { + (1., replaced_text) + } else { + (value / text_width, replaced_text) + } + } + }, + _ => (1., replace_whitespace(text)), + }; + + // Text scaling + let old_transform = self.get_transform().clone(); + let new_transform = old_transform + .pre_translate(Vector2D::new(x as f32, 0.)) + .pre_scale(scale_factor as f32, 1.) + .pre_translate(Vector2D::new(-x as f32, 0.)); + self.set_transform(&new_transform); + + self.draw_text( + &font, + 24., + &replaced_text, + Point2D::new(x, y), + &pattern.source(), + draw_options.as_raqote(), + ); + + // Restore the transform + self.set_transform(&old_transform); + } fn get_format(&self) -> SurfaceFormat { SurfaceFormat::Raqote(()) } diff --git a/tests/wpt/metadata/2dcontext/text-styles/canvas_text_font_001.htm.ini b/tests/wpt/metadata/2dcontext/text-styles/canvas_text_font_001.htm.ini new file mode 100644 index 00000000000..7b5158316c0 --- /dev/null +++ b/tests/wpt/metadata/2dcontext/text-styles/canvas_text_font_001.htm.ini @@ -0,0 +1,2 @@ +[canvas_text_font_001.htm] + expected: FAIL