[librsvg: 1/7] Implement geometry properties




commit c9571ccb8b98fc8d9c1cde568e95d1f4fcbefaaf
Author: Jeremias Ortega <jeremias ortega tech>
Date:   Sun Feb 27 22:22:50 2022 -0600

    Implement geometry properties
    
    Closes https://gitlab.gnome.org/GNOME/librsvg/-/issues/772 
https://gitlab.gnome.org/GNOME/librsvg/-/issues/773 https://gitlab.gnome.org/GNOME/librsvg/-/issues/774 
https://gitlab.gnome.org/GNOME/librsvg/-/issues/775 https://gitlab.gnome.org/GNOME/librsvg/-/issues/776
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/670>

 src/api.rs                        |  22 ++---
 src/c_api/handle.rs               |   4 +-
 src/c_api/sizing.rs               |   6 +-
 src/handle.rs                     |  14 ++--
 src/image.rs                      |  16 +---
 src/properties.rs                 |  27 ++++---
 src/property_defs.rs              |  90 +++++++++++++++++++++
 src/shapes.rs                     | 164 +++++++++++++++-----------------------
 src/structure.rs                  |  66 +++++++--------
 tests/src/intrinsic_dimensions.rs |   8 +-
 tests/src/predicates/svg.rs       |   2 +-
 tests/src/reference.rs            |  20 ++---
 12 files changed, 239 insertions(+), 200 deletions(-)
---
diff --git a/src/api.rs b/src/api.rs
index 8316b2907..16175f60c 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -268,11 +268,11 @@ const DEFAULT_DPI_Y: f64 = 96.0;
 /// ```
 /// In this case, the length fields will be set to `Some()`, and `vbox` to `None`.
 pub struct IntrinsicDimensions {
-    /// `width` attribute of the `<svg>`, if present
-    pub width: Option<Length>,
+    /// `width` attribute of the `<svg>`
+    pub width: Length,
 
-    /// `height` attribute of the `<svg>`, if present
-    pub height: Option<Length>,
+    /// `height` attribute of the `<svg>`
+    pub height: Length,
 
     /// `viewBox` attribute of the `<svg>`, if present
     pub vbox: Option<cairo::Rectangle>,
@@ -343,8 +343,8 @@ impl<'a> CairoRenderer<'a> {
         let d = self.handle.0.get_intrinsic_dimensions();
 
         IntrinsicDimensions {
-            width: d.width.map(Into::into),
-            height: d.height.map(Into::into),
+            width: Into::into(d.width),
+            height: Into::into(d.height),
             vbox: d.vbox.map(|v| cairo::Rectangle::from(*v)),
         }
     }
@@ -366,14 +366,8 @@ impl<'a> CairoRenderer<'a> {
     /// application can use a viewport size to scale percentage-based dimensions.
     pub fn intrinsic_size_in_pixels(&self) -> Option<(f64, f64)> {
         let dim = self.intrinsic_dimensions();
-
-        // missing width/height default to "auto", which compute to "100%"
-        let width = dim
-            .width
-            .unwrap_or_else(|| Length::new(1.0, LengthUnit::Percent));
-        let height = dim
-            .height
-            .unwrap_or_else(|| Length::new(1.0, LengthUnit::Percent));
+        let width = dim.width;
+        let height = dim.height;
 
         if width.unit == LengthUnit::Percent || height.unit == LengthUnit::Percent {
             return None;
diff --git a/src/c_api/handle.rs b/src/c_api/handle.rs
index 0619325c0..9106f9da8 100644
--- a/src/c_api/handle.rs
+++ b/src/c_api/handle.rs
@@ -1736,8 +1736,8 @@ pub unsafe extern "C" fn rsvg_handle_get_intrinsic_dimensions(
     let h = d.height;
     let r = d.vbox.map(RsvgRectangle::from);
 
-    set_out_param(out_has_width, out_width, &w.map(Into::into));
-    set_out_param(out_has_height, out_height, &h.map(Into::into));
+    set_out_param(out_has_width, out_width, &Into::into(w));
+    set_out_param(out_has_height, out_height, &Into::into(h));
     set_out_param(out_has_viewbox, out_viewbox, &r);
 }
 
diff --git a/src/c_api/sizing.rs b/src/c_api/sizing.rs
index b475d4281..902c188f7 100644
--- a/src/c_api/sizing.rs
+++ b/src/c_api/sizing.rs
@@ -12,7 +12,7 @@
 
 use float_cmp::approx_eq;
 
-use crate::api::{CairoRenderer, IntrinsicDimensions, Length, RenderingError};
+use crate::api::{CairoRenderer, IntrinsicDimensions, RenderingError};
 use crate::dpi::Dpi;
 use crate::handle::Handle;
 use crate::length::*;
@@ -101,10 +101,6 @@ fn size_in_pixels_from_percentage_width_and_height(
 
     let (w, h) = handle.width_height_to_user(dpi);
 
-    // missing width/height default to "auto", which compute to "100%"
-    let width = width.unwrap_or_else(|| Length::new(1.0, Percent));
-    let height = height.unwrap_or_else(|| Length::new(1.0, Percent));
-
     // Avoid division by zero below.  If the viewBox is zero-sized, there's
     // not much we can do.
     if approx_eq!(f64, vbox.width, 0.0) || approx_eq!(f64, vbox.height, 0.0) {
diff --git a/src/handle.rs b/src/handle.rs
index 4c8ea852b..2480eb9a8 100644
--- a/src/handle.rs
+++ b/src/handle.rs
@@ -116,13 +116,8 @@ impl Handle {
     pub fn width_height_to_user(&self, dpi: Dpi) -> (f64, f64) {
         let dimensions = self.get_intrinsic_dimensions();
 
-        // missing width/height default to "auto", which compute to "100%"
-        let width = dimensions
-            .width
-            .unwrap_or_else(|| ULength::new(1.0, LengthUnit::Percent));
-        let height = dimensions
-            .height
-            .unwrap_or_else(|| ULength::new(1.0, LengthUnit::Percent));
+        let width = dimensions.width;
+        let height = dimensions.height;
 
         let view_params = ViewParams::new(dpi, 0.0, 0.0);
         let root = self.document.root();
@@ -360,7 +355,10 @@ impl Handle {
     }
 
     pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
-        borrow_element_as!(self.document.root(), Svg).get_intrinsic_dimensions()
+        let root = self.document.root();
+        let cascaded = CascadedValues::new_from_node(&root);
+        let values = cascaded.get();
+        borrow_element_as!(self.document.root(), Svg).get_intrinsic_dimensions(values)
     }
 
     pub fn set_stylesheet(&mut self, css: &str) -> Result<(), LoadingError> {
diff --git a/src/image.rs b/src/image.rs
index 95e48a242..d2f8c7155 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -18,10 +18,6 @@ use crate::xml::Attributes;
 
 #[derive(Default)]
 pub struct Image {
-    x: Length<Horizontal>,
-    y: Length<Vertical>,
-    width: LengthOrAuto<Horizontal>,
-    height: LengthOrAuto<Vertical>,
     aspect: AspectRatio,
     href: Option<String>,
 }
@@ -30,10 +26,6 @@ impl SetAttributes for Image {
     fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
         for (attr, value) in attrs.iter() {
             match attr.expanded() {
-                expanded_name!("", "x") => self.x = attr.parse(value)?,
-                expanded_name!("", "y") => self.y = attr.parse(value)?,
-                expanded_name!("", "width") => self.width = attr.parse(value)?,
-                expanded_name!("", "height") => self.height = attr.parse(value)?,
                 expanded_name!("", "preserveAspectRatio") => self.aspect = attr.parse(value)?,
 
                 // "path" is used by some older Adobe Illustrator versions
@@ -74,14 +66,14 @@ impl Draw for Image {
         let view_params = draw_ctx.get_view_params();
         let params = NormalizeParams::new(values, &view_params);
 
-        let x = self.x.to_user(&params);
-        let y = self.y.to_user(&params);
+        let x = values.x().0.to_user(&params);
+        let y = values.y().0.to_user(&params);
 
-        let w = match self.width {
+        let w = match values.width().0 {
             LengthOrAuto::Length(l) => l.to_user(&params),
             LengthOrAuto::Auto => surface.width() as f64,
         };
-        let h = match self.height {
+        let h = match values.height().0 {
             LengthOrAuto::Length(l) => l.to_user(&params),
             LengthOrAuto::Auto => surface.height() as f64,
         };
diff --git a/src/properties.rs b/src/properties.rs
index 071646e34..eb5fc24c6 100644
--- a/src/properties.rs
+++ b/src/properties.rs
@@ -397,15 +397,6 @@ make_properties! {
     }
 
     // longhands that are presentation attributes right now, but need to be turned into properties:
-    // "cx" - applies only to circle, ellipse
-    // "cy" - applies only to circle, ellipse
-    // "height" - applies only to foreignObject, image, rect, svg, symbol, use
-    // "width"  - applies only to foreignObject, image, rect, svg, symbol, use
-    // "x"      - applies only to foreignObject, image, rect, svg, symbol, use
-    // "y"      - applies only to foreignObject, image, rect, svg, symbol, use
-    // "r"      - applies only to circle
-    // "rx"     - applies only to ellipse, rect
-    // "ry"     - applies only to ellipse, rect
     // "d"      - applies only to path
 
     longhands: {
@@ -417,6 +408,8 @@ make_properties! {
         // "color-interpolation"      => (PresentationAttr::Yes, unimplemented),
         "color-interpolation-filters" => (PresentationAttr::Yes, color_interpolation_filters : 
ColorInterpolationFilters),
         // "cursor"                   => (PresentationAttr::Yes, unimplemented),
+        "cx"                          => (PresentationAttr::Yes, cx: CX),
+        "cy"                          => (PresentationAttr::Yes, cy: CY),
         "direction"                   => (PresentationAttr::Yes, direction                   : Direction),
         "display"                     => (PresentationAttr::Yes, display                     : Display),
         // "dominant-baseline"        => (PresentationAttr::Yes, unimplemented),
@@ -450,6 +443,7 @@ make_properties! {
         // So, we put the property here, not in the shorthands, and deal with it as a
         // special case in the text handling code.
         "glyph-orientation-vertical"  => (PresentationAttr::Yes, glyph_orientation_vertical  : 
GlyphOrientationVertical),
+        "height" => (PresentationAttr::Yes, height: Height),
 
         // "image-rendering"          => (PresentationAttr::Yes, unimplemented),
         "letter-spacing"              => (PresentationAttr::Yes, letter_spacing              : 
LetterSpacing),
@@ -461,6 +455,9 @@ make_properties! {
         "opacity"                     => (PresentationAttr::Yes, opacity                     : Opacity),
         "overflow"                    => (PresentationAttr::Yes, overflow                    : Overflow),
         // "pointer-events"           => (PresentationAttr::Yes, unimplemented),
+        "r"                           => (PresentationAttr::Yes, r: R),
+        "rx"                          => (PresentationAttr::Yes, rx: RX),
+        "ry"                          => (PresentationAttr::Yes, ry: RY),
         "shape-rendering"             => (PresentationAttr::Yes, shape_rendering             : 
ShapeRendering),
         "stop-color"                  => (PresentationAttr::Yes, stop_color                  : StopColor),
         "stop-opacity"                => (PresentationAttr::Yes, stop_opacity                : StopOpacity),
@@ -490,7 +487,10 @@ make_properties! {
         "visibility"                  => (PresentationAttr::Yes, visibility                  : Visibility),
         // "white-space"              => (PresentationAttr::Yes, unimplemented),
         // "word-spacing"             => (PresentationAttr::Yes, unimplemented),
+        "width"                       => (PresentationAttr::Yes, width: Width),
         "writing-mode"                => (PresentationAttr::Yes, writing_mode                : WritingMode),
+        "x"                           => (PresentationAttr::Yes, x: X),
+        "y"                           => (PresentationAttr::Yes, y: Y),
     }
 
     longhands_not_supported_by_markup5ever: {
@@ -699,6 +699,8 @@ impl SpecifiedValues {
         compute!(ClipRule, clip_rule);
         compute!(Color, color);
         compute!(ColorInterpolationFilters, color_interpolation_filters);
+        compute!(CX, cx);
+        compute!(CY, cy);
         compute!(Direction, direction);
         compute!(Display, display);
         compute!(EnableBackground, enable_background);
@@ -714,6 +716,7 @@ impl SpecifiedValues {
         compute!(FontVariant, font_variant);
         compute!(FontWeight, font_weight);
         compute!(GlyphOrientationVertical, glyph_orientation_vertical);
+        compute!(Height, height);
         compute!(Isolation, isolation);
         compute!(LetterSpacing, letter_spacing);
         compute!(LightingColor, lighting_color);
@@ -726,6 +729,9 @@ impl SpecifiedValues {
         compute!(Opacity, opacity);
         compute!(Overflow, overflow);
         compute!(PaintOrder, paint_order);
+        compute!(R, r);
+        compute!(RX, rx);
+        compute!(RY, ry);
         compute!(ShapeRendering, shape_rendering);
         compute!(StopColor, stop_color);
         compute!(StopOpacity, stop_opacity);
@@ -744,9 +750,12 @@ impl SpecifiedValues {
         compute!(TransformProperty, transform_property);
         compute!(UnicodeBidi, unicode_bidi);
         compute!(Visibility, visibility);
+        compute!(Width, width);
         compute!(WritingMode, writing_mode);
+        compute!(X, x);
         compute!(XmlSpace, xml_space);
         compute!(XmlLang, xml_lang);
+        compute!(Y, y);
 
         computed.transform = self.transform.unwrap_or_else(|| {
             match self.get_property(PropertyId::TransformProperty) {
diff --git a/src/property_defs.rs b/src/property_defs.rs
index b4699b04a..1ffd6e029 100644
--- a/src/property_defs.rs
+++ b/src/property_defs.rs
@@ -181,6 +181,26 @@ make_property!(
     "sRGB" => Srgb,
 );
 
+make_property!(
+    /// `cx` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#CX
+    CX,
+    default: Length::<Horizontal>::parse_str("0").unwrap(),
+    inherits_automatically: false,
+    newtype_parse: Length<Horizontal>,
+);
+
+make_property!(
+    /// `cy` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#CY
+    CY,
+    default: Length::<Vertical>::parse_str("0").unwrap(),
+    inherits_automatically: false,
+    newtype_parse: Length<Vertical>,
+);
+
 make_property!(
     /// `direction` property.
     ///
@@ -510,6 +530,16 @@ make_property!(
     inherits_automatically: false,
 );
 
+make_property!(
+    /// `height` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#Sizing
+    Height,
+    default: LengthOrAuto::<Vertical>::Auto,
+    inherits_automatically: false,
+    newtype_parse: LengthOrAuto<Vertical>,
+);
+
 make_property!(
     /// `isolation` property.
     ///
@@ -774,6 +804,36 @@ fn parses_paint_order() {
     assert!(PaintOrder::parse_str("markers stroke fill hello").is_err());
 }
 
+make_property!(
+    /// `r` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#R
+    R,
+    default: Length::<Both>::parse_str("0").unwrap(),
+    inherits_automatically: false,
+    newtype_parse: Length<Both>,
+);
+
+make_property!(
+    /// `rx` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#RX
+    RX,
+    default: LengthOrAuto::<Horizontal>::Auto,
+    inherits_automatically: false,
+    newtype_parse: LengthOrAuto<Horizontal>,
+);
+
+make_property!(
+    /// `ry` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#RY
+    RY,
+    default: LengthOrAuto::<Vertical>::Auto,
+    inherits_automatically: false,
+    newtype_parse: LengthOrAuto<Vertical>,
+);
+
 make_property!(
     /// `shape-rendering` property.
     ///
@@ -1097,6 +1157,16 @@ make_property!(
     "collapse" => Collapse,
 );
 
+make_property!(
+    /// `width` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#Sizing
+    Width,
+    default: LengthOrAuto::<Horizontal>::Auto,
+    inherits_automatically: false,
+    newtype_parse: LengthOrAuto<Horizontal>,
+);
+
 make_property!(
     /// `writing-mode` property.
     ///
@@ -1147,6 +1217,16 @@ impl WritingMode {
     }
 }
 
+make_property!(
+    /// `x` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#X
+    X,
+    default: Length::<Horizontal>::parse_str("0").unwrap(),
+    inherits_automatically: false,
+    newtype_parse: Length<Horizontal>,
+);
+
 make_property!(
     /// `xml:lang` attribute.
     ///
@@ -1204,3 +1284,13 @@ make_property!(
     "default" => Default,
     "preserve" => Preserve,
 );
+
+make_property!(
+    /// `y` attribute.
+    ///
+    /// https://www.w3.org/TR/SVG2/geometry.html#Y
+    Y,
+    default: Length::<Vertical>::parse_str("0").unwrap(),
+    inherits_automatically: false,
+    newtype_parse: Length<Vertical>,
+);
diff --git a/src/shapes.rs b/src/shapes.rs
index b8f0f6f3a..f9d0d35d7 100644
--- a/src/shapes.rs
+++ b/src/shapes.rs
@@ -17,6 +17,7 @@ use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow};
 use crate::parsers::{optional_comma, Parse, ParseValue};
 use crate::path_builder::{LargeArc, Path as SvgPath, PathBuilder, Sweep};
+use crate::properties::ComputedValues;
 use crate::xml::Attributes;
 
 #[derive(PartialEq)]
@@ -37,7 +38,7 @@ impl ShapeDef {
 }
 
 trait BasicShape {
-    fn make_shape(&self, params: &NormalizeParams) -> ShapeDef;
+    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef;
 }
 
 macro_rules! impl_draw {
@@ -54,7 +55,7 @@ macro_rules! impl_draw {
                 let values = cascaded.get();
                 let view_params = draw_ctx.get_view_params();
                 let params = NormalizeParams::new(values, &view_params);
-                let shape_def = self.make_shape(&params);
+                let shape_def = self.make_shape(&params, values);
 
                 let is_visible = values.is_visible();
                 let paint_order = values.paint_order();
@@ -249,7 +250,7 @@ impl SetAttributes for Path {
 }
 
 impl BasicShape for Path {
-    fn make_shape(&self, _params: &NormalizeParams) -> ShapeDef {
+    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
         ShapeDef::new(self.path.clone(), Markers::Yes)
     }
 }
@@ -330,7 +331,7 @@ impl SetAttributes for Polygon {
 }
 
 impl BasicShape for Polygon {
-    fn make_shape(&self, _params: &NormalizeParams) -> ShapeDef {
+    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
         ShapeDef::new(Rc::new(make_poly(&self.points, true)), Markers::Yes)
     }
 }
@@ -355,7 +356,7 @@ impl SetAttributes for Polyline {
 }
 
 impl BasicShape for Polyline {
-    fn make_shape(&self, _params: &NormalizeParams) -> ShapeDef {
+    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
         ShapeDef::new(Rc::new(make_poly(&self.points, false)), Markers::Yes)
     }
 }
@@ -387,7 +388,7 @@ impl SetAttributes for Line {
 }
 
 impl BasicShape for Line {
-    fn make_shape(&self, params: &NormalizeParams) -> ShapeDef {
+    fn make_shape(&self, params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
         let mut builder = PathBuilder::default();
 
         let x1 = self.x1.to_user(params);
@@ -403,65 +404,35 @@ impl BasicShape for Line {
 }
 
 #[derive(Default)]
-pub struct Rect {
-    x: Length<Horizontal>,
-    y: Length<Vertical>,
-    width: LengthOrAuto<Horizontal>,
-    height: LengthOrAuto<Vertical>,
-
-    // Radiuses for rounded corners
-    rx: Option<Length<Horizontal>>,
-    ry: Option<Length<Vertical>>,
-}
+pub struct Rect {}
 
 impl_draw!(Rect);
 
-impl SetAttributes for Rect {
-    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
-        for (attr, value) in attrs.iter() {
-            match attr.expanded() {
-                expanded_name!("", "x") => self.x = attr.parse(value)?,
-                expanded_name!("", "y") => self.y = attr.parse(value)?,
-                expanded_name!("", "width") => self.width = attr.parse(value)?,
-                expanded_name!("", "height") => self.height = attr.parse(value)?,
-                expanded_name!("", "rx") => self.rx = attr.parse(value)?,
-                expanded_name!("", "ry") => self.ry = attr.parse(value)?,
-                _ => (),
-            }
-        }
-
-        Ok(())
-    }
-}
+impl SetAttributes for Rect {}
 
 impl BasicShape for Rect {
     #[allow(clippy::many_single_char_names)]
-    fn make_shape(&self, params: &NormalizeParams) -> ShapeDef {
-        let x = self.x.to_user(params);
-        let y = self.y.to_user(params);
+    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
+        let x = values.x().0.to_user(params);
+        let y = values.y().0.to_user(params);
 
-        let w = match self.width {
+        let w = match values.width().0 {
             LengthOrAuto::Length(l) => l.to_user(params),
             LengthOrAuto::Auto => 0.0,
         };
-        let h = match self.height {
+        let h = match values.height().0 {
             LengthOrAuto::Length(l) => l.to_user(params),
             LengthOrAuto::Auto => 0.0,
         };
 
-        let specified_rx = self.rx.map(|l| l.to_user(params));
-        let specified_ry = self.ry.map(|l| l.to_user(params));
-
-        fn nonnegative_or_none(l: f64) -> Option<f64> {
-            if l < 0.0 {
-                None
-            } else {
-                Some(l)
-            }
-        }
-
-        let norm_rx = specified_rx.and_then(nonnegative_or_none);
-        let norm_ry = specified_ry.and_then(nonnegative_or_none);
+        let norm_rx = match values.rx().0 {
+            LengthOrAuto::Length(l) => Some(l.to_user(params)),
+            LengthOrAuto::Auto => None,
+        };
+        let norm_ry = match values.ry().0 {
+            LengthOrAuto::Length(l) => Some(l.to_user(params)),
+            LengthOrAuto::Auto => None,
+        };
 
         let mut rx;
         let mut ry;
@@ -622,71 +593,66 @@ impl BasicShape for Rect {
 }
 
 #[derive(Default)]
-pub struct Circle {
-    cx: Length<Horizontal>,
-    cy: Length<Vertical>,
-    r: ULength<Both>,
-}
+pub struct Circle {}
 
 impl_draw!(Circle);
 
-impl SetAttributes for Circle {
-    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
-        for (attr, value) in attrs.iter() {
-            match attr.expanded() {
-                expanded_name!("", "cx") => self.cx = attr.parse(value)?,
-                expanded_name!("", "cy") => self.cy = attr.parse(value)?,
-                expanded_name!("", "r") => self.r = attr.parse(value)?,
-                _ => (),
-            }
-        }
-
-        Ok(())
-    }
-}
+impl SetAttributes for Circle {}
 
 impl BasicShape for Circle {
-    fn make_shape(&self, params: &NormalizeParams) -> ShapeDef {
-        let cx = self.cx.to_user(params);
-        let cy = self.cy.to_user(params);
-        let r = self.r.to_user(params);
+    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
+        let cx = values.cx().0.to_user(params);
+        let cy = values.cy().0.to_user(params);
+        let r = values.r().0.to_user(params);
 
         ShapeDef::new(Rc::new(make_ellipse(cx, cy, r, r)), Markers::No)
     }
 }
 
 #[derive(Default)]
-pub struct Ellipse {
-    cx: Length<Horizontal>,
-    cy: Length<Vertical>,
-    rx: ULength<Horizontal>,
-    ry: ULength<Vertical>,
-}
+pub struct Ellipse {}
 
 impl_draw!(Ellipse);
 
-impl SetAttributes for Ellipse {
-    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
-        for (attr, value) in attrs.iter() {
-            match attr.expanded() {
-                expanded_name!("", "cx") => self.cx = attr.parse(value)?,
-                expanded_name!("", "cy") => self.cy = attr.parse(value)?,
-                expanded_name!("", "rx") => self.rx = attr.parse(value)?,
-                expanded_name!("", "ry") => self.ry = attr.parse(value)?,
-                _ => (),
+impl SetAttributes for Ellipse {}
+
+impl BasicShape for Ellipse {
+    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
+        let cx = values.cx().0.to_user(params);
+        let cy = values.cy().0.to_user(params);
+        let norm_rx = match values.rx().0 {
+            LengthOrAuto::Length(l) => Some(l.to_user(params)),
+            LengthOrAuto::Auto => None,
+        };
+        let norm_ry = match values.ry().0 {
+            LengthOrAuto::Length(l) => Some(l.to_user(params)),
+            LengthOrAuto::Auto => None,
+        };
+
+        let rx;
+        let ry;
+
+        match (norm_rx, norm_ry) {
+            (None, None) => {
+                rx = 0.0;
+                ry = 0.0;
             }
-        }
 
-        Ok(())
-    }
-}
+            (Some(_rx), None) => {
+                rx = _rx;
+                ry = _rx;
+            }
 
-impl BasicShape for Ellipse {
-    fn make_shape(&self, params: &NormalizeParams) -> ShapeDef {
-        let cx = self.cx.to_user(params);
-        let cy = self.cy.to_user(params);
-        let rx = self.rx.to_user(params);
-        let ry = self.ry.to_user(params);
+            (None, Some(_ry)) => {
+                rx = _ry;
+                ry = _ry;
+            }
+
+            (Some(_rx), Some(_ry)) => {
+                rx = _rx;
+                ry = _ry;
+            }
+        }
 
         ShapeDef::new(Rc::new(make_ellipse(cx, cy, rx, ry)), Markers::No)
     }
diff --git a/src/structure.rs b/src/structure.rs
index 257b04ebd..459d66d7b 100644
--- a/src/structure.rs
+++ b/src/structure.rs
@@ -14,6 +14,7 @@ use crate::layout::StackingContext;
 use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
 use crate::parsers::{Parse, ParseValue};
+use crate::properties::ComputedValues;
 use crate::rect::Rect;
 use crate::viewbox::*;
 use crate::xml::Attributes;
@@ -111,10 +112,10 @@ impl Draw for Switch {
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct IntrinsicDimensions {
     /// Contents of the `width` attribute.
-    pub width: Option<ULength<Horizontal>>,
+    pub width: ULength<Horizontal>,
 
     /// Contents of the `height` attribute.
-    pub height: Option<ULength<Vertical>>,
+    pub height: ULength<Vertical>,
 
     /// Contents of the `viewBox` attribute.
     pub vbox: Option<ViewBox>,
@@ -123,24 +124,20 @@ pub struct IntrinsicDimensions {
 #[derive(Default)]
 pub struct Svg {
     preserve_aspect_ratio: AspectRatio,
-    x: Option<Length<Horizontal>>,
-    y: Option<Length<Vertical>>,
-    width: Option<LengthOrAuto<Horizontal>>,
-    height: Option<LengthOrAuto<Vertical>>,
     vbox: Option<ViewBox>,
 }
 
 impl Svg {
-    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
-        let w = self.width.map(|length_or_auto| match length_or_auto {
+    pub fn get_intrinsic_dimensions(&self, values: &ComputedValues) -> IntrinsicDimensions {
+        let w = match values.width().0 {
             LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
             LengthOrAuto::Length(l) => l,
-        });
+        };
 
-        let h = self.height.map(|length_or_auto| match length_or_auto {
+        let h = match values.height().0 {
             LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
             LengthOrAuto::Length(l) => l,
-        });
+        };
 
         IntrinsicDimensions {
             width: w,
@@ -149,42 +146,49 @@ impl Svg {
         }
     }
 
-    fn get_unnormalized_offset(&self) -> (Length<Horizontal>, Length<Vertical>) {
+    fn get_unnormalized_offset(
+        &self,
+        values: &ComputedValues,
+    ) -> (Length<Horizontal>, Length<Vertical>) {
         // these defaults are per the spec
-        let x = self
-            .x
-            .unwrap_or_else(|| Length::<Horizontal>::parse_str("0").unwrap());
-        let y = self
-            .y
-            .unwrap_or_else(|| Length::<Vertical>::parse_str("0").unwrap());
+        let x = values.x().0;
+        let y = values.y().0;
 
         (x, y)
     }
 
-    fn get_unnormalized_size(&self) -> (ULength<Horizontal>, ULength<Vertical>) {
+    fn get_unnormalized_size(
+        &self,
+        values: &ComputedValues,
+    ) -> (ULength<Horizontal>, ULength<Vertical>) {
         // these defaults are per the spec
-        let w = match self.width {
-            None | Some(LengthOrAuto::Auto) => ULength::<Horizontal>::parse_str("100%").unwrap(),
-            Some(LengthOrAuto::Length(l)) => l,
+        let w = match values.width().0 {
+            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
+            LengthOrAuto::Length(l) => l,
         };
-        let h = match self.height {
-            None | Some(LengthOrAuto::Auto) => ULength::<Vertical>::parse_str("100%").unwrap(),
-            Some(LengthOrAuto::Length(l)) => l,
+        let h = match values.height().0 {
+            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
+            LengthOrAuto::Length(l) => l,
         };
         (w, h)
     }
 
-    fn get_viewport(&self, params: &NormalizeParams, outermost: bool) -> Rect {
+    fn get_viewport(
+        &self,
+        params: &NormalizeParams,
+        values: &ComputedValues,
+        outermost: bool,
+    ) -> Rect {
         // x & y attributes have no effect on outermost svg
         // http://www.w3.org/TR/SVG/struct.html#SVGElement
         let (nx, ny) = if outermost {
             (0.0, 0.0)
         } else {
-            let (x, y) = self.get_unnormalized_offset();
+            let (x, y) = self.get_unnormalized_offset(values);
             (x.to_user(params), y.to_user(params))
         };
 
-        let (w, h) = self.get_unnormalized_size();
+        let (w, h) = self.get_unnormalized_size(values);
         let (nw, nh) = (w.to_user(params), h.to_user(params));
 
         Rect::new(nx, ny, nx + nw, ny + nh)
@@ -209,7 +213,7 @@ impl Svg {
             None
         };
 
-        let svg_viewport = self.get_viewport(&params, !has_parent);
+        let svg_viewport = self.get_viewport(&params, values, !has_parent);
 
         let is_measuring_toplevel_svg = !has_parent && draw_ctx.is_measuring();
 
@@ -248,10 +252,6 @@ impl SetAttributes for Svg {
                 expanded_name!("", "preserveAspectRatio") => {
                     self.preserve_aspect_ratio = attr.parse(value)?
                 }
-                expanded_name!("", "x") => self.x = attr.parse(value)?,
-                expanded_name!("", "y") => self.y = attr.parse(value)?,
-                expanded_name!("", "width") => self.width = attr.parse(value)?,
-                expanded_name!("", "height") => self.height = attr.parse(value)?,
                 expanded_name!("", "viewBox") => self.vbox = attr.parse(value)?,
                 _ => (),
             }
diff --git a/tests/src/intrinsic_dimensions.rs b/tests/src/intrinsic_dimensions.rs
index ac9bd6d55..01f0704a1 100644
--- a/tests/src/intrinsic_dimensions.rs
+++ b/tests/src/intrinsic_dimensions.rs
@@ -17,8 +17,8 @@ fn no_intrinsic_dimensions() {
     assert_eq!(
         CairoRenderer::new(&svg).intrinsic_dimensions(),
         IntrinsicDimensions {
-            width: None,
-            height: None,
+            width: Length::new(1.0, LengthUnit::Percent),
+            height: Length::new(1.0, LengthUnit::Percent),
             vbox: None,
         }
     );
@@ -36,8 +36,8 @@ fn has_intrinsic_dimensions() {
     assert_eq!(
         CairoRenderer::new(&svg).intrinsic_dimensions(),
         IntrinsicDimensions {
-            width: Some(Length::new(10.0, LengthUnit::Cm)),
-            height: Some(Length::new(20.0, LengthUnit::Px)),
+            width: Length::new(10.0, LengthUnit::Cm),
+            height: Length::new(20.0, LengthUnit::Px),
             vbox: Some(cairo::Rectangle {
                 x: 0.0,
                 y: 0.0,
diff --git a/tests/src/predicates/svg.rs b/tests/src/predicates/svg.rs
index f4499e788..d70cb2d9d 100644
--- a/tests/src/predicates/svg.rs
+++ b/tests/src/predicates/svg.rs
@@ -114,7 +114,7 @@ impl DetailPredicate<SvgPredicate> {
             Detail::Size(d) => {
                 let renderer = CairoRenderer::new(handle);
                 let dimensions = renderer.intrinsic_dimensions();
-                (dimensions.width, dimensions.height) == (Some(d.w), Some(d.h))
+                (dimensions.width, dimensions.height) == (d.w, d.h)
             }
         }
     }
diff --git a/tests/src/reference.rs b/tests/src/reference.rs
index d04b4fb62..1eb088a01 100644
--- a/tests/src/reference.rs
+++ b/tests/src/reference.rs
@@ -132,16 +132,14 @@ fn image_size(dim: IntrinsicDimensions, dpi: f64) -> (i32, i32) {
 
     use librsvg::LengthUnit::*;
 
-    if let (Some(width), Some(height)) = (width, height) {
-        if !(has_supported_unit(&width) && has_supported_unit(&height)) {
-            panic!("SVG has unsupported unit type in width or height");
-        }
+    if !(has_supported_unit(&width) && has_supported_unit(&height)) {
+        panic!("SVG has unsupported unit type in width or height");
     }
 
     #[rustfmt::skip]
     let (width, height) = match (width, height, vbox) {
-        (Some(Length { length: w, unit: Percent }),
-         Some(Length { length: h, unit: Percent }), vbox) if w == 1.0 && h == 1.0 => {
+        (Length { length: w, unit: Percent },
+         Length { length: h, unit: Percent }, vbox) if w == 1.0 && h == 1.0 => {
             if let Some(vbox) = vbox {
                 (vbox.width, vbox.height)
             } else {
@@ -149,18 +147,14 @@ fn image_size(dim: IntrinsicDimensions, dpi: f64) -> (i32, i32) {
             }
         }
 
-        (Some(Length { length: _, unit: Percent }),
-         Some(Length { length: _, unit: Percent }), _) => {
+        (Length { length: _, unit: Percent },
+         Length { length: _, unit: Percent }, _) => {
             panic!("Test suite only supports percentage width/height at 100%");
         }
 
-        (Some(w), Some(h), _) => {
+        (w, h, _) => {
             (normalize(&w, dpi), normalize(&h, dpi))
         }
-
-        (None, None, Some(vbox)) => (vbox.width, vbox.height),
-
-        (_, _, _) => panic!("Test suite does not support the dimensions of this file"),
     };
 
     // Keep in sync with c_api.rs


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