rsvg/
properties.rs

1//! CSS properties, specified values, computed values.
2//!
3//! To implement support for a CSS property, do the following:
4//!
5//! * Create a type that will hold the property's values.  Please do this in the file
6//!   `property_defs.rs`; you should cut-and-paste from the existing property definitions or
7//!   read the documentation of the [`make_property`] macro.  You should read the
8//!   documentation for the [`property_defs`][crate::property_defs] module to see all that
9//!   is involved in creating a type for a property.
10//!
11//! * Modify the call to the `make_properties` macro in this module to include the new
12//!   property's name.
13//!
14//! * Modify the rest of librsvg wherever the computed value of the property needs to be used.
15//!   This is available in methods that take an argument of type [`ComputedValues`].
16
17use cssparser::{
18    self, BasicParseErrorKind, ParseErrorKind, Parser, ParserInput, RuleBodyParser, ToCss,
19};
20use markup5ever::{expanded_name, local_name, ns, ExpandedName, LocalName, QualName};
21use std::collections::HashSet;
22
23#[cfg(doc)]
24use crate::make_property;
25
26use crate::css::{DeclParser, Declaration, Origin, RuleBodyItem};
27use crate::error::*;
28use crate::parsers::{Parse, ParseValue};
29use crate::property_macros::Property;
30use crate::rsvg_log;
31use crate::session::Session;
32use crate::transform::{Transform, TransformAttribute, TransformProperty};
33use crate::xml::Attributes;
34
35// Re-export the actual properties so they are easy to find from a single place `properties::*`.
36pub use crate::font_props::*;
37pub use crate::property_defs::*;
38
39/// Representation of a single CSS property value.
40///
41/// `Unspecified` is the `Default`; it means that the corresponding property is not present.
42///
43/// `Inherit` means that the property is explicitly set to inherit
44/// from the parent element.  This is useful for properties which the
45/// SVG or CSS specs mandate that should not be inherited by default.
46///
47/// `Specified` is a value given by the SVG or CSS stylesheet.  This will later be
48/// resolved into part of a `ComputedValues` struct.
49#[derive(Clone)]
50pub enum SpecifiedValue<T>
51where
52    T: Property + Clone + Default,
53{
54    Unspecified,
55    Inherit,
56    Specified(T),
57}
58
59impl<T> SpecifiedValue<T>
60where
61    T: Property + Clone + Default,
62{
63    pub fn compute(&self, src: &T, src_values: &ComputedValues) -> T {
64        let value: T = match *self {
65            SpecifiedValue::Unspecified => {
66                if <T as Property>::inherits_automatically() {
67                    src.clone()
68                } else {
69                    Default::default()
70                }
71            }
72
73            SpecifiedValue::Inherit => src.clone(),
74
75            SpecifiedValue::Specified(ref v) => v.clone(),
76        };
77
78        value.compute(src_values)
79    }
80}
81
82/// Whether a property also has a presentation attribute.
83///
84/// <https://svgwg.org/svg2-draft/styling.html#PresentationAttributes>
85#[derive(PartialEq)]
86enum PresentationAttr {
87    No,
88    Yes,
89}
90
91/// How to parse a value, whether it comes from a property or from a presentation attribute
92#[derive(PartialEq)]
93pub enum ParseAs {
94    Property,
95    PresentationAttr,
96}
97
98impl PropertyId {
99    fn as_u8(&self) -> u8 {
100        *self as u8
101    }
102
103    fn as_usize(&self) -> usize {
104        *self as usize
105    }
106}
107
108/// Holds the specified values for the CSS properties of an element.
109#[derive(Clone)]
110pub struct SpecifiedValues {
111    indices: [u8; PropertyId::UnsetProperty as usize],
112    props: Vec<ParsedProperty>,
113
114    transform: Option<Transform>,
115}
116
117impl Default for SpecifiedValues {
118    fn default() -> Self {
119        SpecifiedValues {
120            // this many elements, with the same value
121            indices: [PropertyId::UnsetProperty.as_u8(); PropertyId::UnsetProperty as usize],
122            props: Vec::new(),
123            transform: None,
124        }
125    }
126}
127
128impl ComputedValues {
129    // TODO for madds: this function will go away, to be replaced by the one generated
130    // automatically by the macros.
131    pub fn transform(&self) -> Transform {
132        self.transform
133    }
134
135    pub fn is_overflow(&self) -> bool {
136        matches!(self.overflow(), Overflow::Auto | Overflow::Visible)
137    }
138
139    /// Whether we should draw the element or skip both space allocation
140    /// and drawing.
141    /// <https://www.w3.org/TR/SVG2/render.html#VisibilityControl>
142    pub fn is_displayed(&self) -> bool {
143        self.display() != Display::None
144    }
145
146    /// Whether we should draw the element or allocate its space but
147    /// skip drawing.
148    /// <https://www.w3.org/TR/SVG2/render.html#VisibilityControl>
149    pub fn is_visible(&self) -> bool {
150        self.visibility() == Visibility::Visible
151    }
152}
153
154/// Macro to generate all the machinery for properties.
155///
156/// This generates the following:
157///
158/// * `PropertyId`, an fieldless enum with simple values to identify all the properties.
159/// * `ParsedProperty`, a variant enum for all the specified property values.
160/// * `ComputedValue`, a variant enum for all the computed values.
161/// * `parse_value`, the main function to parse a property or attribute value from user input.
162///
163/// There is a lot of repetitive code, for example, because sometimes
164/// we need to operate on `PropertyId::Foo`, `ParsedProperty::Foo` and
165/// `ComputedValue::Foo` together.  This is why all this is done with a macro.
166///
167/// See the only invocation of this macro to see how it is used; it is just
168/// a declarative list of property names.
169///
170/// **NOTE:** If you get a compiler error similar to this:
171///
172/// ```text
173/// 362 |         "mix-blend-mode"              => mix_blend_mode              : MixBlendMode,
174///     |         ^^^^^^^^^^^^^^^^ no rules expected this token in macro call
175/// ```
176///
177/// Then it may be that you put the name inside the `longhands` block, when it should be
178/// inside the `longhands_not_supported_by_markup5ever` block.  This is because the
179/// [`markup5ever`] crate does not have predefined names for every single property out
180/// there; just the common ones.
181///
182/// [`markup5ever`]: https://docs.rs/markup5ever
183macro_rules! make_properties {
184    {
185        shorthands: {
186            $($short_str:tt => ( $short_presentation_attr:expr, $short_field:ident: $short_name:ident ),)*
187        }
188
189        longhands: {
190            $($long_str:tt => ( $long_presentation_attr:expr, $long_field:ident: $long_name:ident ),)+
191        }
192
193        // These are for when expanded_name!("" "foo") is not defined yet
194        // in markup5ever.  We create an ExpandedName by hand in that case.
195        longhands_not_supported_by_markup5ever: {
196            $($long_m5e_str:tt => ($long_m5e_presentation_attr:expr, $long_m5e_field:ident: $long_m5e_name:ident ),)+
197        }
198
199        non_properties: {
200            $($nonprop_field:ident: $nonprop_name:ident,)+
201        }
202    }=> {
203        /// Used to match `ParsedProperty` to their discriminant
204        ///
205        /// The `PropertyId::UnsetProperty` can be used as a sentinel value, as
206        /// it does not match any `ParsedProperty` discriminant; it is really the
207        /// number of valid values in this enum.
208        #[repr(u8)]
209        #[derive(Copy, Clone, PartialEq)]
210        enum PropertyId {
211            $($short_name,)+
212            $($long_name,)+
213            $($long_m5e_name,)+
214            $($nonprop_name,)+
215
216            UnsetProperty,
217        }
218
219        impl PropertyId {
220            fn is_shorthand(self) -> bool {
221                match self {
222                    $(PropertyId::$short_name => true,)+
223                    _ => false,
224                }
225            }
226        }
227
228        /// Embodies "which property is this" plus the property's value
229        #[derive(Clone)]
230        pub enum ParsedProperty {
231            // we put all the properties here; these are for SpecifiedValues
232            $($short_name(SpecifiedValue<$short_name>),)+
233            $($long_name(SpecifiedValue<$long_name>),)+
234            $($long_m5e_name(SpecifiedValue<$long_m5e_name>),)+
235            $($nonprop_name(SpecifiedValue<$nonprop_name>),)+
236        }
237
238        enum ComputedValue {
239            $(
240                $long_name($long_name),
241            )+
242
243            $(
244                $long_m5e_name($long_m5e_name),
245            )+
246
247            $(
248                $nonprop_name($nonprop_name),
249            )+
250        }
251
252        /// Holds the computed values for the CSS properties of an element.
253        #[derive(Debug, Default, Clone)]
254        pub struct ComputedValues {
255            $(
256                $long_field: $long_name,
257            )+
258
259            $(
260                $long_m5e_field: $long_m5e_name,
261            )+
262
263            $(
264                $nonprop_field: $nonprop_name,
265            )+
266
267            transform: Transform,
268        }
269
270        impl ParsedProperty {
271            fn get_property_id(&self) -> PropertyId {
272                match *self {
273                    $(ParsedProperty::$long_name(_) => PropertyId::$long_name,)+
274                    $(ParsedProperty::$long_m5e_name(_) => PropertyId::$long_m5e_name,)+
275                    $(ParsedProperty::$short_name(_) => PropertyId::$short_name,)+
276                    $(ParsedProperty::$nonprop_name(_) => PropertyId::$nonprop_name,)+
277                }
278            }
279
280            fn unspecified(id: PropertyId) -> Self {
281                use SpecifiedValue::Unspecified;
282
283                match id {
284                    $(PropertyId::$long_name => ParsedProperty::$long_name(Unspecified),)+
285                    $(PropertyId::$long_m5e_name => ParsedProperty::$long_m5e_name(Unspecified),)+
286                    $(PropertyId::$short_name => ParsedProperty::$short_name(Unspecified),)+
287                    $(PropertyId::$nonprop_name => ParsedProperty::$nonprop_name(Unspecified),)+
288
289                    PropertyId::UnsetProperty => unreachable!(),
290                }
291            }
292        }
293
294        impl ComputedValues {
295            $(
296                pub fn $long_field(&self) -> $long_name {
297                    if let ComputedValue::$long_name(v) = self.get_value(PropertyId::$long_name) {
298                        v
299                    } else {
300                        unreachable!();
301                    }
302                }
303            )+
304
305            $(
306                pub fn $long_m5e_field(&self) -> $long_m5e_name {
307                    if let ComputedValue::$long_m5e_name(v) = self.get_value(PropertyId::$long_m5e_name) {
308                        v
309                    } else {
310                        unreachable!();
311                    }
312                }
313            )+
314
315            $(
316                pub fn $nonprop_field(&self) -> $nonprop_name {
317                    if let ComputedValue::$nonprop_name(v) = self.get_value(PropertyId::$nonprop_name) {
318                        v
319                    } else {
320                        unreachable!();
321                    }
322                }
323            )+
324
325            fn set_value(&mut self, computed: ComputedValue) {
326                match computed {
327                    $(ComputedValue::$long_name(v) => self.$long_field = v,)+
328                    $(ComputedValue::$long_m5e_name(v) => self.$long_m5e_field = v,)+
329                    $(ComputedValue::$nonprop_name(v) => self.$nonprop_field = v,)+
330                }
331            }
332
333            fn get_value(&self, id: PropertyId) -> ComputedValue {
334                assert!(!id.is_shorthand());
335
336                match id {
337                    $(
338                        PropertyId::$long_name =>
339                            ComputedValue::$long_name(self.$long_field.clone()),
340                    )+
341                    $(
342                        PropertyId::$long_m5e_name =>
343                            ComputedValue::$long_m5e_name(self.$long_m5e_field.clone()),
344                    )+
345                    $(
346                        PropertyId::$nonprop_name =>
347                            ComputedValue::$nonprop_name(self.$nonprop_field.clone()),
348                    )+
349                    _ => unreachable!(),
350                }
351            }
352        }
353
354        /// Parses a value from either a style property or from an element's attribute.
355        pub fn parse_value<'i>(
356            prop_name: &QualName,
357            input: &mut Parser<'i, '_>,
358            parse_as: ParseAs,
359        ) -> Result<ParsedProperty, ParseError<'i>> {
360            match prop_name.expanded() {
361                $(
362                    expanded_name!("", $long_str) if !(parse_as == ParseAs::PresentationAttr && $long_presentation_attr == PresentationAttr::No) => {
363                        Ok(ParsedProperty::$long_name(parse_input(input)?))
364                    }
365                )+
366
367                $(
368                    e if e == ExpandedName {
369                        ns: &ns!(),
370                        local: &LocalName::from($long_m5e_str),
371                    } && !(parse_as == ParseAs::PresentationAttr && $long_m5e_presentation_attr == PresentationAttr::No) => {
372                        Ok(ParsedProperty::$long_m5e_name(parse_input(input)?))
373                    }
374                )+
375
376                $(
377                    expanded_name!("", $short_str) if parse_as == ParseAs::Property => {
378                        // No shorthand has a presentation attribute.
379                        assert!($short_presentation_attr == PresentationAttr::No);
380
381                        Ok(ParsedProperty::$short_name(parse_input(input)?))
382                    }
383                )+
384
385                _ => {
386                    let loc = input.current_source_location();
387                    Err(loc.new_custom_error(ValueErrorKind::UnknownProperty))
388                }
389            }
390        }
391    };
392}
393
394#[rustfmt::skip]
395make_properties! {
396    shorthands: {
397        // No shorthand has a presentation attribute.
398        "font"    => (PresentationAttr::No, font   : Font),
399        "marker"  => (PresentationAttr::No, marker : Marker),
400    }
401
402    // longhands that are presentation attributes right now, but need to be turned into properties:
403    // "d"      - applies only to path
404
405    longhands: {
406        // "alignment-baseline"       => (PresentationAttr::Yes, unimplemented),
407        "baseline-shift"              => (PresentationAttr::Yes, baseline_shift              : BaselineShift),
408        "clip-path"                   => (PresentationAttr::Yes, clip_path                   : ClipPath),
409        "clip-rule"                   => (PresentationAttr::Yes, clip_rule                   : ClipRule),
410        "color"                       => (PresentationAttr::Yes, color                       : Color),
411        // "color-interpolation"      => (PresentationAttr::Yes, unimplemented),
412        "color-interpolation-filters" => (PresentationAttr::Yes, color_interpolation_filters : ColorInterpolationFilters),
413        // "cursor"                   => (PresentationAttr::Yes, unimplemented),
414        "cx"                          => (PresentationAttr::Yes, cx: CX),
415        "cy"                          => (PresentationAttr::Yes, cy: CY),
416        "direction"                   => (PresentationAttr::Yes, direction                   : Direction),
417        "display"                     => (PresentationAttr::Yes, display                     : Display),
418        "dominant-baseline"           => (PresentationAttr::Yes, dominant_baseline           : DominantBaseline),
419        "enable-background"           => (PresentationAttr::Yes, enable_background           : EnableBackground),
420
421        // "applies to any element except animation elements"
422        // https://www.w3.org/TR/SVG2/styling.html#PresentationAttributes
423        "fill"                        => (PresentationAttr::Yes, fill                        : Fill),
424
425        "fill-opacity"                => (PresentationAttr::Yes, fill_opacity                : FillOpacity),
426        "fill-rule"                   => (PresentationAttr::Yes, fill_rule                   : FillRule),
427        "filter"                      => (PresentationAttr::Yes, filter                      : Filter),
428        "flood-color"                 => (PresentationAttr::Yes, flood_color                 : FloodColor),
429        "flood-opacity"               => (PresentationAttr::Yes, flood_opacity               : FloodOpacity),
430        "font-family"                 => (PresentationAttr::Yes, font_family                 : FontFamily),
431        "font-size"                   => (PresentationAttr::Yes, font_size                   : FontSize),
432        // "font-size-adjust"         => (PresentationAttr::Yes, unimplemented),
433        "font-stretch"                => (PresentationAttr::Yes, font_stretch                : FontStretch),
434        "font-style"                  => (PresentationAttr::Yes, font_style                  : FontStyle),
435        "font-variant"                => (PresentationAttr::Yes, font_variant                : FontVariant),
436        "font-weight"                 => (PresentationAttr::Yes, font_weight                 : FontWeight),
437
438        // "glyph-orientation-horizontal" - obsolete, removed from SVG2
439
440        // "glyph-orientation-vertical" - obsolete, now shorthand -
441        // https://svgwg.org/svg2-draft/text.html#GlyphOrientationVerticalProperty
442        // https://www.w3.org/TR/css-writing-modes-3/#propdef-glyph-orientation-vertical
443        //
444        // Note that even though CSS Writing Modes 3 turned glyph-orientation-vertical
445        // into a shorthand, SVG1.1 still makes it available as a presentation attribute.
446        // So, we put the property here, not in the shorthands, and deal with it as a
447        // special case in the text handling code.
448        "glyph-orientation-vertical"  => (PresentationAttr::Yes, glyph_orientation_vertical  : GlyphOrientationVertical),
449        "height" => (PresentationAttr::Yes, height: Height),
450
451        "image-rendering"             => (PresentationAttr::Yes, image_rendering             : ImageRendering),
452        "letter-spacing"              => (PresentationAttr::Yes, letter_spacing              : LetterSpacing),
453        "lighting-color"              => (PresentationAttr::Yes, lighting_color              : LightingColor),
454        "marker-end"                  => (PresentationAttr::Yes, marker_end                  : MarkerEnd),
455        "marker-mid"                  => (PresentationAttr::Yes, marker_mid                  : MarkerMid),
456        "marker-start"                => (PresentationAttr::Yes, marker_start                : MarkerStart),
457        "mask"                        => (PresentationAttr::Yes, mask                        : Mask),
458        "opacity"                     => (PresentationAttr::Yes, opacity                     : Opacity),
459        "overflow"                    => (PresentationAttr::Yes, overflow                    : Overflow),
460        // "pointer-events"           => (PresentationAttr::Yes, unimplemented),
461        "r"                           => (PresentationAttr::Yes, r: R),
462        "rx"                          => (PresentationAttr::Yes, rx: RX),
463        "ry"                          => (PresentationAttr::Yes, ry: RY),
464        "shape-rendering"             => (PresentationAttr::Yes, shape_rendering             : ShapeRendering),
465        "stop-color"                  => (PresentationAttr::Yes, stop_color                  : StopColor),
466        "stop-opacity"                => (PresentationAttr::Yes, stop_opacity                : StopOpacity),
467        "stroke"                      => (PresentationAttr::Yes, stroke                      : Stroke),
468        "stroke-dasharray"            => (PresentationAttr::Yes, stroke_dasharray            : StrokeDasharray),
469        "stroke-dashoffset"           => (PresentationAttr::Yes, stroke_dashoffset           : StrokeDashoffset),
470        "stroke-linecap"              => (PresentationAttr::Yes, stroke_line_cap             : StrokeLinecap),
471        "stroke-linejoin"             => (PresentationAttr::Yes, stroke_line_join            : StrokeLinejoin),
472        "stroke-miterlimit"           => (PresentationAttr::Yes, stroke_miterlimit           : StrokeMiterlimit),
473        "stroke-opacity"              => (PresentationAttr::Yes, stroke_opacity              : StrokeOpacity),
474        "stroke-width"                => (PresentationAttr::Yes, stroke_width                : StrokeWidth),
475        "text-anchor"                 => (PresentationAttr::Yes, text_anchor                 : TextAnchor),
476        "text-decoration"             => (PresentationAttr::Yes, text_decoration             : TextDecoration),
477        // "text-overflow"            => (PresentationAttr::Yes, unimplemented),
478        "text-rendering"              => (PresentationAttr::Yes, text_rendering              : TextRendering),
479
480        // "transform" - Special case as presentation attribute:
481        // The SVG1.1 "transform" attribute has a different grammar than the
482        // SVG2 "transform" property.  Here we define for the properties machinery,
483        // and it is handled specially as an attribute in parse_presentation_attributes().
484        "transform"                   => (PresentationAttr::No, transform_property           : TransformProperty),
485
486        // "transform-box"            => (PresentationAttr::Yes, unimplemented),
487        // "transform-origin"         => (PresentationAttr::Yes, unimplemented),
488        "unicode-bidi"                => (PresentationAttr::Yes, unicode_bidi                : UnicodeBidi),
489        "visibility"                  => (PresentationAttr::Yes, visibility                  : Visibility),
490        // "word-spacing"             => (PresentationAttr::Yes, unimplemented),
491        "width"                       => (PresentationAttr::Yes, width: Width),
492        "writing-mode"                => (PresentationAttr::Yes, writing_mode                : WritingMode),
493        "x"                           => (PresentationAttr::Yes, x: X),
494        "y"                           => (PresentationAttr::Yes, y: Y),
495    }
496    
497    longhands_not_supported_by_markup5ever: {
498        "isolation"                   => (PresentationAttr::No,  isolation                   : Isolation),
499        "line-height"                 => (PresentationAttr::No,  line_height                 : LineHeight),
500        "mask-type"                   => (PresentationAttr::Yes, mask_type                   : MaskType),
501        "mix-blend-mode"              => (PresentationAttr::No,  mix_blend_mode              : MixBlendMode),
502        "paint-order"                 => (PresentationAttr::Yes, paint_order                 : PaintOrder),
503        "text-orientation"            => (PresentationAttr::No,  text_orientation            : TextOrientation),
504        "vector-effect"               => (PresentationAttr::Yes, vector_effect               : VectorEffect),
505        "white-space"                 => (PresentationAttr::Yes, white_space                 : WhiteSpace),
506    }
507
508    // These are not properties, but presentation attributes.  However,
509    // both xml:lang and xml:space *do* inherit.  We are abusing the
510    // property inheritance code for these XML-specific attributes.
511    non_properties: {
512        xml_lang: XmlLang,
513        xml_space: XmlSpace,
514    }
515}
516
517impl SpecifiedValues {
518    fn property_index(&self, id: PropertyId) -> Option<usize> {
519        let v = self.indices[id.as_usize()];
520
521        if v == PropertyId::UnsetProperty.as_u8() {
522            None
523        } else {
524            Some(v as usize)
525        }
526    }
527
528    fn set_property(&mut self, prop: &ParsedProperty, replace: bool) {
529        let id = prop.get_property_id();
530        assert!(!id.is_shorthand());
531
532        if let Some(index) = self.property_index(id) {
533            if replace {
534                self.props[index] = prop.clone();
535            }
536        } else {
537            self.props.push(prop.clone());
538            let pos = self.props.len() - 1;
539            self.indices[id.as_usize()] = pos as u8;
540        }
541    }
542
543    fn get_property(&self, id: PropertyId) -> ParsedProperty {
544        assert!(!id.is_shorthand());
545
546        if let Some(index) = self.property_index(id) {
547            self.props[index].clone()
548        } else {
549            ParsedProperty::unspecified(id)
550        }
551    }
552
553    fn set_property_expanding_shorthands(&mut self, prop: &ParsedProperty, replace: bool) {
554        match *prop {
555            ParsedProperty::Font(SpecifiedValue::Specified(ref f)) => {
556                self.expand_font_shorthand(f, replace)
557            }
558            ParsedProperty::Marker(SpecifiedValue::Specified(ref m)) => {
559                self.expand_marker_shorthand(m, replace)
560            }
561            ParsedProperty::Font(SpecifiedValue::Inherit) => {
562                self.expand_font_shorthand_inherit(replace)
563            }
564            ParsedProperty::Marker(SpecifiedValue::Inherit) => {
565                self.expand_marker_shorthand_inherit(replace)
566            }
567
568            _ => self.set_property(prop, replace),
569        }
570    }
571
572    fn expand_font_shorthand(&mut self, font: &Font, replace: bool) {
573        let FontSpec {
574            style,
575            variant,
576            weight,
577            stretch,
578            size,
579            line_height,
580            family,
581        } = font.to_font_spec();
582
583        self.set_property(
584            &ParsedProperty::FontStyle(SpecifiedValue::Specified(style)),
585            replace,
586        );
587        self.set_property(
588            &ParsedProperty::FontVariant(SpecifiedValue::Specified(variant)),
589            replace,
590        );
591        self.set_property(
592            &ParsedProperty::FontWeight(SpecifiedValue::Specified(weight)),
593            replace,
594        );
595        self.set_property(
596            &ParsedProperty::FontStretch(SpecifiedValue::Specified(stretch)),
597            replace,
598        );
599        self.set_property(
600            &ParsedProperty::FontSize(SpecifiedValue::Specified(size)),
601            replace,
602        );
603        self.set_property(
604            &ParsedProperty::LineHeight(SpecifiedValue::Specified(line_height)),
605            replace,
606        );
607        self.set_property(
608            &ParsedProperty::FontFamily(SpecifiedValue::Specified(family)),
609            replace,
610        );
611    }
612
613    fn expand_marker_shorthand(&mut self, marker: &Marker, replace: bool) {
614        let Marker(v) = marker;
615
616        self.set_property(
617            &ParsedProperty::MarkerStart(SpecifiedValue::Specified(MarkerStart(v.clone()))),
618            replace,
619        );
620        self.set_property(
621            &ParsedProperty::MarkerMid(SpecifiedValue::Specified(MarkerMid(v.clone()))),
622            replace,
623        );
624        self.set_property(
625            &ParsedProperty::MarkerEnd(SpecifiedValue::Specified(MarkerEnd(v.clone()))),
626            replace,
627        );
628    }
629
630    fn expand_font_shorthand_inherit(&mut self, replace: bool) {
631        self.set_property(&ParsedProperty::FontStyle(SpecifiedValue::Inherit), replace);
632        self.set_property(
633            &ParsedProperty::FontVariant(SpecifiedValue::Inherit),
634            replace,
635        );
636        self.set_property(
637            &ParsedProperty::FontWeight(SpecifiedValue::Inherit),
638            replace,
639        );
640        self.set_property(
641            &ParsedProperty::FontStretch(SpecifiedValue::Inherit),
642            replace,
643        );
644        self.set_property(&ParsedProperty::FontSize(SpecifiedValue::Inherit), replace);
645        self.set_property(
646            &ParsedProperty::LineHeight(SpecifiedValue::Inherit),
647            replace,
648        );
649        self.set_property(
650            &ParsedProperty::FontFamily(SpecifiedValue::Inherit),
651            replace,
652        );
653    }
654
655    fn expand_marker_shorthand_inherit(&mut self, replace: bool) {
656        self.set_property(
657            &ParsedProperty::MarkerStart(SpecifiedValue::Inherit),
658            replace,
659        );
660        self.set_property(&ParsedProperty::MarkerMid(SpecifiedValue::Inherit), replace);
661        self.set_property(&ParsedProperty::MarkerEnd(SpecifiedValue::Inherit), replace);
662    }
663
664    pub fn set_parsed_property(&mut self, prop: &ParsedProperty) {
665        self.set_property_expanding_shorthands(prop, true);
666    }
667
668    /* user agent property have less priority than presentation attributes */
669    pub fn set_parsed_property_user_agent(&mut self, prop: &ParsedProperty) {
670        self.set_property_expanding_shorthands(prop, false);
671    }
672
673    pub fn to_computed_values(&self, computed: &mut ComputedValues) {
674        macro_rules! compute {
675            ($name:ident, $field:ident) => {{
676                // This extra block --------^
677                // is so that prop_val will be dropped within the macro invocation;
678                // otherwise all the temporary values cause this function to use
679                // an unreasonably large amount of stack space.
680                let prop_val = self.get_property(PropertyId::$name);
681                if let ParsedProperty::$name(s) = prop_val {
682                    computed.set_value(ComputedValue::$name(
683                        s.compute(&computed.$field(), computed),
684                    ));
685                } else {
686                    unreachable!();
687                }
688            }};
689        }
690
691        // First, compute font_size.  It needs to be done before everything
692        // else, so that properties that depend on its computed value
693        // will be able to use it.  For example, baseline-shift
694        // depends on font-size.
695
696        compute!(FontSize, font_size);
697
698        // Then, do all the other properties.
699
700        compute!(BaselineShift, baseline_shift);
701        compute!(ClipPath, clip_path);
702        compute!(ClipRule, clip_rule);
703        compute!(Color, color);
704        compute!(ColorInterpolationFilters, color_interpolation_filters);
705        compute!(CX, cx);
706        compute!(CY, cy);
707        compute!(Direction, direction);
708        compute!(Display, display);
709        compute!(DominantBaseline, dominant_baseline);
710        compute!(EnableBackground, enable_background);
711        compute!(Fill, fill);
712        compute!(FillOpacity, fill_opacity);
713        compute!(FillRule, fill_rule);
714        compute!(Filter, filter);
715        compute!(FloodColor, flood_color);
716        compute!(FloodOpacity, flood_opacity);
717        compute!(FontFamily, font_family);
718        compute!(FontStretch, font_stretch);
719        compute!(FontStyle, font_style);
720        compute!(FontVariant, font_variant);
721        compute!(FontWeight, font_weight);
722        compute!(GlyphOrientationVertical, glyph_orientation_vertical);
723        compute!(Height, height);
724        compute!(ImageRendering, image_rendering);
725        compute!(Isolation, isolation);
726        compute!(LetterSpacing, letter_spacing);
727        compute!(LightingColor, lighting_color);
728        compute!(MarkerEnd, marker_end);
729        compute!(MarkerMid, marker_mid);
730        compute!(MarkerStart, marker_start);
731        compute!(Mask, mask);
732        compute!(MaskType, mask_type);
733        compute!(MixBlendMode, mix_blend_mode);
734        compute!(Opacity, opacity);
735        compute!(Overflow, overflow);
736        compute!(PaintOrder, paint_order);
737        compute!(R, r);
738        compute!(RX, rx);
739        compute!(RY, ry);
740        compute!(ShapeRendering, shape_rendering);
741        compute!(StopColor, stop_color);
742        compute!(StopOpacity, stop_opacity);
743        compute!(Stroke, stroke);
744        compute!(StrokeDasharray, stroke_dasharray);
745        compute!(StrokeDashoffset, stroke_dashoffset);
746        compute!(StrokeLinecap, stroke_line_cap);
747        compute!(StrokeLinejoin, stroke_line_join);
748        compute!(StrokeOpacity, stroke_opacity);
749        compute!(StrokeMiterlimit, stroke_miterlimit);
750        compute!(StrokeWidth, stroke_width);
751        compute!(TextAnchor, text_anchor);
752        compute!(TextDecoration, text_decoration);
753        compute!(TextOrientation, text_orientation);
754        compute!(TextRendering, text_rendering);
755        compute!(TransformProperty, transform_property);
756        compute!(UnicodeBidi, unicode_bidi);
757        compute!(VectorEffect, vector_effect);
758        compute!(Visibility, visibility);
759        compute!(Width, width);
760        compute!(WhiteSpace, white_space);
761        compute!(WritingMode, writing_mode);
762        compute!(X, x);
763        compute!(XmlSpace, xml_space);
764        compute!(XmlLang, xml_lang);
765        compute!(Y, y);
766
767        computed.transform = self.transform.unwrap_or_else(|| {
768            match self.get_property(PropertyId::TransformProperty) {
769                ParsedProperty::TransformProperty(SpecifiedValue::Specified(ref t)) => {
770                    t.to_transform()
771                }
772                _ => Transform::identity(),
773            }
774        });
775    }
776
777    /// This is a somewhat egregious hack to allow xml:lang to be stored as a presentational
778    /// attribute. Presentational attributes can often be influenced by stylesheets,
779    /// so they're cascaded after selector matching is done, but xml:lang can be queried by
780    /// CSS selectors, so they need to be cascaded *first*.
781    pub fn inherit_xml_lang(
782        &self,
783        computed: &mut ComputedValues,
784        parent: Option<crate::node::Node>,
785    ) {
786        use crate::node::NodeBorrow;
787        let prop_val = self.get_property(PropertyId::XmlLang);
788        if let ParsedProperty::XmlLang(s) = prop_val {
789            if let Some(parent) = parent {
790                computed.set_value(ComputedValue::XmlLang(
791                    parent.borrow_element().get_computed_values().xml_lang(),
792                ));
793            }
794            computed.set_value(ComputedValue::XmlLang(
795                s.compute(&computed.xml_lang(), computed),
796            ));
797        } else {
798            unreachable!();
799        }
800    }
801
802    pub fn is_overflow(&self) -> bool {
803        if let Some(overflow_index) = self.property_index(PropertyId::Overflow) {
804            match self.props[overflow_index] {
805                ParsedProperty::Overflow(SpecifiedValue::Specified(Overflow::Auto)) => true,
806                ParsedProperty::Overflow(SpecifiedValue::Specified(Overflow::Visible)) => true,
807                ParsedProperty::Overflow(_) => false,
808                _ => unreachable!(),
809            }
810        } else {
811            false
812        }
813    }
814
815    fn parse_one_presentation_attribute(&mut self, session: &Session, attr: QualName, value: &str) {
816        let mut input = ParserInput::new(value);
817        let mut parser = Parser::new(&mut input);
818
819        match parse_value(&attr, &mut parser, ParseAs::PresentationAttr) {
820            Ok(prop) => {
821                if parser.expect_exhausted().is_ok() {
822                    self.set_parsed_property(&prop);
823                } else {
824                    rsvg_log!(
825                        session,
826                        "(ignoring invalid presentation attribute {:?}\n    value=\"{}\")\n",
827                        attr.expanded(),
828                        value,
829                    );
830                }
831            }
832
833            // not a presentation attribute; just ignore it
834            Err(ParseError {
835                kind: ParseErrorKind::Custom(ValueErrorKind::UnknownProperty),
836                ..
837            }) => (),
838
839            // https://www.w3.org/TR/CSS2/syndata.html#unsupported-values
840            // For all the following cases, ignore illegal values; don't set the whole node to
841            // be in error in that case.
842            Err(ParseError {
843                kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)),
844                ..
845            }) => {
846                let mut tok = String::new();
847
848                t.to_css(&mut tok).unwrap(); // FIXME: what do we do with a fmt::Error?
849                rsvg_log!(
850                    session,
851                    "(ignoring invalid presentation attribute {:?}\n    value=\"{}\"\n    \
852                     unexpected token '{}')",
853                    attr.expanded(),
854                    value,
855                    tok,
856                );
857            }
858
859            Err(ParseError {
860                kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
861                ..
862            }) => {
863                rsvg_log!(
864                    session,
865                    "(ignoring invalid presentation attribute {:?}\n    value=\"{}\"\n    \
866                     unexpected end of input)",
867                    attr.expanded(),
868                    value,
869                );
870            }
871
872            Err(ParseError {
873                kind: ParseErrorKind::Basic(_),
874                ..
875            }) => {
876                rsvg_log!(
877                    session,
878                    "(ignoring invalid presentation attribute {:?}\n    value=\"{}\"\n    \
879                     unexpected error)",
880                    attr.expanded(),
881                    value,
882                );
883            }
884
885            Err(ParseError {
886                kind: ParseErrorKind::Custom(ref v),
887                ..
888            }) => {
889                rsvg_log!(
890                    session,
891                    "(ignoring invalid presentation attribute {:?}\n    value=\"{}\"\n    {})",
892                    attr.expanded(),
893                    value,
894                    v
895                );
896            }
897        }
898    }
899
900    pub fn parse_presentation_attributes(&mut self, session: &Session, attrs: &Attributes) {
901        for (attr, value) in attrs.iter() {
902            match attr.expanded() {
903                expanded_name!("", "transform") => {
904                    // FIXME: we parse the transform attribute here because we don't yet have
905                    // a better way to distinguish attributes whose values have different
906                    // grammars than properties.
907                    let transform_attr = TransformAttribute::parse_str(value)
908                        .unwrap_or_else(|_| TransformAttribute::default());
909                    self.transform = Some(transform_attr.to_transform());
910                }
911
912                expanded_name!(xml "lang") => {
913                    // xml:lang is a non-presentation attribute and as such cannot have the
914                    // "inherit" value.  So, we don't call parse_one_presentation_attribute()
915                    // for it, but rather call its parser directly.
916                    let parse_result: Result<XmlLang, _> = attr.parse(value);
917                    match parse_result {
918                        Ok(lang) => {
919                            self.set_parsed_property(&ParsedProperty::XmlLang(
920                                SpecifiedValue::Specified(lang),
921                            ));
922                        }
923
924                        Err(e) => {
925                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
926                        }
927                    }
928                }
929
930                expanded_name!(xml "space") => {
931                    // xml:space is a non-presentation attribute and as such cannot have the
932                    // "inherit" value.  So, we don't call parse_one_presentation_attribute()
933                    // for it, but rather call its parser directly.
934                    let parse_result: Result<XmlSpace, _> = attr.parse(value);
935                    match parse_result {
936                        Ok(space) => {
937                            self.set_parsed_property(&ParsedProperty::XmlSpace(
938                                SpecifiedValue::Specified(space),
939                            ));
940                        }
941
942                        Err(e) => {
943                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
944                        }
945                    }
946                }
947
948                _ => self.parse_one_presentation_attribute(session, attr, value),
949            }
950        }
951    }
952
953    pub fn set_property_from_declaration(
954        &mut self,
955        declaration: &Declaration,
956        origin: Origin,
957        important_styles: &mut HashSet<QualName>,
958    ) {
959        if !declaration.important && important_styles.contains(&declaration.prop_name) {
960            return;
961        }
962
963        if declaration.important {
964            important_styles.insert(declaration.prop_name.clone());
965        }
966
967        if origin == Origin::UserAgent {
968            self.set_parsed_property_user_agent(&declaration.property);
969        } else {
970            self.set_parsed_property(&declaration.property);
971        }
972    }
973
974    pub fn parse_style_declarations(
975        &mut self,
976        declarations: &str,
977        origin: Origin,
978        important_styles: &mut HashSet<QualName>,
979        session: &Session,
980    ) {
981        let mut input = ParserInput::new(declarations);
982        let mut parser = Parser::new(&mut input);
983
984        RuleBodyParser::new(&mut parser, &mut DeclParser)
985            .filter_map(|r| match r {
986                Ok(RuleBodyItem::Decl(decl)) => Some(decl),
987                Ok(RuleBodyItem::Rule(_)) => None,
988                Err(e) => {
989                    rsvg_log!(session, "Invalid declaration; ignoring: {:?}", e);
990                    None
991                }
992            })
993            .for_each(|decl| self.set_property_from_declaration(&decl, origin, important_styles));
994    }
995}
996
997// Parses the value for the type `T` of the property out of the Parser, including `inherit` values.
998fn parse_input<'i, T>(input: &mut Parser<'i, '_>) -> Result<SpecifiedValue<T>, ParseError<'i>>
999where
1000    T: Property + Clone + Default + Parse,
1001{
1002    if input
1003        .try_parse(|p| p.expect_ident_matching("inherit"))
1004        .is_ok()
1005    {
1006        Ok(SpecifiedValue::Inherit)
1007    } else {
1008        Parse::parse(input).map(SpecifiedValue::Specified)
1009    }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014    use super::*;
1015    use crate::iri::Iri;
1016    use crate::length::*;
1017
1018    #[test]
1019    fn empty_values_computes_to_defaults() {
1020        let specified = SpecifiedValues::default();
1021
1022        let mut computed = ComputedValues::default();
1023        specified.to_computed_values(&mut computed);
1024
1025        assert_eq!(computed.stroke_width(), StrokeWidth::default());
1026    }
1027
1028    #[test]
1029    fn set_one_property() {
1030        let length = Length::<Both>::new(42.0, LengthUnit::Px);
1031
1032        let mut specified = SpecifiedValues::default();
1033        specified.set_parsed_property(&ParsedProperty::StrokeWidth(SpecifiedValue::Specified(
1034            StrokeWidth(length),
1035        )));
1036
1037        let mut computed = ComputedValues::default();
1038        specified.to_computed_values(&mut computed);
1039
1040        assert_eq!(computed.stroke_width(), StrokeWidth(length));
1041    }
1042
1043    #[test]
1044    fn replace_existing_property() {
1045        let length1 = Length::<Both>::new(42.0, LengthUnit::Px);
1046        let length2 = Length::<Both>::new(42.0, LengthUnit::Px);
1047
1048        let mut specified = SpecifiedValues::default();
1049
1050        specified.set_parsed_property(&ParsedProperty::StrokeWidth(SpecifiedValue::Specified(
1051            StrokeWidth(length1),
1052        )));
1053
1054        specified.set_parsed_property(&ParsedProperty::StrokeWidth(SpecifiedValue::Specified(
1055            StrokeWidth(length2),
1056        )));
1057
1058        let mut computed = ComputedValues::default();
1059        specified.to_computed_values(&mut computed);
1060
1061        assert_eq!(computed.stroke_width(), StrokeWidth(length2));
1062    }
1063
1064    #[test]
1065    fn expands_marker_shorthand() {
1066        let mut specified = SpecifiedValues::default();
1067        let iri = Iri::parse_str("url(#foo)").unwrap();
1068
1069        let marker = Marker(iri.clone());
1070        specified.set_parsed_property(&ParsedProperty::Marker(SpecifiedValue::Specified(marker)));
1071
1072        let mut computed = ComputedValues::default();
1073        specified.to_computed_values(&mut computed);
1074
1075        assert_eq!(computed.marker_start(), MarkerStart(iri.clone()));
1076        assert_eq!(computed.marker_mid(), MarkerMid(iri.clone()));
1077        assert_eq!(computed.marker_end(), MarkerEnd(iri.clone()));
1078    }
1079
1080    #[test]
1081    fn replaces_marker_shorthand() {
1082        let mut specified = SpecifiedValues::default();
1083        let iri1 = Iri::parse_str("url(#foo)").unwrap();
1084        let iri2 = Iri::None;
1085
1086        let marker1 = Marker(iri1.clone());
1087        specified.set_parsed_property(&ParsedProperty::Marker(SpecifiedValue::Specified(marker1)));
1088
1089        let marker2 = Marker(iri2.clone());
1090        specified.set_parsed_property(&ParsedProperty::Marker(SpecifiedValue::Specified(marker2)));
1091
1092        let mut computed = ComputedValues::default();
1093        specified.to_computed_values(&mut computed);
1094
1095        assert_eq!(computed.marker_start(), MarkerStart(iri2.clone()));
1096        assert_eq!(computed.marker_mid(), MarkerMid(iri2.clone()));
1097        assert_eq!(computed.marker_end(), MarkerEnd(iri2.clone()));
1098    }
1099
1100    #[test]
1101    fn computes_property_that_does_not_inherit_automatically() {
1102        assert!(!<Opacity as Property>::inherits_automatically());
1103
1104        let half_opacity = Opacity::parse_str("0.5").unwrap();
1105
1106        // first level, as specified with opacity
1107
1108        let mut with_opacity = SpecifiedValues::default();
1109        with_opacity.set_parsed_property(&ParsedProperty::Opacity(SpecifiedValue::Specified(
1110            half_opacity.clone(),
1111        )));
1112
1113        let mut computed_0_5 = ComputedValues::default();
1114        with_opacity.to_computed_values(&mut computed_0_5);
1115
1116        assert_eq!(computed_0_5.opacity(), half_opacity.clone());
1117
1118        // second level, no opacity specified, and it doesn't inherit
1119
1120        let without_opacity = SpecifiedValues::default();
1121
1122        let mut computed = computed_0_5.clone();
1123        without_opacity.to_computed_values(&mut computed);
1124
1125        assert_eq!(computed.opacity(), Opacity::default());
1126
1127        // another at second level, opacity set to explicitly inherit
1128
1129        let mut with_inherit_opacity = SpecifiedValues::default();
1130        with_inherit_opacity.set_parsed_property(&ParsedProperty::Opacity(SpecifiedValue::Inherit));
1131
1132        let mut computed = computed_0_5.clone();
1133        with_inherit_opacity.to_computed_values(&mut computed);
1134
1135        assert_eq!(computed.opacity(), half_opacity.clone());
1136    }
1137}