1
//! Parser for the `viewBox` attribute.
2

            
3
use cssparser::Parser;
4
use std::ops::Deref;
5

            
6
use crate::error::*;
7
use crate::parsers::{CommaSeparatedList, Parse};
8
use crate::rect::Rect;
9

            
10
/// Newtype around a [`Rect`], used to represent the `viewBox` attribute.
11
///
12
/// A `ViewBox` is a new user-space coordinate system mapped onto the rectangle defined by
13
/// the current viewport.  See <https://www.w3.org/TR/SVG2/coords.html#ViewBoxAttribute>
14
///
15
/// `ViewBox` derefs to [`Rect`], so you can use [`Rect`]'s methods and fields directly like
16
/// `vbox.x0` or `vbox.width()`.
17
52820
#[derive(Debug, Copy, Clone, PartialEq)]
18
2
pub struct ViewBox(Rect);
19

            
20
impl Deref for ViewBox {
21
    type Target = Rect;
22

            
23
2015242
    fn deref(&self) -> &Rect {
24
        &self.0
25
2015242
    }
26
}
27

            
28
impl From<Rect> for ViewBox {
29
52847
    fn from(r: Rect) -> ViewBox {
30
52847
        ViewBox(r)
31
52847
    }
32
}
33

            
34
impl Parse for ViewBox {
35
    // Parse a viewBox attribute
36
    // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
37
    //
38
    // viewBox: double [,] double [,] double [,] double [,]
39
    //
40
    // x, y, w, h
41
    //
42
    // Where w and h must be nonnegative.
43
523
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<ViewBox, ParseError<'i>> {
44
523
        let loc = parser.current_source_location();
45

            
46
523
        let CommaSeparatedList::<f64, 4, 4>(v) = CommaSeparatedList::parse(parser)?;
47
517
        let (x, y, width, height) = (v[0], v[1], v[2], v[3]);
48

            
49
1030
        if width >= 0.0 && height >= 0.0 {
50
514
            Ok(ViewBox(Rect::new(x, y, x + width, y + height)))
51
        } else {
52
1
            Err(loc.new_custom_error(ValueErrorKind::value_error(
53
                "width and height must not be negative",
54
            )))
55
        }
56
521
    }
57
}
58

            
59
#[cfg(test)]
60
mod tests {
61
    use super::*;
62

            
63
    #[test]
64
2
    fn parses_valid_viewboxes() {
65
1
        assert_eq!(
66
1
            ViewBox::parse_str("  1 2 3 4").unwrap(),
67
1
            ViewBox(Rect::new(1.0, 2.0, 4.0, 6.0))
68
        );
69

            
70
1
        assert_eq!(
71
1
            ViewBox::parse_str(" -1.5 -2.5e1,34,56e2  ").unwrap(),
72
1
            ViewBox(Rect::new(-1.5, -25.0, 32.5, 5575.0))
73
        );
74
2
    }
75

            
76
    #[test]
77
2
    fn parsing_invalid_viewboxes_yields_error() {
78
1
        assert!(ViewBox::parse_str("").is_err());
79
1
        assert!(ViewBox::parse_str(" 1,2,-3,-4 ").is_err());
80
1
        assert!(ViewBox::parse_str("qwerasdfzxcv").is_err());
81
1
        assert!(ViewBox::parse_str(" 1 2 3 4   5").is_err());
82
1
        assert!(ViewBox::parse_str(" 1 2 foo 3 4").is_err());
83

            
84
        // https://gitlab.gnome.org/GNOME/librsvg/issues/344
85
1
        assert!(ViewBox::parse_str("0 0 9E80.7").is_err());
86
2
    }
87
}