1use crate::error::{ParserError, PrinterError};
4use crate::macros::enum_property;
5use crate::printer::Printer;
6use crate::targets::{Browsers, Targets};
7use crate::traits::{FallbackValues, IsCompatible, Parse, ToCss};
8use crate::values::length::LengthPercentage;
9use crate::values::{color::CssColor, url::Url};
10#[cfg(feature = "visitor")]
11use crate::visitor::Visit;
12use cssparser::*;
13
14#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
17#[cfg_attr(feature = "visitor", derive(Visit))]
18#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
19#[cfg_attr(
20 feature = "serde",
21 derive(serde::Serialize, serde::Deserialize),
22 serde(tag = "type", rename_all = "kebab-case")
23)]
24#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
25pub enum SVGPaint<'i> {
26 Url {
28 #[cfg_attr(feature = "serde", serde(borrow))]
29 url: Url<'i>,
31 fallback: Option<SVGPaintFallback>,
33 },
34 #[cfg_attr(feature = "serde", serde(with = "crate::serialization::ValueWrapper::<CssColor>"))]
36 Color(CssColor),
37 ContextFill,
39 ContextStroke,
41 None,
43}
44
45#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
49#[cfg_attr(feature = "visitor", derive(Visit))]
50#[cfg_attr(
51 feature = "serde",
52 derive(serde::Serialize, serde::Deserialize),
53 serde(tag = "type", content = "value", rename_all = "kebab-case")
54)]
55#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
56#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
57pub enum SVGPaintFallback {
58 None,
60 Color(CssColor),
62}
63
64impl<'i> FallbackValues for SVGPaint<'i> {
65 fn get_fallbacks(&mut self, targets: Targets) -> Vec<Self> {
66 match self {
67 SVGPaint::Color(color) => color
68 .get_fallbacks(targets)
69 .into_iter()
70 .map(|color| SVGPaint::Color(color))
71 .collect(),
72 SVGPaint::Url {
73 url,
74 fallback: Some(SVGPaintFallback::Color(color)),
75 } => color
76 .get_fallbacks(targets)
77 .into_iter()
78 .map(|color| SVGPaint::Url {
79 url: url.clone(),
80 fallback: Some(SVGPaintFallback::Color(color)),
81 })
82 .collect(),
83 _ => Vec::new(),
84 }
85 }
86}
87
88impl IsCompatible for SVGPaint<'_> {
89 fn is_compatible(&self, browsers: Browsers) -> bool {
90 match self {
91 SVGPaint::Color(c)
92 | SVGPaint::Url {
93 fallback: Some(SVGPaintFallback::Color(c)),
94 ..
95 } => c.is_compatible(browsers),
96 SVGPaint::Url { .. } | SVGPaint::None | SVGPaint::ContextFill | SVGPaint::ContextStroke => true,
97 }
98 }
99}
100
101enum_property! {
102 pub enum StrokeLinecap {
104 Butt,
106 Round,
108 Square,
110 }
111}
112
113enum_property! {
114 pub enum StrokeLinejoin {
116 Miter,
118 MiterClip,
120 Round,
122 Bevel,
124 Arcs,
126 }
127}
128
129#[derive(Debug, Clone, PartialEq)]
131#[cfg_attr(feature = "visitor", derive(Visit))]
132#[cfg_attr(
133 feature = "serde",
134 derive(serde::Serialize, serde::Deserialize),
135 serde(tag = "type", content = "value", rename_all = "kebab-case")
136)]
137#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
138#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
139pub enum StrokeDasharray {
140 None,
142 Values(Vec<LengthPercentage>),
144}
145
146impl<'i> Parse<'i> for StrokeDasharray {
147 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
148 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
149 return Ok(StrokeDasharray::None);
150 }
151
152 input.skip_whitespace();
153 let mut results = vec![LengthPercentage::parse(input)?];
154 loop {
155 input.skip_whitespace();
156 let comma_location = input.current_source_location();
157 let comma = input.try_parse(|i| i.expect_comma()).is_ok();
158 if let Ok(item) = input.try_parse(LengthPercentage::parse) {
159 results.push(item);
160 } else if comma {
161 return Err(comma_location.new_unexpected_token_error(Token::Comma));
162 } else {
163 break;
164 }
165 }
166
167 Ok(StrokeDasharray::Values(results))
168 }
169}
170
171impl ToCss for StrokeDasharray {
172 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
173 where
174 W: std::fmt::Write,
175 {
176 match self {
177 StrokeDasharray::None => dest.write_str("none"),
178 StrokeDasharray::Values(values) => {
179 let mut first = true;
180 for value in values {
181 if first {
182 first = false;
183 } else {
184 dest.write_char(' ')?;
185 }
186 value.to_css_unitless(dest)?;
187 }
188 Ok(())
189 }
190 }
191 }
192}
193
194#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
196#[cfg_attr(feature = "visitor", derive(Visit))]
197#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
198#[cfg_attr(
199 feature = "serde",
200 derive(serde::Serialize, serde::Deserialize),
201 serde(tag = "type", content = "value", rename_all = "kebab-case")
202)]
203#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
204pub enum Marker<'i> {
205 None,
207 #[cfg_attr(feature = "serde", serde(borrow))]
209 Url(Url<'i>),
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
214#[css(case = lower)]
215#[cfg_attr(feature = "visitor", derive(Visit))]
216#[cfg_attr(
217 feature = "serde",
218 derive(serde::Serialize, serde::Deserialize),
219 serde(rename_all = "lowercase")
220)]
221#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
222#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
223pub enum ColorInterpolation {
224 Auto,
226 SRGB,
228 LinearRGB,
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
234#[cfg_attr(feature = "visitor", derive(Visit))]
235#[cfg_attr(
236 feature = "serde",
237 derive(serde::Serialize, serde::Deserialize),
238 serde(rename_all = "lowercase")
239)]
240#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
241#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
242pub enum ColorRendering {
243 Auto,
245 OptimizeSpeed,
247 OptimizeQuality,
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
253#[css(case = lower)]
254#[cfg_attr(feature = "visitor", derive(Visit))]
255#[cfg_attr(
256 feature = "serde",
257 derive(serde::Serialize, serde::Deserialize),
258 serde(rename_all = "lowercase")
259)]
260#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
261#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
262pub enum ShapeRendering {
263 Auto,
265 OptimizeSpeed,
267 CrispEdges,
269 GeometricPrecision,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
275#[css(case = lower)]
276#[cfg_attr(feature = "visitor", derive(Visit))]
277#[cfg_attr(
278 feature = "serde",
279 derive(serde::Serialize, serde::Deserialize),
280 serde(rename_all = "lowercase")
281)]
282#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
283#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
284pub enum TextRendering {
285 Auto,
287 OptimizeSpeed,
289 OptimizeLegibility,
291 GeometricPrecision,
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
297#[cfg_attr(feature = "visitor", derive(Visit))]
298#[cfg_attr(
299 feature = "serde",
300 derive(serde::Serialize, serde::Deserialize),
301 serde(rename_all = "lowercase")
302)]
303#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
304#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
305pub enum ImageRendering {
306 Auto,
308 OptimizeSpeed,
310 OptimizeQuality,
312}