[librsvg: 7/10] element: move element code to its own module



commit f084dceb1a3bf4936e189085e63cd9aa750bff2c
Author: Paolo Borelli <pborelli gnome org>
Date:   Mon Mar 16 11:56:13 2020 +0100

    element: move element code to its own module
    
    Move all element code in element.rs, this include the creation
    function which is now named create_element.

 Makefile.am                                      |   4 +-
 po/POTFILES.in                                   |   2 +-
 rsvg_internals/src/create_node.rs                | 344 ------------
 rsvg_internals/src/css.rs                        |   3 +-
 rsvg_internals/src/document.rs                   |   6 +-
 rsvg_internals/src/drawing_ctx.rs                |   3 +-
 rsvg_internals/src/element.rs                    | 652 +++++++++++++++++++++++
 rsvg_internals/src/filter.rs                     |   5 +-
 rsvg_internals/src/filters/blend.rs              |   5 +-
 rsvg_internals/src/filters/color_matrix.rs       |   5 +-
 rsvg_internals/src/filters/component_transfer.rs |   7 +-
 rsvg_internals/src/filters/composite.rs          |   5 +-
 rsvg_internals/src/filters/convolve_matrix.rs    |   5 +-
 rsvg_internals/src/filters/displacement_map.rs   |   5 +-
 rsvg_internals/src/filters/flood.rs              |   5 +-
 rsvg_internals/src/filters/gaussian_blur.rs      |   5 +-
 rsvg_internals/src/filters/image.rs              |   5 +-
 rsvg_internals/src/filters/light/light_source.rs |   9 +-
 rsvg_internals/src/filters/light/lighting.rs     |   9 +-
 rsvg_internals/src/filters/merge.rs              |   7 +-
 rsvg_internals/src/filters/mod.rs                |   7 +-
 rsvg_internals/src/filters/morphology.rs         |   5 +-
 rsvg_internals/src/filters/offset.rs             |   5 +-
 rsvg_internals/src/filters/tile.rs               |   5 +-
 rsvg_internals/src/filters/turbulence.rs         |   5 +-
 rsvg_internals/src/gradient.rs                   |  26 +-
 rsvg_internals/src/image.rs                      |   3 +-
 rsvg_internals/src/lib.rs                        |   2 +-
 rsvg_internals/src/marker.rs                     |   3 +-
 rsvg_internals/src/node.rs                       | 329 +-----------
 rsvg_internals/src/pattern.rs                    |  13 +-
 rsvg_internals/src/shapes.rs                     |  15 +-
 rsvg_internals/src/structure.rs                  |  19 +-
 rsvg_internals/src/style.rs                      |   5 +-
 rsvg_internals/src/text.rs                       |   9 +-
 rsvg_internals/src/xml.rs                        |   3 +-
 36 files changed, 786 insertions(+), 759 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 3b3c25d7..e1a92eab 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,13 +29,13 @@ LIBRSVG_INTERNALS_SRC =                                             \
        rsvg_internals/src/color.rs                             \
        rsvg_internals/src/cond.rs                              \
        rsvg_internals/src/coord_units.rs                       \
-       rsvg_internals/src/create_node.rs                       \
        rsvg_internals/src/css.rs                               \
-       rsvg_internals/src/error.rs                             \
        rsvg_internals/src/dasharray.rs                         \
        rsvg_internals/src/document.rs                          \
        rsvg_internals/src/dpi.rs                               \
        rsvg_internals/src/drawing_ctx.rs                       \
+       rsvg_internals/src/element.rs                           \
+       rsvg_internals/src/error.rs                             \
        rsvg_internals/src/filter.rs                            \
        rsvg_internals/src/filters/bounds.rs                    \
        rsvg_internals/src/filters/blend.rs                     \
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c5591b34..52aefded 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -21,9 +21,9 @@ rsvg_internals/src/bbox.rs
 rsvg_internals/src/color.rs
 rsvg_internals/src/cond.rs
 rsvg_internals/src/coord_units.rs
-rsvg_internals/src/create_node.rs
 rsvg_internals/src/css.rs
 rsvg_internals/src/drawing_ctx.rs
+rsvg_internals/src/element.rs
 rsvg_internals/src/error.rs
 rsvg_internals/src/filters/blend.rs
 rsvg_internals/src/filters/bounds.rs
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index 4af35e72..e8580e43 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -90,9 +90,10 @@ use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};
 use url::Url;
 
 use crate::allowed_url::AllowedUrl;
+use crate::element::ElementType;
 use crate::error::*;
 use crate::io::{self, BinaryData};
-use crate::node::{ElementType, NodeBorrow, NodeCascade, RsvgNode};
+use crate::node::{NodeBorrow, NodeCascade, RsvgNode};
 use crate::properties::{parse_property, ComputedValues, ParsedProperty};
 
 /// A parsed CSS declaration
diff --git a/rsvg_internals/src/document.rs b/rsvg_internals/src/document.rs
index 018eb0bd..18210469 100644
--- a/rsvg_internals/src/document.rs
+++ b/rsvg_internals/src/document.rs
@@ -8,13 +8,13 @@ use std::collections::HashMap;
 use std::rc::Rc;
 
 use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
-use crate::create_node::create_node;
 use crate::css::{self, Origin, Stylesheet};
+use crate::element::ElementType;
 use crate::error::{AcquireError, LoadingError};
 use crate::handle::LoadOptions;
 use crate::io::{self, BinaryData};
 use crate::limits;
-use crate::node::{ElementType, NodeBorrow, NodeData, RsvgNode};
+use crate::node::{NodeBorrow, NodeData, RsvgNode};
 use crate::property_bag::PropertyBag;
 use crate::structure::{IntrinsicDimensions, Svg};
 use crate::surface_utils::shared_surface::SharedImageSurface;
@@ -438,7 +438,7 @@ impl DocumentBuilder {
         pbag: &PropertyBag,
         parent: Option<RsvgNode>,
     ) -> RsvgNode {
-        let mut node = create_node(name, pbag);
+        let mut node = RsvgNode::new(NodeData::new_element(name, pbag));
 
         if let Some(id) = node.borrow_element().get_id() {
             // This is so we don't overwrite an existing id
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 126f1c8b..2620dbfd 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -14,11 +14,12 @@ use crate::coord_units::CoordUnits;
 use crate::dasharray::Dasharray;
 use crate::document::{AcquiredNode, AcquiredNodes};
 use crate::dpi::Dpi;
+use crate::element::ElementType;
 use crate::error::{AcquireError, RenderingError};
 use crate::filters;
 use crate::gradient::{LinearGradient, RadialGradient};
 use crate::marker;
-use crate::node::{CascadedValues, ElementType, NodeBorrow, NodeDraw, RsvgNode};
+use crate::node::{CascadedValues, NodeBorrow, NodeDraw, RsvgNode};
 use crate::paint_server::{PaintServer, PaintSource};
 use crate::path_builder::*;
 use crate::pattern::Pattern;
diff --git a/rsvg_internals/src/element.rs b/rsvg_internals/src/element.rs
new file mode 100644
index 00000000..0ed9b165
--- /dev/null
+++ b/rsvg_internals/src/element.rs
@@ -0,0 +1,652 @@
+//! SVG Elements.
+//!
+//! The [`create_element`] function takes an XML element name, and
+//! creates an [`Element`] for it.
+//!
+//! [`create_element`]: fn.create_element.html
+
+use locale_config::Locale;
+use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
+use once_cell::sync::Lazy;
+use std::collections::{HashMap, HashSet};
+use std::fmt;
+
+use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
+use crate::css::Declaration;
+use crate::error::*;
+use crate::filter::Filter;
+use crate::filters::{
+    blend::FeBlend,
+    color_matrix::FeColorMatrix,
+    component_transfer::{FeComponentTransfer, FeFuncA, FeFuncB, FeFuncG, FeFuncR},
+    composite::FeComposite,
+    convolve_matrix::FeConvolveMatrix,
+    displacement_map::FeDisplacementMap,
+    flood::FeFlood,
+    gaussian_blur::FeGaussianBlur,
+    image::FeImage,
+    light::{
+        light_source::FeDistantLight, light_source::FePointLight, light_source::FeSpotLight,
+        lighting::FeDiffuseLighting, lighting::FeSpecularLighting,
+    },
+    merge::{FeMerge, FeMergeNode},
+    morphology::FeMorphology,
+    offset::FeOffset,
+    tile::FeTile,
+    turbulence::FeTurbulence,
+};
+use crate::gradient::{LinearGradient, RadialGradient, Stop};
+use crate::image::Image;
+use crate::marker::Marker;
+use crate::node::*;
+use crate::parsers::Parse;
+use crate::pattern::Pattern;
+use crate::properties::{ComputedValues, SpecifiedValue, SpecifiedValues};
+use crate::property_bag::PropertyBag;
+use crate::property_defs::Overflow;
+use crate::shapes::{Circle, Ellipse, Line, Path, Polygon, Polyline, Rect};
+use crate::structure::{ClipPath, Group, Link, Mask, NonRendering, Svg, Switch, Symbol, Use};
+use crate::style::Style;
+use crate::text::{TRef, TSpan, Text};
+use crate::transform::Transform;
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum ElementType {
+    Circle,
+    ClipPath,
+    Ellipse,
+    Filter,
+    Group,
+    Image,
+    Line,
+    LinearGradient,
+    Link,
+    Marker,
+    Mask,
+    NonRendering,
+    Path,
+    Pattern,
+    Polygon,
+    Polyline,
+    RadialGradient,
+    Rect,
+    Stop,
+    Style,
+    Svg,
+    Switch,
+    Symbol,
+    Text,
+    TRef,
+    TSpan,
+    Use,
+
+    // Filter primitives, these start with "Fe" as element names are e.g. "feBlend"
+    FeBlend,
+    FeColorMatrix,
+    FeComponentTransfer,
+    FeComposite,
+    FeConvolveMatrix,
+    FeDiffuseLighting,
+    FeDisplacementMap,
+    FeDistantLight,
+    FeFlood,
+    FeFuncA,
+    FeFuncB,
+    FeFuncG,
+    FeFuncR,
+    FeGaussianBlur,
+    FeImage,
+    FeMerge,
+    FeMergeNode,
+    FeMorphology,
+    FeOffset,
+    FePointLight,
+    FeSpecularLighting,
+    FeSpotLight,
+    FeTile,
+    FeTurbulence,
+}
+
+// After creating/parsing a Element, it will be in a success or an error state.
+// We represent this with a Result, aliased as a ElementResult.  There is no
+// extra information for the Ok case; all the interesting stuff is in the
+// Err case.
+//
+// https://www.w3.org/TR/SVG/implnote.html#ErrorProcessing
+//
+// When an element has an error during parsing, the SVG spec calls the element
+// to be "in error".  We skip rendering of elements that are in error.
+//
+// When we parse an element's attributes, we stop as soon as we
+// encounter the first error:  a parse error, or an invalid value,
+// etc.  No further attributes will be processed, although note that
+// the order in which an element's attributes are processed is not
+// defined.
+//
+// Alternatively, we could try to parse/validate all the attributes
+// that come in an element and build up a Vec<ElementError>.  However, we
+// don't do this now.  Doing that may be more useful for an SVG
+// validator, not a renderer like librsvg is.
+pub type ElementResult = Result<(), NodeError>;
+
+/// Contents of an element node in the DOM
+pub struct Element {
+    element_type: ElementType,
+    element_name: QualName,
+    id: Option<String>,    // id attribute from XML element
+    class: Option<String>, // class attribute from XML element
+    specified_values: SpecifiedValues,
+    important_styles: HashSet<QualName>,
+    result: ElementResult,
+    transform: Transform,
+    values: ComputedValues,
+    cond: bool,
+    style_attr: String,
+    node_impl: Box<dyn NodeTrait>,
+}
+
+impl Element {
+    pub fn get_type(&self) -> ElementType {
+        self.element_type
+    }
+
+    pub fn get_node_trait(&self) -> &dyn NodeTrait {
+        self.node_impl.as_ref()
+    }
+
+    pub fn get_impl<T: NodeTrait>(&self) -> &T {
+        if let Some(t) = (&self.node_impl).downcast_ref::<T>() {
+            t
+        } else {
+            panic!("could not downcast");
+        }
+    }
+
+    pub fn element_name(&self) -> &QualName {
+        &self.element_name
+    }
+
+    pub fn get_id(&self) -> Option<&str> {
+        self.id.as_ref().map(String::as_str)
+    }
+
+    pub fn get_class(&self) -> Option<&str> {
+        self.class.as_ref().map(String::as_str)
+    }
+
+    pub fn get_specified_values(&self) -> &SpecifiedValues {
+        &self.specified_values
+    }
+
+    pub fn get_computed_values(&self) -> &ComputedValues {
+        &self.values
+    }
+
+    pub fn set_computed_values(&mut self, values: &ComputedValues) {
+        self.values = values.clone();
+    }
+
+    pub fn get_cond(&self) -> bool {
+        self.cond
+    }
+
+    pub fn get_transform(&self) -> Transform {
+        self.transform
+    }
+
+    pub fn is_overflow(&self) -> bool {
+        self.specified_values.is_overflow()
+    }
+
+    pub fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>, locale: &Locale) {
+        if self.node_impl.overflow_hidden() {
+            self.specified_values.overflow = SpecifiedValue::Specified(Overflow::Hidden);
+        }
+
+        self.save_style_attribute(pbag);
+
+        if let Err(e) = self
+            .set_transform_attribute(pbag)
+            .and_then(|_| self.set_conditional_processing_attributes(pbag, locale))
+            .and_then(|_| self.node_impl.set_atts(parent, pbag))
+            .and_then(|_| self.set_presentation_attributes(pbag))
+        {
+            self.set_error(e);
+        }
+
+        self.node_impl
+            .set_overridden_properties(&mut self.specified_values);
+    }
+
+    fn save_style_attribute(&mut self, pbag: &PropertyBag<'_>) {
+        for (attr, value) in pbag.iter() {
+            match attr.expanded() {
+                expanded_name!("", "style") => self.style_attr.push_str(value),
+                _ => (),
+            }
+        }
+    }
+
+    fn set_transform_attribute(&mut self, pbag: &PropertyBag<'_>) -> Result<(), NodeError> {
+        for (attr, value) in pbag.iter() {
+            match attr.expanded() {
+                expanded_name!("", "transform") => {
+                    return Transform::parse_str(value)
+                        .attribute(attr)
+                        .and_then(|affine| {
+                            self.transform = affine;
+                            Ok(())
+                        });
+                }
+                _ => (),
+            }
+        }
+
+        Ok(())
+    }
+
+    fn set_conditional_processing_attributes(
+        &mut self,
+        pbag: &PropertyBag<'_>,
+        locale: &Locale,
+    ) -> Result<(), NodeError> {
+        let mut cond = self.cond;
+
+        for (attr, value) in pbag.iter() {
+            let mut parse = || -> Result<_, ValueErrorKind> {
+                match attr.expanded() {
+                    expanded_name!("", "requiredExtensions") if cond => {
+                        cond = RequiredExtensions::from_attribute(value)
+                            .map(|RequiredExtensions(res)| res)?;
+                    }
+
+                    expanded_name!("", "requiredFeatures") if cond => {
+                        cond = RequiredFeatures::from_attribute(value)
+                            .map(|RequiredFeatures(res)| res)?;
+                    }
+
+                    expanded_name!("", "systemLanguage") if cond => {
+                        cond = SystemLanguage::from_attribute(value, locale)
+                            .map(|SystemLanguage(res)| res)?;
+                    }
+
+                    _ => {}
+                }
+
+                Ok(cond)
+            };
+
+            parse().map(|c| self.cond = c).attribute(attr)?;
+        }
+
+        Ok(())
+    }
+
+    /// Hands the pbag to the node's state, to apply the presentation attributes
+    fn set_presentation_attributes(&mut self, pbag: &PropertyBag<'_>) -> Result<(), NodeError> {
+        match self.specified_values.parse_presentation_attributes(pbag) {
+            Ok(_) => Ok(()),
+            Err(e) => {
+                // FIXME: we'll ignore errors here for now.
+                //
+                // If we set the node to be in error, we expose buggy handling of the
+                // enable-background property; we are not parsing it correctly. This
+                // causes tests/fixtures/reftests/bugs/587721-text-transform.svg to fail
+                // because it has enable-background="new 0 0 1179.75118 687.74173" in the
+                // toplevel svg element.
+                //
+                //   self.set_error(e);
+                //   return;
+
+                rsvg_log!("(attribute error: {})", e);
+                Ok(())
+            }
+        }
+    }
+
+    // Applies a style declaration to the node's specified_values
+    pub fn apply_style_declaration(&mut self, declaration: &Declaration) {
+        self.specified_values
+            .set_property_from_declaration(declaration, &mut self.important_styles);
+    }
+
+    /// Applies CSS styles from the saved value of the "style" attribute
+    pub fn set_style_attribute(&mut self) {
+        if !self.style_attr.is_empty() {
+            if let Err(e) = self
+                .specified_values
+                .parse_style_declarations(self.style_attr.as_str(), &mut self.important_styles)
+            {
+                self.set_error(e);
+            }
+
+            self.style_attr.clear();
+            self.style_attr.shrink_to_fit();
+        }
+    }
+
+    fn set_error(&mut self, error: NodeError) {
+        rsvg_log!("setting node {} in error: {}", self, error);
+        self.result = Err(error);
+    }
+
+    pub fn is_in_error(&self) -> bool {
+        self.result.is_err()
+    }
+}
+
+impl fmt::Display for Element {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", self.get_type())?;
+        write!(f, " id={}", self.get_id().unwrap_or("None"))?;
+        Ok(())
+    }
+}
+
+macro_rules! e {
+    ($name:ident, $element_type:ident) => {
+        pub fn $name(element_name: &QualName, id: Option<&str>, class: Option<&str>) -> Element {
+            Element {
+                element_type: ElementType::$element_type,
+                element_name: element_name.clone(),
+                id: id.map(str::to_string),
+                class: class.map(str::to_string),
+                specified_values: Default::default(),
+                important_styles: Default::default(),
+                transform: Default::default(),
+                result: Ok(()),
+                values: ComputedValues::default(),
+                cond: true,
+                style_attr: String::new(),
+                node_impl: Box::new(<$element_type>::default()),
+            }
+        }
+    };
+}
+
+#[rustfmt::skip]
+mod creators {
+    use super::*;
+
+    e!(create_circle,                   Circle);
+    e!(create_clip_path,                ClipPath);
+    e!(create_defs,                     NonRendering);
+    e!(create_ellipse,                  Ellipse);
+    e!(create_fe_blend,                 FeBlend);
+    e!(create_fe_color_matrix,          FeColorMatrix);
+    e!(create_fe_component_transfer,    FeComponentTransfer);
+    e!(create_fe_func_a,                FeFuncA);
+    e!(create_fe_func_b,                FeFuncB);
+    e!(create_fe_func_g,                FeFuncG);
+    e!(create_fe_func_r,                FeFuncR);
+    e!(create_fe_composite,             FeComposite);
+    e!(create_fe_convolve_matrix,       FeConvolveMatrix);
+    e!(create_fe_diffuse_lighting,      FeDiffuseLighting);
+    e!(create_fe_distant_light,         FeDistantLight);
+    e!(create_fe_displacement_map,      FeDisplacementMap);
+    e!(create_fe_flood,                 FeFlood);
+    e!(create_fe_gaussian_blur,         FeGaussianBlur);
+    e!(create_fe_image,                 FeImage);
+    e!(create_fe_merge,                 FeMerge);
+    e!(create_fe_merge_node,            FeMergeNode);
+    e!(create_fe_morphology,            FeMorphology);
+    e!(create_fe_offset,                FeOffset);
+    e!(create_fe_point_light,           FePointLight);
+    e!(create_fe_specular_lighting,     FeSpecularLighting);
+    e!(create_fe_spot_light,            FeSpotLight);
+    e!(create_fe_tile,                  FeTile);
+    e!(create_fe_turbulence,            FeTurbulence);
+    e!(create_filter,                   Filter);
+    e!(create_group,                    Group);
+    e!(create_image,                    Image);
+    e!(create_line,                     Line);
+    e!(create_linear_gradient,          LinearGradient);
+    e!(create_link,                     Link);
+    e!(create_marker,                   Marker);
+    e!(create_mask,                     Mask);
+    e!(create_non_rendering,            NonRendering);
+    e!(create_path,                     Path);
+    e!(create_pattern,                  Pattern);
+    e!(create_polygon,                  Polygon);
+    e!(create_polyline,                 Polyline);
+    e!(create_radial_gradient,          RadialGradient);
+    e!(create_rect,                     Rect);
+    e!(create_stop,                     Stop);
+    e!(create_style,                    Style);
+    e!(create_svg,                      Svg);
+    e!(create_switch,                   Switch);
+    e!(create_symbol,                   Symbol);
+    e!(create_text,                     Text);
+    e!(create_tref,                     TRef);
+    e!(create_tspan,                    TSpan);
+    e!(create_use,                      Use);
+
+    /* Hack to make multiImage sort-of work
+     *
+     * disabled for now, as markup5ever doesn't have local names for
+     * multiImage, subImage, subImageRef.  Maybe we can just... create them ourselves?
+     *
+     * Is multiImage even in SVG2?
+     */
+    /*
+    e!(create_multi_image,              Switch);
+    e!(create_sub_image,                Group);
+    e!(create_sub_image_ref,            Image);
+    */
+}
+
+use creators::*;
+
+type ElementCreateFn =
+    fn(element_name: &QualName, id: Option<&str>, class: Option<&str>) -> Element;
+
+// Lines in comments are elements that we don't support.
+#[rustfmt::skip]
+static ELEMENT_CREATORS: Lazy<HashMap<&'static str, (bool, ElementCreateFn)>> = Lazy::new(|| {
+    let creators_table: Vec<(&str, bool, ElementCreateFn)> = vec![
+        // name, supports_class, create_fn
+        ("a",                   true,  create_link),
+        /* ("altGlyph",         true,  ), */
+        /* ("altGlyphDef",      false, ), */
+        /* ("altGlyphItem",     false, ), */
+        /* ("animate",          false, ), */
+        /* ("animateColor",     false, ), */
+        /* ("animateMotion",    false, ), */
+        /* ("animateTransform", false, ), */
+        ("circle",              true,  create_circle),
+        ("clipPath",            true,  create_clip_path),
+        /* ("color-profile",    false, ), */
+        /* ("cursor",           false, ), */
+        ("defs",                true,  create_defs),
+        /* ("desc",             true,  ), */
+        ("ellipse",             true,  create_ellipse),
+        ("feBlend",             true,  create_fe_blend),
+        ("feColorMatrix",       true,  create_fe_color_matrix),
+        ("feComponentTransfer", true,  create_fe_component_transfer),
+        ("feComposite",         true,  create_fe_composite),
+        ("feConvolveMatrix",    true,  create_fe_convolve_matrix),
+        ("feDiffuseLighting",   true,  create_fe_diffuse_lighting),
+        ("feDisplacementMap",   true,  create_fe_displacement_map),
+        ("feDistantLight",      false, create_fe_distant_light),
+        ("feFuncA",             false, create_fe_func_a),
+        ("feFuncB",             false, create_fe_func_b),
+        ("feFuncG",             false, create_fe_func_g),
+        ("feFuncR",             false, create_fe_func_r),
+        ("feFlood",             true,  create_fe_flood),
+        ("feGaussianBlur",      true,  create_fe_gaussian_blur),
+        ("feImage",             true,  create_fe_image),
+        ("feMerge",             true,  create_fe_merge),
+        ("feMergeNode",         false, create_fe_merge_node),
+        ("feMorphology",        true,  create_fe_morphology),
+        ("feOffset",            true,  create_fe_offset),
+        ("fePointLight",        false, create_fe_point_light),
+        ("feSpecularLighting",  true,  create_fe_specular_lighting),
+        ("feSpotLight",         false, create_fe_spot_light),
+        ("feTile",              true,  create_fe_tile),
+        ("feTurbulence",        true,  create_fe_turbulence),
+        ("filter",              true,  create_filter),
+        /* ("font",             true,  ), */
+        /* ("font-face",        false, ), */
+        /* ("font-face-format", false, ), */
+        /* ("font-face-name",   false, ), */
+        /* ("font-face-src",    false, ), */
+        /* ("font-face-uri",    false, ), */
+        /* ("foreignObject",    true,  ), */
+        ("g",                   true,  create_group),
+        /* ("glyph",            true,  ), */
+        /* ("glyphRef",         true,  ), */
+        /* ("hkern",            false, ), */
+        ("image",               true,  create_image),
+        ("line",                true,  create_line),
+        ("linearGradient",      true,  create_linear_gradient),
+        ("marker",              true,  create_marker),
+        ("mask",                true,  create_mask),
+        /* ("metadata",         false, ), */
+        /* ("missing-glyph",    true,  ), */
+        /* ("mpath",            false, ), */
+        /* ("multiImage",       false, create_multi_image), */
+        ("path",                true,  create_path),
+        ("pattern",             true,  create_pattern),
+        ("polygon",             true,  create_polygon),
+        ("polyline",            true,  create_polyline),
+        ("radialGradient",      true,  create_radial_gradient),
+        ("rect",                true,  create_rect),
+        /* ("script",           false, ), */
+        /* ("set",              false, ), */
+        ("stop",                true,  create_stop),
+        ("style",               false, create_style),
+        /* ("subImage",         false, create_sub_image), */
+        /* ("subImageRef",      false, create_sub_image_ref), */
+        ("svg",                 true,  create_svg),
+        ("switch",              true,  create_switch),
+        ("symbol",              true,  create_symbol),
+        ("text",                true,  create_text),
+        /* ("textPath",         true,  ), */
+        /* ("title",            true,  ), */
+        ("tref",                true,  create_tref),
+        ("tspan",               true,  create_tspan),
+        ("use",                 true,  create_use),
+        /* ("view",             false, ), */
+        /* ("vkern",            false, ), */
+    ];
+
+    creators_table.into_iter().map(|(n, s, f)| (n, (s, f))).collect()
+});
+
+/// Takes an XML element name and a list of attribute/value pairs and creates an [`Element`].
+///
+/// This operation does not fail.  Unknown element names simply produce a [`NonRendering`]
+/// element.
+///
+/// [`Element`]: type.Element.html
+/// [`NonRendering`]: ../structure/struct.NonRendering.html
+pub fn create_element(name: &QualName, pbag: &PropertyBag) -> Element {
+    let mut id = None;
+    let mut class = None;
+
+    for (attr, value) in pbag.iter() {
+        match attr.expanded() {
+            expanded_name!("", "id") => id = Some(value),
+            expanded_name!("", "class") => class = Some(value),
+            _ => (),
+        }
+    }
+
+    let (supports_class, create_fn) = if name.ns == ns!(svg) {
+        match ELEMENT_CREATORS.get(name.local.as_ref()) {
+            // hack in the SVG namespace for supported element names
+            Some(&(supports_class, create_fn)) => (supports_class, create_fn),
+
+            // Whenever we encounter a element name we don't understand, represent it as a
+            // non-rendering element.  This is like a group, but it doesn't do any rendering
+            // of children.  The effect is that we will ignore all children of unknown elements.
+            None => (true, create_non_rendering as ElementCreateFn),
+        }
+    } else {
+        (true, create_non_rendering as ElementCreateFn)
+    };
+
+    if !supports_class {
+        class = None;
+    };
+
+    //    sizes::print_sizes();
+
+    create_fn(name, id, class)
+}
+
+#[cfg(ignore)]
+mod sizes {
+    //! This module is in this file just because here we have all the imports.
+
+    use super::*;
+
+    macro_rules! print_size {
+        ($ty:ty) => {
+            println!("sizeof {}: {}", stringify!($ty), mem::size_of::<$ty>());
+        };
+    }
+
+    pub fn print_sizes() {
+        use crate::properties::{ComputedValues, ParsedProperty, SpecifiedValues};
+        use std::mem;
+
+        print_size!(NodeData);
+        print_size!(SpecifiedValues);
+        print_size!(ComputedValues);
+        print_size!(ParsedProperty);
+
+        print_size!(Circle);
+        print_size!(ClipPath);
+        print_size!(NonRendering);
+        print_size!(Ellipse);
+        print_size!(FeBlend);
+        print_size!(FeColorMatrix);
+        print_size!(FeComponentTransfer);
+        print_size!(FeFuncA);
+        print_size!(FeFuncB);
+        print_size!(FeFuncG);
+        print_size!(FeFuncR);
+        print_size!(FeComposite);
+        print_size!(FeConvolveMatrix);
+        print_size!(FeDiffuseLighting);
+        print_size!(FeDistantLight);
+        print_size!(FeDisplacementMap);
+        print_size!(FeFlood);
+        print_size!(FeGaussianBlur);
+        print_size!(FeImage);
+        print_size!(FeMerge);
+        print_size!(FeMergeNode);
+        print_size!(FeMorphology);
+        print_size!(FeOffset);
+        print_size!(FePointLight);
+        print_size!(FeSpecularLighting);
+        print_size!(FeSpotLight);
+        print_size!(FeTile);
+        print_size!(FeTurbulence);
+        print_size!(Filter);
+        print_size!(Group);
+        print_size!(Image);
+        print_size!(Line);
+        print_size!(LinearGradient);
+        print_size!(Link);
+        print_size!(Marker);
+        print_size!(Mask);
+        print_size!(NonRendering);
+        print_size!(Path);
+        print_size!(Pattern);
+        print_size!(Polygon);
+        print_size!(Polyline);
+        print_size!(RadialGradient);
+        print_size!(Rect);
+        print_size!(Stop);
+        print_size!(Style);
+        print_size!(Svg);
+        print_size!(Switch);
+        print_size!(Symbol);
+        print_size!(Text);
+        print_size!(TRef);
+        print_size!(TSpan);
+        print_size!(Use);
+    }
+}
diff --git a/rsvg_internals/src/filter.rs b/rsvg_internals/src/filter.rs
index 10c692b0..b55a0f7f 100644
--- a/rsvg_internals/src/filter.rs
+++ b/rsvg_internals/src/filter.rs
@@ -5,9 +5,10 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::ValueErrorKind;
 use crate::length::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{Parse, ParseValue};
 use crate::properties::ComputedValues;
 use crate::property_bag::PropertyBag;
@@ -111,7 +112,7 @@ impl Filter {
 }
 
 impl NodeTrait for Filter {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         // Parse filterUnits first as it affects x, y, width, height checks.
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs
index 9c75522f..2d6dd16a 100755
--- a/rsvg_internals/src/filters/blend.rs
+++ b/rsvg_internals/src/filters/blend.rs
@@ -3,8 +3,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
 
@@ -55,7 +56,7 @@ impl Default for FeBlend {
 impl NodeTrait for FeBlend {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/color_matrix.rs b/rsvg_internals/src/filters/color_matrix.rs
index 7a1dd186..e1e12416 100644
--- a/rsvg_internals/src/filters/color_matrix.rs
+++ b/rsvg_internals/src/filters/color_matrix.rs
@@ -4,8 +4,9 @@ use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::number_list::{NumberList, NumberListLength};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
@@ -53,7 +54,7 @@ impl Default for FeColorMatrix {
 impl NodeTrait for FeColorMatrix {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         // First, determine the operation type.
diff --git a/rsvg_internals/src/filters/component_transfer.rs 
b/rsvg_internals/src/filters/component_transfer.rs
index f0848657..5fc339e4 100644
--- a/rsvg_internals/src/filters/component_transfer.rs
+++ b/rsvg_internals/src/filters/component_transfer.rs
@@ -5,8 +5,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
-use crate::node::{ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeBorrow, NodeTrait, RsvgNode};
 use crate::number_list::{NumberList, NumberListLength};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
@@ -37,7 +38,7 @@ impl NodeTrait for FeComponentTransfer {
     impl_node_as_filter_effect!();
 
     #[inline]
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)
     }
 }
@@ -204,7 +205,7 @@ macro_rules! func_x {
                 &mut self,
                 _parent: Option<&RsvgNode>,
                 pbag: &PropertyBag<'_>,
-            ) -> NodeResult {
+            ) -> ElementResult {
                 for (attr, value) in pbag.iter() {
                     match attr.expanded() {
                         expanded_name!("", "type") => self.function_type = attr.parse(value)?,
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index 277bf3aa..2d6225d0 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -3,8 +3,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
 
@@ -53,7 +54,7 @@ impl Default for FeComposite {
 impl NodeTrait for FeComposite {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/convolve_matrix.rs b/rsvg_internals/src/filters/convolve_matrix.rs
index f0dc251f..abe0ebe7 100644
--- a/rsvg_internals/src/filters/convolve_matrix.rs
+++ b/rsvg_internals/src/filters/convolve_matrix.rs
@@ -4,8 +4,9 @@ use nalgebra::{DMatrix, Dynamic, VecStorage};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::number_list::{NumberList, NumberListLength};
 use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
 use crate::property_bag::PropertyBag;
@@ -56,7 +57,7 @@ impl Default for FeConvolveMatrix {
 impl NodeTrait for FeConvolveMatrix {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/displacement_map.rs b/rsvg_internals/src/filters/displacement_map.rs
index 86776bc7..7add536f 100644
--- a/rsvg_internals/src/filters/displacement_map.rs
+++ b/rsvg_internals/src/filters/displacement_map.rs
@@ -3,8 +3,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
 use crate::surface_utils::{iterators::Pixels, shared_surface::ExclusiveImageSurface};
@@ -47,7 +48,7 @@ impl Default for FeDisplacementMap {
 impl NodeTrait for FeDisplacementMap {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/flood.rs b/rsvg_internals/src/filters/flood.rs
index 9886a308..34253602 100644
--- a/rsvg_internals/src/filters/flood.rs
+++ b/rsvg_internals/src/filters/flood.rs
@@ -1,6 +1,7 @@
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
+use crate::element::ElementResult;
+use crate::node::{CascadedValues, NodeTrait, RsvgNode};
 use crate::property_bag::PropertyBag;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
@@ -25,7 +26,7 @@ impl NodeTrait for FeFlood {
     impl_node_as_filter_effect!();
 
     #[inline]
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)
     }
 }
diff --git a/rsvg_internals/src/filters/gaussian_blur.rs b/rsvg_internals/src/filters/gaussian_blur.rs
index 813c6bd9..201dfccf 100644
--- a/rsvg_internals/src/filters/gaussian_blur.rs
+++ b/rsvg_internals/src/filters/gaussian_blur.rs
@@ -6,8 +6,9 @@ use nalgebra::{DMatrix, Dynamic, VecStorage};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{NumberOptionalNumber, ParseValue};
 use crate::property_bag::PropertyBag;
 use crate::rect::IRect;
@@ -44,7 +45,7 @@ impl Default for FeGaussianBlur {
 impl NodeTrait for FeGaussianBlur {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index bf9a7844..f53aa7b3 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -4,8 +4,9 @@ use crate::allowed_url::{Fragment, Href};
 use crate::aspect_ratio::AspectRatio;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
 use crate::rect::Rect;
@@ -112,7 +113,7 @@ impl FeImage {
 impl NodeTrait for FeImage {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/light/light_source.rs 
b/rsvg_internals/src/filters/light/light_source.rs
index c390e2c3..61dc1f37 100644
--- a/rsvg_internals/src/filters/light/light_source.rs
+++ b/rsvg_internals/src/filters/light/light_source.rs
@@ -2,8 +2,9 @@ use cssparser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use nalgebra::Vector3;
 
+use crate::element::ElementResult;
 use crate::filters::context::FilterContext;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
 use crate::util::clamp;
@@ -103,7 +104,7 @@ impl FeDistantLight {
 }
 
 impl NodeTrait for FeDistantLight {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "azimuth") => self.azimuth = attr.parse(value)?,
@@ -135,7 +136,7 @@ impl FePointLight {
 }
 
 impl NodeTrait for FePointLight {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
@@ -184,7 +185,7 @@ impl FeSpotLight {
 }
 
 impl NodeTrait for FeSpotLight {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
diff --git a/rsvg_internals/src/filters/light/lighting.rs b/rsvg_internals/src/filters/light/lighting.rs
index 071d0067..283029fb 100644
--- a/rsvg_internals/src/filters/light/lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -7,6 +7,7 @@ use rayon::prelude::*;
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
 use crate::filters::{
     context::{FilterContext, FilterOutput, FilterResult},
@@ -18,7 +19,7 @@ use crate::filters::{
     },
     FilterEffect, FilterError, PrimitiveWithInput,
 };
-use crate::node::{CascadedValues, ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeBorrow, NodeTrait, RsvgNode};
 use crate::parsers::{NumberOptionalNumber, ParseValue};
 use crate::property_bag::PropertyBag;
 use crate::surface_utils::{
@@ -48,7 +49,7 @@ impl Common {
         }
     }
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
@@ -95,7 +96,7 @@ impl Default for FeDiffuseLighting {
 impl NodeTrait for FeDiffuseLighting {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
@@ -164,7 +165,7 @@ impl Default for FeSpecularLighting {
 impl NodeTrait for FeSpecularLighting {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs
index d1949bd3..4c7cb34b 100644
--- a/rsvg_internals/src/filters/merge.rs
+++ b/rsvg_internals/src/filters/merge.rs
@@ -2,7 +2,8 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::node::{ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::element::{ElementResult, ElementType};
+use crate::node::{NodeBorrow, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
 use crate::rect::IRect;
@@ -37,14 +38,14 @@ impl NodeTrait for FeMerge {
     impl_node_as_filter_effect!();
 
     #[inline]
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)
     }
 }
 
 impl NodeTrait for FeMergeNode {
     #[inline]
-    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "in") => self.in_ = Some(attr.parse(value)?),
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index 61d93fa6..ca5e8f37 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -9,10 +9,11 @@ use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::{ElementResult, ElementType};
 use crate::error::{RenderingError, ValueErrorKind};
 use crate::filter::Filter;
 use crate::length::*;
-use crate::node::{CascadedValues, ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeBorrow, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::properties::ComputedValues;
 use crate::property_bag::PropertyBag;
@@ -113,7 +114,7 @@ impl Primitive {
 }
 
 impl NodeTrait for Primitive {
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         // With ObjectBoundingBox, only fractions and percents are allowed.
         let primitiveunits = parent
             .and_then(|parent| {
@@ -218,7 +219,7 @@ impl PrimitiveWithInput {
 }
 
 impl NodeTrait for PrimitiveWithInput {
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/morphology.rs b/rsvg_internals/src/filters/morphology.rs
index 719bbc1d..b229730c 100644
--- a/rsvg_internals/src/filters/morphology.rs
+++ b/rsvg_internals/src/filters/morphology.rs
@@ -5,8 +5,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
 use crate::property_bag::PropertyBag;
 use crate::rect::IRect;
@@ -47,7 +48,7 @@ impl Default for FeMorphology {
 impl NodeTrait for FeMorphology {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs
index 0604d9ae..0ff30d7c 100644
--- a/rsvg_internals/src/filters/offset.rs
+++ b/rsvg_internals/src/filters/offset.rs
@@ -2,7 +2,8 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::element::ElementResult;
+use crate::node::{NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
 
@@ -31,7 +32,7 @@ impl Default for FeOffset {
 impl NodeTrait for FeOffset {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/filters/tile.rs b/rsvg_internals/src/filters/tile.rs
index c8dbfdc4..1670e28e 100644
--- a/rsvg_internals/src/filters/tile.rs
+++ b/rsvg_internals/src/filters/tile.rs
@@ -1,6 +1,7 @@
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::element::ElementResult;
+use crate::node::{NodeTrait, RsvgNode};
 use crate::property_bag::PropertyBag;
 
 use super::context::{FilterContext, FilterInput, FilterOutput, FilterResult};
@@ -24,7 +25,7 @@ impl Default for FeTile {
 impl NodeTrait for FeTile {
     impl_node_as_filter_effect!();
 
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)
     }
 }
diff --git a/rsvg_internals/src/filters/turbulence.rs b/rsvg_internals/src/filters/turbulence.rs
index d7fa55eb..2d359ebe 100644
--- a/rsvg_internals/src/filters/turbulence.rs
+++ b/rsvg_internals/src/filters/turbulence.rs
@@ -3,8 +3,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeTrait, RsvgNode};
 use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
 use crate::property_bag::PropertyBag;
 use crate::surface_utils::{
@@ -59,7 +60,7 @@ impl NodeTrait for FeTurbulence {
     impl_node_as_filter_effect!();
 
     #[inline]
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(parent, pbag)?;
 
         for (attr, value) in pbag.iter() {
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index ff08efdf..2d085948 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -9,9 +9,10 @@ use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNode, AcquiredNodes, NodeStack};
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
 use crate::length::*;
-use crate::node::{CascadedValues, ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeBorrow, NodeTrait, RsvgNode};
 use crate::paint_server::{AsPaintSource, PaintSource};
 use crate::parsers::{Parse, ParseValue};
 use crate::properties::ComputedValues;
@@ -129,7 +130,7 @@ fn validate_offset(length: Length<Both>) -> Result<Length<Both>, ValueErrorKind>
 }
 
 impl NodeTrait for Stop {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "offset") => {
@@ -575,7 +576,7 @@ impl_get_unresolved!(LinearGradient);
 impl_get_unresolved!(RadialGradient);
 
 impl Common {
-    fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "gradientUnits") => self.units = Some(attr.parse(value)?),
@@ -595,7 +596,7 @@ impl Common {
 }
 
 impl NodeTrait for LinearGradient {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(pbag)?;
 
         for (attr, value) in pbag.iter() {
@@ -614,7 +615,7 @@ impl NodeTrait for LinearGradient {
 }
 
 impl NodeTrait for RadialGradient {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(pbag)?;
 
         for (attr, value) in pbag.iter() {
@@ -813,8 +814,9 @@ fn acquire_gradient(
 mod tests {
     use super::*;
     use crate::float_eq_cairo::ApproxEqCairo;
-    use crate::node::{ElementType, NodeData, RsvgNode};
+    use crate::node::{NodeData, RsvgNode};
     use markup5ever::{namespace_url, ns, QualName};
+    use std::ptr;
 
     #[test]
     fn parses_spread_method() {
@@ -846,12 +848,11 @@ mod tests {
 
     #[test]
     fn gradient_resolved_from_defaults_is_really_resolved() {
+        let bag = unsafe { PropertyBag::new_from_xml2_attributes(0, ptr::null()) };
+
         let node = RsvgNode::new(NodeData::new_element(
-            ElementType::LinearGradient,
             &QualName::new(None, ns!(svg), local_name!("linearGradient")),
-            None,
-            None,
-            Box::new(LinearGradient::default()),
+            &bag,
         ));
 
         let borrow = node.borrow_element();
@@ -861,11 +862,8 @@ mod tests {
         assert!(gradient.is_resolved());
 
         let node = RsvgNode::new(NodeData::new_element(
-            ElementType::RadialGradient,
             &QualName::new(None, ns!(svg), local_name!("radialGradient")),
-            None,
-            None,
-            Box::new(RadialGradient::default()),
+            &bag,
         ));
 
         let borrow = node.borrow_element();
diff --git a/rsvg_internals/src/image.rs b/rsvg_internals/src/image.rs
index 340b6bda..14c5fa5b 100644
--- a/rsvg_internals/src/image.rs
+++ b/rsvg_internals/src/image.rs
@@ -7,6 +7,7 @@ use crate::aspect_ratio::AspectRatio;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
+use crate::element::ElementResult;
 use crate::error::*;
 use crate::length::*;
 use crate::node::*;
@@ -27,7 +28,7 @@ pub struct Image {
 }
 
 impl NodeTrait for Image {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 7f589e5c..0939d062 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -86,12 +86,12 @@ mod aspect_ratio;
 mod bbox;
 mod color;
 mod cond;
-mod create_node;
 mod css;
 mod dasharray;
 mod document;
 mod dpi;
 mod drawing_ctx;
+mod element;
 mod error;
 mod filter;
 pub mod filters;
diff --git a/rsvg_internals/src/marker.rs b/rsvg_internals/src/marker.rs
index 55065077..6f4ca74a 100644
--- a/rsvg_internals/src/marker.rs
+++ b/rsvg_internals/src/marker.rs
@@ -12,6 +12,7 @@ use crate::aspect_ratio::*;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::iri::IRI;
@@ -175,7 +176,7 @@ impl Marker {
 }
 
 impl NodeTrait for Marker {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "markerUnits") => self.units = attr.parse(value)?,
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index 009dbb82..668d7d89 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -5,33 +5,26 @@
 //! Librsvg puts a [`NodeData`] as the type parameter of [`Node`].  For convenience,
 //! librsvg has a type alias `RsvgNode = Node<NodeData>`.
 //!
-//! Nodes are not constructed directly by callers; this is done in the [`create_node`] module.
+//! Nodes are not constructed directly by callers;
 //!
 //! [rctree]: ../../rctree/index.html
 //! [`Node`]: ../../rctree/struct.Node.html
 //! [`NodeData`]: struct.NodeData.html
-//! [`create_node`]: ../create_node/index.html
 
 use downcast_rs::*;
-use locale_config::Locale;
-use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
+use markup5ever::QualName;
 use std::cell::{Ref, RefMut};
-use std::collections::HashSet;
 use std::fmt;
 
 use crate::bbox::BoundingBox;
-use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
-use crate::css::Declaration;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::*;
 use crate::error::*;
 use crate::filters::FilterEffect;
-use crate::parsers::Parse;
-use crate::properties::{ComputedValues, SpecifiedValue, SpecifiedValues};
+use crate::properties::{ComputedValues, SpecifiedValues};
 use crate::property_bag::PropertyBag;
-use crate::property_defs::Overflow;
 use crate::text::NodeChars;
-use crate::transform::Transform;
 
 /// Strong reference to an element in the SVG tree.
 ///
@@ -82,44 +75,9 @@ pub enum NodeData {
     Text(NodeChars),
 }
 
-/// Contents of an element node in the DOM
-pub struct Element {
-    element_type: ElementType,
-    element_name: QualName,
-    id: Option<String>,    // id attribute from XML element
-    class: Option<String>, // class attribute from XML element
-    specified_values: SpecifiedValues,
-    important_styles: HashSet<QualName>,
-    result: NodeResult,
-    transform: Transform,
-    values: ComputedValues,
-    cond: bool,
-    style_attr: String,
-    node_impl: Box<dyn NodeTrait>,
-}
-
 impl NodeData {
-    pub fn new_element(
-        element_type: ElementType,
-        element_name: &QualName,
-        id: Option<&str>,
-        class: Option<&str>,
-        node_impl: Box<dyn NodeTrait>,
-    ) -> NodeData {
-        NodeData::Element(Box::new(Element {
-            element_type,
-            element_name: element_name.clone(),
-            id: id.map(str::to_string),
-            class: class.map(str::to_string),
-            specified_values: Default::default(),
-            important_styles: Default::default(),
-            transform: Default::default(),
-            result: Ok(()),
-            values: ComputedValues::default(),
-            cond: true,
-            style_attr: String::new(),
-            node_impl,
-        }))
+    pub fn new_element(name: &QualName, pbag: &PropertyBag) -> NodeData {
+        NodeData::Element(Box::new(create_element(name, pbag)))
     }
 
     pub fn new_chars() -> NodeData {
@@ -127,184 +85,6 @@ impl NodeData {
     }
 }
 
-impl Element {
-    pub fn get_type(&self) -> ElementType {
-        self.element_type
-    }
-
-    pub fn get_node_trait(&self) -> &dyn NodeTrait {
-        self.node_impl.as_ref()
-    }
-
-    pub fn get_impl<T: NodeTrait>(&self) -> &T {
-        if let Some(t) = (&self.node_impl).downcast_ref::<T>() {
-            t
-        } else {
-            panic!("could not downcast");
-        }
-    }
-
-    pub fn element_name(&self) -> &QualName {
-        &self.element_name
-    }
-
-    pub fn get_id(&self) -> Option<&str> {
-        self.id.as_ref().map(String::as_str)
-    }
-
-    pub fn get_class(&self) -> Option<&str> {
-        self.class.as_ref().map(String::as_str)
-    }
-
-    pub fn get_cond(&self) -> bool {
-        self.cond
-    }
-
-    pub fn get_transform(&self) -> Transform {
-        self.transform
-    }
-
-    pub fn is_overflow(&self) -> bool {
-        self.specified_values.is_overflow()
-    }
-
-    pub fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>, locale: &Locale) {
-        if self.node_impl.overflow_hidden() {
-            self.specified_values.overflow = SpecifiedValue::Specified(Overflow::Hidden);
-        }
-
-        self.save_style_attribute(pbag);
-
-        if let Err(e) = self
-            .set_transform_attribute(pbag)
-            .and_then(|_| self.set_conditional_processing_attributes(pbag, locale))
-            .and_then(|_| self.node_impl.set_atts(parent, pbag))
-            .and_then(|_| self.set_presentation_attributes(pbag))
-        {
-            self.set_error(e);
-        }
-
-        self.node_impl
-            .set_overridden_properties(&mut self.specified_values);
-    }
-
-    fn save_style_attribute(&mut self, pbag: &PropertyBag<'_>) {
-        for (attr, value) in pbag.iter() {
-            match attr.expanded() {
-                expanded_name!("", "style") => self.style_attr.push_str(value),
-                _ => (),
-            }
-        }
-    }
-
-    fn set_transform_attribute(&mut self, pbag: &PropertyBag<'_>) -> Result<(), NodeError> {
-        for (attr, value) in pbag.iter() {
-            match attr.expanded() {
-                expanded_name!("", "transform") => {
-                    return Transform::parse_str(value)
-                        .attribute(attr)
-                        .and_then(|affine| {
-                            self.transform = affine;
-                            Ok(())
-                        });
-                }
-                _ => (),
-            }
-        }
-
-        Ok(())
-    }
-
-    fn set_conditional_processing_attributes(
-        &mut self,
-        pbag: &PropertyBag<'_>,
-        locale: &Locale,
-    ) -> Result<(), NodeError> {
-        let mut cond = self.cond;
-
-        for (attr, value) in pbag.iter() {
-            let mut parse = || -> Result<_, ValueErrorKind> {
-                match attr.expanded() {
-                    expanded_name!("", "requiredExtensions") if cond => {
-                        cond = RequiredExtensions::from_attribute(value)
-                            .map(|RequiredExtensions(res)| res)?;
-                    }
-
-                    expanded_name!("", "requiredFeatures") if cond => {
-                        cond = RequiredFeatures::from_attribute(value)
-                            .map(|RequiredFeatures(res)| res)?;
-                    }
-
-                    expanded_name!("", "systemLanguage") if cond => {
-                        cond = SystemLanguage::from_attribute(value, locale)
-                            .map(|SystemLanguage(res)| res)?;
-                    }
-
-                    _ => {}
-                }
-
-                Ok(cond)
-            };
-
-            parse().map(|c| self.cond = c).attribute(attr)?;
-        }
-
-        Ok(())
-    }
-
-    /// Hands the pbag to the node's state, to apply the presentation attributes
-    fn set_presentation_attributes(&mut self, pbag: &PropertyBag<'_>) -> Result<(), NodeError> {
-        match self.specified_values.parse_presentation_attributes(pbag) {
-            Ok(_) => Ok(()),
-            Err(e) => {
-                // FIXME: we'll ignore errors here for now.
-                //
-                // If we set the node to be in error, we expose buggy handling of the
-                // enable-background property; we are not parsing it correctly. This
-                // causes tests/fixtures/reftests/bugs/587721-text-transform.svg to fail
-                // because it has enable-background="new 0 0 1179.75118 687.74173" in the
-                // toplevel svg element.
-                //
-                //   self.set_error(e);
-                //   return;
-
-                rsvg_log!("(attribute error: {})", e);
-                Ok(())
-            }
-        }
-    }
-
-    // Applies a style declaration to the node's specified_values
-    pub fn apply_style_declaration(&mut self, declaration: &Declaration) {
-        self.specified_values
-            .set_property_from_declaration(declaration, &mut self.important_styles);
-    }
-
-    /// Applies CSS styles from the saved value of the "style" attribute
-    pub fn set_style_attribute(&mut self) {
-        if !self.style_attr.is_empty() {
-            if let Err(e) = self
-                .specified_values
-                .parse_style_declarations(self.style_attr.as_str(), &mut self.important_styles)
-            {
-                self.set_error(e);
-            }
-
-            self.style_attr.clear();
-            self.style_attr.shrink_to_fit();
-        }
-    }
-
-    fn set_error(&mut self, error: NodeError) {
-        rsvg_log!("setting node {} in error: {}", self, error);
-        self.result = Err(error);
-    }
-
-    pub fn is_in_error(&self) -> bool {
-        self.result.is_err()
-    }
-}
-
 impl fmt::Display for NodeData {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
@@ -320,14 +100,6 @@ impl fmt::Display for NodeData {
     }
 }
 
-impl fmt::Display for Element {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self.get_type())?;
-        write!(f, " id={}", self.get_id().unwrap_or("None"))?;
-        Ok(())
-    }
-}
-
 /// Can obtain computed values from a node
 ///
 /// In our tree of SVG elements (Node in our parlance), each node stores a `ComputedValues` that
@@ -382,7 +154,7 @@ impl<'a> CascadedValues<'a> {
     pub fn new_from_values(node: &'a RsvgNode, values: &ComputedValues) -> CascadedValues<'a> {
         let mut v = values.clone();
         node.borrow_element()
-            .specified_values
+            .get_specified_values()
             .to_computed_values(&mut v);
 
         CascadedValues {
@@ -396,7 +168,7 @@ impl<'a> CascadedValues<'a> {
     /// `ComputedValues` from the `CascadedValues` that got passed to `draw()`.
     pub fn get(&'a self) -> &'a ComputedValues {
         match self.inner {
-            CascadedInner::FromNode(ref element) => &element.values,
+            CascadedInner::FromNode(ref e) => e.get_computed_values(),
             CascadedInner::FromValues(ref v) => v,
         }
     }
@@ -407,7 +179,7 @@ pub trait NodeTrait: Downcast {
     /// Sets per-node attributes from the `pbag`
     ///
     /// Each node is supposed to iterate the `pbag`, and parse any attributes it needs.
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult;
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult;
 
     /// Sets any special-cased properties that the node may have, that are different
     /// from defaults in the node's `SpecifiedValues`.
@@ -439,85 +211,6 @@ pub trait NodeTrait: Downcast {
 
 impl_downcast!(NodeTrait);
 
-// After creating/parsing a Node, it will be in a success or an error state.
-// We represent this with a Result, aliased as a NodeResult.  There is no
-// extra information for the Ok case; all the interesting stuff is in the
-// Err case.
-//
-// https://www.w3.org/TR/SVG/implnote.html#ErrorProcessing
-//
-// When an element has an error during parsing, the SVG spec calls the element
-// to be "in error".  We skip rendering of elements that are in error.
-//
-// When we parse an element's attributes, we stop as soon as we
-// encounter the first error:  a parse error, or an invalid value,
-// etc.  No further attributes will be processed, although note that
-// the order in which an element's attributes are processed is not
-// defined.
-//
-// Alternatively, we could try to parse/validate all the attributes
-// that come in an element and build up a Vec<NodeError>.  However, we
-// don't do this now.  Doing that may be more useful for an SVG
-// validator, not a renderer like librsvg is.
-pub type NodeResult = Result<(), NodeError>;
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum ElementType {
-    Circle,
-    ClipPath,
-    Ellipse,
-    Filter,
-    Group,
-    Image,
-    Line,
-    LinearGradient,
-    Link,
-    Marker,
-    Mask,
-    NonRendering,
-    Path,
-    Pattern,
-    Polygon,
-    Polyline,
-    RadialGradient,
-    Rect,
-    Stop,
-    Style,
-    Svg,
-    Switch,
-    Symbol,
-    Text,
-    TRef,
-    TSpan,
-    Use,
-
-    // Filter primitives, these start with "Fe" as element names are e.g. "feBlend"
-    FeBlend,
-    FeColorMatrix,
-    FeComponentTransfer,
-    FeComposite,
-    FeConvolveMatrix,
-    FeDiffuseLighting,
-    FeDisplacementMap,
-    FeDistantLight,
-    FeFlood,
-    FeFuncA,
-    FeFuncB,
-    FeFuncG,
-    FeFuncR,
-    FeGaussianBlur,
-    FeImage,
-    FeMerge,
-    FeMergeNode,
-    FeMorphology,
-    FeOffset,
-    FePointLight,
-    FeSpecularLighting,
-    FeSpotLight,
-    FeTile,
-    FeTurbulence,
-}
-
 /// Helper trait to get different NodeData variants
 pub trait NodeBorrow {
     /// Returns `false` for NodeData::Text, `true` otherwise.
@@ -591,8 +284,8 @@ impl NodeCascade for RsvgNode {
         {
             let mut elt = self.borrow_element_mut();
 
-            elt.specified_values.to_computed_values(&mut values);
-            elt.values = values.clone();
+            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()) {
diff --git a/rsvg_internals/src/pattern.rs b/rsvg_internals/src/pattern.rs
index 24ff7992..7525ebff 100644
--- a/rsvg_internals/src/pattern.rs
+++ b/rsvg_internals/src/pattern.rs
@@ -10,6 +10,7 @@ use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNodes, NodeStack};
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::length::*;
@@ -118,7 +119,7 @@ pub struct Pattern {
 }
 
 impl NodeTrait for Pattern {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "patternUnits") => self.common.units = Some(attr.parse(value)?),
@@ -562,17 +563,17 @@ impl Pattern {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::node::{ElementType, NodeData, RsvgNode};
+    use crate::node::{NodeData, RsvgNode};
     use markup5ever::{namespace_url, ns, QualName};
+    use std::ptr;
 
     #[test]
     fn pattern_resolved_from_defaults_is_really_resolved() {
+        let bag = unsafe { PropertyBag::new_from_xml2_attributes(0, ptr::null()) };
+
         let node = RsvgNode::new(NodeData::new_element(
-            ElementType::Pattern,
             &QualName::new(None, ns!(svg), local_name!("pattern")),
-            None,
-            None,
-            Box::new(Pattern::default()),
+            &bag,
         ));
 
         let borrow = node.borrow_element();
diff --git a/rsvg_internals/src/shapes.rs b/rsvg_internals/src/shapes.rs
index ea0c5888..d1fb0330 100644
--- a/rsvg_internals/src/shapes.rs
+++ b/rsvg_internals/src/shapes.rs
@@ -8,6 +8,7 @@ use std::rc::Rc;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::ElementResult;
 use crate::error::*;
 use crate::length::*;
 use crate::node::*;
@@ -115,7 +116,7 @@ pub struct Path {
 }
 
 impl NodeTrait for Path {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             if attr.expanded() == expanded_name!("", "d") {
                 let mut builder = PathBuilder::new();
@@ -219,7 +220,7 @@ pub struct Polygon {
 }
 
 impl NodeTrait for Polygon {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             if attr.expanded() == expanded_name!("", "points") {
                 self.points = attr.parse(value).map(Some)?;
@@ -254,7 +255,7 @@ pub struct Polyline {
 }
 
 impl NodeTrait for Polyline {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             if attr.expanded() == expanded_name!("", "points") {
                 self.points = attr.parse(value).map(Some)?;
@@ -290,7 +291,7 @@ pub struct Line {
 }
 
 impl NodeTrait for Line {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x1") => self.x1 = attr.parse(value)?,
@@ -352,7 +353,7 @@ pub struct Rect {
 }
 
 impl NodeTrait for Rect {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
@@ -580,7 +581,7 @@ pub struct Circle {
 }
 
 impl NodeTrait for Circle {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "cx") => self.cx = attr.parse(value)?,
@@ -633,7 +634,7 @@ pub struct Ellipse {
 }
 
 impl NodeTrait for Ellipse {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "cx") => self.cx = attr.parse(value)?,
diff --git a/rsvg_internals/src/structure.rs b/rsvg_internals/src/structure.rs
index e87992c5..06ae97d0 100644
--- a/rsvg_internals/src/structure.rs
+++ b/rsvg_internals/src/structure.rs
@@ -9,6 +9,7 @@ use crate::coord_units::CoordUnits;
 use crate::document::AcquiredNodes;
 use crate::dpi::Dpi;
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
+use crate::element::ElementResult;
 use crate::error::*;
 use crate::length::*;
 use crate::node::*;
@@ -22,7 +23,7 @@ use crate::viewbox::*;
 pub struct Group();
 
 impl NodeTrait for Group {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> ElementResult {
         Ok(())
     }
 
@@ -50,7 +51,7 @@ impl NodeTrait for Group {
 pub struct NonRendering;
 
 impl NodeTrait for NonRendering {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> ElementResult {
         Ok(())
     }
 }
@@ -59,7 +60,7 @@ impl NodeTrait for NonRendering {
 pub struct Switch();
 
 impl NodeTrait for Switch {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, _: &PropertyBag<'_>) -> ElementResult {
         Ok(())
     }
 
@@ -183,7 +184,7 @@ impl Svg {
 }
 
 impl NodeTrait for Svg {
-    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         // x & y attributes have no effect on outermost svg
         // http://www.w3.org/TR/SVG/struct.html#SVGElement
         let is_inner_svg = parent.is_some();
@@ -308,7 +309,7 @@ impl Use {
 }
 
 impl NodeTrait for Use {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!(xlink "href") => {
@@ -362,7 +363,7 @@ impl Symbol {
 }
 
 impl NodeTrait for Symbol {
-    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "preserveAspectRatio") => {
@@ -395,7 +396,7 @@ impl ClipPath {
 }
 
 impl NodeTrait for ClipPath {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "clipPathUnits") => self.units = attr.parse(value)?,
@@ -455,7 +456,7 @@ impl Mask {
 }
 
 impl NodeTrait for Mask {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
@@ -484,7 +485,7 @@ pub struct Link {
 }
 
 impl NodeTrait for Link {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!(xlink "href") => self.link = Some(value.to_owned()),
diff --git a/rsvg_internals/src/style.rs b/rsvg_internals/src/style.rs
index 2d73afba..9f7d3172 100644
--- a/rsvg_internals/src/style.rs
+++ b/rsvg_internals/src/style.rs
@@ -2,8 +2,9 @@
 
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::element::ElementResult;
 use crate::error::*;
-use crate::node::{NodeResult, NodeTrait, RsvgNode};
+use crate::node::{NodeTrait, RsvgNode};
 use crate::property_bag::PropertyBag;
 
 /// Represents the syntax used in the <style> node.
@@ -51,7 +52,7 @@ impl Style {
 }
 
 impl NodeTrait for Style {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             if attr.expanded() == expanded_name!("", "type") {
                 self.type_ = Some(StyleType::parse(value).attribute(attr)?);
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 3db1bc34..da8c2b99 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -8,11 +8,12 @@ use crate::allowed_url::Fragment;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::{ElementResult, ElementType};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::font_props::FontWeightSpec;
 use crate::length::*;
-use crate::node::{CascadedValues, ElementType, NodeBorrow, NodeResult, NodeTrait, RsvgNode};
+use crate::node::{CascadedValues, NodeBorrow, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::properties::ComputedValues;
 use crate::property_bag::PropertyBag;
@@ -604,7 +605,7 @@ impl Text {
 }
 
 impl NodeTrait for Text {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value)?,
@@ -723,7 +724,7 @@ fn extract_chars_children_to_chunks_recursively(
 }
 
 impl NodeTrait for TRef {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!(xlink "href") => {
@@ -775,7 +776,7 @@ impl TSpan {
 }
 
 impl NodeTrait for TSpan {
-    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> ElementResult {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "x") => self.x = attr.parse(value).map(Some)?,
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index 91c1a7fa..077a8748 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -13,10 +13,11 @@ use std::str;
 
 use crate::allowed_url::AllowedUrl;
 use crate::document::{Document, DocumentBuilder};
+use crate::element::ElementType;
 use crate::error::LoadingError;
 use crate::io::{self, get_input_stream_for_loading};
 use crate::limits::MAX_LOADED_ELEMENTS;
-use crate::node::{ElementType, NodeBorrow, RsvgNode};
+use crate::node::{NodeBorrow, RsvgNode};
 use crate::property_bag::PropertyBag;
 use crate::style::{Style, StyleType};
 use crate::xml2_load::Xml2Parser;


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