1use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::values::CustomIdent;
10use crate::values::generics::svg as generic;
11use crate::values::specified::AllowQuirks;
12use crate::values::specified::LengthPercentage;
13use crate::values::specified::SVGPathData;
14use crate::values::specified::color::Color;
15use crate::values::specified::url::SpecifiedUrl;
16use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
17use cssparser::{Parser, Token};
18use std::fmt::{self, Write};
19use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
20use style_traits::{StyleParseErrorKind, ToCss};
21
22pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;
24
25pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
27
28pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
30
31pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
33
34pub fn is_context_value_enabled() -> bool {
36 crate::pref!("gfx.font_rendering.opentype_svg.enabled")
37}
38
39macro_rules! parse_svg_length {
40 ($ty:ty, $lp:ty) => {
41 impl Parse for $ty {
42 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
43 if let Ok(lp) =
44 input.try_parse(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
45 {
46 return Ok(generic::SVGLength::LengthPercentage(lp));
47 }
48
49 try_match_ident_ignore_ascii_case! { input,
50 "context-value" if is_context_value_enabled() => {
51 Ok(generic::SVGLength::ContextValue)
52 },
53 }
54 }
55 }
56 };
57}
58
59parse_svg_length!(SVGLength, LengthPercentage);
60parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);
61
62impl Parse for SVGStrokeDashArray {
63 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
64 if let Ok(values) = input.try_parse(|i| {
65 CommaWithSpace::parse(i, |i| {
66 NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
67 })
68 }) {
69 return Ok(generic::SVGStrokeDashArray::Values(values.into()));
70 }
71
72 try_match_ident_ignore_ascii_case! { input,
73 "context-value" if is_context_value_enabled() => {
74 Ok(generic::SVGStrokeDashArray::ContextValue)
75 },
76 "none" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),
77 }
78 }
79}
80
81pub type SVGOpacity = generic::SVGOpacity<Opacity>;
83
84#[repr(u8)]
86#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]
87pub enum PaintOrder {
88 Normal = 0,
90 Fill = 1,
92 Stroke = 2,
94 Markers = 3,
96}
97
98pub const PAINT_ORDER_COUNT: u8 = 3;
100
101pub const PAINT_ORDER_SHIFT: u8 = 2;
103
104pub const PAINT_ORDER_MASK: u8 = 0b11;
106
107#[derive(
118 Clone,
119 Copy,
120 Debug,
121 MallocSizeOf,
122 PartialEq,
123 SpecifiedValueInfo,
124 ToComputedValue,
125 ToResolvedValue,
126 ToShmem,
127 ToTyped,
128)]
129#[repr(transparent)]
130#[typed(todo_derive_fields)]
131pub struct SVGPaintOrder(pub u8);
132
133impl SVGPaintOrder {
134 pub fn normal() -> Self {
136 SVGPaintOrder(0)
137 }
138
139 pub fn order_at(&self, pos: u8) -> PaintOrder {
141 match (self.0 >> (pos * PAINT_ORDER_SHIFT)) & PAINT_ORDER_MASK {
142 0 => PaintOrder::Normal,
143 1 => PaintOrder::Fill,
144 2 => PaintOrder::Stroke,
145 3 => PaintOrder::Markers,
146 _ => unreachable!("this cannot happen"),
147 }
148 }
149}
150
151impl Parse for SVGPaintOrder {
152 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SVGPaintOrder, ParseError> {
153 if let Ok(()) = input.try_parse(|i| i.expect_ident_matching("normal")) {
154 return Ok(SVGPaintOrder::normal());
155 }
156
157 let mut value = 0;
158 let mut seen = 0;
161 let mut pos = 0;
162
163 loop {
164 let result: Result<_, ParseError> = input.try_parse(|input| {
165 try_match_ident_ignore_ascii_case! { input,
166 "fill" => Ok(PaintOrder::Fill),
167 "stroke" => Ok(PaintOrder::Stroke),
168 "markers" => Ok(PaintOrder::Markers),
169 }
170 });
171
172 match result {
173 Ok(val) => {
174 if (seen & (1 << val as u8)) != 0 {
175 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
177 }
178
179 value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);
180 seen |= 1 << (val as u8);
181 pos += 1;
182 },
183 Err(_) => break,
184 }
185 }
186
187 if value == 0 {
188 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
190 }
191
192 for i in pos..PAINT_ORDER_COUNT {
194 for paint in 1..(PAINT_ORDER_COUNT + 1) {
195 if (seen & (1 << paint)) == 0 {
197 seen |= 1 << paint;
198 value |= paint << (i * PAINT_ORDER_SHIFT);
199 break;
200 }
201 }
202 }
203
204 Ok(SVGPaintOrder(value))
205 }
206}
207
208impl ToCss for SVGPaintOrder {
209 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
210 where
211 W: Write,
212 {
213 if self.0 == 0 {
214 return dest.write_str("normal");
215 }
216
217 let mut last_pos_to_serialize = 0;
218 for i in (1..PAINT_ORDER_COUNT).rev() {
219 let component = self.order_at(i);
220 let earlier_component = self.order_at(i - 1);
221 if component < earlier_component {
222 last_pos_to_serialize = i - 1;
223 break;
224 }
225 }
226
227 for pos in 0..last_pos_to_serialize + 1 {
228 if pos != 0 {
229 dest.write_char(' ')?
230 }
231 self.order_at(pos).to_css(dest)?;
232 }
233 Ok(())
234 }
235}
236
237#[derive(
239 Clone,
240 Copy,
241 Eq,
242 Debug,
243 Default,
244 MallocSizeOf,
245 PartialEq,
246 SpecifiedValueInfo,
247 ToComputedValue,
248 ToResolvedValue,
249 ToShmem,
250)]
251#[repr(C)]
252pub struct ContextPropertyBits(u8);
253bitflags! {
254 impl ContextPropertyBits: u8 {
255 const FILL = 1 << 0;
257 const STROKE = 1 << 1;
259 const FILL_OPACITY = 1 << 2;
261 const STROKE_OPACITY = 1 << 3;
263 }
264}
265
266#[derive(
269 Clone,
270 Debug,
271 Default,
272 MallocSizeOf,
273 PartialEq,
274 SpecifiedValueInfo,
275 ToComputedValue,
276 ToCss,
277 ToResolvedValue,
278 ToShmem,
279 ToTyped,
280)]
281#[repr(C)]
282pub struct MozContextProperties {
283 #[css(iterable, if_empty = "none")]
284 #[ignore_malloc_size_of = "Arc"]
285 idents: crate::ArcSlice<CustomIdent>,
286 #[css(skip)]
287 bits: ContextPropertyBits,
288}
289
290impl Parse for MozContextProperties {
291 fn parse(
292 _context: &ParserContext,
293 input: &mut Parser,
294 ) -> Result<MozContextProperties, ParseError> {
295 let mut values = vec![];
296 let mut bits = ContextPropertyBits::empty();
297 loop {
298 {
299 let ident = input.expect_ident()?;
300
301 if ident.eq_ignore_ascii_case("none") && values.is_empty() {
302 return Ok(Self::default());
303 }
304
305 let ident = CustomIdent::from_ident(ident, &["all", "none", "auto"])?;
306
307 if ident.0 == atom!("fill") {
308 bits.insert(ContextPropertyBits::FILL);
309 } else if ident.0 == atom!("stroke") {
310 bits.insert(ContextPropertyBits::STROKE);
311 } else if ident.0 == atom!("fill-opacity") {
312 bits.insert(ContextPropertyBits::FILL_OPACITY);
313 } else if ident.0 == atom!("stroke-opacity") {
314 bits.insert(ContextPropertyBits::STROKE_OPACITY);
315 }
316
317 values.push(ident);
318 }
319
320 match input.next() {
321 Ok(&Token::Comma) => continue,
322 Err(..) => break,
323 Ok(_) => return Err(ParseError::unexpected_token()),
324 }
325 }
326
327 if values.is_empty() {
328 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
329 }
330
331 Ok(MozContextProperties {
332 idents: crate::ArcSlice::from_iter(values.into_iter()),
333 bits,
334 })
335 }
336}
337
338#[derive(
342 Animate,
343 Clone,
344 ComputeSquaredDistance,
345 Debug,
346 Deserialize,
347 MallocSizeOf,
348 PartialEq,
349 Serialize,
350 SpecifiedValueInfo,
351 ToAnimatedValue,
352 ToAnimatedZero,
353 ToComputedValue,
354 ToCss,
355 ToResolvedValue,
356 ToShmem,
357 ToTyped,
358)]
359#[repr(C, u8)]
360#[typed(todo_derive_fields)]
361pub enum DProperty {
362 #[css(function)]
364 Path(SVGPathData),
365 #[animation(error)]
367 None,
368}
369
370impl DProperty {
371 #[inline]
373 pub fn none() -> Self {
374 DProperty::None
375 }
376}
377
378impl Parse for DProperty {
379 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
380 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
382 return Ok(DProperty::none());
383 }
384
385 input.expect_function_matching("path")?;
387 let path_data = input.parse_nested_block(|i| Parse::parse(context, i))?;
388 Ok(DProperty::Path(path_data))
389 }
390}
391
392#[derive(
393 Clone,
394 Copy,
395 Debug,
396 Default,
397 Eq,
398 MallocSizeOf,
399 Parse,
400 PartialEq,
401 SpecifiedValueInfo,
402 ToComputedValue,
403 ToCss,
404 ToResolvedValue,
405 ToShmem,
406 ToTyped,
407)]
408#[css(bitflags(single = "none", mixed = "non-scaling-stroke"))]
409#[repr(C)]
410pub struct VectorEffect(u8);
412bitflags! {
413 impl VectorEffect: u8 {
414 const NONE = 0;
416 const NON_SCALING_STROKE = 1 << 0;
418 }
419}
420
421impl VectorEffect {
422 #[inline]
424 pub fn none() -> Self {
425 Self::NONE
426 }
427}
428
429#[allow(missing_docs)]
431#[derive(
432 Clone,
433 Copy,
434 Debug,
435 Deserialize,
436 Eq,
437 FromPrimitive,
438 Hash,
439 MallocSizeOf,
440 Parse,
441 PartialEq,
442 Serialize,
443 SpecifiedValueInfo,
444 ToComputedValue,
445 ToCss,
446 ToResolvedValue,
447 ToShmem,
448 ToTyped,
449)]
450#[repr(u8)]
451pub enum TextAnchor {
452 Start,
453 Middle,
454 End,
455}
456
457#[allow(missing_docs)]
459#[derive(
460 Clone,
461 Copy,
462 Debug,
463 Deserialize,
464 Eq,
465 FromPrimitive,
466 Hash,
467 MallocSizeOf,
468 Parse,
469 PartialEq,
470 Serialize,
471 SpecifiedValueInfo,
472 ToComputedValue,
473 ToCss,
474 ToResolvedValue,
475 ToShmem,
476 ToTyped,
477)]
478#[repr(u8)]
479pub enum ShapeRendering {
480 Auto,
481 Optimizespeed,
482 Crispedges,
483 Geometricprecision,
484}
485
486#[allow(missing_docs)]
488#[derive(
489 Clone,
490 Copy,
491 Debug,
492 Deserialize,
493 Eq,
494 FromPrimitive,
495 Hash,
496 MallocSizeOf,
497 Parse,
498 PartialEq,
499 Serialize,
500 SpecifiedValueInfo,
501 ToComputedValue,
502 ToCss,
503 ToResolvedValue,
504 ToShmem,
505 ToTyped,
506)]
507#[repr(u8)]
508pub enum StrokeLinecap {
509 Butt,
510 Round,
511 Square,
512}
513
514#[allow(missing_docs)]
516#[derive(
517 Clone,
518 Copy,
519 Debug,
520 Deserialize,
521 Eq,
522 FromPrimitive,
523 Hash,
524 MallocSizeOf,
525 Parse,
526 PartialEq,
527 Serialize,
528 SpecifiedValueInfo,
529 ToComputedValue,
530 ToCss,
531 ToResolvedValue,
532 ToShmem,
533 ToTyped,
534)]
535#[repr(u8)]
536pub enum StrokeLinejoin {
537 Miter,
538 Round,
539 Bevel,
540}
541
542#[allow(missing_docs)]
544#[derive(
545 Clone,
546 Copy,
547 Debug,
548 Deserialize,
549 Eq,
550 FromPrimitive,
551 Hash,
552 MallocSizeOf,
553 Parse,
554 PartialEq,
555 Serialize,
556 SpecifiedValueInfo,
557 ToComputedValue,
558 ToCss,
559 ToResolvedValue,
560 ToShmem,
561 ToTyped,
562)]
563#[repr(u8)]
564pub enum MaskType {
565 Luminance,
566 Alpha,
567}
568
569#[allow(missing_docs)]
571#[derive(
572 Clone,
573 Copy,
574 Debug,
575 Deserialize,
576 Eq,
577 FromPrimitive,
578 Hash,
579 MallocSizeOf,
580 Parse,
581 PartialEq,
582 Serialize,
583 SpecifiedValueInfo,
584 ToComputedValue,
585 ToCss,
586 ToResolvedValue,
587 ToShmem,
588 ToTyped,
589)]
590#[repr(u8)]
591pub enum MaskMode {
592 MatchSource,
593 Alpha,
594 Luminance,
595}
596
597#[allow(missing_docs)]
599#[derive(
600 Clone,
601 Copy,
602 Debug,
603 Deserialize,
604 Eq,
605 FromPrimitive,
606 Hash,
607 MallocSizeOf,
608 Parse,
609 PartialEq,
610 Serialize,
611 SpecifiedValueInfo,
612 ToComputedValue,
613 ToCss,
614 ToResolvedValue,
615 ToShmem,
616 ToTyped,
617)]
618#[repr(u8)]
619pub enum MaskComposite {
620 Add,
621 Subtract,
622 Intersect,
623 Exclude,
624}
625
626#[allow(missing_docs)]
628#[derive(
629 Clone,
630 Copy,
631 Debug,
632 Deserialize,
633 Eq,
634 FromPrimitive,
635 Hash,
636 MallocSizeOf,
637 Parse,
638 PartialEq,
639 Serialize,
640 SpecifiedValueInfo,
641 ToComputedValue,
642 ToCss,
643 ToResolvedValue,
644 ToShmem,
645 ToTyped,
646)]
647#[repr(u8)]
648pub enum ColorInterpolation {
649 Auto,
650 Srgb,
651 Linearrgb,
652}