Skip to main content

rsvg/
node.rs

1//! Tree nodes, the representation of SVG elements.
2//!
3//! Librsvg uses the [rctree crate][rctree] to represent the SVG tree of elements.
4//! Its [`rctree::Node`] struct provides a generic wrapper over nodes in a tree.
5//! Librsvg puts a [`NodeData`] as the type parameter of [`rctree::Node`].  For convenience,
6//! librsvg has a type alias [`Node`]` = rctree::Node<NodeData>`.
7//!
8//! Nodes are not constructed directly by callers;
9
10use markup5ever::QualName;
11use std::cell::{Ref, RefMut};
12use std::fmt;
13use std::rc::Rc;
14
15use crate::document::AcquiredNodes;
16use crate::drawing_ctx::{DrawingCtx, Viewport};
17use crate::element::*;
18use crate::error::*;
19use crate::paint_server::PaintSource;
20use crate::properties::ComputedValues;
21use crate::rsvg_log;
22use crate::session::Session;
23use crate::text::Chars;
24use crate::xml::Attributes;
25
26/// Strong reference to an element in the SVG tree.
27///
28/// See the [module documentation][self] for more information.
29pub type Node = rctree::Node<NodeData>;
30
31/// Weak reference to an element in the SVG tree.
32///
33/// See the [module documentation][self] for more information.
34pub type WeakNode = rctree::WeakNode<NodeData>;
35
36/// Data for a single DOM node.
37///
38/// ## Memory consumption
39///
40/// SVG files look like this, roughly:
41///
42/// ```xml
43/// <svg>
44///   <rect x="10" y="20"/>
45///   <path d="..."/>
46///   <text x="10" y="20">Hello</text>
47///   <!-- etc -->
48/// </svg>
49/// ```
50///
51/// Each element has a bunch of data, including the styles, which is
52/// the biggest consumer of memory within the `Element` struct.  But
53/// between each element there is a text node; in the example above
54/// there are a bunch of text nodes with just whitespace (newlines and
55/// spaces), and a single text node with "`Hello`" in it from the
56/// `<text>` element.
57///
58/// ## Accessing the node's contents
59///
60/// Code that traverses the DOM tree needs to find out at runtime what
61/// each node stands for.  First, use the `is_chars` or `is_element`
62/// methods from the `NodeBorrow` trait to see if you can then call
63/// `borrow_chars`, `borrow_element`, or `borrow_element_mut`.
64pub enum NodeData {
65    Element(Box<Element>),
66    Text(Box<Chars>),
67}
68
69impl NodeData {
70    pub fn new_element(session: &Session, name: &QualName, attrs: Attributes) -> NodeData {
71        NodeData::Element(Box::new(Element::new(session, name, attrs)))
72    }
73
74    pub fn new_chars(initial_text: &str) -> NodeData {
75        NodeData::Text(Box::new(Chars::new(initial_text)))
76    }
77}
78
79impl fmt::Display for NodeData {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match *self {
82            NodeData::Element(ref e) => {
83                write!(f, "{e}")?;
84            }
85            NodeData::Text(_) => {
86                write!(f, "Chars")?;
87            }
88        }
89
90        Ok(())
91    }
92}
93
94/// Can obtain computed values from a node
95///
96/// In our tree of SVG elements (Node in our parlance), each node stores a `ComputedValues` that
97/// gets computed during the initial CSS cascade.  However, sometimes nodes need to be rendered
98/// outside the normal hierarchy.  For example, the `<use>` element can "instance" a subtree from
99/// elsewhere in the SVG; it causes the instanced subtree to re-cascade from the computed values for
100/// the `<use>` element.
101///
102/// You can then call the `get()` method on the resulting `CascadedValues` to get a
103/// `&ComputedValues` whose fields you can access.
104pub struct CascadedValues<'a> {
105    inner: CascadedInner<'a>,
106    pub context_stroke: Option<Rc<PaintSource>>,
107    pub context_fill: Option<Rc<PaintSource>>,
108}
109
110enum CascadedInner<'a> {
111    FromNode(Ref<'a, Element>),
112    FromValues(Box<ComputedValues>),
113}
114
115impl<'a> CascadedValues<'a> {
116    /// Creates a `CascadedValues` that has the same cascading mode as &self
117    ///
118    /// This is what nodes should normally use to draw their children from their `draw()` method.
119    /// Nodes that need to override the cascade for their children can use `new_from_values()`
120    /// instead.
121    pub fn clone_with_node(&self, node: &'a Node) -> CascadedValues<'a> {
122        match self.inner {
123            CascadedInner::FromNode(_) => CascadedValues {
124                inner: CascadedInner::FromNode(node.borrow_element()),
125                context_fill: self.context_fill.clone(),
126                context_stroke: self.context_stroke.clone(),
127            },
128
129            CascadedInner::FromValues(ref v) => CascadedValues::new_from_values(
130                node,
131                v,
132                self.context_fill.clone(),
133                self.context_stroke.clone(),
134            ),
135        }
136    }
137
138    /// Creates a `CascadedValues` that will hold the `node`'s computed values
139    ///
140    /// This is to be used only in the toplevel drawing function, or in elements like `<marker>`
141    /// that don't propagate their parent's cascade to their children.  All others should use
142    /// `new()` to derive the cascade from an existing one.
143    pub fn new_from_node(node: &Node) -> CascadedValues<'_> {
144        CascadedValues {
145            inner: CascadedInner::FromNode(node.borrow_element()),
146            context_fill: None,
147            context_stroke: None,
148        }
149    }
150
151    /// Creates a `CascadedValues` that will override the `node`'s cascade with the specified
152    /// `values`
153    ///
154    /// This is for the `<use>` element, which draws the element which it references with the
155    /// `<use>`'s own cascade, not with the element's original cascade.
156    pub fn new_from_values(
157        node: &'a Node,
158        values: &ComputedValues,
159        fill: Option<Rc<PaintSource>>,
160        stroke: Option<Rc<PaintSource>>,
161    ) -> CascadedValues<'a> {
162        let mut v = Box::new(values.clone());
163        node.borrow_element()
164            .get_specified_values()
165            .to_computed_values(&mut v);
166
167        CascadedValues {
168            inner: CascadedInner::FromValues(v),
169            context_fill: fill,
170            context_stroke: stroke,
171        }
172    }
173
174    /// Returns the cascaded `ComputedValues`.
175    ///
176    /// Nodes should use this from their `Draw::draw()` implementation to get the
177    /// `ComputedValues` from the `CascadedValues` that got passed to `draw()`.
178    pub fn get(&'a self) -> &'a ComputedValues {
179        match self.inner {
180            CascadedInner::FromNode(ref e) => e.get_computed_values(),
181            CascadedInner::FromValues(ref v) => v,
182        }
183    }
184}
185
186/// Helper trait to get different NodeData variants
187pub trait NodeBorrow {
188    /// Returns `false` for NodeData::Text, `true` otherwise.
189    fn is_element(&self) -> bool;
190
191    /// Returns `true` for NodeData::Text, `false` otherwise.
192    fn is_chars(&self) -> bool;
193
194    /// Borrows a `Chars` reference.
195    ///
196    /// Panics: will panic if `&self` is not a `NodeData::Text` node
197    fn borrow_chars(&self) -> Ref<'_, Chars>;
198
199    /// Borrows an `Element` reference
200    ///
201    /// Panics: will panic if `&self` is not a `NodeData::Element` node
202    fn borrow_element(&self) -> Ref<'_, Element>;
203
204    /// Borrows an `Element` reference mutably
205    ///
206    /// Panics: will panic if `&self` is not a `NodeData::Element` node
207    fn borrow_element_mut(&mut self) -> RefMut<'_, Element>;
208
209    /// Borrows an `ElementData` reference to the concrete element type.
210    ///
211    /// Panics: will panic if `&self` is not a `NodeData::Element` node
212    fn borrow_element_data(&self) -> Ref<'_, ElementData>;
213}
214
215impl NodeBorrow for Node {
216    fn is_element(&self) -> bool {
217        matches!(*self.borrow(), NodeData::Element(_))
218    }
219
220    fn is_chars(&self) -> bool {
221        matches!(*self.borrow(), NodeData::Text(_))
222    }
223
224    fn borrow_chars(&self) -> Ref<'_, Chars> {
225        Ref::map(self.borrow(), |n| match n {
226            NodeData::Text(c) => &**c,
227            _ => panic!("tried to borrow_chars for a non-text node"),
228        })
229    }
230
231    fn borrow_element(&self) -> Ref<'_, Element> {
232        Ref::map(self.borrow(), |n| match n {
233            NodeData::Element(e) => &**e,
234            _ => panic!("tried to borrow_element for a non-element node"),
235        })
236    }
237
238    fn borrow_element_mut(&mut self) -> RefMut<'_, Element> {
239        RefMut::map(self.borrow_mut(), |n| match &mut *n {
240            NodeData::Element(e) => &mut **e,
241            _ => panic!("tried to borrow_element_mut for a non-element node"),
242        })
243    }
244
245    fn borrow_element_data(&self) -> Ref<'_, ElementData> {
246        Ref::map(self.borrow(), |n| match n {
247            NodeData::Element(e) => &e.element_data,
248            _ => panic!("tried to borrow_element_data for a non-element node"),
249        })
250    }
251}
252
253#[doc(hidden)]
254#[macro_export]
255macro_rules! is_element_of_type {
256    ($node:expr, $element_type:ident) => {
257        matches!(
258            $node.borrow_element().element_data,
259            $crate::element::ElementData::$element_type(_)
260        )
261    };
262}
263
264#[doc(hidden)]
265#[macro_export]
266macro_rules! borrow_element_as {
267    ($node:expr, $element_type:ident) => {
268        std::cell::Ref::map($node.borrow_element_data(), |d| match d {
269            &$crate::element::ElementData::$element_type(ref e) => &*e,
270            _ => panic!("tried to borrow_element_as {}", stringify!($element_type)),
271        })
272    };
273}
274
275/// Helper trait for cascading recursively
276pub trait NodeCascade {
277    fn cascade(&mut self, values: &ComputedValues);
278}
279
280impl NodeCascade for Node {
281    fn cascade(&mut self, values: &ComputedValues) {
282        // We box this because ComputedValues is a big structure.  Since this function is
283        // recursive, we want to minimize stack consumption during recursion.
284        //
285        // As of 2024/Oct/24, the unboxed versions uses 1792 bytes of stack between each
286        // recursive call to cascade(); with the boxed version it is just 8 bytes.
287        //
288        // We should probably change this to a non-recursive tree traversal at some point.
289
290        let mut values = Box::new(values.clone());
291
292        {
293            let mut elt = self.borrow_element_mut();
294
295            elt.get_specified_values().to_computed_values(&mut values);
296            elt.set_computed_values(&values);
297        }
298
299        for mut child in self.children().filter(|c| c.is_element()) {
300            child.cascade(&values);
301        }
302    }
303}
304
305/// Helper trait for drawing recursively.
306///
307/// This is a trait because [`Node`] is a type alias over [`rctree::Node`], not a concrete type.
308pub trait NodeDraw {
309    fn draw(
310        &self,
311        acquired_nodes: &mut AcquiredNodes<'_>,
312        cascaded: &CascadedValues<'_>,
313        viewport: &Viewport,
314        draw_ctx: &mut DrawingCtx,
315        clipping: bool,
316    ) -> DrawResult;
317
318    fn draw_children(
319        &self,
320        acquired_nodes: &mut AcquiredNodes<'_>,
321        cascaded: &CascadedValues<'_>,
322        viewport: &Viewport,
323        draw_ctx: &mut DrawingCtx,
324    ) -> DrawResult;
325}
326
327impl NodeDraw for Node {
328    fn draw(
329        &self,
330        acquired_nodes: &mut AcquiredNodes<'_>,
331        cascaded: &CascadedValues<'_>,
332        viewport: &Viewport,
333        draw_ctx: &mut DrawingCtx,
334        clipping: bool,
335    ) -> DrawResult {
336        match *self.borrow() {
337            NodeData::Element(ref e) => {
338                rsvg_log!(draw_ctx.session(), "({}", e);
339                draw_ctx.print_stack_depth("Node::draw");
340                let res = match e.draw(self, acquired_nodes, cascaded, viewport, draw_ctx, clipping)
341                {
342                    Ok(bbox) => Ok(bbox),
343
344                    Err(boxed_e) => match *boxed_e {
345                        // https://www.w3.org/TR/css-transforms-1/#transform-function-lists
346                        //
347                        // "If a transform function causes the current transformation matrix of an
348                        // object to be non-invertible, the object and its content do not get
349                        // displayed."
350                        InternalRenderingError::InvalidTransform => Ok(viewport.empty_bbox()),
351
352                        InternalRenderingError::CircularReference(node) => {
353                            if node != *self {
354                                return Ok(viewport.empty_bbox());
355                            } else {
356                                return Err(Box::new(InternalRenderingError::CircularReference(
357                                    node,
358                                )));
359                            }
360                        }
361
362                        _ => Err(boxed_e),
363                    },
364                };
365
366                rsvg_log!(draw_ctx.session(), ")");
367
368                res
369            }
370
371            _ => Ok(viewport.empty_bbox()),
372        }
373    }
374
375    fn draw_children(
376        &self,
377        acquired_nodes: &mut AcquiredNodes<'_>,
378        cascaded: &CascadedValues<'_>,
379        viewport: &Viewport,
380        draw_ctx: &mut DrawingCtx,
381    ) -> DrawResult {
382        draw_ctx.print_stack_depth("Node::draw_children");
383
384        let mut bbox = viewport.empty_bbox();
385
386        for child in self.children().filter(|c| c.is_element()) {
387            let child_bbox = draw_ctx.draw_node_from_stack(
388                &child,
389                acquired_nodes,
390                &CascadedValues::clone_with_node(cascaded, &child),
391                viewport,
392            )?;
393            bbox.insert(&child_bbox);
394        }
395
396        Ok(bbox)
397    }
398}