[librsvg: 1/4] Render and style text links




commit 195b58c84285eab8769a5bc17e99ebac8328990a
Author: Michael Howell <michael notriddle com>
Date:   Fri Oct 8 15:29:56 2021 -0700

    Render and style text links
    
    Fixes #738
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/601>

 src/structure.rs                                   |  18 ++++++++--
 src/text.rs                                        |  40 ++++++++++++++++++---
 .../bugs/a-inside-text-content-738-ref.png         | Bin 0 -> 2493 bytes
 .../reftests/bugs/a-inside-text-content-738.svg    |  24 +++++++++++++
 4 files changed, 76 insertions(+), 6 deletions(-)
---
diff --git a/src/structure.rs b/src/structure.rs
index 9facec72..8d47197d 100644
--- a/src/structure.rs
+++ b/src/structure.rs
@@ -7,7 +7,7 @@ use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNodes, NodeId};
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
-use crate::element::{Draw, ElementResult, SetAttributes};
+use crate::element::{Draw, Element, ElementResult, SetAttributes};
 use crate::error::*;
 use crate::href::{is_href, set_href};
 use crate::layout::StackingContext;
@@ -15,6 +15,7 @@ use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
 use crate::parsers::{Parse, ParseValue};
 use crate::rect::Rect;
+use crate::text::TSpan;
 use crate::viewbox::*;
 use crate::xml::Attributes;
 
@@ -511,7 +512,9 @@ impl Draw for Mask {}
 
 #[derive(Default)]
 pub struct Link {
-    link: Option<String>,
+    pub link: Option<String>,
+    // These attributes are only applicable in <text><a></a></text>.
+    pub tspan: TSpan,
 }
 
 impl SetAttributes for Link {
@@ -523,6 +526,8 @@ impl SetAttributes for Link {
             }
         }
 
+        self.tspan.set_attributes(attrs)?;
+
         Ok(())
     }
 }
@@ -536,6 +541,15 @@ impl Draw for Link {
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
+
+        // If this element is inside of <text>, do not draw it.
+        // The <text> takes care of it.
+        for an in node.ancestors() {
+            if matches!(&*an.borrow_element(), Element::Text(_)) {
+                return Ok(draw_ctx.empty_bbox());
+            }
+        }
+
         let cascaded = CascadedValues::new(cascaded, node);
         let values = cascaded.get();
 
diff --git a/src/text.rs b/src/text.rs
index 46baef3b..20a673c6 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -38,6 +38,7 @@ struct Chunk {
     x: Option<f64>,
     y: Option<f64>,
     spans: Vec<Span>,
+    link: Option<String>,
 }
 
 struct MeasuredChunk {
@@ -46,12 +47,14 @@ struct MeasuredChunk {
     y: Option<f64>,
     advance: (f64, f64),
     spans: Vec<MeasuredSpan>,
+    link: Option<String>,
 }
 
 struct PositionedChunk {
     next_chunk_x: f64,
     next_chunk_y: f64,
     spans: Vec<PositionedSpan>,
+    link: Option<String>,
 }
 
 struct Span {
@@ -78,11 +81,12 @@ struct PositionedSpan {
 }
 
 impl Chunk {
-    fn new(values: &ComputedValues, x: Option<f64>, y: Option<f64>) -> Chunk {
+    fn new(values: &ComputedValues, x: Option<f64>, y: Option<f64>, link: Option<String>) -> Chunk {
         Chunk {
             values: Rc::new(values.clone()),
             x,
             y,
+            link,
             spans: Vec::new(),
         }
     }
@@ -110,6 +114,7 @@ impl MeasuredChunk {
             y: chunk.y,
             advance,
             spans: measured_spans,
+            link: chunk.link.clone(),
         }
     }
 }
@@ -183,6 +188,7 @@ impl PositionedChunk {
             next_chunk_x: x,
             next_chunk_y: y,
             spans: positioned,
+            link: measured.link.clone(),
         }
     }
 }
@@ -330,6 +336,7 @@ fn children_to_chunks(
     dx: f64,
     dy: f64,
     depth: usize,
+    link: Option<String>,
 ) {
     for child in node.children() {
         if child.is_chars() {
@@ -352,6 +359,23 @@ fn children_to_chunks(
                         dx,
                         dy,
                         depth + 1,
+                        link.clone(),
+                    );
+                }
+
+                Element::Link(ref link) => {
+                    let tspan = &link.tspan;
+                    let cascaded = CascadedValues::new(cascaded, &child);
+                    tspan.to_chunks(
+                        &child,
+                        acquired_nodes,
+                        &cascaded,
+                        draw_ctx,
+                        chunks,
+                        dx,
+                        dy,
+                        depth + 1,
+                        link.link.clone(),
                     );
                 }
 
@@ -498,7 +522,7 @@ impl Text {
         let view_params = draw_ctx.get_view_params();
         let params = NormalizeParams::new(values, &view_params);
 
-        chunks.push(Chunk::new(values, Some(x), Some(y)));
+        chunks.push(Chunk::new(values, Some(x), Some(y), None));
 
         let dx = self.dx.to_user(&params);
         let dy = self.dy.to_user(&params);
@@ -512,6 +536,7 @@ impl Text {
             dx,
             dy,
             0,
+            None,
         );
         chunks
     }
@@ -592,7 +617,12 @@ impl Draw for Text {
 
                 for chunk in &positioned_chunks {
                     for span in &chunk.spans {
-                        let span_bbox = span.draw(an, dc, clipping)?;
+                        let span_bbox = match chunk.link.as_ref() {
+                            Some(l) if !l.is_empty() => {
+                                dc.with_link_tag(l.as_str(), &mut |dc| span.draw(an, dc, clipping))?
+                            }
+                            _ => span.draw(an, dc, clipping)?,
+                        };
                         bbox.insert(&span_bbox);
                     }
                 }
@@ -695,6 +725,7 @@ impl TSpan {
         dx: f64,
         dy: f64,
         depth: usize,
+        link: Option<String>,
     ) {
         let values = cascaded.get();
         if !values.is_displayed() {
@@ -711,7 +742,7 @@ impl TSpan {
         let span_dy = dy + self.dy.to_user(&params);
 
         if x.is_some() || y.is_some() {
-            chunks.push(Chunk::new(values, x, y));
+            chunks.push(Chunk::new(values, x, y, link.clone()));
         }
 
         children_to_chunks(
@@ -723,6 +754,7 @@ impl TSpan {
             span_dx,
             span_dy,
             depth,
+            link,
         );
     }
 }
diff --git a/tests/fixtures/reftests/bugs/a-inside-text-content-738-ref.png 
b/tests/fixtures/reftests/bugs/a-inside-text-content-738-ref.png
new file mode 100644
index 00000000..ea52230c
Binary files /dev/null and b/tests/fixtures/reftests/bugs/a-inside-text-content-738-ref.png differ
diff --git a/tests/fixtures/reftests/bugs/a-inside-text-content-738.svg 
b/tests/fixtures/reftests/bugs/a-inside-text-content-738.svg
new file mode 100644
index 00000000..7c3a0482
--- /dev/null
+++ b/tests/fixtures/reftests/bugs/a-inside-text-content-738.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   version="1.1"
+   viewBox="0 0 500 600"
+   font-family="sans-serif"
+   font-size="18">
+  <defs>
+    <style>
+      a {fill: black; font-family: Helvetica; font-size:10; }
+      text { font-family: Helvetica; font-size:10; }
+    </style>
+  </defs>
+
+  <text x="250" y="25" class="head" text-anchor="middle">SVG CSS Tests</text>
+  <g transform="translate(0,50)">
+    <text x="50">a:link</text>
+    <text x="250" class="test"><a xlink:href="#foo">xlink:href</a></text>
+  </g>
+
+</svg>


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