svgrtypes/
aspect_ratio.rs

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