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        // if values.fill == "context-fill" {
185        //     values.fill=self.context_fill
186        // }
187        // if values.stroke == "context-stroke" {
188        //     values.stroke=self.context_stroke
189        // }
190    }
191}
192
193/// Helper trait to get different NodeData variants
194pub trait NodeBorrow {
195    /// Returns `false` for NodeData::Text, `true` otherwise.
196    fn is_element(&self) -> bool;
197
198    /// Returns `true` for NodeData::Text, `false` otherwise.
199    fn is_chars(&self) -> bool;
200
201    /// Borrows a `Chars` reference.
202    ///
203    /// Panics: will panic if `&self` is not a `NodeData::Text` node
204    fn borrow_chars(&self) -> Ref<'_, Chars>;
205
206    /// Borrows an `Element` reference
207    ///
208    /// Panics: will panic if `&self` is not a `NodeData::Element` node
209    fn borrow_element(&self) -> Ref<'_, Element>;
210
211    /// Borrows an `Element` reference mutably
212    ///
213    /// Panics: will panic if `&self` is not a `NodeData::Element` node
214    fn borrow_element_mut(&mut self) -> RefMut<'_, Element>;
215
216    /// Borrows an `ElementData` reference to the concrete element type.
217    ///
218    /// Panics: will panic if `&self` is not a `NodeData::Element` node
219    fn borrow_element_data(&self) -> Ref<'_, ElementData>;
220}
221
222impl NodeBorrow for Node {
223    fn is_element(&self) -> bool {
224        matches!(*self.borrow(), NodeData::Element(_))
225    }
226
227    fn is_chars(&self) -> bool {
228        matches!(*self.borrow(), NodeData::Text(_))
229    }
230
231    fn borrow_chars(&self) -> Ref<'_, Chars> {
232        Ref::map(self.borrow(), |n| match n {
233            NodeData::Text(c) => &**c,
234            _ => panic!("tried to borrow_chars for a non-text node"),
235        })
236    }
237
238    fn borrow_element(&self) -> Ref<'_, Element> {
239        Ref::map(self.borrow(), |n| match n {
240            NodeData::Element(e) => &**e,
241            _ => panic!("tried to borrow_element for a non-element node"),
242        })
243    }
244
245    fn borrow_element_mut(&mut self) -> RefMut<'_, Element> {
246        RefMut::map(self.borrow_mut(), |n| match &mut *n {
247            NodeData::Element(e) => &mut **e,
248            _ => panic!("tried to borrow_element_mut for a non-element node"),
249        })
250    }
251
252    fn borrow_element_data(&self) -> Ref<'_, ElementData> {
253        Ref::map(self.borrow(), |n| match n {
254            NodeData::Element(e) => &e.element_data,
255            _ => panic!("tried to borrow_element_data for a non-element node"),
256        })
257    }
258}
259
260#[doc(hidden)]
261#[macro_export]
262macro_rules! is_element_of_type {
263    ($node:expr, $element_type:ident) => {
264        matches!(
265            $node.borrow_element().element_data,
266            $crate::element::ElementData::$element_type(_)
267        )
268    };
269}
270
271#[doc(hidden)]
272#[macro_export]
273macro_rules! borrow_element_as {
274    ($node:expr, $element_type:ident) => {
275        std::cell::Ref::map($node.borrow_element_data(), |d| match d {
276            $crate::element::ElementData::$element_type(ref e) => &*e,
277            _ => panic!("tried to borrow_element_as {}", stringify!($element_type)),
278        })
279    };
280}
281
282/// Helper trait for cascading recursively
283pub trait NodeCascade {
284    fn cascade(&mut self, values: &ComputedValues);
285}
286
287impl NodeCascade for Node {
288    fn cascade(&mut self, values: &ComputedValues) {
289        // We box this because ComputedValues is a big structure.  Since this function is
290        // recursive, we want to minimize stack consumption during recursion.
291        //
292        // As of 2024/Oct/24, the unboxed versions uses 1792 bytes of stack between each
293        // recursive call to cascade(); with the boxed version it is just 8 bytes.
294        //
295        // We should probably change this to a non-recursive tree traversal at some point.
296
297        let mut values = Box::new(values.clone());
298
299        {
300            let mut elt = self.borrow_element_mut();
301
302            elt.get_specified_values().to_computed_values(&mut values);
303            elt.set_computed_values(&values);
304        }
305
306        for mut child in self.children().filter(|c| c.is_element()) {
307            child.cascade(&values);
308        }
309    }
310}
311
312/// Helper trait for drawing recursively.
313///
314/// This is a trait because [`Node`] is a type alias over [`rctree::Node`], not a concrete type.
315pub trait NodeDraw {
316    fn draw(
317        &self,
318        acquired_nodes: &mut AcquiredNodes<'_>,
319        cascaded: &CascadedValues<'_>,
320        viewport: &Viewport,
321        draw_ctx: &mut DrawingCtx,
322        clipping: bool,
323    ) -> DrawResult;
324
325    fn draw_children(
326        &self,
327        acquired_nodes: &mut AcquiredNodes<'_>,
328        cascaded: &CascadedValues<'_>,
329        viewport: &Viewport,
330        draw_ctx: &mut DrawingCtx,
331        clipping: bool,
332    ) -> DrawResult;
333}
334
335impl NodeDraw for Node {
336    fn draw(
337        &self,
338        acquired_nodes: &mut AcquiredNodes<'_>,
339        cascaded: &CascadedValues<'_>,
340        viewport: &Viewport,
341        draw_ctx: &mut DrawingCtx,
342        clipping: bool,
343    ) -> DrawResult {
344        match *self.borrow() {
345            NodeData::Element(ref e) => {
346                rsvg_log!(draw_ctx.session(), "({}", e);
347                draw_ctx.print_stack_depth("Node::draw");
348                let res = match e.draw(self, acquired_nodes, cascaded, viewport, draw_ctx, clipping)
349                {
350                    Ok(bbox) => Ok(bbox),
351
352                    Err(boxed_e) => match *boxed_e {
353                        // https://www.w3.org/TR/css-transforms-1/#transform-function-lists
354                        //
355                        // "If a transform function causes the current transformation matrix of an
356                        // object to be non-invertible, the object and its content do not get
357                        // displayed."
358                        InternalRenderingError::InvalidTransform => Ok(viewport.empty_bbox()),
359
360                        InternalRenderingError::CircularReference(node) => {
361                            if node != *self {
362                                return Ok(viewport.empty_bbox());
363                            } else {
364                                return Err(Box::new(InternalRenderingError::CircularReference(
365                                    node,
366                                )));
367                            }
368                        }
369
370                        _ => Err(boxed_e),
371                    },
372                };
373
374                rsvg_log!(draw_ctx.session(), ")");
375
376                res
377            }
378
379            _ => Ok(viewport.empty_bbox()),
380        }
381    }
382
383    fn draw_children(
384        &self,
385        acquired_nodes: &mut AcquiredNodes<'_>,
386        cascaded: &CascadedValues<'_>,
387        viewport: &Viewport,
388        draw_ctx: &mut DrawingCtx,
389        clipping: bool,
390    ) -> DrawResult {
391        draw_ctx.print_stack_depth("Node::draw_children");
392
393        let mut bbox = viewport.empty_bbox();
394
395        for child in self.children().filter(|c| c.is_element()) {
396            let child_bbox = draw_ctx.draw_node_from_stack(
397                &child,
398                acquired_nodes,
399                &CascadedValues::clone_with_node(cascaded, &child),
400                viewport,
401                clipping,
402            )?;
403            bbox.insert(&child_bbox);
404        }
405
406        Ok(bbox)
407    }
408}