svgtypes/
aspect_ratio.rs

1// Copyright 2018 the SVG Types Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::{Error, Stream};
5
6/// Representation of the `align` value of the [`preserveAspectRatio`] attribute.
7///
8/// [`preserveAspectRatio`]: https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
9#[allow(missing_docs)]
10#[derive(Clone, Copy, PartialEq, Eq, Debug)]
11pub enum Align {
12    None,
13    XMinYMin,
14    XMidYMin,
15    XMaxYMin,
16    XMinYMid,
17    XMidYMid,
18    XMaxYMid,
19    XMinYMax,
20    XMidYMax,
21    XMaxYMax,
22}
23
24/// Representation of the [`preserveAspectRatio`] attribute.
25///
26/// SVG 2 removed the `defer` keyword, but we still support it.
27///
28/// [`preserveAspectRatio`]: https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
29#[derive(Clone, Copy, PartialEq, Eq, Debug)]
30pub struct AspectRatio {
31    /// `<defer>` value.
32    ///
33    /// Set to `true` when `defer` value is present.
34    pub defer: bool,
35    /// `<align>` value.
36    pub align: Align,
37    /// `<meetOrSlice>` value.
38    ///
39    /// - Set to `true` when `slice` value is present.
40    /// - Set to `false` when `meet` value is present or value is not set at all.
41    pub slice: bool,
42}
43
44impl std::str::FromStr for AspectRatio {
45    type Err = Error;
46
47    fn from_str(text: &str) -> Result<Self, Error> {
48        let mut s = Stream::from(text);
49
50        s.skip_spaces();
51
52        let defer = s.starts_with(b"defer");
53        if defer {
54            s.advance(5);
55            s.consume_byte(b' ')?;
56            s.skip_spaces();
57        }
58
59        let start = s.pos();
60        let align = s.consume_ascii_ident();
61        let align = match align {
62            "none" => Align::None,
63            "xMinYMin" => Align::XMinYMin,
64            "xMidYMin" => Align::XMidYMin,
65            "xMaxYMin" => Align::XMaxYMin,
66            "xMinYMid" => Align::XMinYMid,
67            "xMidYMid" => Align::XMidYMid,
68            "xMaxYMid" => Align::XMaxYMid,
69            "xMinYMax" => Align::XMinYMax,
70            "xMidYMax" => Align::XMidYMax,
71            "xMaxYMax" => Align::XMaxYMax,
72            _ => return Err(Error::UnexpectedData(s.calc_char_pos_at(start))),
73        };
74
75        s.skip_spaces();
76
77        let mut slice = false;
78        if !s.at_end() {
79            let start = s.pos();
80            let v = s.consume_ascii_ident();
81            match v {
82                "meet" => {}
83                "slice" => slice = true,
84                "" => {}
85                _ => return Err(Error::UnexpectedData(s.calc_char_pos_at(start))),
86            };
87        }
88
89        Ok(AspectRatio {
90            defer,
91            align,
92            slice,
93        })
94    }
95}
96
97impl Default for AspectRatio {
98    #[inline]
99    fn default() -> Self {
100        AspectRatio {
101            defer: false,
102            align: Align::XMidYMid,
103            slice: false,
104        }
105    }
106}
107
108#[rustfmt::skip]
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use std::str::FromStr;
113
114    macro_rules! test {
115        ($name:ident, $text:expr, $result:expr) => (
116            #[test]
117            fn $name() {
118                let v = AspectRatio::from_str($text).unwrap();
119                assert_eq!(v, $result);
120            }
121        )
122    }
123
124    test!(parse_1, "none", AspectRatio {
125        defer: false,
126        align: Align::None,
127        slice: false,
128    });
129
130    test!(parse_2, "defer none", AspectRatio {
131        defer: true,
132        align: Align::None,
133        slice: false,
134    });
135
136    test!(parse_3, "xMinYMid", AspectRatio {
137        defer: false,
138        align: Align::XMinYMid,
139        slice: false,
140    });
141
142    test!(parse_4, "xMinYMid slice", AspectRatio {
143        defer: false,
144        align: Align::XMinYMid,
145        slice: true,
146    });
147
148    test!(parse_5, "xMinYMid meet", AspectRatio {
149        defer: false,
150        align: Align::XMinYMid,
151        slice: false,
152    });
153}