From 7f0cebd44291ab73794b5b2f51fc9074684a8600 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Thu, 22 May 2025 19:42:50 +0200 Subject: [PATCH] layout: Support `wavy` and `double` for `text-decoration-line` (#37079) - Add support for `text-decoration-line: double`: Line drawing is done similar to how it works in Firefox and Chromium. A gap of half of line thickness is added between each line. - Fix support for `text-decoration-line: wavy`: Wavy lines rectangles were not calcualted properly, which meant they were rendered as solid lines. Now the amplitude of the wave is 1.5 times line thickness. Testing: A manual test is updated `tests/html/text_deco_simple.html` to cover more cases. In general, rendering of text-decorations is difficult to test via reftesting. Fixes #17887. Signed-off-by: Martin Robinson --- components/layout/display_list/conversions.rs | 8 ++- components/layout/display_list/mod.rs | 43 ++++++++++++--- tests/html/text_deco_simple.html | 53 ++++++++++++++----- 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/components/layout/display_list/conversions.rs b/components/layout/display_list/conversions.rs index 6d78a17e886..a2517c863f1 100644 --- a/components/layout/display_list/conversions.rs +++ b/components/layout/display_list/conversions.rs @@ -125,11 +125,15 @@ impl ToWebRender for ComputedTextDecorationStyle { type Type = LineStyle; fn to_webrender(&self) -> Self::Type { match *self { - ComputedTextDecorationStyle::Solid => LineStyle::Solid, + ComputedTextDecorationStyle::Solid | ComputedTextDecorationStyle::Double => { + LineStyle::Solid + }, ComputedTextDecorationStyle::Dotted => LineStyle::Dotted, ComputedTextDecorationStyle::Dashed => LineStyle::Dashed, ComputedTextDecorationStyle::Wavy => LineStyle::Wavy, - _ => LineStyle::Solid, + ComputedTextDecorationStyle::MozNone => { + unreachable!("Should never try to draw a moz-none text decoration") + }, } } } diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index 58ec58c9ce2..3716ff35b2c 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -11,7 +11,7 @@ use base::id::ScrollTreeNodeId; use clip::{Clip, ClipId}; use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo}; use embedder_traits::Cursor; -use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit}; +use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit, Vector2D}; use fonts::GlyphStore; use gradient::WebRenderGradient; use range::Range as ServoRange; @@ -21,7 +21,9 @@ use servo_geometry::MaxRect; use style::Zero; use style::color::{AbsoluteColor, ColorSpace}; use style::computed_values::border_image_outset::T as BorderImageOutset; -use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle; +use style::computed_values::text_decoration_style::{ + T as ComputedTextDecorationStyle, T as TextDecorationStyle, +}; use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::properties::longhands::visibility::computed_value::T as Visibility; @@ -737,6 +739,7 @@ impl Fragment { let rect = fragment.rect.translate(containing_block.origin.to_vector()); let mut baseline_origin = rect.origin; baseline_origin.y += fragment.font_metrics.ascent; + let glyphs = glyphs( &fragment.glyphs, baseline_origin, @@ -785,11 +788,13 @@ impl Fragment { rect.origin.y += font_metrics.ascent - font_metrics.underline_offset; rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx)); + self.build_display_list_for_text_decoration( &parent_style, builder, &rect, text_decoration, + TextDecorationLine::UNDERLINE, ); } } @@ -804,6 +809,7 @@ impl Fragment { builder, &rect, text_decoration, + TextDecorationLine::OVERLINE, ); } } @@ -896,6 +902,7 @@ impl Fragment { builder, &rect, text_decoration, + TextDecorationLine::LINE_THROUGH, ); } } @@ -911,22 +918,46 @@ impl Fragment { builder: &mut DisplayListBuilder, rect: &PhysicalRect, text_decoration: &FragmentTextDecoration, + line: TextDecorationLine, ) { - let rect = rect.to_webrender(); - let wavy_line_thickness = (0.33 * rect.size().height).ceil(); if text_decoration.style == ComputedTextDecorationStyle::MozNone { return; } + + let mut rect = rect.to_webrender(); + let line_thickness = rect.height().ceil(); + + if text_decoration.style == ComputedTextDecorationStyle::Wavy { + rect = rect.inflate(0.0, line_thickness * 1.0); + } + let common_properties = builder.common_properties(rect, parent_style); builder.wr().push_line( &common_properties, &rect, - wavy_line_thickness, + line_thickness, wr::LineOrientation::Horizontal, &rgba(text_decoration.color), text_decoration.style.to_webrender(), ); - // XXX(ferjm) support text-decoration-style: double + + if text_decoration.style == TextDecorationStyle::Double { + let half_height = (rect.height() / 2.0).floor().max(1.0); + let y_offset = match line { + TextDecorationLine::OVERLINE => -rect.height() - half_height, + _ => rect.height() + half_height, + }; + let rect = rect.translate(Vector2D::new(0.0, y_offset)); + let common_properties = builder.common_properties(rect, parent_style); + builder.wr().push_line( + &common_properties, + &rect, + line_thickness, + wr::LineOrientation::Horizontal, + &rgba(text_decoration.color), + text_decoration.style.to_webrender(), + ); + } } } diff --git a/tests/html/text_deco_simple.html b/tests/html/text_deco_simple.html index 73d48199c96..52ced9aede0 100644 --- a/tests/html/text_deco_simple.html +++ b/tests/html/text_deco_simple.html @@ -1,17 +1,46 @@ + - - - -

none

-

text underline

-

text underline

-

text underline

-

-

text underline pqrstg

-

text overline xxxxxxxxXXXXXXX

-

text line-through xxxxxxxxXXXXX

-

text blink

+ + + +

text-decoration:

+

text-decoration:

+

text-decoration:

+

text-decoration:

+ +

text-decoration:

+

text-decoration:

+

text-decoration:

+ +

text-decoration:

+

text-decoration:

+

text-decoration:

+ +

text-decoration:

+

text-decoration:

+

text-decoration:

+ +

text-decoration:

+

text-decoration:

+

text-decoration:

+ + +