[librsvg: 13/15] Use the objectBoundingBox of the whole text element for each span's individual paint source




commit 426aede0d99e1fa9914581be27bc1ef34df67f4d
Author: Federico Mena Quintero <federico gnome org>
Date:   Mon Oct 18 14:49:25 2021 -0500

    Use the objectBoundingBox of the whole text element for each span's individual paint source
    
    Per the spec, if a <tspan> has paint that depends on objectBoundingBox, that box
    corresponds to the whole <text> element, not to the individual <tspan>.
    
    Now we do this:
    
    1. PositionedSpan::layout() -> LayoutSpan, which has resolved paint
    sources, but not in user-space coordinates yet.
    
    2. Combine all the bboxes from the LayoutSpans to find the whole text_bbox.
    
    3. Convert the paint sources to user space with the text_bbox; produce
    TextSpans; gather them in a layout::Text.
    
    4. Send off the layout::Text to DrawingCtx::draw_text().
    
    In the future, layout::Text will contain per-glyph positions so we can
    implement more features of the SVG text spec.
    
    This commit also adds a test using the Ahem font, to make a reftest
    with plain squares for each glyph.
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/613>

 src/drawing_ctx.rs                                 |  20 +++-
 src/layout.rs                                      |   5 +
 src/text.rs                                        | 115 ++++++++++++++-------
 .../reftests/text-objectBoundingBox-ref.png        | Bin 0 -> 925 bytes
 tests/fixtures/reftests/text-objectBoundingBox.svg |  14 +++
 5 files changed, 115 insertions(+), 39 deletions(-)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index 267619a0..205fe117 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -23,7 +23,7 @@ use crate::filter::FilterValueList;
 use crate::filters::{self, FilterSpec};
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::gradient::{GradientVariant, SpreadMethod, UserSpaceGradient};
-use crate::layout::{Image, Shape, StackingContext, Stroke, TextSpan};
+use crate::layout::{Image, Shape, StackingContext, Stroke, Text, TextSpan};
 use crate::length::*;
 use crate::marker;
 use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
@@ -1357,7 +1357,7 @@ impl DrawingCtx {
         }
     }
 
-    pub fn draw_text_span(
+    fn draw_text_span(
         &mut self,
         span: &TextSpan,
         acquired_nodes: &mut AcquiredNodes<'_>,
@@ -1470,6 +1470,22 @@ impl DrawingCtx {
         })
     }
 
+    pub fn draw_text(
+        &mut self,
+        text: &Text,
+        acquired_nodes: &mut AcquiredNodes<'_>,
+        clipping: bool,
+    ) -> Result<BoundingBox, RenderingError> {
+        let mut bbox = self.empty_bbox();
+
+        for span in &text.spans {
+            let span_bbox = self.draw_text_span(span, acquired_nodes, clipping)?;
+            bbox.insert(&span_bbox);
+        }
+
+        Ok(bbox)
+    }
+
     pub fn get_snapshot(
         &self,
         width: i32,
diff --git a/src/layout.rs b/src/layout.rs
index 243de5c0..19ebd63b 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -113,6 +113,11 @@ pub struct TextSpan {
     pub link_target: Option<String>,
 }
 
+/// Fully laid-out text in user-space coordinates.
+pub struct Text {
+    pub spans: Vec<TextSpan>,
+}
+
 /// Font-related properties extracted from `ComputedValues`.
 pub struct FontProperties {
     pub xml_lang: XmlLang,
diff --git a/src/text.rs b/src/text.rs
index 06c67808..ca3b7edc 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -9,13 +9,14 @@ use crate::document::{AcquiredNodes, NodeId};
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
 use crate::element::{Draw, Element, ElementResult, SetAttributes};
 use crate::error::*;
-use crate::layout::{FontProperties, StackingContext, Stroke, TextSpan};
+use crate::layout::{self, FontProperties, StackingContext, Stroke, TextSpan};
 use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow};
+use crate::paint_server::PaintSource;
 use crate::parsers::ParseValue;
 use crate::properties::{
-    ComputedValues, Direction, FontStretch, FontStyle, FontVariant, FontWeight, TextAnchor,
-    UnicodeBidi, WritingMode, XmlLang, XmlSpace,
+    ComputedValues, Direction, FontStretch, FontStyle, FontVariant, FontWeight, PaintOrder,
+    TextAnchor, TextRendering, UnicodeBidi, WritingMode, XmlLang, XmlSpace,
 };
 use crate::rect::Rect;
 use crate::space::{xml_space_normalize, NormalizeDefault, XmlSpaceNormalize};
@@ -82,6 +83,27 @@ struct PositionedSpan {
     rendered_position: (f64, f64),
 }
 
+/// A laid-out and resolved text span.
+///
+/// The only thing not in user-space units are the `stroke_paint` and `fill_paint`.
+///
+/// This is the non-user-space version of `layout::TextSpan`.
+struct LayoutSpan {
+    layout: pango::Layout,
+    gravity: pango::Gravity,
+    bbox: Option<BoundingBox>,
+    is_visible: bool,
+    x: f64,
+    y: f64,
+    paint_order: PaintOrder,
+    stroke: Stroke,
+    stroke_paint: PaintSource,
+    fill_paint: PaintSource,
+    text_rendering: TextRendering,
+    link_target: Option<String>,
+    values: Rc<ComputedValues>,
+}
+
 impl Chunk {
     fn new(values: &ComputedValues, x: Option<f64>, y: Option<f64>, link: Option<String>) -> Chunk {
         Chunk {
@@ -334,7 +356,7 @@ impl PositionedSpan {
         draw_ctx: &mut DrawingCtx,
         view_params: &ViewParams,
         link_target: Option<String>,
-    ) -> TextSpan {
+    ) -> LayoutSpan {
         let params = NormalizeParams::new(&self.values, &view_params);
 
         let layout = self.layout.clone();
@@ -347,38 +369,26 @@ impl PositionedSpan {
 
         let bbox = compute_text_box(&layout, x, y, draw_ctx.get_transform(), gravity);
 
-        let bbox_for_paint = bbox.unwrap_or_else(|| draw_ctx.empty_bbox());
-
-        let stroke_paint = self
-            .values
-            .stroke()
-            .0
-            .resolve(
-                acquired_nodes,
-                self.values.stroke_opacity().0,
-                self.values.color().0,
-                None,
-                None,
-            )
-            .to_user_space(&bbox_for_paint, view_params, &self.values);
-
-        let fill_paint = self
-            .values
-            .fill()
-            .0
-            .resolve(
-                acquired_nodes,
-                self.values.fill_opacity().0,
-                self.values.color().0,
-                None,
-                None,
-            )
-            .to_user_space(&bbox_for_paint, view_params, &self.values);
+        let stroke_paint = self.values.stroke().0.resolve(
+            acquired_nodes,
+            self.values.stroke_opacity().0,
+            self.values.color().0,
+            None,
+            None,
+        );
+
+        let fill_paint = self.values.fill().0.resolve(
+            acquired_nodes,
+            self.values.fill_opacity().0,
+            self.values.color().0,
+            None,
+            None,
+        );
 
         let paint_order = self.values.paint_order();
         let text_rendering = self.values.text_rendering();
 
-        TextSpan {
+        LayoutSpan {
             layout,
             gravity,
             bbox,
@@ -391,6 +401,7 @@ impl PositionedSpan {
             fill_paint,
             text_rendering,
             link_target,
+            values: self.values.clone(),
         }
     }
 }
@@ -695,14 +706,44 @@ impl Draw for Text {
                     }
                 }
 
-                let mut bbox = dc.empty_bbox();
+                let text_bbox = layout_spans.iter().fold(dc.empty_bbox(), |mut bbox, span| {
+                    if let Some(ref span_bbox) = span.bbox {
+                        bbox.insert(span_bbox);
+                    }
 
-                for layout_span in layout_spans {
-                    let span_bbox = dc.draw_text_span(&layout_span, an, clipping)?;
-                    bbox.insert(&span_bbox);
+                    bbox
+                });
+
+                let mut text_spans = Vec::new();
+                for span in layout_spans {
+                    let stroke_paint =
+                        span.stroke_paint
+                            .to_user_space(&text_bbox, &view_params, &span.values);
+                    let fill_paint =
+                        span.fill_paint
+                            .to_user_space(&text_bbox, &view_params, &span.values);
+
+                    let text_span = TextSpan {
+                        layout: span.layout,
+                        gravity: span.gravity,
+                        bbox: span.bbox,
+                        is_visible: span.is_visible,
+                        x: span.x,
+                        y: span.y,
+                        paint_order: span.paint_order,
+                        stroke: span.stroke,
+                        stroke_paint,
+                        fill_paint,
+                        text_rendering: span.text_rendering,
+                        link_target: span.link_target,
+                    };
+
+                    text_spans.push(text_span);
                 }
 
-                Ok(bbox)
+                let text_layout = layout::Text { spans: text_spans };
+
+                dc.draw_text(&text_layout, an, clipping)
             },
         )
     }
diff --git a/tests/fixtures/reftests/text-objectBoundingBox-ref.png 
b/tests/fixtures/reftests/text-objectBoundingBox-ref.png
new file mode 100644
index 00000000..725a8f14
Binary files /dev/null and b/tests/fixtures/reftests/text-objectBoundingBox-ref.png differ
diff --git a/tests/fixtures/reftests/text-objectBoundingBox.svg 
b/tests/fixtures/reftests/text-objectBoundingBox.svg
new file mode 100644
index 00000000..e1eea306
--- /dev/null
+++ b/tests/fixtures/reftests/text-objectBoundingBox.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; width="600" height="210">
+  <linearGradient id="gradient">
+    <stop offset="0.00" style="stop-color: black;"/>
+    <stop offset="0.25" style="stop-color: black;"/>
+    <stop offset="0.25" style="stop-color: white;"/>
+    <stop offset="0.75" style="stop-color: white;"/>
+    <stop offset="0.75" style="stop-color: black;"/>
+    <stop offset="1.00" style="stop-color: black;"/>
+  </linearGradient>
+  <line x1="300" y1="0" x2="300" y2="210" stroke-width="2" stroke="blue"/>
+  <text style="font: 50px Ahem;" fill="url(#gradient)"><tspan text-anchor="start" x="300" 
y="50">abcd</tspan><tspan text-anchor="end" x="300" y="100">abcd</tspan></text>
+  <rect x="100" y="150" width="400" height="50" fill="url(#gradient)"/>
+</svg>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]