svgdom/
attribute_value.rs

1use std::fmt;
2
3use crate::{
4    Angle,
5    AspectRatio,
6    AttributeId,
7    Color,
8    Length,
9    LengthList,
10    LengthUnit,
11    Node,
12    NumberList,
13    PaintFallback,
14    Path,
15    Points,
16    Transform,
17    ValueWriteOptions,
18    ViewBox,
19    WriteBuffer,
20};
21
22// TODO: custom debug
23
24/// Value of the SVG attribute.
25#[derive(Clone, PartialEq, Debug)]
26#[allow(missing_docs)]
27pub enum AttributeValue {
28    None,
29    Inherit,
30    CurrentColor,
31    AspectRatio(AspectRatio),
32    Color(Color),
33    /// FuncIRI
34    FuncLink(Node),
35    Paint(Node, Option<PaintFallback>),
36    Length(Length),
37    LengthList(LengthList),
38    Angle(Angle),
39    /// IRI
40    Link(Node),
41    Number(f64),
42    NumberList(NumberList),
43    Path(Path),
44    Points(Points),
45    Transform(Transform),
46    ViewBox(ViewBox),
47    String(String),
48}
49
50macro_rules! impl_from {
51    ($vt:ty, $vtn:ident) => (
52        impl From<$vt> for AttributeValue {
53            fn from(value: $vt) -> Self {
54                AttributeValue::$vtn(value)
55            }
56        }
57    )
58}
59
60impl_from!(AspectRatio, AspectRatio);
61impl_from!(Color, Color);
62impl_from!(Length, Length);
63impl_from!(LengthList, LengthList);
64impl_from!(Angle, Angle);
65impl_from!(f64, Number);
66impl_from!(NumberList, NumberList);
67impl_from!(Path, Path);
68impl_from!(Points, Points);
69impl_from!(String, String);
70impl_from!(Transform, Transform);
71impl_from!(ViewBox, ViewBox);
72
73// TODO: bad, hidden allocation
74impl<'a> From<&'a str> for AttributeValue {
75    fn from(value: &str) -> Self {
76        AttributeValue::String(value.to_owned())
77    }
78}
79
80impl From<i32> for AttributeValue {
81    fn from(value: i32) -> Self {
82        AttributeValue::Number(f64::from(value))
83    }
84}
85
86impl From<(i32, LengthUnit)> for AttributeValue {
87    fn from(value: (i32, LengthUnit)) -> Self {
88        AttributeValue::Length(Length::new(f64::from(value.0), value.1))
89    }
90}
91
92impl From<(f64, LengthUnit)> for AttributeValue {
93    fn from(value: (f64, LengthUnit)) -> Self {
94        AttributeValue::Length(Length::new(value.0, value.1))
95    }
96}
97
98impl From<PaintFallback> for AttributeValue {
99    fn from(value: PaintFallback) -> Self {
100        match value {
101            PaintFallback::None => AttributeValue::None,
102            PaintFallback::CurrentColor => AttributeValue::CurrentColor,
103            PaintFallback::Color(c) => AttributeValue::Color(c),
104        }
105    }
106}
107
108// TODO: fix docs
109macro_rules! impl_is_type {
110    ($name:ident, $t:ident) => (
111        #[allow(missing_docs)]
112        pub fn $name(&self) -> bool {
113            match *self {
114                AttributeValue::$t(..) => true,
115                _ => false,
116            }
117        }
118    )
119}
120
121macro_rules! impl_is_type_without_value {
122    ($name:ident, $t:ident) => (
123        #[allow(missing_docs)]
124        pub fn $name(&self) -> bool {
125            match *self {
126                AttributeValue::$t => true,
127                _ => false,
128            }
129        }
130    )
131}
132
133impl AttributeValue {
134    impl_is_type_without_value!(is_none, None);
135    impl_is_type_without_value!(is_inherit, Inherit);
136    impl_is_type_without_value!(is_current_color, CurrentColor);
137    impl_is_type!(is_aspect_ratio, AspectRatio);
138    impl_is_type!(is_color, Color);
139    impl_is_type!(is_length, Length);
140    impl_is_type!(is_length_list, LengthList);
141    impl_is_type!(is_angle, Angle);
142    impl_is_type!(is_link, Link);
143    impl_is_type!(is_func_link, FuncLink);
144    impl_is_type!(is_paint, Paint);
145    impl_is_type!(is_number, Number);
146    impl_is_type!(is_number_list, NumberList);
147    impl_is_type!(is_path, Path);
148    impl_is_type!(is_points, Points);
149    impl_is_type!(is_string, String);
150    impl_is_type!(is_transform, Transform);
151    impl_is_type!(is_viewbox, ViewBox);
152
153    /// Checks that the current attribute value contains a `Node`.
154    ///
155    /// E.g. `Link`, `FuncLink` and `Paint`.
156    pub fn is_link_container(&self) -> bool {
157        match *self {
158              AttributeValue::Link(_)
159            | AttributeValue::FuncLink(_)
160            | AttributeValue::Paint(_, _) => true,
161            _ => false,
162        }
163    }
164
165    /// Constructs a new attribute value with a default value, if it's known.
166    pub fn default_value(id: AttributeId) -> Option<AttributeValue> {
167        macro_rules! some {
168            ($expr:expr) => (Some(AttributeValue::from($expr)))
169        }
170
171        match id {
172              AttributeId::AlignmentBaseline
173            | AttributeId::Clip
174            | AttributeId::ColorProfile
175            | AttributeId::ColorRendering
176            | AttributeId::Cursor
177            | AttributeId::DominantBaseline
178            | AttributeId::GlyphOrientationVertical
179            | AttributeId::ImageRendering
180            | AttributeId::Kerning
181            | AttributeId::ShapeRendering
182            | AttributeId::TextRendering => some!("auto"),
183
184              AttributeId::ClipPath
185            | AttributeId::Filter
186            | AttributeId::FontSizeAdjust
187            | AttributeId::Marker
188            | AttributeId::MarkerEnd
189            | AttributeId::MarkerMid
190            | AttributeId::MarkerStart
191            | AttributeId::Mask
192            | AttributeId::Stroke
193            | AttributeId::StrokeDasharray
194            | AttributeId::TextDecoration => Some(AttributeValue::None),
195
196              AttributeId::FontStretch
197            | AttributeId::FontStyle
198            | AttributeId::FontVariant
199            | AttributeId::FontWeight
200            | AttributeId::LetterSpacing
201            | AttributeId::UnicodeBidi
202            | AttributeId::WordSpacing => some!("normal"),
203
204              AttributeId::Fill
205            | AttributeId::FloodColor
206            | AttributeId::StopColor => some!(Color::black()),
207
208              AttributeId::FillOpacity
209            | AttributeId::FloodOpacity
210            | AttributeId::Opacity
211            | AttributeId::StopOpacity
212            | AttributeId::StrokeOpacity => some!(1.0),
213
214              AttributeId::ClipRule
215            | AttributeId::FillRule => some!("nonzero"),
216
217            AttributeId::BaselineShift =>               some!("baseline"),
218            AttributeId::ColorInterpolation =>          some!("sRGB"),
219            AttributeId::ColorInterpolationFilters =>   some!("linearRGB"),
220            AttributeId::Direction =>                   some!("ltr"),
221            AttributeId::Display =>                     some!("inline"),
222            AttributeId::EnableBackground =>            some!("accumulate"),
223            AttributeId::FontSize =>                    some!("medium"),
224            AttributeId::GlyphOrientationHorizontal =>  some!("0deg"),
225            AttributeId::LightingColor =>               some!(Color::white()),
226            AttributeId::StrokeDashoffset =>            some!((0.0, LengthUnit::None)),
227            AttributeId::StrokeLinecap =>               some!("butt"),
228            AttributeId::StrokeLinejoin =>              some!("miter"),
229            AttributeId::StrokeMiterlimit =>            some!((4.0, LengthUnit::None)),
230            AttributeId::StrokeWidth =>                 some!((1.0, LengthUnit::None)),
231            AttributeId::TextAnchor =>                  some!("start"),
232            AttributeId::Visibility =>                  some!("visible"),
233            AttributeId::WritingMode =>                 some!("lr-tb"),
234            _ => None,
235        }
236    }
237}
238
239impl WriteBuffer for AttributeValue {
240    fn write_buf_opt(&self, opt: &ValueWriteOptions, buf: &mut Vec<u8>) {
241        match *self {
242            AttributeValue::None => {
243                buf.extend_from_slice(b"none");
244            }
245            AttributeValue::Inherit => {
246                buf.extend_from_slice(b"inherit");
247            }
248            AttributeValue::CurrentColor => {
249                buf.extend_from_slice(b"currentColor");
250            }
251            AttributeValue::String(ref s) => {
252                buf.extend_from_slice(s.as_bytes());
253            }
254            AttributeValue::Number(ref n) => {
255                n.write_buf_opt(opt, buf);
256            }
257            AttributeValue::NumberList(ref list) => {
258                list.write_buf_opt(opt, buf);
259            }
260            AttributeValue::Length(ref l) => {
261                l.write_buf_opt(opt, buf);
262            }
263            AttributeValue::LengthList(ref list) => {
264                list.write_buf_opt(opt, buf);
265            }
266            AttributeValue::Angle(ref a) => {
267                a.write_buf_opt(opt, buf);
268            }
269            AttributeValue::Transform(ref t) => {
270                t.write_buf_opt(opt, buf);
271            }
272            AttributeValue::Path(ref p) => {
273                p.write_buf_opt(opt, buf);
274            }
275            AttributeValue::Points(ref p) => {
276                p.write_buf_opt(opt, buf);
277            }
278            AttributeValue::Link(ref n) => {
279                buf.push(b'#');
280                buf.extend_from_slice(n.id().as_bytes());
281            }
282            AttributeValue::FuncLink(ref n) => {
283                buf.extend_from_slice(b"url(#");
284                buf.extend_from_slice(n.id().as_bytes());
285                buf.push(b')');
286            }
287            AttributeValue::Paint(ref n, ref fallback) => {
288                buf.extend_from_slice(b"url(#");
289                buf.extend_from_slice(n.id().as_bytes());
290                buf.push(b')');
291
292                if let Some(fallback) = *fallback {
293                    buf.push(b' ');
294                    match fallback {
295                        PaintFallback::None => buf.extend_from_slice(b"none"),
296                        PaintFallback::CurrentColor => buf.extend_from_slice(b"currentColor"),
297                        PaintFallback::Color(ref c) => c.write_buf_opt(opt, buf),
298                    }
299                }
300            }
301            AttributeValue::Color(ref c) => {
302                c.write_buf_opt(opt, buf);
303            }
304            AttributeValue::ViewBox(vb) => {
305                vb.write_buf_opt(opt, buf);
306            }
307            AttributeValue::AspectRatio(ratio) => {
308                ratio.write_buf_opt(opt, buf);
309            }
310        }
311    }
312}
313
314impl fmt::Display for AttributeValue {
315    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316        write!(f, "{}", self.with_write_opt(&ValueWriteOptions::default()))
317    }
318}