[librsvg: 3/5] Use LocalName and local_name! for attributes and properties



commit 1dee83d1dfdd0ef1497cb0f40e76dc4674bb5eab
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue May 14 19:58:57 2019 -0500

    Use LocalName and local_name! for attributes and properties

 NEWS                                             |   5 +
 librsvg_crate/src/lib.rs                         |   8 +-
 librsvg_crate/tests/primitives.rs                |  20 +++-
 librsvg_crate/tests/render_to_viewport.rs        |   6 +-
 librsvg_crate/tests/utils/mod.rs                 |   8 +-
 rsvg_internals/src/aspect_ratio.rs               |   4 +-
 rsvg_internals/src/c_api.rs                      |   9 +-
 rsvg_internals/src/clip_path.rs                  |   3 +-
 rsvg_internals/src/create_node.rs                |   5 +-
 rsvg_internals/src/css.rs                        |  76 ++++++-------
 rsvg_internals/src/drawing_ctx.rs                |   8 +-
 rsvg_internals/src/error.rs                      |  28 ++---
 rsvg_internals/src/filters/blend.rs              |   8 +-
 rsvg_internals/src/filters/color_matrix.rs       |  13 ++-
 rsvg_internals/src/filters/component_transfer.rs |  18 ++--
 rsvg_internals/src/filters/composite.rs          |  15 ++-
 rsvg_internals/src/filters/convolve_matrix.rs    |  38 +++----
 rsvg_internals/src/filters/displacement_map.rs   |  14 +--
 rsvg_internals/src/filters/gaussian_blur.rs      |   5 +-
 rsvg_internals/src/filters/image.rs              |   5 +-
 rsvg_internals/src/filters/input.rs              |   5 +-
 rsvg_internals/src/filters/light/light_source.rs |  27 +++--
 rsvg_internals/src/filters/light/lighting.rs     |  21 ++--
 rsvg_internals/src/filters/merge.rs              |   5 +-
 rsvg_internals/src/filters/mod.rs                |  13 ++-
 rsvg_internals/src/filters/morphology.rs         |  10 +-
 rsvg_internals/src/filters/node.rs               |  13 ++-
 rsvg_internals/src/filters/offset.rs             |   5 +-
 rsvg_internals/src/filters/turbulence.rs         |  22 ++--
 rsvg_internals/src/gradient.rs                   |  32 +++---
 rsvg_internals/src/image.rs                      |  16 +--
 rsvg_internals/src/io.rs                         |   2 +-
 rsvg_internals/src/iri.rs                        |   5 +-
 rsvg_internals/src/length.rs                     |   5 +-
 rsvg_internals/src/lib.rs                        |   4 +
 rsvg_internals/src/link.rs                       |   3 +-
 rsvg_internals/src/marker.rs                     |  22 ++--
 rsvg_internals/src/mask.rs                       |  17 +--
 rsvg_internals/src/node.rs                       |  16 +--
 rsvg_internals/src/paint_server.rs               |  10 +-
 rsvg_internals/src/parsers.rs                    |   8 +-
 rsvg_internals/src/path_builder.rs               |   8 +-
 rsvg_internals/src/pattern.rs                    |  23 ++--
 rsvg_internals/src/pixbuf_utils.rs               |   4 +-
 rsvg_internals/src/properties.rs                 | 132 +++++++++++------------
 rsvg_internals/src/property_bag.rs               |  23 ++--
 rsvg_internals/src/shapes.rs                     |  40 ++++---
 rsvg_internals/src/stop.rs                       |   3 +-
 rsvg_internals/src/structure.rs                  |  31 +++---
 rsvg_internals/src/style.rs                      |   3 +-
 rsvg_internals/src/text.rs                       |  24 ++---
 rsvg_internals/src/xml.rs                        |   8 +-
 52 files changed, 439 insertions(+), 417 deletions(-)
---
diff --git a/NEWS b/NEWS
index 0748079b..32573d32 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+Version 2.45.7
+- The poly element no longer supports "verts" as an alias for the
+  "points" attribute.  The "verts" name was only used in SVG pre-1.0,
+  and we had been cargo-culting that name ever since.
+
 Version 2.45.6
 - Librsvg now requires Rust 1.30.0 or later.
 
diff --git a/librsvg_crate/src/lib.rs b/librsvg_crate/src/lib.rs
index e3e4b226..ee94f610 100644
--- a/librsvg_crate/src/lib.rs
+++ b/librsvg_crate/src/lib.rs
@@ -100,7 +100,13 @@ use gio::{Cancellable, FileExt};
 use rsvg_internals::{Dpi, Handle};
 
 pub use rsvg_internals::{
-    DefsLookupErrorKind, HrefError, Length, LengthUnit, LoadOptions, LoadingError, RenderingError,
+    DefsLookupErrorKind,
+    HrefError,
+    Length,
+    LengthUnit,
+    LoadOptions,
+    LoadingError,
+    RenderingError,
 };
 
 /// Struct for loading an [`SvgHandle`][SvgHandle].
diff --git a/librsvg_crate/tests/primitives.rs b/librsvg_crate/tests/primitives.rs
index f5d289b0..4f4a270a 100644
--- a/librsvg_crate/tests/primitives.rs
+++ b/librsvg_crate/tests/primitives.rs
@@ -4,7 +4,7 @@ mod utils;
 
 use rsvg_internals::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
 
-use self::utils::{load_svg, render_to_viewport, compare_to_surface, SurfaceSize};
+use self::utils::{compare_to_surface, load_svg, render_to_viewport, SurfaceSize};
 
 #[test]
 fn simple_opacity_with_transform() {
@@ -44,7 +44,11 @@ fn simple_opacity_with_transform() {
 
     let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
 
-    compare_to_surface(&output_surf, &reference_surf, "simple_opacity_with_transform");
+    compare_to_surface(
+        &output_surf,
+        &reference_surf,
+        "simple_opacity_with_transform",
+    );
 }
 
 #[test]
@@ -85,7 +89,11 @@ fn simple_opacity_with_offset_viewport() {
 
     let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
 
-    compare_to_surface(&output_surf, &reference_surf, "simple_opacity_with_offset_viewport");
+    compare_to_surface(
+        &output_surf,
+        &reference_surf,
+        "simple_opacity_with_offset_viewport",
+    );
 }
 
 #[test]
@@ -126,7 +134,11 @@ fn opacity_inside_transformed_group() {
 
     let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
 
-    compare_to_surface(&output_surf, &reference_surf, "opacity_inside_transformed_group");
+    compare_to_surface(
+        &output_surf,
+        &reference_surf,
+        "opacity_inside_transformed_group",
+    );
 }
 
 #[test]
diff --git a/librsvg_crate/tests/render_to_viewport.rs b/librsvg_crate/tests/render_to_viewport.rs
index dc011b82..518a90ef 100644
--- a/librsvg_crate/tests/render_to_viewport.rs
+++ b/librsvg_crate/tests/render_to_viewport.rs
@@ -87,11 +87,7 @@ fn render_to_offsetted_viewport() {
 
     let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
 
-    compare_to_surface(
-        &output_surf,
-        &reference_surf,
-        "render_to_offseted_viewport",
-    );
+    compare_to_surface(&output_surf, &reference_surf, "render_to_offseted_viewport");
 }
 
 #[test]
diff --git a/librsvg_crate/tests/utils/mod.rs b/librsvg_crate/tests/utils/mod.rs
index 17cfcd23..51d4abd9 100644
--- a/librsvg_crate/tests/utils/mod.rs
+++ b/librsvg_crate/tests/utils/mod.rs
@@ -22,9 +22,7 @@ pub fn load_svg(input: &'static [u8]) -> SvgHandle {
     let bytes = glib::Bytes::from_static(input);
     let stream = gio::MemoryInputStream::new_from_bytes(&bytes);
 
-    Loader::new()
-        .read_stream(&stream, None, None)
-        .unwrap()
+    Loader::new().read_stream(&stream, None, None).unwrap()
 }
 
 #[derive(Copy, Clone)]
@@ -146,9 +144,7 @@ pub fn compare_to_surface(
             if diff.num_pixels_changed != 0 && diff.max_diff > MAX_DIFF {
                 println!(
                     "{}: {} pixels changed with maximum difference of {}",
-                    output_base_name,
-                    diff.num_pixels_changed,
-                    diff.max_diff,
+                    output_base_name, diff.num_pixels_changed, diff.max_diff,
                 );
                 unreachable!("surfaces are too different");
             }
diff --git a/rsvg_internals/src/aspect_ratio.rs b/rsvg_internals/src/aspect_ratio.rs
index 7795f821..f26e219f 100644
--- a/rsvg_internals/src/aspect_ratio.rs
+++ b/rsvg_internals/src/aspect_ratio.rs
@@ -222,7 +222,9 @@ impl Parse for AspectRatio {
     type Err = ValueErrorKind;
 
     fn parse(parser: &mut Parser<'_, '_>) -> Result<AspectRatio, ValueErrorKind> {
-        let defer = parser.try_parse(|p| p.expect_ident_matching("defer")).is_ok();
+        let defer = parser
+            .try_parse(|p| p.expect_ident_matching("defer"))
+            .is_ok();
 
         let align_xy = parser.try_parse(|p| {
             p.expect_ident()
diff --git a/rsvg_internals/src/c_api.rs b/rsvg_internals/src/c_api.rs
index 062c2f12..0e8ff856 100644
--- a/rsvg_internals/src/c_api.rs
+++ b/rsvg_internals/src/c_api.rs
@@ -589,8 +589,9 @@ impl CHandle {
             LoadState::Start => self.read_stream(state, stream, cancellable),
             LoadState::Loading { .. } | LoadState::ClosedOk { .. } | LoadState::ClosedError => {
                 panic!(
-                "handle must not be already loaded in order to call rsvg_handle_read_stream_sync()",
-            )
+                    "handle must not be already loaded in order to call \
+                     rsvg_handle_read_stream_sync()",
+                )
             }
         }
     }
@@ -630,8 +631,8 @@ impl CHandle {
 
             LoadState::ClosedError => {
                 rsvg_g_warning(
-                    "Handle could not read or parse the SVG; did you check for errors during \
-                     the loading stage?",
+                    "Handle could not read or parse the SVG; did you check for errors during the \
+                     loading stage?",
                 );
                 Err(RenderingError::HandleIsNotLoaded)
             }
diff --git a/rsvg_internals/src/clip_path.rs b/rsvg_internals/src/clip_path.rs
index 78d1ed44..e204e52f 100644
--- a/rsvg_internals/src/clip_path.rs
+++ b/rsvg_internals/src/clip_path.rs
@@ -2,7 +2,6 @@ use std::cell::Cell;
 
 use cairo::{self, MatrixTrait};
 
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::DrawingCtx;
@@ -73,7 +72,7 @@ impl NodeTrait for NodeClipPath {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::ClipPathUnits => self.units.set(attr.parse(value)?),
+                local_name!("clipPathUnits") => self.units.set(attr.parse(value)?),
                 _ => (),
             }
         }
diff --git a/rsvg_internals/src/create_node.rs b/rsvg_internals/src/create_node.rs
index 10a1724a..8910e439 100644
--- a/rsvg_internals/src/create_node.rs
+++ b/rsvg_internals/src/create_node.rs
@@ -1,6 +1,5 @@
 use std::collections::HashMap;
 
-use crate::attributes::Attribute;
 use crate::clip_path::NodeClipPath;
 use crate::filters::{
     blend::Blend,
@@ -277,8 +276,8 @@ pub fn create_node_and_register_id(
 
     for (attr, value) in pbag.iter() {
         match attr {
-            Attribute::Id => id = Some(value),
-            Attribute::Class => class = Some(value),
+            local_name!("id") => id = Some(value),
+            local_name!("class") => class = Some(value),
             _ => (),
         }
     }
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index 384c8b49..a6f4cd0b 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -1,19 +1,25 @@
 use cssparser::{
-    self, parse_important, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserInput,
+    self,
+    parse_important,
+    AtRuleParser,
+    CowRcStr,
+    DeclarationParser,
+    Parser,
+    ParserInput,
 };
 use std::collections::hash_map::{Entry, Iter as HashMapIter};
 use std::collections::HashMap;
 use std::ptr;
-use std::str::{self, FromStr};
+use std::str;
 
 use libc;
+use markup5ever::LocalName;
 use url::Url;
 
 use glib::translate::*;
 use glib_sys::{gboolean, gpointer, GList};
 
 use crate::allowed_url::AllowedUrl;
-use crate::attributes::Attribute;
 use crate::croco::*;
 use crate::error::*;
 use crate::io::{self, BinaryData};
@@ -23,14 +29,14 @@ use crate::util::utf8_cstr;
 
 /// A parsed CSS declaration (`name: value [!important]`)
 pub struct Declaration {
-    pub attribute: Attribute,
+    pub attribute: LocalName,
     pub property: ParsedProperty,
     pub important: bool,
 }
 
 pub struct DeclarationList {
     // Maps property_name -> Declaration
-    declarations: HashMap<Attribute, Declaration>,
+    declarations: HashMap<LocalName, Declaration>,
 }
 
 pub struct DeclParser;
@@ -44,20 +50,17 @@ impl<'i> DeclarationParser<'i> for DeclParser {
         name: CowRcStr<'i>,
         input: &mut Parser<'i, 't>,
     ) -> Result<Declaration, cssparser::ParseError<'i, ValueErrorKind>> {
-        if let Ok(attribute) = Attribute::from_str(name.as_ref()) {
-            let property = parse_attribute_value_into_parsed_property(attribute, input, true)
-                .map_err(|e| input.new_custom_error(e))?;
+        let attribute = LocalName::from(name.as_ref());
+        let property = parse_attribute_value_into_parsed_property(&attribute, input, true)
+            .map_err(|e| input.new_custom_error(e))?;
 
-            let important = input.try_parse(parse_important).is_ok();
+        let important = input.try_parse(parse_important).is_ok();
 
-            Ok(Declaration {
-                attribute,
-                property,
-                important,
-            })
-        } else {
-            Err(input.new_custom_error(ValueErrorKind::UnknownProperty))
-        }
+        Ok(Declaration {
+            attribute,
+            property,
+            important,
+        })
     }
 }
 
@@ -91,7 +94,7 @@ impl DeclarationList {
     }
 
     fn add_declaration(&mut self, declaration: Declaration) {
-        match self.declarations.entry(declaration.attribute) {
+        match self.declarations.entry(declaration.attribute.clone()) {
             Entry::Occupied(mut e) => {
                 let decl = e.get_mut();
 
@@ -111,7 +114,7 @@ impl DeclarationList {
     }
 }
 
-pub struct DeclarationListIter<'a>(HashMapIter<'a, Attribute, Declaration>);
+pub struct DeclarationListIter<'a>(HashMapIter<'a, LocalName, Declaration>);
 
 impl<'a> Iterator for DeclarationListIter<'a> {
     type Item = &'a Declaration;
@@ -397,26 +400,25 @@ unsafe extern "C" fn css_property(
 
                 let important = from_glib(a_is_important);
 
-                if let Ok(attribute) = Attribute::from_str(prop_name) {
-                    let mut input = ParserInput::new(&prop_value);
-                    let mut parser = Parser::new(&mut input);
-
-                    match parse_attribute_value_into_parsed_property(attribute, &mut parser, true) {
-                        Ok(property) => {
-                            let declaration = Declaration {
-                                attribute,
-                                property,
-                                important,
-                            };
-
-                            handler_data
-                                .css_rules
-                                .add_declaration(Selector::new(&selector_name), declaration);
-                        }
-                        Err(_) => (), // invalid property name or invalid value; ignore
+                let attribute = LocalName::from(prop_name);
+
+                let mut input = ParserInput::new(&prop_value);
+                let mut parser = Parser::new(&mut input);
+
+                match parse_attribute_value_into_parsed_property(&attribute, &mut parser, true) {
+                    Ok(property) => {
+                        let declaration = Declaration {
+                            attribute,
+                            property,
+                            important,
+                        };
+
+                        handler_data
+                            .css_rules
+                            .add_declaration(Selector::new(&selector_name), declaration);
                     }
+                    Err(_) => (), // invalid property name or invalid value; ignore
                 }
-                // else unknown property name; ignore
             }
         }
 
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index ade5322c..5d1bc254 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -21,8 +21,12 @@ use crate::paint_server::{PaintServer, PaintSource};
 use crate::pattern::NodePattern;
 use crate::properties::ComputedValues;
 use crate::property_defs::{
-    ClipRule, FillRule, ShapeRendering, StrokeDasharray,
-    StrokeLinecap, StrokeLinejoin,
+    ClipRule,
+    FillRule,
+    ShapeRendering,
+    StrokeDasharray,
+    StrokeLinecap,
+    StrokeLinejoin,
 };
 use crate::rect::RectangleExt;
 use crate::surface_utils::shared_surface::SharedImageSurface;
diff --git a/rsvg_internals/src/error.rs b/rsvg_internals/src/error.rs
index eabbf1d1..8f9f25de 100644
--- a/rsvg_internals/src/error.rs
+++ b/rsvg_internals/src/error.rs
@@ -8,8 +8,8 @@ use glib::error::ErrorDomain;
 use glib::translate::*;
 use glib_sys;
 use libc;
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::parsers::ParseError;
 
 /// A simple error which refers to an attribute's value
@@ -28,26 +28,26 @@ pub enum ValueErrorKind {
 /// A complete error for an attribute and its erroneous value
 #[derive(Debug, Clone, PartialEq)]
 pub struct NodeError {
-    attr: Attribute,
+    attr: LocalName,
     err: ValueErrorKind,
 }
 
 impl NodeError {
-    pub fn parse_error(attr: Attribute, error: ParseError) -> NodeError {
+    pub fn parse_error(attr: LocalName, error: ParseError) -> NodeError {
         NodeError {
             attr,
             err: ValueErrorKind::Parse(error),
         }
     }
 
-    pub fn value_error(attr: Attribute, description: &str) -> NodeError {
+    pub fn value_error(attr: LocalName, description: &str) -> NodeError {
         NodeError {
             attr,
             err: ValueErrorKind::Value(description.to_string()),
         }
     }
 
-    pub fn attribute_error(attr: Attribute, error: ValueErrorKind) -> NodeError {
+    pub fn attribute_error(attr: LocalName, error: ValueErrorKind) -> NodeError {
         NodeError { attr, err: error }
     }
 }
@@ -124,12 +124,12 @@ impl From<cairo::Status> for RenderingError {
 
 /// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
 ///
-/// A `NodeError` requires an `Attribute` to which the error refers,
-/// plus the actual `ValueErrorKind` that describes the error.  However,
-/// parsing functions for attribute value types will want to return their
-/// own kind of error, instead of `ValueErrorKind`.  If that particular error
-/// type has an `impl From<FooError> for ValueErrorKind`, then this
-/// trait helps assign attribute values in `set_atts()` methods as follows:
+/// A `NodeError` requires a `LocalName` that corresponds to the attribute to which the
+/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
+/// parsing functions for attribute value types will want to return their own kind of
+/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
+/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
+/// `set_atts()` methods as follows:
 ///
 /// ```ignore
 /// use error::AttributeResultExt;
@@ -138,17 +138,17 @@ impl From<cairo::Status> for RenderingError {
 ///
 /// // It is assumed that there is an impl From<FooError> for ValueErrorKind
 ///
-/// self.foo = parse_foo(value).attribute(Attribute::Foo)?;
+/// self.foo = parse_foo(value).attribute(local_name!("foo"))?;
 /// ```
 ///
 /// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
 /// `NodeError` with the provided `attr`.
 pub trait AttributeResultExt<O, E> {
-    fn attribute(self, attr: Attribute) -> Result<O, NodeError>;
+    fn attribute(self, attr: LocalName) -> Result<O, NodeError>;
 }
 
 impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O, E> for Result<O, E> {
-    fn attribute(self, attr: Attribute) -> Result<O, NodeError> {
+    fn attribute(self, attr: LocalName) -> Result<O, NodeError> {
         self.map_err(|e| e.into())
             .map_err(|e| NodeError::attribute_error(attr, e))
     }
diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs
index a246c90b..0344aec1 100644
--- a/rsvg_internals/src/filters/blend.rs
+++ b/rsvg_internals/src/filters/blend.rs
@@ -1,8 +1,8 @@
 use std::cell::{Cell, RefCell};
 
 use cairo;
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -49,10 +49,10 @@ impl NodeTrait for Blend {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::In2 => {
+                local_name!("in2") => {
                     self.in2.replace(Some(Input::parse(attr, value)?));
                 }
-                Attribute::Mode => self.mode.set(Mode::parse(attr, value)?),
+                local_name!("mode") => self.mode.set(Mode::parse(attr, value)?),
                 _ => (),
             }
         }
@@ -126,7 +126,7 @@ impl Filter for Blend {
 }
 
 impl Mode {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "normal" => Ok(Mode::Normal),
             "multiply" => Ok(Mode::Multiply),
diff --git a/rsvg_internals/src/filters/color_matrix.rs b/rsvg_internals/src/filters/color_matrix.rs
index 4c7668d3..62ac5557 100644
--- a/rsvg_internals/src/filters/color_matrix.rs
+++ b/rsvg_internals/src/filters/color_matrix.rs
@@ -1,9 +1,9 @@
 use std::cell::RefCell;
 
 use cairo::{self, ImageSurface};
+use markup5ever::LocalName;
 use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -53,7 +53,7 @@ impl NodeTrait for ColorMatrix {
 
         // First, determine the operation type.
         let mut operation_type = OperationType::Matrix;
-        for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == Attribute::Type) {
+        for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == local_name!("type")) {
             operation_type = OperationType::parse(attr, value)?;
         }
 
@@ -71,7 +71,10 @@ impl NodeTrait for ColorMatrix {
                 ),
             );
         } else {
-            for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == Attribute::Values) {
+            for (attr, value) in pbag
+                .iter()
+                .filter(|(attr, _)| *attr == local_name!("values"))
+            {
                 let new_matrix = match operation_type {
                     OperationType::LuminanceToAlpha => unreachable!(),
                     OperationType::Matrix => {
@@ -97,7 +100,7 @@ impl NodeTrait for ColorMatrix {
                     }
                     OperationType::Saturate => {
                         let s = parsers::number(value)
-                            .map_err(|err| NodeError::attribute_error(attr, err))?;
+                            .map_err(|err| NodeError::attribute_error(attr.clone(), err))?;
                         if s < 0.0 || s > 1.0 {
                             return Err(NodeError::value_error(attr, "expected value from 0 to 1"));
                         }
@@ -229,7 +232,7 @@ impl Filter for ColorMatrix {
 }
 
 impl OperationType {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "matrix" => Ok(OperationType::Matrix),
             "saturate" => Ok(OperationType::Saturate),
diff --git a/rsvg_internals/src/filters/component_transfer.rs 
b/rsvg_internals/src/filters/component_transfer.rs
index f557c596..bd4c6281 100644
--- a/rsvg_internals/src/filters/component_transfer.rs
+++ b/rsvg_internals/src/filters/component_transfer.rs
@@ -2,8 +2,8 @@ use std::cell::{Cell, Ref, RefCell};
 use std::cmp::min;
 
 use cairo::{self, ImageSurface};
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
@@ -213,8 +213,8 @@ impl NodeTrait for FuncX {
     fn set_atts(&self, _node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Type => self.function_type.set(FunctionType::parse(attr, value)?),
-                Attribute::TableValues => {
+                local_name!("type") => self.function_type.set(FunctionType::parse(attr, value)?),
+                local_name!("tableValues") => {
                     let NumberList(v) = NumberList::parse_str(value, NumberListLength::Unbounded)
                         .map_err(|err| {
                         if let NumberListError::Parse(err) = err {
@@ -225,19 +225,19 @@ impl NodeTrait for FuncX {
                     })?;
                     self.table_values.replace(v);
                 }
-                Attribute::Slope => self.slope.set(
+                local_name!("slope") => self.slope.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::Intercept => self.intercept.set(
+                local_name!("intercept") => self.intercept.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::Amplitude => self.amplitude.set(
+                local_name!("amplitude") => self.amplitude.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::Exponent => self.exponent.set(
+                local_name!("exponent") => self.exponent.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::Offset => self.offset.set(
+                local_name!("offset") => self.offset.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
                 _ => (),
@@ -384,7 +384,7 @@ impl Filter for ComponentTransfer {
 }
 
 impl FunctionType {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "identity" => Ok(FunctionType::Identity),
             "table" => Ok(FunctionType::Table),
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index 88e598e8..dc5761d3 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -3,7 +3,6 @@ use std::cell::{Cell, RefCell};
 use cairo::{self, ImageSurface};
 use cssparser::{CowRcStr, Parser, Token};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::{NodeError, ValueErrorKind};
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -66,20 +65,20 @@ impl NodeTrait for Composite {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::In2 => {
-                    self.in2.replace(Some(Input::parse(Attribute::In2, value)?));
+                local_name!("in2") => {
+                    self.in2.replace(Some(Input::parse(attr, value)?));
                 }
-                Attribute::Operator => self.operator.set(attr.parse(value)?),
-                Attribute::K1 => self.k1.set(
+                local_name!("operator") => self.operator.set(attr.parse(value)?),
+                local_name!("k1") => self.k1.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::K2 => self.k2.set(
+                local_name!("k2") => self.k2.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::K3 => self.k3.set(
+                local_name!("k3") => self.k3.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::K4 => self.k4.set(
+                local_name!("k4") => self.k4.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
                 _ => (),
diff --git a/rsvg_internals/src/filters/convolve_matrix.rs b/rsvg_internals/src/filters/convolve_matrix.rs
index b5a45c52..b0bb684b 100644
--- a/rsvg_internals/src/filters/convolve_matrix.rs
+++ b/rsvg_internals/src/filters/convolve_matrix.rs
@@ -1,9 +1,9 @@
 use std::cell::{Cell, RefCell};
 
 use cairo::{self, ImageSurface, MatrixTrait};
+use markup5ever::LocalName;
 use nalgebra::{DMatrix, Dynamic, VecStorage};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -62,9 +62,9 @@ impl NodeTrait for ConvolveMatrix {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Order => self.order.set(
+                local_name!("order") => self.order.set(
                     parsers::integer_optional_integer(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x > 0 && y > 0 {
                                 Ok((x as u32, y as u32))
@@ -76,9 +76,9 @@ impl NodeTrait for ConvolveMatrix {
                             }
                         })?,
                 ),
-                Attribute::Divisor => self.divisor.set(Some(
+                local_name!("divisor") => self.divisor.set(Some(
                     parsers::number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|x| {
                             if x != 0.0 {
                                 Ok(x)
@@ -87,13 +87,13 @@ impl NodeTrait for ConvolveMatrix {
                             }
                         })?,
                 )),
-                Attribute::Bias => self.bias.set(
+                local_name!("bias") => self.bias.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::EdgeMode => self.edge_mode.set(EdgeMode::parse(attr, value)?),
-                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
+                local_name!("edgeMode") => self.edge_mode.set(EdgeMode::parse(attr, value)?),
+                local_name!("kernelUnitLength") => self.kernel_unit_length.set(Some(
                     parsers::number_optional_number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x > 0.0 && y > 0.0 {
                                 Ok((x, y))
@@ -105,7 +105,7 @@ impl NodeTrait for ConvolveMatrix {
                             }
                         })?,
                 )),
-                Attribute::PreserveAlpha => self.preserve_alpha.set(match value {
+                local_name!("preserveAlpha") => self.preserve_alpha.set(match value {
                     "false" => false,
                     "true" => true,
                     _ => {
@@ -122,9 +122,9 @@ impl NodeTrait for ConvolveMatrix {
         // target_x and target_y depend on order.
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::TargetX => self.target_x.set(Some(
+                local_name!("targetX") => self.target_x.set(Some(
                     parsers::integer(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|x| {
                             if x >= 0 && x < self.order.get().0 as i32 {
                                 Ok(x as u32)
@@ -136,9 +136,9 @@ impl NodeTrait for ConvolveMatrix {
                             }
                         })?,
                 )),
-                Attribute::TargetY => self.target_y.set(Some(
+                local_name!("targetY") => self.target_y.set(Some(
                     parsers::integer(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|x| {
                             if x >= 0 && x < self.order.get().1 as i32 {
                                 Ok(x as u32)
@@ -165,7 +165,7 @@ impl NodeTrait for ConvolveMatrix {
         // Finally, parse the kernel matrix.
         for (attr, value) in pbag
             .iter()
-            .filter(|(attr, _)| *attr == Attribute::KernelMatrix)
+            .filter(|(attr, _)| *attr == local_name!("kernelMatrix"))
         {
             self.kernel_matrix.replace(Some({
                 let number_of_elements = self.order.get().0 as usize * self.order.get().1 as usize;
@@ -175,7 +175,7 @@ impl NodeTrait for ConvolveMatrix {
                 let NumberList(v) = NumberList::parse_str(value, NumberListLength::Unbounded)
                     .map_err(|err| {
                         NodeError::parse_error(
-                            attr,
+                            attr.clone(),
                             match err {
                                 NumberListError::IncorrectNumberOfElements => unreachable!(),
                                 NumberListError::Parse(err) => err,
@@ -185,7 +185,7 @@ impl NodeTrait for ConvolveMatrix {
 
                 if v.len() != number_of_elements {
                     return Err(NodeError::value_error(
-                        attr,
+                        attr.clone(),
                         &format!(
                             "incorrect number of elements: expected {}",
                             number_of_elements
@@ -204,7 +204,7 @@ impl NodeTrait for ConvolveMatrix {
         // kernel_matrix must have been specified.
         if self.kernel_matrix.borrow().is_none() {
             return Err(NodeError::value_error(
-                Attribute::KernelMatrix,
+                local_name!("kernelMatrix"),
                 "the value must be set",
             ));
         }
@@ -367,7 +367,7 @@ impl Filter for ConvolveMatrix {
 }
 
 impl EdgeMode {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "duplicate" => Ok(EdgeMode::Duplicate),
             "wrap" => Ok(EdgeMode::Wrap),
diff --git a/rsvg_internals/src/filters/displacement_map.rs b/rsvg_internals/src/filters/displacement_map.rs
index 84317536..e25cc7b8 100644
--- a/rsvg_internals/src/filters/displacement_map.rs
+++ b/rsvg_internals/src/filters/displacement_map.rs
@@ -1,8 +1,8 @@
 use std::cell::{Cell, RefCell};
 
 use cairo::{self, ImageSurface, MatrixTrait};
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -51,16 +51,16 @@ impl NodeTrait for DisplacementMap {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::In2 => {
-                    self.in2.replace(Some(Input::parse(Attribute::In2, value)?));
+                local_name!("in2") => {
+                    self.in2.replace(Some(Input::parse(attr, value)?));
                 }
-                Attribute::Scale => self.scale.set(
+                local_name!("scale") => self.scale.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::XChannelSelector => self
+                local_name!("xChannelSelector") => self
                     .x_channel_selector
                     .set(ColorChannel::parse(attr, value)?),
-                Attribute::YChannelSelector => self
+                local_name!("yChannelSelector") => self
                     .y_channel_selector
                     .set(ColorChannel::parse(attr, value)?),
                 _ => (),
@@ -152,7 +152,7 @@ impl Filter for DisplacementMap {
 }
 
 impl ColorChannel {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "R" => Ok(ColorChannel::R),
             "G" => Ok(ColorChannel::G),
diff --git a/rsvg_internals/src/filters/gaussian_blur.rs b/rsvg_internals/src/filters/gaussian_blur.rs
index 661117aa..69a4d0d9 100644
--- a/rsvg_internals/src/filters/gaussian_blur.rs
+++ b/rsvg_internals/src/filters/gaussian_blur.rs
@@ -5,7 +5,6 @@ use std::f64;
 use cairo::MatrixTrait;
 use nalgebra::{DMatrix, Dynamic, VecStorage};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -48,9 +47,9 @@ impl NodeTrait for GaussianBlur {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::StdDeviation => self.std_deviation.set(
+                local_name!("stdDeviation") => self.std_deviation.set(
                     parsers::number_optional_number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x >= 0.0 && y >= 0.0 {
                                 Ok((x, y))
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index b09d39b0..318abf3b 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -4,7 +4,6 @@ use cairo::{self, ImageSurface, MatrixTrait, PatternTrait, Rectangle};
 
 use crate::allowed_url::{Fragment, Href};
 use crate::aspect_ratio::AspectRatio;
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::{NodeError, RenderingError};
 use crate::float_eq_cairo::ApproxEqCairo;
@@ -179,10 +178,10 @@ impl NodeTrait for Image {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::PreserveAspectRatio => self.aspect.set(attr.parse(value)?),
+                local_name!("preserveAspectRatio") => self.aspect.set(attr.parse(value)?),
 
                 // "path" is used by some older Adobe Illustrator versions
-                Attribute::XlinkHref | Attribute::Path => {
+                local_name!("xlink:href") | local_name!("path") => {
                     let href = Href::parse(value).map_err(|_| {
                         NodeError::parse_error(attr, ParseError::new("could not parse href"))
                     })?;
diff --git a/rsvg_internals/src/filters/input.rs b/rsvg_internals/src/filters/input.rs
index a3339c0c..902667a2 100644
--- a/rsvg_internals/src/filters/input.rs
+++ b/rsvg_internals/src/filters/input.rs
@@ -1,4 +1,5 @@
-use crate::attributes::Attribute;
+use markup5ever::LocalName;
+
 use crate::error::NodeError;
 use crate::parsers::ParseError;
 
@@ -15,7 +16,7 @@ pub enum Input {
 }
 
 impl Input {
-    pub fn parse(attr: Attribute, s: &str) -> Result<Input, NodeError> {
+    pub fn parse(attr: LocalName, s: &str) -> Result<Input, NodeError> {
         match s {
             "SourceGraphic" => Ok(Input::SourceGraphic),
             "SourceAlpha" => Ok(Input::SourceAlpha),
diff --git a/rsvg_internals/src/filters/light/light_source.rs 
b/rsvg_internals/src/filters/light/light_source.rs
index b6c57858..336a8f03 100644
--- a/rsvg_internals/src/filters/light/light_source.rs
+++ b/rsvg_internals/src/filters/light/light_source.rs
@@ -4,7 +4,6 @@ use cairo::MatrixTrait;
 use cssparser;
 use nalgebra::Vector3;
 
-use crate::attributes::Attribute;
 use crate::error::NodeError;
 use crate::filters::context::FilterContext;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -207,11 +206,11 @@ impl NodeTrait for LightSource {
                     ref azimuth,
                     ref elevation,
                 } => match attr {
-                    Attribute::Azimuth => azimuth.set(
+                    local_name!("azimuth") => azimuth.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::Elevation => elevation.set(
+                    local_name!("elevation") => elevation.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
@@ -222,15 +221,15 @@ impl NodeTrait for LightSource {
                     ref y,
                     ref z,
                 } => match attr {
-                    Attribute::X => x.set(
+                    local_name!("x") => x.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::Y => y.set(
+                    local_name!("y") => y.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::Z => z.set(
+                    local_name!("z") => z.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
@@ -246,35 +245,35 @@ impl NodeTrait for LightSource {
                     ref specular_exponent,
                     ref limiting_cone_angle,
                 } => match attr {
-                    Attribute::X => x.set(
+                    local_name!("x") => x.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::Y => y.set(
+                    local_name!("y") => y.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::Z => z.set(
+                    local_name!("z") => z.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::PointsAtX => points_at_x.set(
+                    local_name!("pointsAtX") => points_at_x.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::PointsAtY => points_at_y.set(
+                    local_name!("pointsAtY") => points_at_y.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::PointsAtZ => points_at_z.set(
+                    local_name!("pointsAtZ") => points_at_z.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::SpecularExponent => specular_exponent.set(
+                    local_name!("specularExponent") => specular_exponent.set(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     ),
-                    Attribute::LimitingConeAngle => limiting_cone_angle.set(Some(
+                    local_name!("limitingConeAngle") => limiting_cone_angle.set(Some(
                         parsers::number(value)
                             .map_err(|err| NodeError::attribute_error(attr, err))?,
                     )),
diff --git a/rsvg_internals/src/filters/light/lighting.rs b/rsvg_internals/src/filters/light/lighting.rs
index 7fb60474..17ef3d8d 100644
--- a/rsvg_internals/src/filters/light/lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -7,7 +7,6 @@ use nalgebra::Vector3;
 use num_traits::identities::Zero;
 use rayon::prelude::*;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::filters::{
@@ -31,8 +30,8 @@ use crate::filters::{
 };
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
 use crate::parsers;
-use crate::property_defs::ColorInterpolationFilters;
 use crate::property_bag::PropertyBag;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::surface_utils::{
     shared_surface::{SharedImageSurface, SurfaceType},
     ImageSurfaceDataExt,
@@ -102,12 +101,12 @@ impl NodeTrait for Lighting {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::SurfaceScale => self.surface_scale.set(
+                local_name!("surfaceScale") => self.surface_scale.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
+                local_name!("kernelUnitLength") => self.kernel_unit_length.set(Some(
                     parsers::number_optional_number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x > 0.0 && y > 0.0 {
                                 Ok((x, y))
@@ -129,9 +128,9 @@ impl NodeTrait for Lighting {
             } => {
                 for (attr, value) in pbag.iter() {
                     match attr {
-                        Attribute::DiffuseConstant => diffuse_constant.set(
+                        local_name!("diffuseConstant") => diffuse_constant.set(
                             parsers::number(value)
-                                .map_err(|err| NodeError::attribute_error(attr, err))
+                                .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                                 .and_then(|x| {
                                     if x >= 0.0 {
                                         Ok(x)
@@ -153,9 +152,9 @@ impl NodeTrait for Lighting {
             } => {
                 for (attr, value) in pbag.iter() {
                     match attr {
-                        Attribute::SpecularConstant => specular_constant.set(
+                        local_name!("specularConstant") => specular_constant.set(
                             parsers::number(value)
-                                .map_err(|err| NodeError::attribute_error(attr, err))
+                                .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                                 .and_then(|x| {
                                     if x >= 0.0 {
                                         Ok(x)
@@ -167,9 +166,9 @@ impl NodeTrait for Lighting {
                                     }
                                 })?,
                         ),
-                        Attribute::SpecularExponent => specular_exponent.set(
+                        local_name!("specularExponent") => specular_exponent.set(
                             parsers::number(value)
-                                .map_err(|err| NodeError::attribute_error(attr, err))
+                                .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                                 .and_then(|x| {
                                     if x >= 1.0 && x <= 128.0 {
                                         Ok(x)
diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs
index 6a832a61..8d2595c7 100644
--- a/rsvg_internals/src/filters/merge.rs
+++ b/rsvg_internals/src/filters/merge.rs
@@ -2,7 +2,6 @@ use std::cell::RefCell;
 
 use cairo::{self, ImageSurface};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
 use crate::property_bag::PropertyBag;
@@ -55,8 +54,8 @@ impl NodeTrait for MergeNode {
     fn set_atts(&self, _node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::In => {
-                    self.in_.replace(Some(Input::parse(Attribute::In, value)?));
+                local_name!("in") => {
+                    self.in_.replace(Some(Input::parse(attr, value)?));
                 }
                 _ => (),
             }
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index c871307f..c1e4207c 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -5,7 +5,6 @@ use std::time::Instant;
 use cairo::{self, MatrixTrait};
 use owning_ref::RcRef;
 
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::DrawingCtx;
@@ -164,19 +163,19 @@ impl NodeTrait for Primitive {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(Some(
+                local_name!("x") => self.x.set(Some(
                     attr.parse_and_validate(value, check_units_horizontal)?,
                 )),
-                Attribute::Y => self
+                local_name!("y") => self
                     .y
                     .set(Some(attr.parse_and_validate(value, check_units_vertical)?)),
-                Attribute::Width => self.width.set(Some(
+                local_name!("width") => self.width.set(Some(
                     attr.parse_and_validate(value, check_units_horizontal_and_ensure_nonnegative)?,
                 )),
-                Attribute::Height => self.height.set(Some(
+                local_name!("height") => self.height.set(Some(
                     attr.parse_and_validate(value, check_units_vertical_and_ensure_nonnegative)?,
                 )),
-                Attribute::Result => *self.result.borrow_mut() = Some(value.to_string()),
+                local_name!("result") => *self.result.borrow_mut() = Some(value.to_string()),
                 _ => (),
             }
         }
@@ -212,7 +211,7 @@ impl NodeTrait for PrimitiveWithInput {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::In => drop(self.in_.replace(Some(Input::parse(Attribute::In, value)?))),
+                local_name!("in") => drop(self.in_.replace(Some(Input::parse(attr, value)?))),
                 _ => (),
             }
         }
diff --git a/rsvg_internals/src/filters/morphology.rs b/rsvg_internals/src/filters/morphology.rs
index 954f75e5..a5738408 100644
--- a/rsvg_internals/src/filters/morphology.rs
+++ b/rsvg_internals/src/filters/morphology.rs
@@ -2,8 +2,8 @@ use std::cell::Cell;
 use std::cmp::{max, min};
 
 use cairo::{self, ImageSurface, MatrixTrait};
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -53,10 +53,10 @@ impl NodeTrait for Morphology {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Operator => self.operator.set(Operator::parse(attr, value)?),
-                Attribute::Radius => self.radius.set(
+                local_name!("operator") => self.operator.set(Operator::parse(attr, value)?),
+                local_name!("radius") => self.radius.set(
                     parsers::number_optional_number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x >= 0.0 && y >= 0.0 {
                                 Ok((x, y))
@@ -162,7 +162,7 @@ impl Filter for Morphology {
 }
 
 impl Operator {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "erode" => Ok(Operator::Erode),
             "dilate" => Ok(Operator::Dilate),
diff --git a/rsvg_internals/src/filters/node.rs b/rsvg_internals/src/filters/node.rs
index a4d0507a..38bfb3bb 100644
--- a/rsvg_internals/src/filters/node.rs
+++ b/rsvg_internals/src/filters/node.rs
@@ -3,7 +3,6 @@ use std::cell::Cell;
 
 use cairo::{self, MatrixTrait};
 
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::DrawingCtx;
@@ -110,7 +109,7 @@ impl NodeTrait for NodeFilter {
         // Parse filterUnits first as it affects x, y, width, height checks.
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::FilterUnits => self.filterunits.set(attr.parse(value)?),
+                local_name!("filterUnits") => self.filterunits.set(attr.parse(value)?),
                 _ => (),
             }
         }
@@ -155,19 +154,19 @@ impl NodeTrait for NodeFilter {
         // Parse the rest of the attributes.
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self
+                local_name!("x") => self
                     .x
                     .set(attr.parse_and_validate(value, check_units_horizontal)?),
-                Attribute::Y => self
+                local_name!("y") => self
                     .y
                     .set(attr.parse_and_validate(value, check_units_vertical)?),
-                Attribute::Width => self.width.set(
+                local_name!("width") => self.width.set(
                     attr.parse_and_validate(value, check_units_horizontal_and_ensure_nonnegative)?,
                 ),
-                Attribute::Height => self.height.set(
+                local_name!("height") => self.height.set(
                     attr.parse_and_validate(value, check_units_vertical_and_ensure_nonnegative)?,
                 ),
-                Attribute::PrimitiveUnits => self.primitiveunits.set(attr.parse(value)?),
+                local_name!("primitiveUnits") => self.primitiveunits.set(attr.parse(value)?),
                 _ => (),
             }
         }
diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs
index 5b3e233f..8e88787f 100644
--- a/rsvg_internals/src/filters/offset.rs
+++ b/rsvg_internals/src/filters/offset.rs
@@ -2,7 +2,6 @@ use std::cell::Cell;
 
 use cairo::{self, ImageSurface, MatrixTrait};
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -40,10 +39,10 @@ impl NodeTrait for Offset {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Dx => self.dx.set(
+                local_name!("dx") => self.dx.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::Dy => self.dy.set(
+                local_name!("dy") => self.dy.set(
                     parsers::number(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
                 _ => (),
diff --git a/rsvg_internals/src/filters/turbulence.rs b/rsvg_internals/src/filters/turbulence.rs
index 3de80fde..b0e90de8 100644
--- a/rsvg_internals/src/filters/turbulence.rs
+++ b/rsvg_internals/src/filters/turbulence.rs
@@ -1,14 +1,14 @@
 use std::cell::Cell;
 
 use cairo::{self, ImageSurface, MatrixTrait};
+use markup5ever::LocalName;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::NodeError;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
 use crate::parsers::{self, ParseError};
-use crate::property_defs::ColorInterpolationFilters;
 use crate::property_bag::PropertyBag;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::surface_utils::{
     shared_surface::{SharedImageSurface, SurfaceType},
     ImageSurfaceDataExt,
@@ -65,9 +65,9 @@ impl NodeTrait for Turbulence {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::BaseFrequency => self.base_frequency.set(
+                local_name!("baseFrequency") => self.base_frequency.set(
                     parsers::number_optional_number(value)
-                        .map_err(|err| NodeError::attribute_error(attr, err))
+                        .map_err(|err| NodeError::attribute_error(attr.clone(), err))
                         .and_then(|(x, y)| {
                             if x >= 0.0 && y >= 0.0 {
                                 Ok((x, y))
@@ -76,11 +76,11 @@ impl NodeTrait for Turbulence {
                             }
                         })?,
                 ),
-                Attribute::NumOctaves => self.num_octaves.set(
+                local_name!("numOctaves") => self.num_octaves.set(
                     parsers::integer(value).map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
                 // Yes, seed needs to be parsed as a number and then truncated.
-                Attribute::Seed => self.seed.set(
+                local_name!("seed") => self.seed.set(
                     parsers::number(value)
                         .map(|x| {
                             clamp(
@@ -91,8 +91,10 @@ impl NodeTrait for Turbulence {
                         })
                         .map_err(|err| NodeError::attribute_error(attr, err))?,
                 ),
-                Attribute::StitchTiles => self.stitch_tiles.set(StitchTiles::parse(attr, value)?),
-                Attribute::Type => self.type_.set(NoiseType::parse(attr, value)?),
+                local_name!("stitchTiles") => {
+                    self.stitch_tiles.set(StitchTiles::parse(attr, value)?)
+                }
+                local_name!("type") => self.type_.set(NoiseType::parse(attr, value)?),
                 _ => (),
             }
         }
@@ -428,7 +430,7 @@ impl Filter for Turbulence {
 }
 
 impl StitchTiles {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "stitch" => Ok(StitchTiles::Stitch),
             "noStitch" => Ok(StitchTiles::NoStitch),
@@ -441,7 +443,7 @@ impl StitchTiles {
 }
 
 impl NoiseType {
-    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
+    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
         match s {
             "fractalNoise" => Ok(NoiseType::FractalNoise),
             "turbulence" => Ok(NoiseType::Turbulence),
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index 18931387..53442e2d 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -4,7 +4,6 @@ use cssparser::{self, CowRcStr, Parser, Token};
 use std::cell::RefCell;
 
 use crate::allowed_url::Fragment;
-use crate::attributes::Attribute;
 use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::{AcquiredNode, DrawingCtx, NodeStack};
@@ -668,26 +667,25 @@ impl NodeTrait for NodeGradient {
         for (attr, value) in pbag.iter() {
             match attr {
                 // Attributes common to linear and radial gradients
-                Attribute::GradientUnits => g.common.units = Some(attr.parse(value)?),
-                Attribute::GradientTransform => g.common.affine = Some(attr.parse(value)?),
-                Attribute::SpreadMethod => g.common.spread = Some(attr.parse(value)?),
-                Attribute::XlinkHref => {
-                    g.common.fallback =
-                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
+                local_name!("gradientUnits") => g.common.units = Some(attr.parse(value)?),
+                local_name!("gradientTransform") => g.common.affine = Some(attr.parse(value)?),
+                local_name!("spreadMethod") => g.common.spread = Some(attr.parse(value)?),
+                local_name!("xlink:href") => {
+                    g.common.fallback = Some(Fragment::parse(value).attribute(attr)?)
                 }
 
                 // Attributes specific to each gradient type.  The defaults mandated by the spec
                 // are in GradientVariant::resolve_from_defaults()
-                Attribute::X1 => x1 = Some(attr.parse(value)?),
-                Attribute::Y1 => y1 = Some(attr.parse(value)?),
-                Attribute::X2 => x2 = Some(attr.parse(value)?),
-                Attribute::Y2 => y2 = Some(attr.parse(value)?),
-
-                Attribute::Cx => cx = Some(attr.parse(value)?),
-                Attribute::Cy => cy = Some(attr.parse(value)?),
-                Attribute::R => r = Some(attr.parse(value)?),
-                Attribute::Fx => fx = Some(attr.parse(value)?),
-                Attribute::Fy => fy = Some(attr.parse(value)?),
+                local_name!("x1") => x1 = Some(attr.parse(value)?),
+                local_name!("y1") => y1 = Some(attr.parse(value)?),
+                local_name!("x2") => x2 = Some(attr.parse(value)?),
+                local_name!("y2") => y2 = Some(attr.parse(value)?),
+
+                local_name!("cx") => cx = Some(attr.parse(value)?),
+                local_name!("cy") => cy = Some(attr.parse(value)?),
+                local_name!("r") => r = Some(attr.parse(value)?),
+                local_name!("fx") => fx = Some(attr.parse(value)?),
+                local_name!("fy") => fy = Some(attr.parse(value)?),
 
                 _ => (),
             }
diff --git a/rsvg_internals/src/image.rs b/rsvg_internals/src/image.rs
index 98a22a84..472a1dab 100644
--- a/rsvg_internals/src/image.rs
+++ b/rsvg_internals/src/image.rs
@@ -4,7 +4,6 @@ use std::cell::{Cell, RefCell};
 
 use crate::allowed_url::Href;
 use crate::aspect_ratio::AspectRatio;
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::drawing_ctx::{ClipMode, DrawingCtx};
 use crate::error::{NodeError, RenderingError};
@@ -46,19 +45,19 @@ impl NodeTrait for NodeImage {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(attr.parse(value)?),
-                Attribute::Y => self.y.set(attr.parse(value)?),
-                Attribute::Width => self
+                local_name!("x") => self.x.set(attr.parse(value)?),
+                local_name!("y") => self.y.set(attr.parse(value)?),
+                local_name!("width") => self
                     .w
                     .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
-                Attribute::Height => self
+                local_name!("height") => self
                     .h
                     .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
 
-                Attribute::PreserveAspectRatio => self.aspect.set(attr.parse(value)?),
+                local_name!("preserveAspectRatio") => self.aspect.set(attr.parse(value)?),
 
                 // "path" is used by some older Adobe Illustrator versions
-                Attribute::XlinkHref | Attribute::Path => {
+                local_name!("xlink:href") | local_name!("path") => {
                     let href = Href::parse(value).map_err(|_| {
                         NodeError::parse_error(attr, ParseError::new("could not parse href"))
                     })?;
@@ -155,7 +154,8 @@ impl NodeTrait for NodeImage {
                 }
 
                 Ok(())
-            }).and_then(|()| {
+            })
+            .and_then(|()| {
                 dc.insert_bbox(&bbox);
                 Ok(())
             })
diff --git a/rsvg_internals/src/io.rs b/rsvg_internals/src/io.rs
index 196431d5..d6d1794b 100644
--- a/rsvg_internals/src/io.rs
+++ b/rsvg_internals/src/io.rs
@@ -51,7 +51,7 @@ fn decode_data_uri(uri: &str) -> Result<BinaryData, LoadingError> {
 const GZ_MAGIC_0: u8 = 0x1f;
 const GZ_MAGIC_1: u8 = 0x8b;
 
-pub fn get_input_stream_for_loading<S: IsA<InputStream>> (
+pub fn get_input_stream_for_loading<S: IsA<InputStream>>(
     stream: &S,
     cancellable: Option<&Cancellable>,
 ) -> Result<InputStream, glib::Error> {
diff --git a/rsvg_internals/src/iri.rs b/rsvg_internals/src/iri.rs
index 6191f5ca..e32973f0 100644
--- a/rsvg_internals/src/iri.rs
+++ b/rsvg_internals/src/iri.rs
@@ -36,7 +36,10 @@ impl Parse for IRI {
     type Err = ParseError;
 
     fn parse(parser: &mut Parser<'_, '_>) -> Result<IRI, ParseError> {
-        if parser.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
+        if parser
+            .try_parse(|i| i.expect_ident_matching("none"))
+            .is_ok()
+        {
             Ok(IRI::None)
         } else {
             let url = parser
diff --git a/rsvg_internals/src/length.rs b/rsvg_internals/src/length.rs
index 721f8905..6c744277 100644
--- a/rsvg_internals/src/length.rs
+++ b/rsvg_internals/src/length.rs
@@ -360,7 +360,10 @@ impl Parse for Dasharray {
     type Err = ValueErrorKind;
 
     fn parse(parser: &mut Parser<'_, '_>) -> Result<Dasharray, ValueErrorKind> {
-        if parser.try_parse(|p| p.expect_ident_matching("none")).is_ok() {
+        if parser
+            .try_parse(|p| p.expect_ident_matching("none"))
+            .is_ok()
+        {
             Ok(Dasharray::None)
         } else {
             Ok(Dasharray::Array(parse_dash_array(parser)?))
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 60af77b0..66f3cb36 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -14,6 +14,10 @@ extern crate glib;
 #[macro_use]
 extern crate lazy_static;
 
+// This provides the LocalName struct and local_name! macro
+#[macro_use]
+extern crate markup5ever;
+
 pub use crate::c_api::{
     rsvg_rust_error_get_type,
     rsvg_rust_handle_close,
diff --git a/rsvg_internals/src/link.rs b/rsvg_internals/src/link.rs
index 4f72da05..b0cc189c 100644
--- a/rsvg_internals/src/link.rs
+++ b/rsvg_internals/src/link.rs
@@ -2,7 +2,6 @@ use regex::{Captures, Regex};
 use std::borrow::Cow;
 use std::cell::RefCell;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::RenderingError;
 use crate::node::*;
@@ -24,7 +23,7 @@ impl NodeTrait for NodeLink {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::XlinkHref => *self.link.borrow_mut() = Some(value.to_owned()),
+                local_name!("xlink:href") => *self.link.borrow_mut() = Some(value.to_owned()),
 
                 _ => (),
             }
diff --git a/rsvg_internals/src/marker.rs b/rsvg_internals/src/marker.rs
index 364702a7..55f31e9a 100644
--- a/rsvg_internals/src/marker.rs
+++ b/rsvg_internals/src/marker.rs
@@ -8,7 +8,6 @@ use cssparser::{CowRcStr, Parser, Token};
 use crate::allowed_url::Fragment;
 use crate::angle::Angle;
 use crate::aspect_ratio::*;
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
@@ -77,7 +76,10 @@ impl Parse for MarkerOrient {
     type Err = ValueErrorKind;
 
     fn parse(parser: &mut Parser<'_, '_>) -> Result<MarkerOrient, ValueErrorKind> {
-        if parser.try_parse(|p| p.expect_ident_matching("auto")).is_ok() {
+        if parser
+            .try_parse(|p| p.expect_ident_matching("auto"))
+            .is_ok()
+        {
             Ok(MarkerOrient::Auto)
         } else {
             Angle::parse(parser).map(MarkerOrient::Angle)
@@ -202,25 +204,25 @@ impl NodeTrait for NodeMarker {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::MarkerUnits => self.units.set(attr.parse(value)?),
+                local_name!("markerUnits") => self.units.set(attr.parse(value)?),
 
-                Attribute::RefX => self.ref_x.set(attr.parse(value)?),
+                local_name!("refX") => self.ref_x.set(attr.parse(value)?),
 
-                Attribute::RefY => self.ref_y.set(attr.parse(value)?),
+                local_name!("refY") => self.ref_y.set(attr.parse(value)?),
 
-                Attribute::MarkerWidth => self
+                local_name!("markerWidth") => self
                     .width
                     .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
 
-                Attribute::MarkerHeight => self
+                local_name!("markerHeight") => self
                     .height
                     .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
 
-                Attribute::Orient => self.orient.set(attr.parse(value)?),
+                local_name!("orient") => self.orient.set(attr.parse(value)?),
 
-                Attribute::PreserveAspectRatio => self.aspect.set(attr.parse(value)?),
+                local_name!("preserveAspectRatio") => self.aspect.set(attr.parse(value)?),
 
-                Attribute::ViewBox => self.vbox.set(Some(attr.parse(value)?)),
+                local_name!("viewBox") => self.vbox.set(Some(attr.parse(value)?)),
 
                 _ => (),
             }
diff --git a/rsvg_internals/src/mask.rs b/rsvg_internals/src/mask.rs
index 33af7c59..93555eac 100644
--- a/rsvg_internals/src/mask.rs
+++ b/rsvg_internals/src/mask.rs
@@ -1,7 +1,6 @@
 use cairo::{self, MatrixTrait};
 use std::cell::Cell;
 
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::{CompositingAffines, DrawingCtx};
@@ -13,7 +12,9 @@ use crate::property_bag::PropertyBag;
 use crate::property_defs::Opacity;
 use crate::rect::IRect;
 use crate::surface_utils::{
-    iterators::Pixels, shared_surface::SharedImageSurface, shared_surface::SurfaceType,
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    shared_surface::SurfaceType,
     ImageSurfaceDataExt,
 };
 use crate::unit_interval::UnitInterval;
@@ -187,16 +188,16 @@ impl NodeTrait for NodeMask {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(attr.parse(value)?),
-                Attribute::Y => self.y.set(attr.parse(value)?),
-                Attribute::Width => self
+                local_name!("x") => self.x.set(attr.parse(value)?),
+                local_name!("y") => self.y.set(attr.parse(value)?),
+                local_name!("width") => self
                     .width
                     .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
-                Attribute::Height => self
+                local_name!("height") => self
                     .height
                     .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
-                Attribute::MaskUnits => self.units.set(attr.parse(value)?),
-                Attribute::MaskContentUnits => self.content_units.set(attr.parse(value)?),
+                local_name!("maskUnits") => self.units.set(attr.parse(value)?),
+                local_name!("maskContentUnits") => self.content_units.set(attr.parse(value)?),
                 _ => (),
             }
         }
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index 91426697..6f6a904a 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -1,11 +1,11 @@
 use cairo::{Matrix, MatrixTrait};
 use downcast_rs::*;
+use markup5ever::LocalName;
 use std::cell::{Cell, Ref, RefCell};
 use std::collections::HashSet;
 use std::fmt;
 use std::rc::Rc;
 
-use crate::attributes::Attribute;
 use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
 use crate::css::CssRules;
 use crate::drawing_ctx::DrawingCtx;
@@ -27,7 +27,7 @@ pub struct NodeData {
     id: Option<String>,    // id attribute from XML element
     class: Option<String>, // class attribute from XML element
     specified_values: RefCell<SpecifiedValues>,
-    important_styles: RefCell<HashSet<Attribute>>,
+    important_styles: RefCell<HashSet<LocalName>>,
     result: RefCell<NodeResult>,
     transform: Cell<Matrix>,
     values: RefCell<ComputedValues>,
@@ -364,9 +364,9 @@ impl RsvgNode {
     fn set_transform_attribute(&self, pbag: &PropertyBag<'_>) -> Result<(), NodeError> {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Transform => {
+                local_name!("transform") => {
                     return Matrix::parse_str(value)
-                        .attribute(Attribute::Transform)
+                        .attribute(attr)
                         .and_then(|affine| Ok(self.borrow().transform.set(affine)));
                 }
 
@@ -382,7 +382,7 @@ impl RsvgNode {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Style => style_attr.push_str(value),
+                local_name!("style") => style_attr.push_str(value),
 
                 _ => (),
             }
@@ -413,17 +413,17 @@ impl RsvgNode {
             // FIXME: move this to "try {}" when we can bump the rustc version dependency
             let mut parse = || {
                 match attr {
-                    Attribute::RequiredExtensions if cond => {
+                    local_name!("requiredExtensions") if cond => {
                         cond = RequiredExtensions::from_attribute(value)
                             .map(|RequiredExtensions(res)| res)?;
                     }
 
-                    Attribute::RequiredFeatures if cond => {
+                    local_name!("requiredFeatures") if cond => {
                         cond = RequiredFeatures::from_attribute(value)
                             .map(|RequiredFeatures(res)| res)?;
                     }
 
-                    Attribute::SystemLanguage if cond => {
+                    local_name!("systemLanguage") if cond => {
                         cond = SystemLanguage::from_attribute(value, locale)
                             .map(|SystemLanguage(res)| res)?;
                     }
diff --git a/rsvg_internals/src/paint_server.rs b/rsvg_internals/src/paint_server.rs
index bd8eb45a..b371c40a 100644
--- a/rsvg_internals/src/paint_server.rs
+++ b/rsvg_internals/src/paint_server.rs
@@ -23,11 +23,17 @@ impl Parse for PaintServer {
     type Err = ValueErrorKind;
 
     fn parse(parser: &mut Parser<'_, '_>) -> Result<PaintServer, ValueErrorKind> {
-        if parser.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
+        if parser
+            .try_parse(|i| i.expect_ident_matching("none"))
+            .is_ok()
+        {
             Ok(PaintServer::None)
         } else if let Ok(url) = parser.try_parse(|i| i.expect_url()) {
             let alternate = if !parser.is_exhausted() {
-                if parser.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
+                if parser
+                    .try_parse(|i| i.expect_ident_matching("none"))
+                    .is_ok()
+                {
                     None
                 } else {
                     Some(parser.try_parse(|i| cssparser::Color::parse(i))?)
diff --git a/rsvg_internals/src/parsers.rs b/rsvg_internals/src/parsers.rs
index ccb3547d..f27d8c49 100644
--- a/rsvg_internals/src/parsers.rs
+++ b/rsvg_internals/src/parsers.rs
@@ -1,8 +1,8 @@
 use cssparser::{BasicParseError, Parser, ParserInput, Token};
+use markup5ever::LocalName;
 
 use std::str;
 
-use crate::attributes::Attribute;
 use crate::error::{NodeError, ValueErrorKind};
 
 #[derive(Debug, Clone, PartialEq)]
@@ -77,12 +77,12 @@ pub trait ParseValue<T: Parse<Err = ValueErrorKind>> {
     ) -> Result<T, NodeError>;
 }
 
-impl<T: Parse<Err = ValueErrorKind>> ParseValue<T> for Attribute {
+impl<T: Parse<Err = ValueErrorKind>> ParseValue<T> for LocalName {
     fn parse(&self, value: &str) -> Result<T, NodeError> {
         let mut input = ParserInput::new(value);
         let mut parser = Parser::new(&mut input);
 
-        T::parse(&mut parser).map_err(|e| NodeError::attribute_error(*self, e))
+        T::parse(&mut parser).map_err(|e| NodeError::attribute_error(self.clone(), e))
     }
 
     fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
@@ -95,7 +95,7 @@ impl<T: Parse<Err = ValueErrorKind>> ParseValue<T> for Attribute {
 
         T::parse(&mut parser)
             .and_then(validate)
-            .map_err(|e| NodeError::attribute_error(*self, e))
+            .map_err(|e| NodeError::attribute_error(self.clone(), e))
     }
 }
 
diff --git a/rsvg_internals/src/path_builder.rs b/rsvg_internals/src/path_builder.rs
index db133bfc..31165131 100644
--- a/rsvg_internals/src/path_builder.rs
+++ b/rsvg_internals/src/path_builder.rs
@@ -362,11 +362,11 @@ impl PathBuilder {
 
         // We check the cr's status right after feeding it a new path for a few reasons:
         //
-        // * Any of the individual path commands may cause the cr to enter an error state,
-        //   for example, if they come with coordinates outside of Cairo's supported range.
+        // * Any of the individual path commands may cause the cr to enter an error state, for
+        //   example, if they come with coordinates outside of Cairo's supported range.
         //
-        // * The *next* call to the cr will probably be something that actually checks
-        //   the status (i.e. in cairo-rs), and we don't want to panic there.
+        // * The *next* call to the cr will probably be something that actually checks the status
+        //   (i.e. in cairo-rs), and we don't want to panic there.
 
         let status = cr.status();
 
diff --git a/rsvg_internals/src/pattern.rs b/rsvg_internals/src/pattern.rs
index b5c04bf4..5bbb6fa6 100644
--- a/rsvg_internals/src/pattern.rs
+++ b/rsvg_internals/src/pattern.rs
@@ -5,7 +5,6 @@ use std::f64;
 
 use crate::allowed_url::Fragment;
 use crate::aspect_ratio::*;
-use crate::attributes::Attribute;
 use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::drawing_ctx::{DrawingCtx, NodeStack};
@@ -182,32 +181,32 @@ impl NodeTrait for NodePattern {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::PatternUnits => p.units = Some(attr.parse(value)?),
+                local_name!("patternUnits") => p.units = Some(attr.parse(value)?),
 
-                Attribute::PatternContentUnits => p.content_units = Some(attr.parse(value)?),
+                local_name!("patternContentUnits") => p.content_units = Some(attr.parse(value)?),
 
-                Attribute::ViewBox => p.vbox = Some(Some(attr.parse(value)?)),
+                local_name!("viewBox") => p.vbox = Some(Some(attr.parse(value)?)),
 
-                Attribute::PreserveAspectRatio => {
+                local_name!("preserveAspectRatio") => {
                     p.preserve_aspect_ratio = Some(attr.parse(value)?)
                 }
 
-                Attribute::PatternTransform => p.affine = Some(attr.parse(value)?),
+                local_name!("patternTransform") => p.affine = Some(attr.parse(value)?),
 
-                Attribute::XlinkHref => {
-                    p.fallback = Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?);
+                local_name!("xlink:href") => {
+                    p.fallback = Some(Fragment::parse(value).attribute(attr)?);
                 }
 
-                Attribute::X => p.x = Some(attr.parse(value)?),
+                local_name!("x") => p.x = Some(attr.parse(value)?),
 
-                Attribute::Y => p.y = Some(attr.parse(value)?),
+                local_name!("y") => p.y = Some(attr.parse(value)?),
 
-                Attribute::Width => {
+                local_name!("width") => {
                     p.width =
                         Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                 }
 
-                Attribute::Height => {
+                local_name!("height") => {
                     p.height =
                         Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                 }
diff --git a/rsvg_internals/src/pixbuf_utils.rs b/rsvg_internals/src/pixbuf_utils.rs
index 74b95501..5dd413db 100644
--- a/rsvg_internals/src/pixbuf_utils.rs
+++ b/rsvg_internals/src/pixbuf_utils.rs
@@ -17,7 +17,9 @@ use crate::error::{set_gerror, LoadingError, RenderingError};
 use crate::handle::{Handle, LoadOptions};
 use crate::rect::IRect;
 use crate::surface_utils::{
-    iterators::Pixels, shared_surface::SharedImageSurface, shared_surface::SurfaceType,
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    shared_surface::SurfaceType,
 };
 
 // Pixbuf::new() doesn't return out-of-memory errors properly
diff --git a/rsvg_internals/src/properties.rs b/rsvg_internals/src/properties.rs
index c8c5a7ef..2ec16719 100644
--- a/rsvg_internals/src/properties.rs
+++ b/rsvg_internals/src/properties.rs
@@ -1,8 +1,8 @@
 use cssparser::{self, DeclarationListParser, Parser, ParserInput};
+use markup5ever::LocalName;
 use std::collections::HashSet;
 
-use crate::attributes::Attribute;
-use crate::css::{Declaration, DeclParser};
+use crate::css::{DeclParser, Declaration};
 use crate::error::*;
 use crate::parsers::Parse;
 use crate::property_bag::PropertyBag;
@@ -223,76 +223,76 @@ pub struct ComputedValues {
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
-pub fn parse_attribute_value_into_parsed_property(attr: Attribute, input: &mut Parser, accept_shorthands: 
bool) -> Result<ParsedProperty, ValueErrorKind> {
+pub fn parse_attribute_value_into_parsed_property(attr: &LocalName, input: &mut Parser, accept_shorthands: 
bool) -> Result<ParsedProperty, ValueErrorKind> {
     // please keep these sorted
     match attr {
-        Attribute::BaselineShift =>
+        local_name!("baseline-shift") =>
             Ok(ParsedProperty::BaselineShift(parse_input(input)?)),
 
-        Attribute::ClipPath =>
+        local_name!("clip-path") =>
             Ok(ParsedProperty::ClipPath(parse_input(input)?)),
 
-        Attribute::ClipRule =>
+        local_name!("clip-rule") =>
             Ok(ParsedProperty::ClipRule(parse_input(input)?)),
 
-        Attribute::Color =>
+        local_name!("color") =>
             Ok(ParsedProperty::Color(parse_input(input)?)),
 
-        Attribute::ColorInterpolationFilters =>
+        local_name!("color-interpolation-filters") =>
             Ok(ParsedProperty::ColorInterpolationFilters(parse_input(input)?)),
 
-        Attribute::Direction =>
+        local_name!("direction") =>
             Ok(ParsedProperty::Direction(parse_input(input)?)),
 
-        Attribute::Display =>
+        local_name!("display") =>
             Ok(ParsedProperty::Display(parse_input(input)?)),
 
-        Attribute::EnableBackground =>
+        local_name!("enable-background") =>
             Ok(ParsedProperty::EnableBackground(parse_input(input)?)),
 
-        Attribute::Fill =>
+        local_name!("fill") =>
             Ok(ParsedProperty::Fill(parse_input(input)?)),
 
-        Attribute::FillOpacity =>
+        local_name!("fill-opacity") =>
             Ok(ParsedProperty::FillOpacity(parse_input(input)?)),
 
-        Attribute::FillRule =>
+        local_name!("fill-rule") =>
             Ok(ParsedProperty::FillRule(parse_input(input)?)),
 
-        Attribute::Filter =>
+        local_name!("filter") =>
             Ok(ParsedProperty::Filter(parse_input(input)?)),
 
-        Attribute::FloodColor =>
+        local_name!("flood-color") =>
             Ok(ParsedProperty::FloodColor(parse_input(input)?)),
 
-        Attribute::FloodOpacity =>
+        local_name!("flood-opacity") =>
             Ok(ParsedProperty::FloodOpacity(parse_input(input)?)),
 
-        Attribute::FontFamily =>
+        local_name!("font-family") =>
             Ok(ParsedProperty::FontFamily(parse_input(input)?)),
 
-        Attribute::FontSize =>
+        local_name!("font-size") =>
             Ok(ParsedProperty::FontSize(parse_input(input)?)),
 
-        Attribute::FontStretch =>
+        local_name!("font-stretch") =>
             Ok(ParsedProperty::FontStretch(parse_input(input)?)),
 
-        Attribute::FontStyle =>
+        local_name!("font-style") =>
             Ok(ParsedProperty::FontStyle(parse_input(input)?)),
 
-        Attribute::FontVariant =>
+        local_name!("font-variant") =>
             Ok(ParsedProperty::FontVariant(parse_input(input)?)),
 
-        Attribute::FontWeight =>
+        local_name!("font-weight") =>
             Ok(ParsedProperty::FontWeight(parse_input(input)?)),
 
-        Attribute::LetterSpacing =>
+        local_name!("letter-spacing") =>
             Ok(ParsedProperty::LetterSpacing(parse_input(input)?)),
 
-        Attribute::LightingColor =>
+        local_name!("lighting-color") =>
             Ok(ParsedProperty::LightingColor(parse_input(input)?)),
 
-        Attribute::Marker => {
+        local_name!("marker") => {
             if accept_shorthands {
                 Ok(ParsedProperty::Marker(parse_input(input)?))
             } else {
@@ -300,73 +300,73 @@ pub fn parse_attribute_value_into_parsed_property(attr: Attribute, input: &mut P
             }
         }
 
-        Attribute::MarkerEnd =>
+        local_name!("marker-end") =>
             Ok(ParsedProperty::MarkerEnd(parse_input(input)?)),
 
-        Attribute::MarkerMid =>
+        local_name!("marker-mid") =>
             Ok(ParsedProperty::MarkerMid(parse_input(input)?)),
 
-        Attribute::MarkerStart =>
+        local_name!("marker-start") =>
             Ok(ParsedProperty::MarkerStart(parse_input(input)?)),
 
-        Attribute::Mask =>
+        local_name!("mask") =>
             Ok(ParsedProperty::Mask(parse_input(input)?)),
 
-        Attribute::Opacity =>
+        local_name!("opacity") =>
             Ok(ParsedProperty::Opacity(parse_input(input)?)),
 
-        Attribute::Overflow =>
+        local_name!("overflow") =>
             Ok(ParsedProperty::Overflow(parse_input(input)?)),
 
-        Attribute::ShapeRendering =>
+        local_name!("shape-rendering") =>
             Ok(ParsedProperty::ShapeRendering(parse_input(input)?)),
 
-        Attribute::StopColor =>
+        local_name!("stop-color") =>
             Ok(ParsedProperty::StopColor(parse_input(input)?)),
 
-        Attribute::StopOpacity =>
+        local_name!("stop-opacity") =>
             Ok(ParsedProperty::StopOpacity(parse_input(input)?)),
 
-        Attribute::Stroke =>
+        local_name!("stroke") =>
             Ok(ParsedProperty::Stroke(parse_input(input)?)),
 
-        Attribute::StrokeDasharray =>
+        local_name!("stroke-dasharray") =>
             Ok(ParsedProperty::StrokeDasharray(parse_input(input)?)),
 
-        Attribute::StrokeDashoffset =>
+        local_name!("stroke-dashoffset") =>
             Ok(ParsedProperty::StrokeDashoffset(parse_input(input)?)),
 
-        Attribute::StrokeLinecap =>
+        local_name!("stroke-linecap") =>
             Ok(ParsedProperty::StrokeLinecap(parse_input(input)?)),
 
-        Attribute::StrokeLinejoin =>
+        local_name!("stroke-linejoin") =>
             Ok(ParsedProperty::StrokeLinejoin(parse_input(input)?)),
 
-        Attribute::StrokeOpacity =>
-            Ok(ParsedProperty::StrokeOpacity(parse_input(input)?)),
-
-        Attribute::StrokeMiterlimit =>
+        local_name!("stroke-miterlimit") =>
             Ok(ParsedProperty::StrokeMiterlimit(parse_input(input)?)),
 
-        Attribute::StrokeWidth =>
+        local_name!("stroke-opacity") =>
+            Ok(ParsedProperty::StrokeOpacity(parse_input(input)?)),
+
+        local_name!("stroke-width") =>
             Ok(ParsedProperty::StrokeWidth(parse_input(input)?)),
 
-        Attribute::TextAnchor =>
+        local_name!("text-anchor") =>
             Ok(ParsedProperty::TextAnchor(parse_input(input)?)),
 
-        Attribute::TextDecoration =>
+        local_name!("text-decoration") =>
             Ok(ParsedProperty::TextDecoration(parse_input(input)?)),
 
-        Attribute::TextRendering =>
+        local_name!("text-rendering") =>
             Ok(ParsedProperty::TextRendering(parse_input(input)?)),
 
-        Attribute::UnicodeBidi =>
+        local_name!("unicode-bidi") =>
             Ok(ParsedProperty::UnicodeBidi(parse_input(input)?)),
 
-        Attribute::Visibility =>
+        local_name!("visibility") =>
             Ok(ParsedProperty::Visibility(parse_input(input)?)),
 
-        Attribute::WritingMode =>
+        local_name!("writing-mode") =>
             Ok(ParsedProperty::WritingMode(parse_input(input)?)),
 
         _ => Err(ValueErrorKind::UnknownProperty)
@@ -525,15 +525,15 @@ impl SpecifiedValues {
 
     fn parse_attribute_pair(
         &mut self,
-        attr: Attribute,
+        attr: LocalName,
         value: &str,
         accept_shorthands: bool,
     ) -> Result<(), NodeError> {
         let mut input = ParserInput::new(value);
         let mut parser = Parser::new(&mut input);
 
-        match parse_attribute_value_into_parsed_property(attr, &mut parser, accept_shorthands)
-            .attribute(attr)
+        match parse_attribute_value_into_parsed_property(&attr, &mut parser, accept_shorthands)
+            .attribute(attr.clone())
         {
             Ok(prop) => self.set_parsed_property(&prop),
             Err(e) => {
@@ -542,8 +542,8 @@ impl SpecifiedValues {
                 // node to be in error in that case.
 
                 rsvg_log!(
-                    "(style property error for attribute {:?}\n    value=\"{}\"\n    {}\n    property \
-                     will be ignored)",
+                    "(style property error for attribute {:?}\n    value=\"{}\"\n    {}\n    \
+                     property will be ignored)",
                     attr,
                     value,
                     e
@@ -562,22 +562,20 @@ impl SpecifiedValues {
     ) -> Result<(), NodeError> {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::XmlLang => {
+                local_name!("xml:lang") => {
                     // xml:lang is a non-presentation attribute and as such cannot have the
                     // "inherit" value.  So, we don't call parse_attribute_pair() for it, but
                     // rather call its parser directly.
-                    self.xml_lang = SpecifiedValue::Specified(
-                        XmlLang::parse_str(value).attribute(Attribute::XmlLang)?,
-                    );
+                    self.xml_lang =
+                        SpecifiedValue::Specified(XmlLang::parse_str(value).attribute(attr)?);
                 }
 
-                Attribute::XmlSpace => {
+                local_name!("xml:space") => {
                     // xml:space is a non-presentation attribute and as such cannot have the
                     // "inherit" value.  So, we don't call parse_attribute_pair() for it, but
                     // rather call its parser directly.
-                    self.xml_space = SpecifiedValue::Specified(
-                        XmlSpace::parse_str(value).attribute(Attribute::XmlSpace)?,
-                    );
+                    self.xml_space =
+                        SpecifiedValue::Specified(XmlSpace::parse_str(value).attribute(attr)?);
                 }
 
                 _ => self.parse_attribute_pair(attr, value, false)?,
@@ -590,14 +588,14 @@ impl SpecifiedValues {
     pub fn set_property_from_declaration(
         &mut self,
         declaration: &Declaration,
-        important_styles: &mut HashSet<Attribute>,
+        important_styles: &mut HashSet<LocalName>,
     ) {
         if !declaration.important && important_styles.contains(&declaration.attribute) {
             return;
         }
 
         if declaration.important {
-            important_styles.insert(declaration.attribute);
+            important_styles.insert(declaration.attribute.clone());
         }
 
         self.set_parsed_property(&declaration.property);
@@ -606,7 +604,7 @@ impl SpecifiedValues {
     pub fn parse_style_declarations(
         &mut self,
         declarations: &str,
-        important_styles: &mut HashSet<Attribute>,
+        important_styles: &mut HashSet<LocalName>,
     ) -> Result<(), NodeError> {
         let mut input = ParserInput::new(declarations);
         let mut parser = Parser::new(&mut input);
diff --git a/rsvg_internals/src/property_bag.rs b/rsvg_internals/src/property_bag.rs
index 27191795..c92cbdd1 100644
--- a/rsvg_internals/src/property_bag.rs
+++ b/rsvg_internals/src/property_bag.rs
@@ -2,13 +2,13 @@ use libc;
 
 use std::ffi::CStr;
 use std::slice;
-use std::str::{self, FromStr};
+use std::str;
 
-use crate::attributes::Attribute;
+use markup5ever::LocalName;
 
-pub struct PropertyBag<'a>(Vec<(Attribute, &'a CStr)>);
+pub struct PropertyBag<'a>(Vec<(LocalName, &'a CStr)>);
 
-pub struct PropertyBagIter<'a>(slice::Iter<'a, (Attribute, &'a CStr)>);
+pub struct PropertyBagIter<'a>(slice::Iter<'a, (LocalName, &'a CStr)>);
 
 trait Utf8CStrToStr {
     fn to_str_utf8(&self) -> &str;
@@ -57,11 +57,8 @@ impl<'a> PropertyBag<'a> {
                     let key_str = CStr::from_ptr(key);
                     let val_str = CStr::from_ptr(val);
 
-                    // We silently drop unknown attributes.  New attributes should be added in
-                    // build.rs.
-                    if let Ok(attr) = Attribute::from_str(key_str.to_str_utf8()) {
-                        array.push((attr, val_str));
-                    }
+                    let attr = LocalName::from(key_str.to_str_utf8());
+                    array.push((attr, val_str));
                 } else {
                     break;
                 }
@@ -83,10 +80,10 @@ impl<'a> PropertyBag<'a> {
 }
 
 impl<'a> Iterator for PropertyBagIter<'a> {
-    type Item = (Attribute, &'a str);
+    type Item = (LocalName, &'a str);
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.0.next().map(|&(a, v)| (a, v.to_str_utf8()))
+        self.0.next().map(|(a, v)| (a.clone(), v.to_str_utf8()))
     }
 }
 
@@ -126,11 +123,11 @@ mod tests {
 
         for (a, v) in pbag.iter() {
             match a {
-                Attribute::Rx => {
+                local_name!("rx") => {
                     assert!(v == "1");
                     had_rx = true;
                 }
-                Attribute::Ry => {
+                local_name!("ry") => {
                     assert!(v == "2");
                     had_ry = true;
                 }
diff --git a/rsvg_internals/src/shapes.rs b/rsvg_internals/src/shapes.rs
index 5bca9abb..fffba81f 100644
--- a/rsvg_internals/src/shapes.rs
+++ b/rsvg_internals/src/shapes.rs
@@ -3,7 +3,6 @@ use std::cell::Cell;
 use std::cell::RefCell;
 use std::ops::Deref;
 
-use crate::attributes::Attribute;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::length::*;
@@ -125,7 +124,7 @@ impl NodePath {
 impl NodeTrait for NodePath {
     fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
-            if attr == Attribute::D {
+            if attr == local_name!("d") {
                 let mut builder = PathBuilder::new();
 
                 if let Err(e) = path_parser::parse_path_into_builder(value, &mut builder) {
@@ -229,8 +228,7 @@ impl NodePoly {
 impl NodeTrait for NodePoly {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
-            // support for svg < 1.0 which used verts
-            if attr == Attribute::Points || attr == Attribute::Verts {
+            if attr == local_name!("points") {
                 *self.points.borrow_mut() = attr.parse(value.trim()).map(Some)?;
             }
         }
@@ -291,10 +289,10 @@ impl NodeTrait for NodeLine {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X1 => self.x1.set(attr.parse(value)?),
-                Attribute::Y1 => self.y1.set(attr.parse(value)?),
-                Attribute::X2 => self.x2.set(attr.parse(value)?),
-                Attribute::Y2 => self.y2.set(attr.parse(value)?),
+                local_name!("x1") => self.x1.set(attr.parse(value)?),
+                local_name!("y1") => self.y1.set(attr.parse(value)?),
+                local_name!("x2") => self.x2.set(attr.parse(value)?),
+                local_name!("y2") => self.y2.set(attr.parse(value)?),
                 _ => (),
             }
         }
@@ -357,19 +355,19 @@ impl NodeTrait for NodeRect {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(attr.parse(value)?),
-                Attribute::Y => self.y.set(attr.parse(value)?),
-                Attribute::Width => self
+                local_name!("x") => self.x.set(attr.parse(value)?),
+                local_name!("y") => self.y.set(attr.parse(value)?),
+                local_name!("width") => self
                     .w
                     .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
-                Attribute::Height => self
+                local_name!("height") => self
                     .h
                     .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
-                Attribute::Rx => self.rx.set(
+                local_name!("rx") => self.rx.set(
                     attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                         .map(Some)?,
                 ),
-                Attribute::Ry => self.ry.set(
+                local_name!("ry") => self.ry.set(
                     attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                         .map(Some)?,
                 ),
@@ -581,9 +579,9 @@ impl NodeTrait for NodeCircle {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Cx => self.cx.set(attr.parse(value)?),
-                Attribute::Cy => self.cy.set(attr.parse(value)?),
-                Attribute::R => self
+                local_name!("cx") => self.cx.set(attr.parse(value)?),
+                local_name!("cy") => self.cy.set(attr.parse(value)?),
+                local_name!("r") => self
                     .r
                     .set(attr.parse_and_validate(value, LengthBoth::check_nonnegative)?),
 
@@ -635,12 +633,12 @@ impl NodeTrait for NodeEllipse {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Cx => self.cx.set(attr.parse(value)?),
-                Attribute::Cy => self.cy.set(attr.parse(value)?),
-                Attribute::Rx => self
+                local_name!("cx") => self.cx.set(attr.parse(value)?),
+                local_name!("cy") => self.cy.set(attr.parse(value)?),
+                local_name!("rx") => self
                     .rx
                     .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
-                Attribute::Ry => self
+                local_name!("ry") => self
                     .ry
                     .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
 
diff --git a/rsvg_internals/src/stop.rs b/rsvg_internals/src/stop.rs
index f125d5dc..84f9bf6a 100644
--- a/rsvg_internals/src/stop.rs
+++ b/rsvg_internals/src/stop.rs
@@ -1,6 +1,5 @@
 use std::cell::Cell;
 
-use crate::attributes::Attribute;
 use crate::error::*;
 use crate::length::*;
 use crate::node::*;
@@ -37,7 +36,7 @@ impl NodeTrait for NodeStop {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Offset => {
+                local_name!("offset") => {
                     self.offset.set(
                         attr.parse_and_validate(value, validate_offset)
                             .map(|l| UnitInterval::clamp(l.length()))?,
diff --git a/rsvg_internals/src/structure.rs b/rsvg_internals/src/structure.rs
index 135148ca..2df500c7 100644
--- a/rsvg_internals/src/structure.rs
+++ b/rsvg_internals/src/structure.rs
@@ -5,7 +5,6 @@ use cairo::Rectangle;
 
 use crate::allowed_url::Fragment;
 use crate::aspect_ratio::*;
-use crate::attributes::Attribute;
 use crate::dpi::Dpi;
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
 use crate::error::{AttributeResultExt, RenderingError};
@@ -214,31 +213,31 @@ impl NodeTrait for NodeSvg {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::PreserveAspectRatio => {
+                local_name!("preserveAspectRatio") => {
                     self.preserve_aspect_ratio.set(attr.parse(value)?)
                 }
 
-                Attribute::X => {
+                local_name!("x") => {
                     if is_inner_svg {
                         self.x.set(Some(attr.parse(value)?));
                     }
                 }
 
-                Attribute::Y => {
+                local_name!("y") => {
                     if is_inner_svg {
                         self.y.set(Some(attr.parse(value)?));
                     }
                 }
 
-                Attribute::Width => self.w.set(Some(
+                local_name!("width") => self.w.set(Some(
                     attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?,
                 )),
 
-                Attribute::Height => self.h.set(Some(
+                local_name!("height") => self.h.set(Some(
                     attr.parse_and_validate(value, LengthVertical::check_nonnegative)?,
                 )),
 
-                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),
+                local_name!("viewBox") => self.vbox.set(attr.parse(value).map(Some)?),
 
                 _ => (),
             }
@@ -282,7 +281,6 @@ impl NodeTrait for NodeSvg {
                 (
                     // The client's viewport overrides the toplevel's x/y/w/h viewport
                     draw_ctx.toplevel_viewport(),
-
                     // Use our viewBox if available, or try to derive one from
                     // the intrinsic dimensions.
                     self.vbox.get().or_else(|| {
@@ -330,19 +328,18 @@ impl NodeTrait for NodeUse {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::XlinkHref => {
-                    *self.link.borrow_mut() =
-                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
+                local_name!("xlink:href") => {
+                    *self.link.borrow_mut() = Some(Fragment::parse(value).attribute(attr)?)
                 }
 
-                Attribute::X => self.x.set(attr.parse(value)?),
-                Attribute::Y => self.y.set(attr.parse(value)?),
+                local_name!("x") => self.x.set(attr.parse(value)?),
+                local_name!("y") => self.y.set(attr.parse(value)?),
 
-                Attribute::Width => self.w.set(
+                local_name!("width") => self.w.set(
                     attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                         .map(Some)?,
                 ),
-                Attribute::Height => self.h.set(
+                local_name!("height") => self.h.set(
                     attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                         .map(Some)?,
                 ),
@@ -479,11 +476,11 @@ impl NodeTrait for NodeSymbol {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::PreserveAspectRatio => {
+                local_name!("preserveAspectRatio") => {
                     self.preserve_aspect_ratio.set(attr.parse(value)?)
                 }
 
-                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),
+                local_name!("viewBox") => self.vbox.set(attr.parse(value).map(Some)?),
 
                 _ => (),
             }
diff --git a/rsvg_internals/src/style.rs b/rsvg_internals/src/style.rs
index cab5395a..4e92a9d7 100644
--- a/rsvg_internals/src/style.rs
+++ b/rsvg_internals/src/style.rs
@@ -1,4 +1,3 @@
-use crate::attributes::Attribute;
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
 use crate::property_bag::PropertyBag;
 use crate::text::NodeChars;
@@ -57,7 +56,7 @@ impl NodeStyle {
 impl NodeTrait for NodeStyle {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
-            if attr == Attribute::Type {
+            if attr == local_name!("type") {
                 *self.type_.borrow_mut() = Some(value.to_string());
             }
         }
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 3a832b52..68b87dcd 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -5,7 +5,6 @@ use pangocairo;
 use std::cell::{Cell, RefCell};
 
 use crate::allowed_url::Fragment;
-use crate::attributes::Attribute;
 use crate::bbox::BoundingBox;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::{AttributeResultExt, RenderingError};
@@ -15,6 +14,7 @@ use crate::length::*;
 use crate::node::{CascadedValues, NodeResult, NodeTrait, NodeType, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::properties::ComputedValues;
+use crate::property_bag::PropertyBag;
 use crate::property_defs::{
     Direction,
     FontStretch,
@@ -27,7 +27,6 @@ use crate::property_defs::{
     XmlLang,
     XmlSpace,
 };
-use crate::property_bag::PropertyBag;
 use crate::space::{xml_space_normalize, NormalizeDefault, XmlSpaceNormalize};
 
 /// An absolutely-positioned array of `Span`s
@@ -586,10 +585,10 @@ impl NodeTrait for NodeText {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(attr.parse(value)?),
-                Attribute::Y => self.y.set(attr.parse(value)?),
-                Attribute::Dx => self.dx.set(attr.parse(value).map(Some)?),
-                Attribute::Dy => self.dy.set(attr.parse(value).map(Some)?),
+                local_name!("x") => self.x.set(attr.parse(value)?),
+                local_name!("y") => self.y.set(attr.parse(value)?),
+                local_name!("dx") => self.dx.set(attr.parse(value).map(Some)?),
+                local_name!("dy") => self.dy.set(attr.parse(value).map(Some)?),
                 _ => (),
             }
         }
@@ -709,9 +708,8 @@ impl NodeTrait for NodeTRef {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::XlinkHref => {
-                    *self.link.borrow_mut() =
-                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
+                local_name!("xlink:href") => {
+                    *self.link.borrow_mut() = Some(Fragment::parse(value).attribute(attr)?)
                 }
                 _ => (),
             }
@@ -765,10 +763,10 @@ impl NodeTrait for NodeTSpan {
     fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::X => self.x.set(attr.parse(value).map(Some)?),
-                Attribute::Y => self.y.set(attr.parse(value).map(Some)?),
-                Attribute::Dx => self.dx.set(attr.parse(value).map(Some)?),
-                Attribute::Dy => self.dy.set(attr.parse(value).map(Some)?),
+                local_name!("x") => self.x.set(attr.parse(value).map(Some)?),
+                local_name!("y") => self.y.set(attr.parse(value).map(Some)?),
+                local_name!("dx") => self.dx.set(attr.parse(value).map(Some)?),
+                local_name!("dy") => self.dy.set(attr.parse(value).map(Some)?),
                 _ => (),
             }
         }
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index 9a459b0a..fd18957b 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -3,11 +3,11 @@ use encoding::label::encoding_from_whatwg_label;
 use encoding::DecoderTrap;
 use glib::IsA;
 use libc;
+use markup5ever::LocalName;
 use std::collections::HashMap;
 use std::str;
 
 use crate::allowed_url::AllowedUrl;
-use crate::attributes::Attribute;
 use crate::create_node::create_node_and_register_id;
 use crate::css::CssRules;
 use crate::error::LoadingError;
@@ -338,9 +338,9 @@ impl XmlState {
 
         for (attr, value) in pbag.iter() {
             match attr {
-                Attribute::Href => href = Some(value),
-                Attribute::Parse => parse = Some(value),
-                Attribute::Encoding => encoding = Some(value),
+                local_name!("href") => href = Some(value),
+                ref n if *n == LocalName::from("parse") => parse = Some(value),
+                local_name!("encoding") => encoding = Some(value),
                 _ => (),
             }
         }


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