[librsvg: 13/15] Use the objectBoundingBox of the whole text element for each span's individual paint source
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 13/15] Use the objectBoundingBox of the whole text element for each span's individual paint source
- Date: Mon, 18 Oct 2021 23:15:48 +0000 (UTC)
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]