[librsvg: 2/6] length: make the type generic on whether it is signed
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 2/6] length: make the type generic on whether it is signed
- Date: Tue, 22 Dec 2020 17:39:49 +0000 (UTC)
commit 38289d40b30c961d21772eb2f852c288ea7d1870
Author: Paolo Borelli <pborelli gnome org>
Date: Sat Dec 19 21:07:57 2020 +0100
length: make the type generic on whether it is signed
We now encode in the type whether the length is unsigned (non-negative)
or signed. This simplifies parsing of values (no need to validate
manually with check_nonnegative).
To reduce code churn and to help readability, the original Length type
is renamed to CssLength and two aliases Length and ULength are added.
src/dasharray.rs | 10 +--
src/drawing_ctx.rs | 4 +-
src/filter.rs | 68 +++++++----------
src/filters/bounds.rs | 8 +-
src/filters/mod.rs | 74 ++++++------------
src/image.rs | 12 +--
src/length.rs | 204 ++++++++++++++++++++++++++++----------------------
src/lib.rs | 2 +-
src/marker.rs | 18 ++---
src/pattern.rs | 19 ++---
src/shapes.rs | 54 ++++---------
src/structure.rs | 76 +++++++------------
12 files changed, 232 insertions(+), 317 deletions(-)
---
diff --git a/src/dasharray.rs b/src/dasharray.rs
index 35080c55..9af37877 100644
--- a/src/dasharray.rs
+++ b/src/dasharray.rs
@@ -9,7 +9,7 @@ use crate::parsers::{optional_comma, Parse};
#[derive(Debug, PartialEq, Clone)]
pub enum Dasharray {
None,
- Array(Vec<Length<Both>>),
+ Array(Vec<ULength<Both>>),
}
impl Default for Dasharray {
@@ -30,11 +30,7 @@ impl Parse for Dasharray {
let mut dasharray = Vec::new();
loop {
- let loc = parser.current_source_location();
-
- let d = Length::<Both>::parse(parser)?
- .check_nonnegative()
- .map_err(|e| loc.new_custom_error(e))?;
+ let d = ULength::<Both>::parse(parser)?;
dasharray.push(d);
if parser.is_exhausted() {
@@ -55,7 +51,7 @@ mod tests {
#[test]
fn parses_dash_array() {
// helper to cut down boilderplate
- let length_parse = |s| Length::<Both>::parse_str(s).unwrap();
+ let length_parse = |s| ULength::<Both>::parse_str(s).unwrap();
let expected = Dasharray::Array(vec![
length_parse("1"),
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index b471fc7e..1c7f89f0 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -42,7 +42,7 @@ use crate::transform::Transform;
use crate::unit_interval::UnitInterval;
use crate::viewbox::ViewBox;
-/// Holds values that are required to normalize `Length` values to a current viewport.
+/// Holds values that are required to normalize `CssLength` values to a current viewport.
///
/// This struct is created by calling `DrawingCtx::push_view_box()` or
/// `DrawingCtx::get_view_params()`.
@@ -371,7 +371,7 @@ impl DrawingCtx {
/// Pushes a viewport size for normalizing `Length` values.
///
- /// You should pass the returned `ViewParams` to all subsequent `Length.normalize()`
+ /// You should pass the returned `ViewParams` to all subsequent `CssLength.normalize()`
/// calls that correspond to this viewport.
///
/// The viewport will stay in place, and will be the one returned by
diff --git a/src/filter.rs b/src/filter.rs
index ffa7d5a3..8938f64a 100644
--- a/src/filter.rs
+++ b/src/filter.rs
@@ -20,8 +20,8 @@ use crate::rect::Rect;
pub struct Filter {
x: Length<Horizontal>,
y: Length<Vertical>,
- width: Length<Horizontal>,
- height: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
filterunits: CoordUnits,
primitiveunits: CoordUnits,
}
@@ -32,8 +32,8 @@ impl Default for Filter {
Self {
x: Length::<Horizontal>::parse_str("-10%").unwrap(),
y: Length::<Vertical>::parse_str("-10%").unwrap(),
- width: Length::<Horizontal>::parse_str("120%").unwrap(),
- height: Length::<Vertical>::parse_str("120%").unwrap(),
+ width: ULength::<Horizontal>::parse_str("120%").unwrap(),
+ height: ULength::<Vertical>::parse_str("120%").unwrap(),
filterunits: CoordUnits::ObjectBoundingBox,
primitiveunits: CoordUnits::UserSpaceOnUse,
}
@@ -86,56 +86,22 @@ impl SetAttributes for Filter {
// With ObjectBoundingBox, only fractions and percents are allowed.
let no_units_allowed = self.filterunits == CoordUnits::ObjectBoundingBox;
- let check_units_horizontal = |length: Length<Horizontal>| {
- if !no_units_allowed {
- return Ok(length);
- }
-
- match length.unit {
- LengthUnit::Px | LengthUnit::Percent => Ok(length),
- _ => Err(ValueErrorKind::parse_error(
- "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
- )),
- }
- };
-
- let check_units_vertical = |length: Length<Vertical>| {
- if !no_units_allowed {
- return Ok(length);
- }
-
- match length.unit {
- LengthUnit::Px | LengthUnit::Percent => Ok(length),
- _ => Err(ValueErrorKind::parse_error(
- "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
- )),
- }
- };
-
- let check_units_horizontal_and_ensure_nonnegative = |length: Length<Horizontal>| {
- check_units_horizontal(length).and_then(Length::<Horizontal>::check_nonnegative)
- };
-
- let check_units_vertical_and_ensure_nonnegative = |length: Length<Vertical>| {
- check_units_vertical(length).and_then(Length::<Vertical>::check_nonnegative)
- };
-
// Parse the rest of the attributes.
for (attr, value) in attrs.iter() {
match attr.expanded() {
expanded_name!("", "x") => {
- self.x = attr.parse_and_validate(value, check_units_horizontal)?
+ self.x = attr.parse_and_validate(value, |v| check_units(v, no_units_allowed))?
}
expanded_name!("", "y") => {
- self.y = attr.parse_and_validate(value, check_units_vertical)?
+ self.y = attr.parse_and_validate(value, |v| check_units(v, no_units_allowed))?
}
expanded_name!("", "width") => {
- self.width = attr
- .parse_and_validate(value, check_units_horizontal_and_ensure_nonnegative)?
+ self.width =
+ attr.parse_and_validate(value, |v| check_units(v, no_units_allowed))?
}
expanded_name!("", "height") => {
self.height =
- attr.parse_and_validate(value, check_units_vertical_and_ensure_nonnegative)?
+ attr.parse_and_validate(value, |v| check_units(v, no_units_allowed))?
}
expanded_name!("", "primitiveUnits") => self.primitiveunits = attr.parse(value)?,
_ => (),
@@ -146,6 +112,22 @@ impl SetAttributes for Filter {
}
}
+fn check_units<N: Normalize, V: Validate>(
+ length: CssLength<N, V>,
+ no_units_allowed: bool,
+) -> Result<CssLength<N, V>, ValueErrorKind> {
+ if !no_units_allowed {
+ return Ok(length);
+ }
+
+ match length.unit {
+ LengthUnit::Px | LengthUnit::Percent => Ok(length),
+ _ => Err(ValueErrorKind::parse_error(
+ "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
+ )),
+ }
+}
+
impl Draw for Filter {}
#[derive(Debug, Clone, PartialEq)]
diff --git a/src/filters/bounds.rs b/src/filters/bounds.rs
index 051a175c..0f56bdcb 100644
--- a/src/filters/bounds.rs
+++ b/src/filters/bounds.rs
@@ -21,8 +21,8 @@ pub struct BoundsBuilder<'a> {
/// Filter primitive properties.
x: Option<Length<Horizontal>>,
y: Option<Length<Vertical>>,
- width: Option<Length<Horizontal>>,
- height: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
}
impl<'a> BoundsBuilder<'a> {
@@ -32,8 +32,8 @@ impl<'a> BoundsBuilder<'a> {
ctx: &'a FilterContext,
x: Option<Length<Horizontal>>,
y: Option<Length<Vertical>>,
- width: Option<Length<Horizontal>>,
- height: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
) -> Self {
Self {
ctx,
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index 50b6479c..33b23ed6 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -74,8 +74,8 @@ pub mod turbulence;
struct Primitive {
x: Option<Length<Horizontal>>,
y: Option<Length<Vertical>>,
- width: Option<Length<Horizontal>>,
- height: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
result: Option<CustomIdent>,
}
@@ -149,44 +149,11 @@ impl Primitive {
.unwrap_or(CoordUnits::UserSpaceOnUse);
// With ObjectBoundingBox, only fractions and percents are allowed.
- let no_units_allowed = primitiveunits == CoordUnits::ObjectBoundingBox;
-
- let check_units_horizontal = |length: Length<Horizontal>| {
- if !no_units_allowed {
- return Ok(length);
- }
-
- match length.unit {
- LengthUnit::Px | LengthUnit::Percent => Ok(length),
- _ => Err(FilterError::InvalidUnits),
- }
- };
-
- let check_units_vertical = |length: Length<Vertical>| {
- if !no_units_allowed {
- return Ok(length);
- }
-
- match length.unit {
- LengthUnit::Px | LengthUnit::Percent => Ok(length),
- _ => Err(FilterError::InvalidUnits),
- }
- };
-
- if let Some(x) = self.x {
- check_units_horizontal(x)?;
- }
-
- if let Some(y) = self.y {
- check_units_vertical(y)?;
- }
-
- if let Some(w) = self.width {
- check_units_horizontal(w)?;
- }
-
- if let Some(h) = self.height {
- check_units_vertical(h)?;
+ if primitiveunits == CoordUnits::ObjectBoundingBox {
+ check_units(self.x)?;
+ check_units(self.y)?;
+ check_units(self.width)?;
+ check_units(self.height)?;
}
Ok(BoundsBuilder::new(
@@ -199,22 +166,25 @@ impl Primitive {
}
}
+fn check_units<N: Normalize, V: Validate>(
+ length: Option<CssLength<N, V>>,
+) -> Result<(), FilterError> {
+ match length {
+ Some(l) if l.unit == LengthUnit::Px || l.unit == LengthUnit::Percent => Ok(()),
+ Some(_) => Err(FilterError::InvalidUnits),
+ None => Ok(()),
+ }
+}
+
impl SetAttributes for Primitive {
fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
for (attr, value) in attrs.iter() {
match attr.expanded() {
- expanded_name!("", "x") => self.x = Some(attr.parse(value)?),
- expanded_name!("", "y") => self.y = Some(attr.parse(value)?),
- expanded_name!("", "width") => {
- self.width = Some(
- attr.parse_and_validate(value, Length::<Horizontal>::check_nonnegative)?,
- )
- }
- expanded_name!("", "height") => {
- self.height =
- Some(attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?)
- }
- expanded_name!("", "result") => self.result = Some(attr.parse(value)?),
+ expanded_name!("", "x") => self.x = attr.parse(value).map(Some)?,
+ expanded_name!("", "y") => self.y = attr.parse(value).map(Some)?,
+ expanded_name!("", "width") => self.width = attr.parse(value).map(Some)?,
+ expanded_name!("", "height") => self.height = attr.parse(value).map(Some)?,
+ expanded_name!("", "result") => self.result = attr.parse(value).map(Some)?,
_ => (),
}
}
diff --git a/src/image.rs b/src/image.rs
index a62052b2..2f72ce91 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -19,8 +19,8 @@ use crate::rect::Rect;
pub struct Image {
x: Length<Horizontal>,
y: Length<Vertical>,
- width: Length<Horizontal>,
- height: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
aspect: AspectRatio,
href: Option<String>,
}
@@ -31,12 +31,8 @@ impl SetAttributes for Image {
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_and_validate(value, Length::check_nonnegative)?
- }
- expanded_name!("", "height") => {
- self.height = attr.parse_and_validate(value, Length::check_nonnegative)?
- }
+ 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
diff --git a/src/length.rs b/src/length.rs
index 432472a0..ea43a6d3 100644
--- a/src/length.rs
+++ b/src/length.rs
@@ -1,46 +1,58 @@
//! CSS length values.
//!
-//! [`Length`] is the struct librsvg uses to represent CSS lengths. See its documentation for
-//! an example of how to construct it.
+//! [`CssLength`] is the struct librsvg uses to represent CSS lengths.
+//! See its documentation for examples of how to construct it.
//!
-//! Length values need to know whether they will be normalized with respect to the width,
-//! height, or both dimensions of the current viewport. So, a `Length` has a type parameter
-//! [`Normalize`]; the full type is `Length<N: Normalize>`. We provide [`Horizontal`],
+//! `CssLength` values need to know whether they will be normalized with respect to the width,
+//! height, or both dimensions of the current viewport. `CssLength` values can be signed or
+//! unsigned. So, a `CssLength` has two type parameters, [`Normalize`] and [`Validate`];
+//! the full type is `CssLength<N: Normalize, V: Validate>`. We provide [`Horizontal`],
//! [`Vertical`], and [`Both`] implementations of [`Normalize`]; these let length values know
-//! how to normalize themselves with respect to the current viewport.
+//! how to normalize themselves with respect to the current viewport. We also provide
+//! [`Signed`] and [`Unsigned`] implementations of [`Validate`].
+//!
+//! For ease of use, we define two type aliases [`Length`] and [`ULength`] corresponding to
+//! signed and unsigned.
//!
//! For example, the implementation of [`Circle`] defines this structure with fields for the
//! `(center_x, center_y, radius)`:
//!
//! ```
-//! # use librsvg::doctest_only::{Length,Horizontal,Vertical,Both};
+//! # use librsvg::doctest_only::{Length,ULength,Horizontal,Vertical,Both};
//! pub struct Circle {
//! cx: Length<Horizontal>,
//! cy: Length<Vertical>,
-//! r: Length<Both>,
+//! r: ULength<Both>,
//! }
//! ```
//!
//! This means that:
//!
-//! * `cx` and `cy` define the center of the circle, and they will be normalized with respect
-//! to the current viewport's width and height, respectively. If the SVG document specified
-//! `<circle cx="50%" cy="30%">`, the values would be normalized to be at 50% of the the
-//! viewport's width, and 30% of the viewport's height.
+//! * `cx` and `cy` define the center of the circle, they can be positive or negative, and
+//! they will be normalized with respect to the current viewport's width and height,
+//! respectively. If the SVG document specified `<circle cx="50%" cy="30%">`, the values
+//! would be normalized to be at 50% of the the viewport's width, and 30% of the viewport's
+//! height.
//!
-//! * `r` needs to be resolved against the [normalized diagonal][diag] of the current viewport.
+//! * `r` is non-negative and needs to be resolved against the [normalized diagonal][diag]
+//! of the current viewport.
//!
-//! The `N` type parameter of `Length<N>` is enough to know how to normalize a length value;
-//! the [`normalize`] method will handle it automatically.
+//! The `N` type parameter of `CssLength<N, I>` is enough to know how to normalize a length
+//! value; the [`normalize`] method will handle it automatically.
//!
//! [`Circle`]: ../shapes/struct.Circle.html
-//! [`Length`]: struct.Length.html
+//! [`CssLength`]: struct.CssLength.html
+//! [`Length`]: type.Length.html
+//! [`ULength`]: type.ULength.html
//! [`Horizontal`]: struct.Horizontal.html
//! [`Vertical`]: struct.Vertical.html
//! [`Both`]: struct.Both.html
//! [`Normalize`]: trait.Normalize.html
+//! [`Signed`]: struct.Signed.html
+//! [`Unsigned`]: struct.Unsigned.html
+//! [`Validate`]: trait.Validate.html
//! [diag]: https://www.w3.org/TR/SVG/coords.html#Units
-//! [`normalize`]: struct.Length.html#method.normalize
+//! [`normalize`]: struct.CssLength.html#method.normalize
use cssparser::{Parser, Token};
use std::f64::consts::*;
@@ -117,28 +129,28 @@ impl RsvgLength {
}
}
-/// Used for the type parameter of `Length<N: Normalize>`.
+/// Used for the `N` type parameter of `CssLength<N: Normalize, V: Validate>`.
pub trait Normalize {
/// Computes an orientation-based scaling factor.
///
- /// This is used in the [`Length.normalize`] method to resolve lengths with percentage
+ /// This is used in the [`CssLength.normalize`] method to resolve lengths with percentage
/// units; they need to be resolved with respect to the width, height, or [normalized
/// diagonal][diag] of the current viewport.
///
- /// [`Length.normalize`]: struct.Length.html#method.normalize
+ /// [`CssLength.normalize`]: struct.CssLength.html#method.normalize
/// [diag]: https://www.w3.org/TR/SVG/coords.html#Units
fn normalize(x: f64, y: f64) -> f64;
}
-/// Allows declaring `Length<Horizontal>`.
+/// Allows declaring `CssLength<Horizontal>`.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Horizontal;
-/// Allows declaring `Length<Vertical>`.
+/// Allows declaring `CssLength<Vertical>`.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Vertical;
-/// Allows declaring `Length<Both>`.
+/// Allows declaring `CssLength<Both>`.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Both;
@@ -163,19 +175,51 @@ impl Normalize for Both {
}
}
+/// Used for the `V` type parameter of `CssLength<N: Normalize, V: Validate>`.
+pub trait Validate {
+ /// Checks if the specified value is acceptable
+ ///
+ /// This is used when parsing a length value
+ fn validate(v: f64) -> Result<f64, ValueErrorKind> {
+ Ok(v)
+ }
+}
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub struct Signed;
+
+impl Validate for Signed {}
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub struct Unsigned;
+
+impl Validate for Unsigned {
+ fn validate(v: f64) -> Result<f64, ValueErrorKind> {
+ if v >= 0.0 {
+ Ok(v)
+ } else {
+ Err(ValueErrorKind::Value(
+ "value must be non-negative".to_string(),
+ ))
+ }
+ }
+}
+
/// A CSS length value.
///
/// This is equivalent to [CSS lengths].
///
/// [CSS lengths]: https://www.w3.org/TR/CSS22/syndata.html#length-units
///
-/// `Length` implements the [`Parse`] trait, so it can be parsed out of a
+/// `CssLength` implements the [`Parse`] trait, so it can be parsed out of a
/// [`cssparser::Parser`].
///
+/// This type will be normally used through the type aliases [`Length`] and [`ULength`]
+///
/// Examples of construction:
///
/// ```
-/// # use librsvg::doctest_only::{Length,LengthUnit,Horizontal,Vertical,Both};
+/// # use librsvg::doctest_only::{Length,ULength,LengthUnit,Horizontal,Vertical,Both};
/// # use librsvg::doctest_only::Parse;
/// // Explicit type
/// let width: Length<Horizontal> = Length::new(42.0, LengthUnit::Cm);
@@ -184,12 +228,14 @@ impl Normalize for Both {
/// let height = Length::<Vertical>::new(42.0, LengthUnit::Cm);
///
/// // Parsed
-/// let radius = Length::<Both>::parse_str("5px").unwrap();
+/// let radius = ULength::<Both>::parse_str("5px").unwrap();
/// ```
///
-/// During the rendering phase, a `Length` needs to be normalized into the current coordinate
-/// system's units with the [`normalize`] method.
+/// During the rendering phase, a `CssLength` needs to be normalized into the current
+/// coordinate system's units with the [`normalize`] method.
///
+/// [`Length`]: type.Length.html
+/// [`ULength`]: type.ULength.html
/// [`Normalize`]: trait.Normalize.html
/// [`Horizontal`]: struct.Horizontal.html
/// [`Vertical`]: struct.Vertical.html
@@ -199,7 +245,7 @@ impl Normalize for Both {
/// [`cssparser::Parser`]: https://docs.rs/cssparser/0.27.1/cssparser/struct.Parser.html
/// [`Parse`]: ../parsers/trait.Parse.html
#[derive(Debug, PartialEq, Copy, Clone)]
-pub struct Length<N: Normalize> {
+pub struct CssLength<N: Normalize, V: Validate> {
/// Numeric part of the length
pub length: f64,
@@ -208,10 +254,13 @@ pub struct Length<N: Normalize> {
/// Dummy; used internally for the type parameter `N`
orientation: PhantomData<N>,
+
+ /// Dummy; used internally for the type parameter `V`
+ validation: PhantomData<V>,
}
-impl<N: Normalize> From<Length<N>> for RsvgLength {
- fn from(l: Length<N>) -> RsvgLength {
+impl<N: Normalize, V: Validate> From<CssLength<N, V>> for RsvgLength {
+ fn from(l: CssLength<N, V>) -> RsvgLength {
RsvgLength {
length: l.length,
unit: l.unit,
@@ -219,9 +268,9 @@ impl<N: Normalize> From<Length<N>> for RsvgLength {
}
}
-impl<N: Normalize> Default for Length<N> {
+impl<N: Normalize, V: Validate> Default for CssLength<N, V> {
fn default() -> Self {
- Length::new(0.0, LengthUnit::Px)
+ CssLength::new(0.0, LengthUnit::Px)
}
}
@@ -230,8 +279,8 @@ const CM_PER_INCH: f64 = 2.54;
const MM_PER_INCH: f64 = 25.4;
const PICA_PER_INCH: f64 = 6.0;
-impl<N: Normalize> Parse for Length<N> {
- fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Length<N>, ParseError<'i>> {
+impl<N: Normalize, V: Validate> Parse for CssLength<N, V> {
+ fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<CssLength<N, V>, ParseError<'i>> {
let l_value;
let l_unit;
@@ -272,15 +321,18 @@ impl<N: Normalize> Parse for Length<N> {
let l_value = f64::from(finite_f32(l_value).map_err(|e| parser.new_custom_error(e))?);
- Ok(Length::new(l_value, l_unit))
+ <V as Validate>::validate(l_value)
+ .map_err(|e| parser.new_custom_error(e))
+ .map(|l_value| CssLength::new(l_value, l_unit))
}
}
-impl<N: Normalize> Length<N> {
- /// Creates a Length.
+impl<N: Normalize, V: Validate> CssLength<N, V> {
+ /// Creates a CssLength.
///
- /// The compiler needs to know the type parameter `N` which represents the length's
- /// orientation. You can specify it explicitly, or call the parametrized method:
+ /// The compiler needs to know the type parameters `N` and `V` which represents the
+ /// length's orientation and validation.
+ /// You can specify them explicitly, or call the parametrized method:
///
/// ```
/// # use librsvg::doctest_only::{Length,LengthUnit,Horizontal,Vertical};
@@ -290,42 +342,12 @@ impl<N: Normalize> Length<N> {
/// // Inferred type
/// let height = Length::<Vertical>::new(42.0, LengthUnit::Cm);
/// ```
- pub fn new(l: f64, unit: LengthUnit) -> Length<N> {
- Length {
+ pub fn new(l: f64, unit: LengthUnit) -> CssLength<N, V> {
+ CssLength {
length: l,
unit,
orientation: PhantomData,
- }
- }
-
- /// Returns `Ok(self)` if the length is >= 0, or an error.
- ///
- /// This is usually used right after parsing a length value, as part of a validation step:
- ///
- /// ```
- /// # use librsvg::doctest_only::{Length,Horizontal,LengthUnit};
- /// # use librsvg::doctest_only::ElementError;
- /// # use librsvg::doctest_only::ParseValue;
- /// # use markup5ever::{QualName, Prefix, Namespace, LocalName};
- /// # let attr = QualName::new(
- /// # Some(Prefix::from("")),
- /// # Namespace::from(""),
- /// # LocalName::from(""),
- /// # );
- /// let length: Length<Horizontal> = attr.parse_and_validate("0", Length::check_nonnegative)?;
- /// assert_eq!(length, Length::new(0.0, LengthUnit::Px));
- ///
- /// let result: Result<Length<Horizontal>, ElementError> = attr.parse_and_validate("-2",
Length::check_nonnegative);
- /// assert!(result.is_err());
- /// # Ok::<(), ElementError>(())
- /// ```
- pub fn check_nonnegative(self) -> Result<Self, ValueErrorKind> {
- if self.length >= 0.0 {
- Ok(self)
- } else {
- Err(ValueErrorKind::Value(
- "value must be non-negative".to_string(),
- ))
+ validation: PhantomData,
}
}
@@ -400,6 +422,12 @@ fn viewport_percentage(x: f64, y: f64) -> f64 {
(x * x + y * y).sqrt() / SQRT_2
}
+/// Alias for `CssLength` types that can have negative values
+pub type Length<N> = CssLength<N, Signed>;
+
+/// Alias for `CssLength` types that are non negative
+pub type ULength<N> = CssLength<N, Unsigned>;
+
#[cfg(test)]
mod tests {
use super::*;
@@ -471,6 +499,21 @@ mod tests {
);
}
+ #[test]
+ fn parses_unsigned() {
+ assert_eq!(
+ ULength::<Horizontal>::parse_str("42").unwrap(),
+ ULength::<Horizontal>::new(42.0, LengthUnit::Px)
+ );
+
+ assert_eq!(
+ ULength::<Both>::parse_str("0pt").unwrap(),
+ ULength::<Both>::new(0.0, LengthUnit::Pt)
+ );
+
+ assert!(ULength::<Horizontal>::parse_str("-42px").is_err());
+ }
+
#[test]
fn empty_length_yields_error() {
assert!(Length::<Both>::parse_str("").is_err());
@@ -481,21 +524,6 @@ mod tests {
assert!(Length::<Both>::parse_str("8furlong").is_err());
}
- #[test]
- fn check_nonnegative_works() {
- // and_then with anonymous function
- assert!(Length::<Both>::parse_str("0")
- .unwrap()
- .check_nonnegative()
- .is_ok());
-
- // and_then with named function
- assert!(Length::<Both>::parse_str("-10")
- .unwrap()
- .check_nonnegative()
- .is_err());
- }
-
#[test]
fn normalize_default_works() {
let params = ViewParams::new(Dpi::new(40.0, 40.0), 100.0, 100.0);
diff --git a/src/lib.rs b/src/lib.rs
index 0f3c7bea..159c6322 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -152,6 +152,6 @@ pub mod doctest_only {
pub use crate::error::ValueErrorKind;
pub use crate::href::is_href;
pub use crate::href::set_href;
- pub use crate::length::{Both, Horizontal, Length, LengthUnit, Vertical};
+ pub use crate::length::{Both, CssLength, Horizontal, Length, LengthUnit, ULength, Vertical};
pub use crate::parsers::{Parse, ParseValue};
}
diff --git a/src/marker.rs b/src/marker.rs
index 349528c7..18f49bac 100644
--- a/src/marker.rs
+++ b/src/marker.rs
@@ -73,8 +73,8 @@ pub struct Marker {
units: MarkerUnits,
ref_x: Length<Horizontal>,
ref_y: Length<Vertical>,
- width: Length<Horizontal>,
- height: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
orient: MarkerOrient,
aspect: AspectRatio,
vbox: Option<ViewBox>,
@@ -87,8 +87,8 @@ impl Default for Marker {
ref_x: Default::default(),
ref_y: Default::default(),
// the following two are per the spec
- width: Length::<Horizontal>::parse_str("3").unwrap(),
- height: Length::<Vertical>::parse_str("3").unwrap(),
+ width: ULength::<Horizontal>::parse_str("3").unwrap(),
+ height: ULength::<Vertical>::parse_str("3").unwrap(),
orient: MarkerOrient::default(),
aspect: AspectRatio::default(),
vbox: None,
@@ -181,14 +181,8 @@ impl SetAttributes for Marker {
expanded_name!("", "markerUnits") => self.units = attr.parse(value)?,
expanded_name!("", "refX") => self.ref_x = attr.parse(value)?,
expanded_name!("", "refY") => self.ref_y = attr.parse(value)?,
- expanded_name!("", "markerWidth") => {
- self.width =
- attr.parse_and_validate(value, Length::<Horizontal>::check_nonnegative)?
- }
- expanded_name!("", "markerHeight") => {
- self.height =
- attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?
- }
+ expanded_name!("", "markerWidth") => self.width = attr.parse(value)?,
+ expanded_name!("", "markerHeight") => self.height = attr.parse(value)?,
expanded_name!("", "orient") => self.orient = attr.parse(value)?,
expanded_name!("", "preserveAspectRatio") => self.aspect = attr.parse(value)?,
expanded_name!("", "viewBox") => self.vbox = Some(attr.parse(value)?),
diff --git a/src/pattern.rs b/src/pattern.rs
index 66533e1e..598d4cba 100644
--- a/src/pattern.rs
+++ b/src/pattern.rs
@@ -36,8 +36,8 @@ struct Common {
transform: Option<Transform>,
x: Option<Length<Horizontal>>,
y: Option<Length<Vertical>>,
- width: Option<Length<Horizontal>>,
- height: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
}
/// State used during the pattern resolution process
@@ -96,8 +96,8 @@ pub struct ResolvedPattern {
transform: Transform,
x: Length<Horizontal>,
y: Length<Vertical>,
- width: Length<Horizontal>,
- height: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
// Link to the node whose children are the pattern's resolved children.
children: Children,
@@ -146,15 +146,8 @@ impl SetAttributes for Pattern {
}
expanded_name!("", "x") => self.common.x = Some(attr.parse(value)?),
expanded_name!("", "y") => self.common.y = Some(attr.parse(value)?),
- expanded_name!("", "width") => {
- self.common.width = Some(
- attr.parse_and_validate(value, Length::<Horizontal>::check_nonnegative)?,
- )
- }
- expanded_name!("", "height") => {
- self.common.height =
- Some(attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?)
- }
+ expanded_name!("", "width") => self.common.width = Some(attr.parse(value)?),
+ expanded_name!("", "height") => self.common.height = Some(attr.parse(value)?),
_ => (),
}
}
diff --git a/src/shapes.rs b/src/shapes.rs
index 1fd72368..90fd522a 100644
--- a/src/shapes.rs
+++ b/src/shapes.rs
@@ -301,12 +301,12 @@ impl BasicShape for Line {
pub struct Rect {
x: Length<Horizontal>,
y: Length<Vertical>,
- w: Length<Horizontal>,
- h: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
// Radiuses for rounded corners
- rx: Option<Length<Horizontal>>,
- ry: Option<Length<Vertical>>,
+ rx: Option<ULength<Horizontal>>,
+ ry: Option<ULength<Vertical>>,
}
impl_draw!(Rect);
@@ -317,24 +317,10 @@ impl SetAttributes for Rect {
match attr.expanded() {
expanded_name!("", "x") => self.x = attr.parse(value)?,
expanded_name!("", "y") => self.y = attr.parse(value)?,
- expanded_name!("", "width") => {
- self.w =
- attr.parse_and_validate(value, Length::<Horizontal>::check_nonnegative)?
- }
- expanded_name!("", "height") => {
- self.h =
- attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?
- }
- expanded_name!("", "rx") => {
- self.rx = attr
- .parse_and_validate(value, Length::<Horizontal>::check_nonnegative)
- .map(Some)?
- }
- expanded_name!("", "ry") => {
- self.ry = attr
- .parse_and_validate(value, Length::<Vertical>::check_nonnegative)
- .map(Some)?
- }
+ expanded_name!("", "width") => self.width = attr.parse(value)?,
+ expanded_name!("", "height") => self.height = attr.parse(value)?,
+ expanded_name!("", "rx") => self.rx = attr.parse(value).map(Some)?,
+ expanded_name!("", "ry") => self.ry = attr.parse(value).map(Some)?,
_ => (),
}
}
@@ -349,8 +335,8 @@ impl BasicShape for Rect {
let x = self.x.normalize(values, ¶ms);
let y = self.y.normalize(values, ¶ms);
- let w = self.w.normalize(values, ¶ms);
- let h = self.h.normalize(values, ¶ms);
+ let w = self.width.normalize(values, ¶ms);
+ let h = self.height.normalize(values, ¶ms);
let mut rx;
let mut ry;
@@ -520,7 +506,7 @@ impl BasicShape for Rect {
pub struct Circle {
cx: Length<Horizontal>,
cy: Length<Vertical>,
- r: Length<Both>,
+ r: ULength<Both>,
}
impl_draw!(Circle);
@@ -531,9 +517,7 @@ impl SetAttributes for Circle {
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_and_validate(value, Length::<Both>::check_nonnegative)?
- }
+ expanded_name!("", "r") => self.r = attr.parse(value)?,
_ => (),
}
}
@@ -558,8 +542,8 @@ impl BasicShape for Circle {
pub struct Ellipse {
cx: Length<Horizontal>,
cy: Length<Vertical>,
- rx: Length<Horizontal>,
- ry: Length<Vertical>,
+ rx: ULength<Horizontal>,
+ ry: ULength<Vertical>,
}
impl_draw!(Ellipse);
@@ -570,14 +554,8 @@ impl SetAttributes for Ellipse {
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_and_validate(value, Length::<Horizontal>::check_nonnegative)?
- }
- expanded_name!("", "ry") => {
- self.ry =
- attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?
- }
+ expanded_name!("", "rx") => self.rx = attr.parse(value)?,
+ expanded_name!("", "ry") => self.ry = attr.parse(value)?,
_ => (),
}
}
diff --git a/src/structure.rs b/src/structure.rs
index 8219f1a4..73b6e99f 100644
--- a/src/structure.rs
+++ b/src/structure.rs
@@ -89,10 +89,10 @@ impl Draw for Switch {
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct IntrinsicDimensions {
/// Contents of the `width` attribute.
- pub width: Option<Length<Horizontal>>,
+ pub width: Option<ULength<Horizontal>>,
/// Contents of the `height` attribute.
- pub height: Option<Length<Vertical>>,
+ pub height: Option<ULength<Vertical>>,
/// Contents of the `viewBox` attribute.
pub vbox: Option<ViewBox>,
@@ -103,16 +103,16 @@ pub struct Svg {
preserve_aspect_ratio: AspectRatio,
x: Option<Length<Horizontal>>,
y: Option<Length<Vertical>>,
- w: Option<Length<Horizontal>>,
- h: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
vbox: Option<ViewBox>,
}
impl Svg {
pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
IntrinsicDimensions {
- width: self.w,
- height: self.h,
+ width: self.width,
+ height: self.height,
vbox: self.vbox,
}
}
@@ -129,14 +129,14 @@ impl Svg {
(x, y)
}
- fn get_unnormalized_size(&self) -> (Length<Horizontal>, Length<Vertical>) {
+ fn get_unnormalized_size(&self) -> (ULength<Horizontal>, ULength<Vertical>) {
// these defaults are per the spec
let w = self
- .w
- .unwrap_or_else(|| Length::<Horizontal>::parse_str("100%").unwrap());
+ .width
+ .unwrap_or_else(|| ULength::<Horizontal>::parse_str("100%").unwrap());
let h = self
- .h
- .unwrap_or_else(|| Length::<Vertical>::parse_str("100%").unwrap());
+ .height
+ .unwrap_or_else(|| ULength::<Vertical>::parse_str("100%").unwrap());
(w, h)
}
@@ -212,15 +212,8 @@ impl SetAttributes for Svg {
}
expanded_name!("", "x") => self.x = Some(attr.parse(value)?),
expanded_name!("", "y") => self.y = Some(attr.parse(value)?),
- expanded_name!("", "width") => {
- self.w = Some(
- attr.parse_and_validate(value, Length::<Horizontal>::check_nonnegative)?,
- )
- }
- expanded_name!("", "height") => {
- self.h =
- Some(attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?)
- }
+ expanded_name!("", "width") => self.width = Some(attr.parse(value)?),
+ expanded_name!("", "height") => self.height = Some(attr.parse(value)?),
expanded_name!("", "viewBox") => self.vbox = attr.parse(value).map(Some)?,
_ => (),
}
@@ -253,8 +246,8 @@ pub struct Use {
link: Option<NodeId>,
x: Length<Horizontal>,
y: Length<Vertical>,
- w: Option<Length<Horizontal>>,
- h: Option<Length<Vertical>>,
+ width: Option<ULength<Horizontal>>,
+ height: Option<ULength<Vertical>>,
}
impl Use {
@@ -268,12 +261,12 @@ impl Use {
// "If the ‘use’ element references a ‘symbol’ element"
let w = self
- .w
- .unwrap_or_else(|| Length::<Horizontal>::parse_str("100%").unwrap())
+ .width
+ .unwrap_or_else(|| ULength::<Horizontal>::parse_str("100%").unwrap())
.normalize(values, ¶ms);
let h = self
- .h
- .unwrap_or_else(|| Length::<Vertical>::parse_str("100%").unwrap())
+ .height
+ .unwrap_or_else(|| ULength::<Vertical>::parse_str("100%").unwrap())
.normalize(values, ¶ms);
Rect::new(x, y, x + w, y + h)
@@ -289,19 +282,10 @@ impl SetAttributes for Use {
&mut self.link,
NodeId::parse(value).attribute(attr.clone())?,
),
-
expanded_name!("", "x") => self.x = attr.parse(value)?,
expanded_name!("", "y") => self.y = attr.parse(value)?,
- expanded_name!("", "width") => {
- self.w = attr
- .parse_and_validate(value, Length::<Horizontal>::check_nonnegative)
- .map(Some)?
- }
- expanded_name!("", "height") => {
- self.h = attr
- .parse_and_validate(value, Length::<Vertical>::check_nonnegative)
- .map(Some)?
- }
+ expanded_name!("", "width") => self.width = attr.parse(value).map(Some)?,
+ expanded_name!("", "height") => self.height = attr.parse(value).map(Some)?,
_ => (),
}
}
@@ -392,8 +376,8 @@ coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
pub struct Mask {
x: Length<Horizontal>,
y: Length<Vertical>,
- width: Length<Horizontal>,
- height: Length<Vertical>,
+ width: ULength<Horizontal>,
+ height: ULength<Vertical>,
units: MaskUnits,
content_units: MaskContentUnits,
@@ -405,8 +389,8 @@ impl Default for Mask {
// these values are per the spec
x: Length::<Horizontal>::parse_str("-10%").unwrap(),
y: Length::<Vertical>::parse_str("-10%").unwrap(),
- width: Length::<Horizontal>::parse_str("120%").unwrap(),
- height: Length::<Vertical>::parse_str("120%").unwrap(),
+ width: ULength::<Horizontal>::parse_str("120%").unwrap(),
+ height: ULength::<Vertical>::parse_str("120%").unwrap(),
units: MaskUnits::default(),
content_units: MaskContentUnits::default(),
@@ -439,14 +423,8 @@ impl SetAttributes for Mask {
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_and_validate(value, Length::<Horizontal>::check_nonnegative)?
- }
- expanded_name!("", "height") => {
- self.height =
- attr.parse_and_validate(value, Length::<Vertical>::check_nonnegative)?
- }
+ expanded_name!("", "width") => self.width = attr.parse(value)?,
+ expanded_name!("", "height") => self.height = attr.parse(value)?,
expanded_name!("", "maskUnits") => self.units = attr.parse(value)?,
expanded_name!("", "maskContentUnits") => self.content_units = attr.parse(value)?,
_ => (),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]