aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2020-03-19 13:24:08 -0400
committerGitHub <noreply@github.com>2020-03-19 13:24:08 -0400
commitffdb83b644667c8e4916fcea3a94e77042bb1a9b (patch)
treed090decbab67f7da3a380650ee06cf64e8f6226d /components
parent3d03b3bca0ee545d15ac3521391327bd08f02aae (diff)
parent777b9d791b47747f2fb4c5753ea188d96f7647af (diff)
downloadservo-ffdb83b644667c8e4916fcea3a94e77042bb1a9b.tar.gz
servo-ffdb83b644667c8e4916fcea3a94e77042bb1a9b.zip
Auto merge of #25782 - kaiakz:master, r=jdm
Add a simple implementation of CanvasRenderingContext2d.fillText <!-- Please describe your changes on the following line: --> I added a simple implementation of CanvasRenderingContext2d.fillText. Some code are merged from @mikrut, and I fixed a bug about text scaling. Also, the bug of text rotation should be fixed after `raqote` merged my other PR. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix (part of) #11681 (GitHub issue number if applicable) <!-- Either: --> - [X] There are tests for these changes <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
Diffstat (limited to 'components')
-rw-r--r--components/canvas/Cargo.toml1
-rw-r--r--components/canvas/canvas_data.rs26
-rw-r--r--components/canvas/raqote_backend.rs81
3 files changed, 104 insertions, 4 deletions
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<f32>, pattern: Pattern, draw_options: Option<&DrawOptions>);
+ fn fill_text(
+ &mut self,
+ text: String,
+ x: f32,
+ y: f32,
+ max_width: Option<f64>,
+ pattern: Pattern,
+ draw_options: &DrawOptions,
+ );
fn get_format(&self) -> SurfaceFormat;
fn get_size(&self) -> Size2D<i32>;
fn get_transform(&self) -> Transform2D<f32>;
@@ -458,10 +467,19 @@ impl<'a> CanvasData<'a> {
}
}
- pub fn fill_text(&self, text: String, x: f64, y: f64, max_width: Option<f64>) {
- 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<f64>) {
+ // 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<f64>,
+ 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(())
}