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