aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock9
-rw-r--r--components/canvas/Cargo.toml1
-rw-r--r--components/canvas/canvas_data.rs26
-rw-r--r--components/canvas/raqote_backend.rs81
-rw-r--r--components/layout_2020/flow/inline.rs65
-rw-r--r--components/layout_2020/positioned.rs4
-rw-r--r--components/layout_2020/style_ext.rs4
-rw-r--r--tests/wpt/include-layout-2020.ini2
-rw-r--r--tests/wpt/metadata-layout-2020/css/CSS2/positioning/abspos-inline-008.xht.ini2
-rw-r--r--tests/wpt/metadata-layout-2020/css/CSS2/positioning/toogle-abspos-on-relpos-inline-child.html.ini2
-rw-r--r--tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini2
-rw-r--r--tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini2
-rw-r--r--tests/wpt/metadata/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini4
-rw-r--r--tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini4
-rw-r--r--tests/wpt/metadata/2dcontext/text-styles/canvas_text_font_001.htm.ini2
-rw-r--r--tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini4
-rw-r--r--tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js.ini4
-rw-r--r--tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.html.ini4
-rw-r--r--tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.worker.js.ini4
-rw-r--r--tests/wpt/mozilla/meta-layout-2020/css/absolute_inline_containing_block_a.html.ini2
20 files changed, 170 insertions, 58 deletions
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<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(())
}
diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs
index f71187e1118..9f98174e2cd 100644
--- a/components/layout_2020/flow/inline.rs
+++ b/components/layout_2020/flow/inline.rs
@@ -12,7 +12,10 @@ use crate::fragments::{
DebugId, Fragment, TextFragment,
};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
-use crate::positioned::{relative_adjustement, AbsolutelyPositionedBox, PositioningContext};
+use crate::positioned::{
+ relative_adjustement, AbsolutelyPositionedBox, HoistedAbsolutelyPositionedBox,
+ PositioningContext,
+};
use crate::sizing::ContentSizes;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::ContainingBlock;
@@ -64,6 +67,7 @@ struct InlineNestingLevelState<'box_tree> {
fragments_so_far: Vec<Fragment>,
inline_start: Length,
max_block_size_of_fragments_so_far: Length,
+ positioning_context: Option<PositioningContext>,
}
struct PartialInlineBoxFragment<'box_tree> {
@@ -86,6 +90,31 @@ struct InlineFormattingContextState<'box_tree, 'a, 'b> {
current_nesting_level: InlineNestingLevelState<'box_tree>,
}
+impl<'box_tree, 'a, 'b> InlineFormattingContextState<'box_tree, 'a, 'b> {
+ fn push_hoisted_box_to_positioning_context(
+ &mut self,
+ hoisted_box: HoistedAbsolutelyPositionedBox,
+ ) {
+ if let Some(context) = self.current_nesting_level.positioning_context.as_mut() {
+ context.push(hoisted_box);
+ return;
+ }
+
+ for nesting_level in self.partial_inline_boxes_stack.iter_mut().rev() {
+ if let Some(context) = nesting_level
+ .parent_nesting_level
+ .positioning_context
+ .as_mut()
+ {
+ context.push(hoisted_box);
+ return;
+ }
+ }
+
+ self.positioning_context.push(hoisted_box);
+ }
+}
+
struct Lines {
// One anonymous fragment per line
fragments: Vec<Fragment>,
@@ -226,6 +255,7 @@ impl InlineFormattingContext {
fragments_so_far: Vec::with_capacity(self.inline_level_boxes.len()),
inline_start: Length::zero(),
max_block_size_of_fragments_so_far: Length::zero(),
+ positioning_context: None,
},
};
loop {
@@ -257,15 +287,14 @@ impl InlineFormattingContext {
panic!("display:none does not generate an abspos box")
},
};
- let hoisted_fragment =
- box_.clone().to_hoisted(initial_start_corner, tree_rank);
- let hoisted_fragment_id = hoisted_fragment.fragment_id;
- ifc.positioning_context.push(hoisted_fragment);
- ifc.lines
- .fragments
- .push(Fragment::AbsoluteOrFixedPositioned(
- AbsoluteOrFixedPositionedFragment(hoisted_fragment_id),
- ));
+ let hoisted_box = box_.clone().to_hoisted(initial_start_corner, tree_rank);
+ let hoisted_fragment_id = hoisted_box.fragment_id;
+ ifc.push_hoisted_box_to_positioning_context(hoisted_box);
+ ifc.current_nesting_level.fragments_so_far.push(
+ Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment(
+ hoisted_fragment_id,
+ )),
+ );
},
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO
@@ -275,6 +304,7 @@ impl InlineFormattingContext {
// Reached the end of ifc.remaining_boxes
if let Some(mut partial) = ifc.partial_inline_boxes_stack.pop() {
partial.finish_layout(
+ layout_context,
&mut ifc.current_nesting_level,
&mut ifc.inline_position,
false,
@@ -392,6 +422,7 @@ impl InlineBox {
if style.clone_position().is_relative() {
start_corner += &relative_adjustement(&style, ifc.containing_block)
}
+ let positioning_context = PositioningContext::new_for_style(&style);
PartialInlineBoxFragment {
tag: self.tag,
style,
@@ -409,6 +440,7 @@ impl InlineBox {
fragments_so_far: Vec::with_capacity(self.children.len()),
inline_start: ifc.inline_position,
max_block_size_of_fragments_so_far: Length::zero(),
+ positioning_context,
},
),
}
@@ -418,6 +450,7 @@ impl InlineBox {
impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
fn finish_layout(
&mut self,
+ layout_context: &LayoutContext,
nesting_level: &mut InlineNestingLevelState,
inline_position: &mut Length,
at_line_break: bool,
@@ -459,6 +492,11 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
fragment.border.block_sum() +
fragment.margin.block_sum(),
);
+
+ if let Some(context) = nesting_level.positioning_context.as_mut() {
+ context.layout_collected_children(layout_context, &mut fragment);
+ }
+
self.parent_nesting_level
.fragments_so_far
.push(Fragment::Box(fragment));
@@ -748,7 +786,12 @@ impl TextRun {
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
- partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
+ partial.finish_layout(
+ layout_context,
+ nesting_level,
+ &mut ifc.inline_position,
+ true,
+ );
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs
index b9dca32db12..a0216ec296c 100644
--- a/components/layout_2020/positioned.rs
+++ b/components/layout_2020/positioned.rs
@@ -172,7 +172,7 @@ impl PositioningContext {
self.for_nearest_positioned_ancestor.is_some()
}
- fn new_for_style(style: &ComputedValues) -> Option<Self> {
+ pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> {
if style.establishes_containing_block_for_all_descendants() {
Some(Self::new_for_containing_block_for_all_descendants())
} else if style.establishes_containing_block() {
@@ -243,7 +243,7 @@ impl PositioningContext {
// Lay out the hoisted boxes collected into this `PositioningContext` and add them
// to the given `BoxFragment`.
- fn layout_collected_children(
+ pub fn layout_collected_children(
&mut self,
layout_context: &LayoutContext,
new_fragment: &mut BoxFragment,
diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs
index 7ed41d1cd91..cdde69a7866 100644
--- a/components/layout_2020/style_ext.rs
+++ b/components/layout_2020/style_ext.rs
@@ -245,7 +245,9 @@ impl ComputedValuesExt for ComputedValues {
/// Returns true if this style establishes a containing block for all descendants
/// including fixed and absolutely positioned ones.
fn establishes_containing_block_for_all_descendants(&self) -> bool {
- if self.has_transform_or_perspective() {
+ if self.get_box().display.outside() != stylo::DisplayOutside::Inline &&
+ self.has_transform_or_perspective()
+ {
return true;
}
diff --git a/tests/wpt/include-layout-2020.ini b/tests/wpt/include-layout-2020.ini
index 30f650b893a..a1ac1c24332 100644
--- a/tests/wpt/include-layout-2020.ini
+++ b/tests/wpt/include-layout-2020.ini
@@ -5,8 +5,6 @@ skip: true
skip: false
[mozilla]
skip: false
-[2dcontext]
- skip: false
[css]
skip: true
[CSS2]
diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/positioning/abspos-inline-008.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/positioning/abspos-inline-008.xht.ini
deleted file mode 100644
index f77c2c83d5d..00000000000
--- a/tests/wpt/metadata-layout-2020/css/CSS2/positioning/abspos-inline-008.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[abspos-inline-008.xht]
- expected: FAIL
diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/positioning/toogle-abspos-on-relpos-inline-child.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/positioning/toogle-abspos-on-relpos-inline-child.html.ini
deleted file mode 100644
index 928300474c1..00000000000
--- a/tests/wpt/metadata-layout-2020/css/CSS2/positioning/toogle-abspos-on-relpos-inline-child.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[toogle-abspos-on-relpos-inline-child.html]
- expected: FAIL
diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini
deleted file mode 100644
index 0c6784a80ed..00000000000
--- a/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[preserve3d-button.html]
- expected: FAIL
diff --git a/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini b/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini
deleted file mode 100644
index b41a8df9ac0..00000000000
--- a/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[filtered-inline-is-container.html]
- expected: FAIL
diff --git a/tests/wpt/metadata/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini b/tests/wpt/metadata/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini
deleted file mode 100644
index c7e4cfa39c4..00000000000
--- a/tests/wpt/metadata/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.gradient.radial.inside3.html]
- [Canvas test: 2d.gradient.radial.inside3]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini
deleted file mode 100644
index cbad443c3c7..00000000000
--- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.closed.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.line.cap.closed.html]
- [Line caps are not drawn at the corners of an unclosed rectangle]
- expected: FAIL
-
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
diff --git a/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini b/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini
deleted file mode 100644
index 0c5eea1b0c4..00000000000
--- a/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.gradient.radial.inside3.html]
- [OffscreenCanvas test: 2d.gradient.radial.inside3]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js.ini
deleted file mode 100644
index 1a8e07fabbc..00000000000
--- a/tests/wpt/metadata/offscreen-canvas/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.gradient.radial.inside3.worker.html]
- [2d]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.html.ini b/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.html.ini
deleted file mode 100644
index cbad443c3c7..00000000000
--- a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.line.cap.closed.html]
- [Line caps are not drawn at the corners of an unclosed rectangle]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.worker.js.ini
deleted file mode 100644
index 96690215a25..00000000000
--- a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.closed.worker.js.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.line.cap.closed.worker.html]
- [Line caps are not drawn at the corners of an unclosed rectangle]
- expected: FAIL
-
diff --git a/tests/wpt/mozilla/meta-layout-2020/css/absolute_inline_containing_block_a.html.ini b/tests/wpt/mozilla/meta-layout-2020/css/absolute_inline_containing_block_a.html.ini
deleted file mode 100644
index eb5471a558b..00000000000
--- a/tests/wpt/mozilla/meta-layout-2020/css/absolute_inline_containing_block_a.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[absolute_inline_containing_block_a.html]
- expected: FAIL