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#[derive(Clone, PartialEq, Debug)]
26#[allow(missing_docs)]
27pub enum AttributeValue {
28 None,
29 Inherit,
30 CurrentColor,
31 AspectRatio(AspectRatio),
32 Color(Color),
33 FuncLink(Node),
35 Paint(Node, Option<PaintFallback>),
36 Length(Length),
37 LengthList(LengthList),
38 Angle(Angle),
39 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
73impl<'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
108macro_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 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 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}