//! Tree nodes, the representation of SVG elements. //! //! Librsvg uses the [rctree crate][rctree] to represent the SVG tree of elements. //! Its [`rctree::Node`] struct provides a generic wrapper over nodes in a tree. //! Librsvg puts a [`NodeData`] as the type parameter of [`rctree::Node`]. For convenience, //! librsvg has a type alias [`Node`]` = rctree::Node`. //! //! Nodes are not constructed directly by callers; use markup5ever::QualName; use std::cell::{Ref, RefMut}; use std::fmt; use std::sync::Arc; use crate::bbox::BoundingBox; use crate::document::AcquiredNodes; use crate::drawing_ctx::{DrawingCtx, Viewport}; use crate::element::*; use crate::error::*; use crate::paint_server::PaintSource; use crate::properties::ComputedValues; use crate::session::Session; use crate::text::Chars; use crate::xml::Attributes; /// Strong reference to an element in the SVG tree. /// /// See the [module documentation][self] for more information. pub type Node = rctree::Node; /// Weak reference to an element in the SVG tree. /// /// See the [module documentation][self] for more information. pub type WeakNode = rctree::WeakNode; /// Data for a single DOM node. /// /// ## Memory consumption /// /// SVG files look like this, roughly: /// /// ```xml /// /// /// /// Hello /// /// /// ``` /// /// Each element has a bunch of data, including the styles, which is /// the biggest consumer of memory within the `Element` struct. But /// between each element there is a text node; in the example above /// there are a bunch of text nodes with just whitespace (newlines and /// spaces), and a single text node with "`Hello`" in it from the /// `` element. /// /// ## Accessing the node's contents /// /// Code that traverses the DOM tree needs to find out at runtime what /// each node stands for. First, use the `is_chars` or `is_element` /// methods from the `NodeBorrow` trait to see if you can then call /// `borrow_chars`, `borrow_element`, or `borrow_element_mut`. pub enum NodeData { Element(Box), Text(Box), } impl NodeData { pub fn new_element(session: &Session, name: &QualName, attrs: Attributes) -> NodeData { NodeData::Element(Box::new(Element::new(session, name, attrs))) } pub fn new_chars(initial_text: &str) -> NodeData { NodeData::Text(Box::new(Chars::new(initial_text))) } } impl fmt::Display for NodeData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { NodeData::Element(ref e) => { write!(f, "{e}")?; } NodeData::Text(_) => { write!(f, "Chars")?; } } Ok(()) } } /// Can obtain computed values from a node /// /// In our tree of SVG elements (Node in our parlance), each node stores a `ComputedValues` that /// gets computed during the initial CSS cascade. However, sometimes nodes need to be rendered /// outside the normal hierarchy. For example, the `` element can "instance" a subtree from /// elsewhere in the SVG; it causes the instanced subtree to re-cascade from the computed values for /// the `` element. /// /// You can then call the `get()` method on the resulting `CascadedValues` to get a /// `&ComputedValues` whose fields you can access. pub struct CascadedValues<'a> { inner: CascadedInner<'a>, pub context_stroke: Option>, pub context_fill: Option>, } enum CascadedInner<'a> { FromNode(Ref<'a, Element>), FromValues(Box), } impl<'a> CascadedValues<'a> { /// Creates a `CascadedValues` that has the same cascading mode as &self /// /// This is what nodes should normally use to draw their children from their `draw()` method. /// Nodes that need to override the cascade for their children can use `new_from_values()` /// instead. pub fn clone_with_node(&self, node: &'a Node) -> CascadedValues<'a> { match self.inner { CascadedInner::FromNode(_) => CascadedValues { inner: CascadedInner::FromNode(node.borrow_element()), context_fill: self.context_fill.clone(), context_stroke: self.context_stroke.clone(), }, CascadedInner::FromValues(ref v) => CascadedValues::new_from_values( node, v, self.context_fill.clone(), self.context_stroke.clone(), ), } } /// Creates a `CascadedValues` that will hold the `node`'s computed values /// /// This is to be used only in the toplevel drawing function, or in elements like `` /// that don't propagate their parent's cascade to their children. All others should use /// `new()` to derive the cascade from an existing one. pub fn new_from_node(node: &Node) -> CascadedValues<'_> { CascadedValues { inner: CascadedInner::FromNode(node.borrow_element()), context_fill: None, context_stroke: None, } } /// Creates a `CascadedValues` that will override the `node`'s cascade with the specified /// `values` /// /// This is for the `` element, which draws the element which it references with the /// ``'s own cascade, not with the element's original cascade. pub fn new_from_values( node: &'a Node, values: &ComputedValues, fill: Option>, stroke: Option>, ) -> CascadedValues<'a> { let mut v = Box::new(values.clone()); node.borrow_element() .get_specified_values() .to_computed_values(&mut v); CascadedValues { inner: CascadedInner::FromValues(v), context_fill: fill, context_stroke: stroke, } } /// Returns the cascaded `ComputedValues`. /// /// Nodes should use this from their `Draw::draw()` implementation to get the /// `ComputedValues` from the `CascadedValues` that got passed to `draw()`. pub fn get(&'a self) -> &'a ComputedValues { match self.inner { CascadedInner::FromNode(ref e) => e.get_computed_values(), CascadedInner::FromValues(ref v) => v, } // if values.fill == "context-fill" { // values.fill=self.context_fill // } // if values.stroke == "context-stroke" { // values.stroke=self.context_stroke // } } } /// Helper trait to get different NodeData variants pub trait NodeBorrow { /// Returns `false` for NodeData::Text, `true` otherwise. fn is_element(&self) -> bool; /// Returns `true` for NodeData::Text, `false` otherwise. fn is_chars(&self) -> bool; /// Borrows a `Chars` reference. /// /// Panics: will panic if `&self` is not a `NodeData::Text` node fn borrow_chars(&self) -> Ref<'_, Chars>; /// Borrows an `Element` reference /// /// Panics: will panic if `&self` is not a `NodeData::Element` node fn borrow_element(&self) -> Ref<'_, Element>; /// Borrows an `Element` reference mutably /// /// Panics: will panic if `&self` is not a `NodeData::Element` node fn borrow_element_mut(&mut self) -> RefMut<'_, Element>; /// Borrows an `ElementData` reference to the concrete element type. /// /// Panics: will panic if `&self` is not a `NodeData::Element` node fn borrow_element_data(&self) -> Ref<'_, ElementData>; } impl NodeBorrow for Node { fn is_element(&self) -> bool { matches!(*self.borrow(), NodeData::Element(_)) } fn is_chars(&self) -> bool { matches!(*self.borrow(), NodeData::Text(_)) } fn borrow_chars(&self) -> Ref<'_, Chars> { Ref::map(self.borrow(), |n| match n { NodeData::Text(c) => &**c, _ => panic!("tried to borrow_chars for a non-text node"), }) } fn borrow_element(&self) -> Ref<'_, Element> { Ref::map(self.borrow(), |n| match n { NodeData::Element(e) => &**e, _ => panic!("tried to borrow_element for a non-element node"), }) } fn borrow_element_mut(&mut self) -> RefMut<'_, Element> { RefMut::map(self.borrow_mut(), |n| match &mut *n { NodeData::Element(e) => &mut **e, _ => panic!("tried to borrow_element_mut for a non-element node"), }) } fn borrow_element_data(&self) -> Ref<'_, ElementData> { Ref::map(self.borrow(), |n| match n { NodeData::Element(e) => &e.element_data, _ => panic!("tried to borrow_element_data for a non-element node"), }) } } #[macro_export] macro_rules! is_element_of_type { ($node:expr, $element_type:ident) => { matches!( $node.borrow_element().element_data, $crate::element::ElementData::$element_type(_) ) }; } #[macro_export] macro_rules! borrow_element_as { ($node:expr, $element_type:ident) => { std::cell::Ref::map($node.borrow_element_data(), |d| match d { $crate::element::ElementData::$element_type(ref e) => &*e, _ => panic!("tried to borrow_element_as {}", stringify!($element_type)), }) }; } /// Helper trait for cascading recursively pub trait NodeCascade { fn cascade(&mut self, values: &ComputedValues); } impl NodeCascade for Node { fn cascade(&mut self, values: &ComputedValues) { let mut values = values.clone(); { let mut elt = self.borrow_element_mut(); elt.get_specified_values().to_computed_values(&mut values); elt.set_computed_values(&values); } for mut child in self.children().filter(|c| c.is_element()) { child.cascade(&values); } } } /// Helper trait for drawing recursively. /// /// This is a trait because [`Node`] is a type alias over [`rctree::Node`], not a concrete type. pub trait NodeDraw { fn draw( &self, acquired_nodes: &mut AcquiredNodes<'_>, cascaded: &CascadedValues<'_>, viewport: &Viewport, draw_ctx: &mut DrawingCtx, clipping: bool, ) -> Result; fn draw_children( &self, acquired_nodes: &mut AcquiredNodes<'_>, cascaded: &CascadedValues<'_>, viewport: &Viewport, draw_ctx: &mut DrawingCtx, clipping: bool, ) -> Result; } impl NodeDraw for Node { fn draw( &self, acquired_nodes: &mut AcquiredNodes<'_>, cascaded: &CascadedValues<'_>, viewport: &Viewport, draw_ctx: &mut DrawingCtx, clipping: bool, ) -> Result { match *self.borrow() { NodeData::Element(ref e) => { match e.draw(self, acquired_nodes, cascaded, viewport, draw_ctx, clipping) { Ok(bbox) => Ok(bbox), // https://www.w3.org/TR/css-transforms-1/#transform-function-lists // // "If a transform function causes the current transformation matrix of an // object to be non-invertible, the object and its content do not get // displayed." Err(RenderingError::InvalidTransform) => Ok(draw_ctx.empty_bbox()), Err(e) => Err(e), } } _ => Ok(draw_ctx.empty_bbox()), } } fn draw_children( &self, acquired_nodes: &mut AcquiredNodes<'_>, cascaded: &CascadedValues<'_>, viewport: &Viewport, draw_ctx: &mut DrawingCtx, clipping: bool, ) -> Result { let mut bbox = draw_ctx.empty_bbox(); for child in self.children().filter(|c| c.is_element()) { let child_bbox = draw_ctx.draw_node_from_stack( &child, acquired_nodes, &CascadedValues::clone_with_node(cascaded, &child), viewport, clipping, )?; bbox.insert(&child_bbox); } Ok(bbox) } }