Skip to main content

rsvg/
property_defs.rs

1//! Definitions for CSS property types.
2//!
3//! Do not import things directly from this module; use the `properties` module instead,
4//! which re-exports things from here.
5//!
6//! This module defines most of the CSS property types that librsvg supports.  Each
7//! property requires a Rust type that will hold its values, and that type should
8//! implement a few traits, as follows.
9//!
10//! # Requirements for a property type
11//!
12//! You should call the [`make_property`] macro to take care of most of these requirements
13//! automatically:
14//!
15//! * A name for the type.  For example, the `fill` property has a [`Fill`] type defined
16//!   in this module.
17//!
18//! * An initial value per the CSS or SVG specs, given through an implementation of the
19//!   [`Default`] trait.
20//!
21//! * Whether the property's computed value inherits to child elements, given through an
22//!   implementation of the [`Property`] trait and its
23//!   [`inherits_automatically`][Property::inherits_automatically] method.
24//!
25//! * A way to derive the CSS *computed value* for the property, given through an
26//!   implementation of the [`Property`] trait and its [`compute`][Property::compute] method.
27//!
28//! * The actual underlying type.  For example, the [`make_property`] macro can generate a
29//!   field-less enum for properties like the `clip-rule` property, which just has
30//!   identifier-based values like `nonzero` and `evenodd`.  For general-purpose types like
31//!   [`Length`], the macro can wrap them in a newtype like `struct`
32//!   [`StrokeWidth`]`(`[`Length`]`)`.  For custom types, the macro call can be used just to
33//!   define the initial/default value and whether the property inherits automatically; you
34//!   should provide the other required trait implementations separately.
35//!
36//! * An implementation of the [`Parse`] trait for the underlying type.
37use std::convert::TryInto;
38use std::str::FromStr;
39
40use cssparser::{Parser, Token};
41use language_tags::LanguageTag;
42
43use crate::dasharray::Dasharray;
44use crate::error::*;
45use crate::filter::FilterValueList;
46use crate::font_props::{
47    Font, FontFamily, FontSize, FontWeight, GlyphOrientationVertical, LetterSpacing, LineHeight,
48};
49use crate::iri::Iri;
50use crate::length::*;
51use crate::paint_server::PaintServer;
52use crate::parse_identifiers;
53use crate::parsers::Parse;
54use crate::properties::ComputedValues;
55use crate::property_macros::Property;
56use crate::rect::Rect;
57use crate::transform::TransformProperty;
58use crate::unit_interval::UnitInterval;
59use crate::{impl_default, impl_property, make_property};
60
61make_property!(
62    /// `baseline-shift` property.
63    ///
64    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#BaselineShiftProperty>
65    ///
66    /// SVG2: <https://www.w3.org/TR/SVG2/text.html#BaselineShiftProperty>
67    BaselineShift,
68    default: Length::<Both>::default(),
69    newtype: Length<Both>,
70    property_impl: {
71        impl Property for BaselineShift {
72            fn inherits_automatically() -> bool {
73                false
74            }
75
76            fn compute(&self, v: &ComputedValues) -> Self {
77                let font_size = v.font_size().value();
78                let parent = v.baseline_shift();
79
80                match (self.0.unit, parent.0.unit) {
81                    (LengthUnit::Percent, _) => {
82                        BaselineShift(Length::<Both>::new(self.0.length * font_size.length + parent.0.length, font_size.unit))
83                    }
84
85                    (x, y) if x == y || parent.0.length == 0.0 => {
86                        BaselineShift(Length::<Both>::new(self.0.length + parent.0.length, self.0.unit))
87                    }
88
89                    _ => {
90                        // FIXME: the limitation here is that the parent's baseline_shift
91                        // and ours have different units.  We should be able to normalize
92                        // the lengths and add them even if they have different units, but
93                        // at the moment that requires access to the draw_ctx, which we
94                        // don't have here.
95                        //
96                        // So for now we won't add to the parent's baseline_shift.
97
98                        parent
99                    }
100                }
101            }
102        }
103    },
104    parse_impl: {
105        impl Parse for BaselineShift {
106            // These values come from Inkscape's SP_CSS_BASELINE_SHIFT_(SUB/SUPER/BASELINE);
107            // see sp_style_merge_baseline_shift_from_parent()
108            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<BaselineShift, crate::error::ParseError<'i>> {
109                parser.try_parse(|p| Ok(BaselineShift(Length::<Both>::parse(p)?)))
110                    .or_else(|_: ParseError<'_>| {
111                        Ok(parse_identifiers!(
112                            parser,
113                            "baseline" => BaselineShift(Length::<Both>::new(0.0, LengthUnit::Percent)),
114                            "sub" => BaselineShift(Length::<Both>::new(-0.2, LengthUnit::Percent)),
115
116                            "super" => BaselineShift(Length::<Both>::new(0.4, LengthUnit::Percent)),
117                        )?)
118                    })
119            }
120        }
121    }
122);
123
124make_property!(
125    /// `clip-path` property.
126    ///
127    /// SVG1.1: <https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty>
128    ///
129    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-clip-path>
130    ClipPath,
131    default: Iri::None,
132    inherits_automatically: false,
133    newtype_parse: Iri,
134);
135
136make_property!(
137    /// `clip-rule` property.
138    ///
139    /// SVG1.1: <https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty>
140    ///
141    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-clip-rule>
142    ClipRule,
143    default: NonZero,
144    inherits_automatically: true,
145
146    identifiers:
147    "nonzero" => NonZero,
148    "evenodd" => EvenOdd,
149);
150
151make_property!(
152    /// `color` property, the fallback for `currentColor` values.
153    ///
154    /// SVG1.1: <https://www.w3.org/TR/SVG11/color.html#ColorProperty>
155    ///
156    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#ColorProperty>
157    ///
158    /// The SVG spec allows the user agent to choose its own initial value for the "color"
159    /// property.  Here we start with opaque black for the initial value.  Clients can
160    /// override this by specifing a custom CSS stylesheet.
161    ///
162    /// Most of the time the `color` property is used to call
163    /// [`crate::color::resolve_color`].
164    Color,
165    default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
166    inherits_automatically: true,
167    newtype_parse: crate::color::Color,
168);
169
170make_property!(
171    /// `color-interpolation-filters` property.
172    ///
173    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty>
174    ///
175    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#propdef-color-interpolation-filters>
176    ColorInterpolationFilters,
177    default: LinearRgb,
178    inherits_automatically: true,
179
180    identifiers:
181    "auto" => Auto,
182    "linearRGB" => LinearRgb,
183    "sRGB" => Srgb,
184);
185
186make_property!(
187    /// `cx` property.
188    ///
189    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#CX>
190    ///
191    /// Note that in SVG1.1, this was an attribute, not a property.
192    CX,
193    default: Length::<Horizontal>::default(),
194    inherits_automatically: false,
195    newtype_parse: Length<Horizontal>,
196);
197
198make_property!(
199    /// `cy` attribute.
200    ///
201    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#CY>
202    ///
203    /// Note that in SVG1.1, this was an attribute, not a property.
204    CY,
205    default: Length::<Vertical>::default(),
206    inherits_automatically: false,
207    newtype_parse: Length<Vertical>,
208);
209
210make_property!(
211    /// `direction` property.
212    ///
213    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#DirectionProperty>
214    ///
215    /// SVG2: <https://www.w3.org/TR/SVG2/text.html#DirectionProperty>
216    Direction,
217    default: Ltr,
218    inherits_automatically: true,
219
220    identifiers:
221    "ltr" => Ltr,
222    "rtl" => Rtl,
223);
224
225make_property!(
226    /// `display` property.
227    ///
228    /// SVG1.1: <https://www.w3.org/TR/CSS2/visuren.html#display-prop>
229    ///
230    /// SVG2: <https://www.w3.org/TR/SVG2/render.html#VisibilityControl>
231    Display,
232    default: Inline,
233    inherits_automatically: false,
234
235    identifiers:
236    "inline" => Inline,
237    "block" => Block,
238    "list-item" => ListItem,
239    "run-in" => RunIn,
240    "compact" => Compact,
241    "marker" => Marker,
242    "table" => Table,
243    "inline-table" => InlineTable,
244    "table-row-group" => TableRowGroup,
245    "table-header-group" => TableHeaderGroup,
246    "table-footer-group" => TableFooterGroup,
247    "table-row" => TableRow,
248    "table-column-group" => TableColumnGroup,
249    "table-column" => TableColumn,
250    "table-cell" => TableCell,
251    "table-caption" => TableCaption,
252    "none" => None,
253);
254
255make_property!(
256    /// `dominant-baseline` property.
257    ///
258    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#BaselineAlignmentProperties>
259    DominantBaseline,
260    default: Auto,
261    inherits_automatically: true,
262
263    identifiers:
264    "auto" => Auto,
265    "ideographic" => Ideographic,
266    "alphabetic" => Alphabetic,
267    "hanging" => Hanging,
268    "mathematical" => Mathematical,
269    "central" => Central,
270    "middle" => Middle,
271    "text-after-edge" => TextAfterEdge,
272    "text-before-edge" => TextBeforeEdge,
273    // CSS3
274    "text-top" => TextTop,
275    "text-bottom" => TextBottom,
276    // No longer supported in SVG2 (https://www.w3.org/TR/SVG2/text.html#DominantBaselineProperty):
277    // use-script, no-change and reset-size
278);
279
280/// `enable-background` property.
281///
282/// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty>
283///
284/// This is deprecated in SVG2.  We just have a parser for it to avoid setting elements in
285/// error if they have this property.  Librsvg does not use the value of this property.
286#[derive(Debug, Clone, Copy, PartialEq)]
287pub enum EnableBackground {
288    Accumulate,
289    New(Option<Rect>),
290}
291
292make_property!(
293    EnableBackground,
294    default: EnableBackground::Accumulate,
295    inherits_automatically: false,
296
297    parse_impl: {
298        impl Parse for EnableBackground {
299            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
300                let loc = parser.current_source_location();
301
302                if parser
303                    .try_parse(|p| p.expect_ident_matching("accumulate"))
304                    .is_ok()
305                {
306                    return Ok(EnableBackground::Accumulate);
307                }
308
309                if parser.try_parse(|p| p.expect_ident_matching("new")).is_ok() {
310                    parser.try_parse(|p| -> Result<_, ParseError<'_>> {
311                        let x = f64::parse(p)?;
312                        let y = f64::parse(p)?;
313                        let w = f64::parse(p)?;
314                        let h = f64::parse(p)?;
315
316                        Ok(EnableBackground::New(Some(Rect::new(x, y, x + w, y + h))))
317                    }).or(Ok(EnableBackground::New(None)))
318                } else {
319                    Err(loc.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'enable-background' property")))
320                }
321            }
322        }
323
324    }
325);
326
327#[cfg(test)]
328#[test]
329fn parses_enable_background() {
330    assert_eq!(
331        EnableBackground::parse_str("accumulate").unwrap(),
332        EnableBackground::Accumulate
333    );
334
335    assert_eq!(
336        EnableBackground::parse_str("new").unwrap(),
337        EnableBackground::New(None)
338    );
339
340    assert_eq!(
341        EnableBackground::parse_str("new 1 2 3 4").unwrap(),
342        EnableBackground::New(Some(Rect::new(1.0, 2.0, 4.0, 6.0)))
343    );
344
345    assert!(EnableBackground::parse_str("new foo").is_err());
346
347    assert!(EnableBackground::parse_str("plonk").is_err());
348}
349
350make_property!(
351    /// `fill` property.
352    ///
353    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillProperty>
354    ///
355    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#FillProperty>
356    Fill,
357    default: PaintServer::SolidColor(crate::color::Color::Rgba(
358        crate::color::RGBA::new(0, 0, 0, 1.0)
359    )),
360    inherits_automatically: true,
361    newtype_parse: PaintServer,
362);
363
364make_property!(
365    /// `fill-opacity` property.
366    ///
367    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty>
368    ///
369    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#FillOpacity>
370    FillOpacity,
371    default: UnitInterval(1.0),
372    inherits_automatically: true,
373    newtype_parse: UnitInterval,
374);
375
376make_property!(
377    /// `fill-rule` property.
378    ///
379    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty>
380    ///
381    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#WindingRule>
382    FillRule,
383    default: NonZero,
384    inherits_automatically: true,
385
386    identifiers:
387    "nonzero" => NonZero,
388    "evenodd" => EvenOdd,
389);
390
391/// `filter` property.
392///
393/// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#FilterProperty>
394///
395/// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FilterProperty>
396///
397/// Note that in SVG2, the filters got offloaded to the [Filter Effects Module Level
398/// 1](https://www.w3.org/TR/filter-effects/) specification.
399#[derive(Debug, Clone, PartialEq)]
400pub enum Filter {
401    None,
402    List(FilterValueList),
403}
404
405make_property!(
406    Filter,
407    default: Filter::None,
408    inherits_automatically: false,
409    parse_impl: {
410        impl Parse for Filter {
411            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
412
413                if parser
414                    .try_parse(|p| p.expect_ident_matching("none"))
415                    .is_ok()
416                {
417                    return Ok(Filter::None);
418                }
419
420                Ok(Filter::List(FilterValueList::parse(parser)?))
421            }
422        }
423    }
424);
425
426make_property!(
427    /// `flood-color` property, for `feFlood` and `feDropShadow` filter elements.
428    ///
429    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#feFloodElement>
430    ///
431    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FloodColorProperty>
432    FloodColor,
433    default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
434    inherits_automatically: false,
435    newtype_parse: crate::color::Color,
436);
437
438make_property!(
439    /// `flood-opacity` property, for `feFlood` and `feDropShadow` filter elements.
440    ///
441    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#feFloodElement>
442    ///
443    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FloodOpacityProperty>
444    FloodOpacity,
445    default: UnitInterval(1.0),
446    inherits_automatically: false,
447    newtype_parse: UnitInterval,
448);
449
450make_property!(
451    // docs are in font_props.rs
452    Font,
453    default: Font::Spec(Default::default()),
454    inherits_automatically: true,
455);
456
457make_property!(
458    // docs are in font_props.rs
459    FontFamily,
460    default: FontFamily("Times New Roman".to_string()),
461    inherits_automatically: true,
462);
463
464make_property!(
465    // docs are in font_props.rs
466    FontSize,
467    default: FontSize::Value(Length::<Both>::new(12.0, LengthUnit::Px)),
468    property_impl: {
469        impl Property for FontSize {
470            fn inherits_automatically() -> bool {
471                true
472            }
473
474            fn compute(&self, v: &ComputedValues) -> Self {
475                self.compute(v)
476            }
477        }
478    }
479);
480
481make_property!(
482    /// `font-stretch` property.
483    ///
484    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontStretchProperty>
485    ///
486    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#font-size-propstret>
487    FontStretch,
488    default: Normal,
489    inherits_automatically: true,
490
491    identifiers:
492    "normal" => Normal,
493    "wider" => Wider,
494    "narrower" => Narrower,
495    "ultra-condensed" => UltraCondensed,
496    "extra-condensed" => ExtraCondensed,
497    "condensed" => Condensed,
498    "semi-condensed" => SemiCondensed,
499    "semi-expanded" => SemiExpanded,
500    "expanded" => Expanded,
501    "extra-expanded" => ExtraExpanded,
502    "ultra-expanded" => UltraExpanded,
503);
504
505make_property!(
506    /// `font-style` property.
507    ///
508    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontStyleProperty>
509    ///
510    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#font-size-propstret>
511    FontStyle,
512    default: Normal,
513    inherits_automatically: true,
514
515    identifiers:
516    "normal" => Normal,
517    "italic" => Italic,
518    "oblique" => Oblique,
519);
520
521make_property!(
522    /// `font-variant` property.
523    ///
524    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontVariantProperty>
525    ///
526    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#propdef-font-variant>
527    ///
528    /// Note that in CSS3, this is a lot more complex than CSS2.1 / SVG1.1.
529    FontVariant,
530    default: Normal,
531    inherits_automatically: true,
532
533    identifiers:
534    "normal" => Normal,
535    "small-caps" => SmallCaps,
536);
537
538make_property!(
539    // docs are in font_props.rs
540    FontWeight,
541    default: FontWeight::Normal,
542    property_impl: {
543        impl Property for FontWeight {
544            fn inherits_automatically() -> bool {
545                true
546            }
547
548            fn compute(&self, v: &ComputedValues) -> Self {
549                self.compute(&v.font_weight())
550            }
551        }
552    }
553);
554
555make_property!(
556    // docs are in font_props.rs
557    //
558    // Although https://www.w3.org/TR/css-writing-modes-3/#propdef-glyph-orientation-vertical specifies
559    // "n/a" for both the initial value (default) and inheritance, we'll use Auto here for the default,
560    // since it translates to TextOrientation::Mixed - which is text-orientation's initial value.
561    GlyphOrientationVertical,
562    default: GlyphOrientationVertical::Auto,
563    inherits_automatically: false,
564);
565
566make_property!(
567    /// `height` property.
568    ///
569    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Sizing>
570    ///
571    /// Note that in SVG1.1, this was an attribute, not a property.
572    Height,
573    default: LengthOrAuto::<Vertical>::Auto,
574    inherits_automatically: false,
575    newtype_parse: LengthOrAuto<Vertical>,
576);
577
578make_property!(
579    /// `image-rendering` property.
580    ///
581    /// CSS Images Module Level 3: <https://www.w3.org/TR/css-images-3/#the-image-rendering>
582    ///
583    /// Note that this property previously accepted the values optimizeSpeed and optimizeQuality.
584    /// These are now deprecated; a user agent must accept them as valid values but must treat
585    /// them as having the same behavior as crisp-edges and smooth respectively.
586    ImageRendering,
587    default: Auto,
588    inherits_automatically: true,
589
590    identifiers:
591    "auto" => Auto,
592    "smooth" => Smooth,
593    "optimizeQuality" => OptimizeQuality,
594    "high-quality" => HighQuality,
595    "crisp-edges" => CrispEdges,
596    "optimizeSpeed" => OptimizeSpeed,
597    "pixelated" => Pixelated,
598);
599
600make_property!(
601    /// `isolation` property.
602    ///
603    /// CSS Compositing and Blending 1: <https://www.w3.org/TR/compositing-1/#isolation>
604    Isolation,
605    default: Auto,
606    inherits_automatically: false,
607
608    identifiers:
609    "auto" => Auto,
610    "isolate" => Isolate,
611);
612
613make_property!(
614    // docs are in font_props.rs
615    LetterSpacing,
616    default: LetterSpacing::Normal,
617    property_impl: {
618        impl Property for LetterSpacing {
619            fn inherits_automatically() -> bool {
620                true
621            }
622
623            fn compute(&self, _v: &ComputedValues) -> Self {
624                self.compute()
625            }
626        }
627    }
628);
629
630make_property!(
631    // docs are in font_props.rs
632    LineHeight,
633    default: LineHeight::Normal,
634    inherits_automatically: true,
635);
636
637make_property!(
638    /// `lighting-color` property for `feDiffuseLighting` and `feSpecularLighting` filter elements.
639    ///
640    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#LightingColorProperty>
641    ///
642    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#LightingColorProperty>
643    LightingColor,
644    default: crate::color::Color::Rgba(crate::color::RGBA::new(255, 255, 255, 1.0)),
645    inherits_automatically: false,
646    newtype_parse: crate::color::Color,
647);
648
649make_property!(
650    /// `marker` shorthand property.
651    ///
652    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand>
653    ///
654    /// This is a shorthand, which expands to the `marker-start`, `marker-mid`,
655    /// `marker-end` longhand properties.
656    Marker,
657    default: Iri::None,
658    inherits_automatically: true,
659    newtype_parse: Iri,
660);
661
662make_property!(
663    /// `marker-end` property.
664    ///
665    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
666    MarkerEnd,
667    default: Iri::None,
668    inherits_automatically: true,
669    newtype_parse: Iri,
670);
671
672make_property!(
673    /// `marker-mid` property.
674    ///
675    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
676    MarkerMid,
677    default: Iri::None,
678    inherits_automatically: true,
679    newtype_parse: Iri,
680);
681
682make_property!(
683    /// `marker-start` property.
684    ///
685    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
686    MarkerStart,
687    default: Iri::None,
688    inherits_automatically: true,
689    newtype_parse: Iri,
690);
691
692make_property!(
693    /// `mask` shorthand property.
694    ///
695    /// SVG1.1: <https://www.w3.org/TR/SVG11/masking.html#MaskProperty>
696    ///
697    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-mask>
698    ///
699    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
700    Mask,
701    default: Iri::None,
702    inherits_automatically: false,
703    newtype_parse: Iri,
704);
705
706make_property!(
707    /// `mask-type` property.
708    ///
709    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-mask-type>
710    MaskType,
711    default: Luminance,
712    inherits_automatically: false,
713
714    identifiers:
715    "luminance" => Luminance,
716    "alpha" => Alpha,
717);
718
719make_property!(
720    /// `mix-blend-mode` property.
721    ///
722    /// Compositing and Blending 1: <https://www.w3.org/TR/compositing/#mix-blend-mode>
723    MixBlendMode,
724    default: Normal,
725    inherits_automatically: false,
726
727    identifiers:
728    "normal" => Normal,
729    "multiply" => Multiply,
730    "screen" => Screen,
731    "overlay" => Overlay,
732    "darken" => Darken,
733    "lighten" => Lighten,
734    "color-dodge" => ColorDodge,
735    "color-burn" => ColorBurn,
736    "hard-light" => HardLight,
737    "soft-light" => SoftLight,
738    "difference" => Difference,
739    "exclusion" => Exclusion,
740    "hue" => Hue,
741    "saturation" => Saturation,
742    "color" => Color,
743    "luminosity" => Luminosity,
744);
745
746make_property!(
747    /// `opacity` property.
748    ///
749    /// CSS Color 3: <https://www.w3.org/TR/css-color-3/#opacity>
750    Opacity,
751    default: UnitInterval(1.0),
752    inherits_automatically: false,
753    newtype_parse: UnitInterval,
754);
755
756make_property!(
757    /// `overflow` shorthand property.
758    ///
759    /// CSS2: <https://www.w3.org/TR/CSS2/visufx.html#overflow>
760    ///
761    /// CSS Overflow 3: <https://www.w3.org/TR/css-overflow-3/#propdef-overflow>
762    ///
763    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
764    Overflow,
765    default: Visible,
766    inherits_automatically: false,
767
768    identifiers:
769    "visible" => Visible,
770    "hidden" => Hidden,
771    "scroll" => Scroll,
772    "auto" => Auto,
773);
774
775impl Overflow {
776    pub fn overflow_allowed(&self) -> bool {
777        matches!(*self, Overflow::Auto | Overflow::Visible)
778    }
779}
780
781/// One of the three operations for the `paint-order` property; see [`PaintOrder`].
782#[repr(u8)]
783#[derive(Debug, Clone, Copy, Eq, PartialEq)]
784pub enum PaintTarget {
785    Fill,
786    Stroke,
787    Markers,
788}
789
790make_property!(
791    /// `paint-order` property.
792    ///
793    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#PaintOrder>
794    ///
795    /// The `targets` field specifies the order in which graphic elements should be filled/stroked.
796    /// Instead of hard-coding an order of fill/stroke/markers, use the order specified by the `targets`.
797    PaintOrder,
798    inherits_automatically: true,
799    fields: {
800        targets: [PaintTarget; 3], default: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers],
801    }
802
803    parse_impl: {
804        impl Parse for PaintOrder {
805            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<PaintOrder, ParseError<'i>> {
806                let allowed_targets = 3;
807                let mut targets = Vec::with_capacity(allowed_targets);
808
809                if parser.try_parse(|p| p.expect_ident_matching("normal")).is_ok() {
810                    return Ok(PaintOrder::default());
811                }
812
813                while !parser.is_exhausted() {
814                    let loc = parser.current_source_location();
815                    let token = parser.next()?;
816
817                    let value = match token {
818                        Token::Ident(cow) if cow.eq_ignore_ascii_case("fill") && !targets.contains(&PaintTarget::Fill) => PaintTarget::Fill,
819                        Token::Ident(cow) if cow.eq_ignore_ascii_case("stroke") && !targets.contains(&PaintTarget::Stroke) => PaintTarget::Stroke,
820                        Token::Ident(cow) if cow.eq_ignore_ascii_case("markers") && !targets.contains(&PaintTarget::Markers) => PaintTarget::Markers,
821                        _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
822                    };
823
824                    targets.push(value);
825                };
826
827                // any values which were not specfied should be painted in default order
828                // (fill, stroke, markers) following the values which were explicitly specified.
829                for &target in &[PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers] {
830                    if !targets.contains(&target) {
831                        targets.push(target);
832                    }
833                }
834                Ok(PaintOrder {
835                    targets: targets[..].try_into().expect("Incorrect number of targets in paint-order")
836                })
837            }
838        }
839    }
840);
841
842#[cfg(test)]
843#[test]
844fn parses_paint_order() {
845    assert_eq!(
846        PaintOrder::parse_str("normal").unwrap(),
847        PaintOrder {
848            targets: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers]
849        }
850    );
851
852    assert_eq!(
853        PaintOrder::parse_str("markers fill").unwrap(),
854        PaintOrder {
855            targets: [PaintTarget::Markers, PaintTarget::Fill, PaintTarget::Stroke]
856        }
857    );
858
859    assert_eq!(
860        PaintOrder::parse_str("stroke").unwrap(),
861        PaintOrder {
862            targets: [PaintTarget::Stroke, PaintTarget::Fill, PaintTarget::Markers]
863        }
864    );
865
866    assert!(PaintOrder::parse_str("stroke stroke").is_err());
867    assert!(PaintOrder::parse_str("markers stroke fill hello").is_err());
868}
869
870make_property!(
871    /// `r` property.
872    ///
873    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#R>
874    ///
875    /// Note that in SVG1.1, this was an attribute, not a property.
876    R,
877    default: ULength::<Both>::default(),
878    inherits_automatically: false,
879    newtype_parse: ULength<Both>,
880);
881
882make_property!(
883    /// `rx` property.
884    ///
885    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#RX>
886    ///
887    /// Note that in SVG1.1, this was an attribute, not a property.
888    RX,
889    default: LengthOrAuto::<Horizontal>::Auto,
890    inherits_automatically: false,
891    newtype_parse: LengthOrAuto<Horizontal>,
892);
893
894make_property!(
895    /// `ry` property.
896    ///
897    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#RY>
898    ///
899    /// Note that in SVG1.1, this was an attribute, not a property.
900    RY,
901    default: LengthOrAuto::<Vertical>::Auto,
902    inherits_automatically: false,
903    newtype_parse: LengthOrAuto<Vertical>,
904);
905
906make_property!(
907    /// `shape-rendering` property.
908    ///
909    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#ShapeRendering>
910    ShapeRendering,
911    default: Auto,
912    inherits_automatically: true,
913
914    identifiers:
915    "auto" => Auto,
916    "optimizeSpeed" => OptimizeSpeed,
917    "geometricPrecision" => GeometricPrecision,
918    "crispEdges" => CrispEdges,
919);
920
921make_property!(
922    /// `stop-color` property for gradient stops.
923    ///
924    /// SVG2: <https://www.w3.org/TR/SVG2/pservers.html#StopColorProperty>
925    StopColor,
926    default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
927    inherits_automatically: false,
928    newtype_parse: crate::color::Color,
929);
930
931make_property!(
932    /// `stop-opacity` property for gradient stops.
933    ///
934    /// SVG2: <https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty>
935    StopOpacity,
936    default: UnitInterval(1.0),
937    inherits_automatically: false,
938    newtype_parse: UnitInterval,
939);
940
941make_property!(
942    /// `stroke` property.
943    ///
944    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint>
945    Stroke,
946    default: PaintServer::None,
947    inherits_automatically: true,
948    newtype_parse: PaintServer,
949);
950
951make_property!(
952    /// `stroke-dasharray` property.
953    ///
954    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeDashing>
955    StrokeDasharray,
956    default: Dasharray::default(),
957    inherits_automatically: true,
958    newtype_parse: Dasharray,
959);
960
961make_property!(
962    /// `stroke-dashoffset` property.
963    ///
964    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeDashingdas>
965    StrokeDashoffset,
966    default: Length::<Both>::default(),
967    inherits_automatically: true,
968    newtype_parse: Length<Both>,
969);
970
971make_property!(
972    /// `stroke-linecap` property.
973    ///
974    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#LineCaps>
975    StrokeLinecap,
976    default: Butt,
977    inherits_automatically: true,
978
979    identifiers:
980    "butt" => Butt,
981    "round" => Round,
982    "square" => Square,
983);
984
985make_property!(
986    /// `stroke-linejoin` property.
987    ///
988    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#LineJoin>
989    StrokeLinejoin,
990    default: Miter,
991    inherits_automatically: true,
992
993    identifiers:
994    "miter" => Miter,
995    "round" => Round,
996    "bevel" => Bevel,
997);
998
999make_property!(
1000    /// `stroke-miterlimit` property.
1001    ///
1002    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty>
1003    StrokeMiterlimit,
1004    default: 4f64,
1005    inherits_automatically: true,
1006    newtype_parse: f64,
1007);
1008
1009make_property!(
1010    /// `stroke-opacity` property.
1011    ///
1012    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeOpacity>
1013    StrokeOpacity,
1014    default: UnitInterval(1.0),
1015    inherits_automatically: true,
1016    newtype_parse: UnitInterval,
1017);
1018
1019make_property!(
1020    /// `stroke-width` property.
1021    ///
1022    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeWidth>
1023    StrokeWidth,
1024    default: Length::<Both>::new(1.0, LengthUnit::Px),
1025    inherits_automatically: true,
1026    newtype_parse: Length::<Both>,
1027);
1028
1029make_property!(
1030    /// `text-anchor` property.
1031    ///
1032    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty>
1033    TextAnchor,
1034    default: Start,
1035    inherits_automatically: true,
1036
1037    identifiers:
1038    "start" => Start,
1039    "middle" => Middle,
1040    "end" => End,
1041);
1042
1043make_property!(
1044    /// `text-decoration` shorthand property.
1045    ///
1046    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#TextDecorationProperty>
1047    ///
1048    /// CSS Text Decoration 3: <https://www.w3.org/TR/css-text-decor-3/#text-decoration-property>
1049    ///
1050    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
1051    TextDecoration,
1052    inherits_automatically: false,
1053
1054    fields: {
1055        overline: bool, default: false,
1056        underline: bool, default: false,
1057        strike: bool, default: false,
1058    }
1059
1060    parse_impl: {
1061        impl Parse for TextDecoration {
1062            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TextDecoration, ParseError<'i>> {
1063                let mut overline = false;
1064                let mut underline = false;
1065                let mut strike = false;
1066
1067                if parser.try_parse(|p| p.expect_ident_matching("none")).is_ok() {
1068                    return Ok(TextDecoration::default());
1069                }
1070
1071                while !parser.is_exhausted() {
1072                    let loc = parser.current_source_location();
1073                    let token = parser.next()?;
1074
1075                    match token {
1076                        Token::Ident(cow) if cow.eq_ignore_ascii_case("overline") => overline = true,
1077                        Token::Ident(cow) if cow.eq_ignore_ascii_case("underline") => underline = true,
1078                        Token::Ident(cow) if cow.eq_ignore_ascii_case("line-through") => strike = true,
1079                        _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
1080                    }
1081                }
1082
1083                Ok(TextDecoration {
1084                    overline,
1085                    underline,
1086                    strike,
1087                })
1088            }
1089        }
1090    }
1091);
1092
1093#[cfg(test)]
1094#[test]
1095fn parses_text_decoration() {
1096    assert_eq!(
1097        TextDecoration::parse_str("none").unwrap(),
1098        TextDecoration {
1099            overline: false,
1100            underline: false,
1101            strike: false,
1102        }
1103    );
1104
1105    assert_eq!(
1106        TextDecoration::parse_str("overline").unwrap(),
1107        TextDecoration {
1108            overline: true,
1109            underline: false,
1110            strike: false,
1111        }
1112    );
1113
1114    assert_eq!(
1115        TextDecoration::parse_str("underline").unwrap(),
1116        TextDecoration {
1117            overline: false,
1118            underline: true,
1119            strike: false,
1120        }
1121    );
1122
1123    assert_eq!(
1124        TextDecoration::parse_str("line-through").unwrap(),
1125        TextDecoration {
1126            overline: false,
1127            underline: false,
1128            strike: true,
1129        }
1130    );
1131
1132    assert_eq!(
1133        TextDecoration::parse_str("underline overline").unwrap(),
1134        TextDecoration {
1135            overline: true,
1136            underline: true,
1137            strike: false,
1138        }
1139    );
1140
1141    assert!(TextDecoration::parse_str("airline").is_err())
1142}
1143
1144make_property!(
1145    /// `text-orientation` property.
1146    ///
1147    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#propdef-text-orientation>
1148    TextOrientation,
1149    default: Mixed,
1150    inherits_automatically: true,
1151
1152    identifiers:
1153    "mixed" => Mixed,
1154    "upright" => Upright,
1155    "sideways" => Sideways,
1156);
1157
1158impl From<GlyphOrientationVertical> for TextOrientation {
1159    /// Converts the `glyph-orientation-vertical` shorthand to a `text-orientation` longhand.
1160    ///
1161    /// See <https://www.w3.org/TR/css-writing-modes-3/#propdef-glyph-orientation-vertical> for the conversion table.
1162    fn from(o: GlyphOrientationVertical) -> TextOrientation {
1163        match o {
1164            GlyphOrientationVertical::Auto => TextOrientation::Mixed,
1165            GlyphOrientationVertical::Angle0 => TextOrientation::Upright,
1166            GlyphOrientationVertical::Angle90 => TextOrientation::Sideways,
1167        }
1168    }
1169}
1170
1171make_property!(
1172    /// `text-rendering` property.
1173    ///
1174    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty>
1175    TextRendering,
1176    default: Auto,
1177    inherits_automatically: true,
1178
1179    identifiers:
1180    "auto" => Auto,
1181    "optimizeSpeed" => OptimizeSpeed,
1182    "optimizeLegibility" => OptimizeLegibility,
1183    "geometricPrecision" => GeometricPrecision,
1184);
1185
1186// FIXME: this is marked as allow(unused) because this `Transform` newtype is never
1187// constructed.  Instead, properties.rs uses `TransformProperty` directly to handle the
1188// distinction between the "transform" attribute from SVG1.1, and the "transform" CSS
1189// property in SVG2.
1190//
1191// I think we can play a bit with the naming or namespacing of things to make this
1192// `Transform` newtype actually work for the machinery in properties.rs - mainly being
1193// careful about properties::Transform (which is a re-export of property_defs::Transform)
1194// versus transform::Transform (which is just an affine).
1195make_property!(
1196    /// `transform` property.
1197    ///
1198    /// CSS Transforms 1: <https://www.w3.org/TR/css-transforms-1/#transform-property>
1199    #[allow(unused)]
1200    Transform,
1201    default: TransformProperty::None,
1202    inherits_automatically: false,
1203    newtype_parse: TransformProperty,
1204);
1205
1206make_property!(
1207    /// `unicode-bidi` property.
1208    ///
1209    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#unicode-bidi>
1210    UnicodeBidi,
1211    default: Normal,
1212    inherits_automatically: false,
1213
1214    identifiers:
1215    "normal" => Normal,
1216    "embed" => Embed,
1217    "isolate" => Isolate,
1218    "bidi-override" => BidiOverride,
1219    "isolate-override" => IsolateOverride,
1220    "plaintext" => Plaintext,
1221);
1222
1223make_property!(
1224    /// `vector-effect` property.
1225    ///
1226    /// SVG2: <https://svgwg.org/svg2-draft/coords.html#VectorEffectProperty>
1227    VectorEffect,
1228    default: None,
1229    inherits_automatically: false,
1230
1231    identifiers:
1232    "none" => None,
1233    "non-scaling-stroke" => NonScalingStroke,
1234    // non-scaling-size, non-rotation, fixed-position not implemented
1235);
1236
1237make_property!(
1238    /// `visibility` property.
1239    ///
1240    /// CSS2: <https://www.w3.org/TR/CSS2/visufx.html#visibility>
1241    Visibility,
1242    default: Visible,
1243    inherits_automatically: true,
1244
1245    identifiers:
1246    "visible" => Visible,
1247    "hidden" => Hidden,
1248    "collapse" => Collapse,
1249);
1250
1251make_property!(
1252    /// `width` property.
1253    ///
1254    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Sizing>
1255    ///
1256    /// Note that in SVG1.1, this was an attribute, not a property.
1257    Width,
1258    default: LengthOrAuto::<Horizontal>::Auto,
1259    inherits_automatically: false,
1260    newtype_parse: LengthOrAuto<Horizontal>,
1261);
1262
1263make_property!(
1264    /// `writing-mode` property.
1265    ///
1266    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#WritingModeProperty>
1267    ///
1268    /// SVG2: <https://svgwg.org/svg2-draft/text.html#WritingModeProperty>
1269    ///
1270    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#block-flow>
1271    ///
1272    /// See the comments in the SVG2 spec for how the SVG1.1 values must be translated
1273    /// into CSS Writing Modes 3 values.
1274    WritingMode,
1275    default: HorizontalTb,
1276    identifiers: {
1277        "horizontal-tb" => HorizontalTb,
1278        "vertical-rl" => VerticalRl,
1279        "vertical-lr" => VerticalLr,
1280        "lr" => Lr,
1281        "lr-tb" => LrTb,
1282        "rl" => Rl,
1283        "rl-tb" => RlTb,
1284        "tb" => Tb,
1285        "tb-rl" => TbRl,
1286    },
1287    property_impl: {
1288        impl Property for WritingMode {
1289            fn inherits_automatically() -> bool {
1290                true
1291            }
1292
1293            fn compute(&self, _v: &ComputedValues) -> Self {
1294                use WritingMode::*;
1295
1296                // Translate SVG1.1 compatibility values to SVG2 / CSS Writing Modes 3.
1297                match *self {
1298                    Lr | LrTb | Rl | RlTb => HorizontalTb,
1299                    Tb | TbRl => VerticalRl,
1300                    _ => *self,
1301                }
1302            }
1303        }
1304    }
1305);
1306
1307impl WritingMode {
1308    pub fn is_horizontal(self) -> bool {
1309        use WritingMode::*;
1310
1311        matches!(self, HorizontalTb | Lr | LrTb | Rl | RlTb)
1312    }
1313}
1314
1315make_property!(
1316    /// `x` property.
1317    ///
1318    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#X>
1319    ///
1320    /// Note that in SVG1.1, this was an attribute, not a property.
1321    X,
1322    default: Length::<Horizontal>::default(),
1323    inherits_automatically: false,
1324    newtype_parse: Length<Horizontal>,
1325);
1326
1327make_property!(
1328    /// `xml:lang` attribute.
1329    ///
1330    /// XML1.0: <https://www.w3.org/TR/xml/#sec-lang-tag>
1331    ///
1332    /// Similar to `XmlSpace`, this is a hack in librsvg: the `xml:lang` attribute is
1333    /// supposed to apply to an element and all its children.  This more or less matches
1334    /// CSS property inheritance, so librsvg reuses the machinery for property inheritance
1335    /// to propagate down the value of the `xml:lang` attribute to an element's children.
1336    XmlLang,
1337    default: None,
1338    inherits_automatically: true,
1339    newtype: Option<Box<LanguageTag>>,
1340    parse_impl: {
1341        impl Parse for XmlLang {
1342            fn parse<'i>(
1343                parser: &mut Parser<'i, '_>,
1344            ) -> Result<XmlLang, ParseError<'i>> {
1345                let language_tag = parser.expect_ident()?;
1346                let language_tag = LanguageTag::from_str(language_tag).map_err(|_| {
1347                    parser.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'xml:lang' parameter"))
1348                })?;
1349                Ok(XmlLang(Some(Box::new(language_tag))))
1350            }
1351        }
1352    },
1353);
1354
1355#[cfg(test)]
1356#[test]
1357fn parses_xml_lang() {
1358    assert_eq!(
1359        XmlLang::parse_str("es-MX").unwrap(),
1360        XmlLang(Some(Box::new(LanguageTag::from_str("es-MX").unwrap())))
1361    );
1362
1363    assert!(XmlLang::parse_str("").is_err());
1364}
1365
1366make_property!(
1367    /// `xml:space` attribute.
1368    ///
1369    /// XML1.0: <https://www.w3.org/TR/xml/#sec-white-space>
1370    ///
1371    /// Similar to `XmlLang`, this is a hack in librsvg.  The `xml:space` attribute is
1372    /// supposed to be applied to all the children of the element in which it appears, so
1373    /// it works more or less the same as CSS property inheritance.  Librsvg reuses the
1374    /// machinery for CSS property inheritance to propagate down the value of `xml:space`
1375    /// to an element's children.
1376    XmlSpace,
1377    default: Default,
1378    inherits_automatically: true,
1379
1380    identifiers:
1381    "default" => Default,
1382    "preserve" => Preserve,
1383);
1384
1385make_property!(
1386    /// `y` property.
1387    ///
1388    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Y>
1389    ///
1390    /// Note that in SVG1.1, this was an attribute, not a property.
1391    Y,
1392    default: Length::<Vertical>::default(),
1393    inherits_automatically: false,
1394    newtype_parse: Length<Vertical>,
1395);
1396
1397make_property!(
1398    /// `whitespace` properties
1399    ///
1400    /// <https://www.w3.org/TR/css-text-3/#white-space-property>
1401    WhiteSpace,
1402
1403    default: Normal,
1404    inherits_automatically: true,
1405
1406    identifiers:
1407    "normal" => Normal,
1408    "pre" => Pre,
1409    "nowrap" => NoWrap,
1410    "pre-wrap" => PreWrap,
1411    "break-spaces" => BreakSpaces,
1412    "pre-line" => PreLine,
1413);