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.
37
use std::convert::TryInto;
38
use std::str::FromStr;
39

            
40
use cssparser::{Parser, Token};
41
use language_tags::LanguageTag;
42

            
43
use crate::dasharray::Dasharray;
44
use crate::error::*;
45
use crate::filter::FilterValueList;
46
use crate::font_props::{
47
    Font, FontFamily, FontSize, FontWeight, GlyphOrientationVertical, LetterSpacing, LineHeight,
48
};
49
use crate::iri::Iri;
50
use crate::length::*;
51
use crate::paint_server::PaintServer;
52
use crate::parse_identifiers;
53
use crate::parsers::Parse;
54
use crate::properties::ComputedValues;
55
use crate::property_macros::Property;
56
use crate::rect::Rect;
57
use crate::transform::TransformProperty;
58
use crate::unit_interval::UnitInterval;
59
use crate::{impl_default, impl_property, make_property};
60

            
61
make_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>::parse_str("0.0").unwrap(),
69
    newtype: Length<Both>,
70
    property_impl: {
71
        impl Property for BaselineShift {
72
1466594
            fn inherits_automatically() -> bool {
73
                false
74
1466594
            }
75

            
76
1467041
            fn compute(&self, v: &ComputedValues) -> Self {
77
1467041
                let font_size = v.font_size().value();
78
1467041
                let parent = v.baseline_shift();
79

            
80
1467041
                match (self.0.unit, parent.0.unit) {
81
                    (LengthUnit::Percent, _) => {
82
446
                        BaselineShift(Length::<Both>::new(self.0.length * font_size.length + parent.0.length, font_size.unit))
83
                    }
84

            
85
1466595
                    (x, y) if x == y || parent.0.length == 0.0 => {
86
1466595
                        BaselineShift(Length::<Both>::new(self.0.length + parent.0.length, self.0.unit))
87
1466595
                    }
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
1467041
            }
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
447
            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<BaselineShift, crate::error::ParseError<'i>> {
109
1341
                parser.try_parse(|p| Ok(BaselineShift(Length::<Both>::parse(p)?)))
110
892
                    .or_else(|_: ParseError<'_>| {
111
890
                        Ok(parse_identifiers!(
112
445
                            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
445
                    })
119
447
            }
120
        }
121
    }
122
);
123

            
124
make_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

            
136
make_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

            
151
make_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::paint_server::resolve_color`].
164
    Color,
165
    default: cssparser::Color::Rgba(cssparser::RGBA::new(Some(0), Some(0), Some(0), Some(1.0))),
166
    inherits_automatically: true,
167
    newtype_parse: cssparser::Color,
168
);
169

            
170
make_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

            
186
make_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>::parse_str("0").unwrap(),
194
    inherits_automatically: false,
195
    newtype_parse: Length<Horizontal>,
196
);
197

            
198
make_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>::parse_str("0").unwrap(),
206
    inherits_automatically: false,
207
    newtype_parse: Length<Vertical>,
208
);
209

            
210
make_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

            
225
make_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

            
255
/// `enable-background` property.
256
///
257
/// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty>
258
///
259
/// This is deprecated in SVG2.  We just have a parser for it to avoid setting elements in
260
/// error if they have this property.  Librsvg does not use the value of this property.
261
4428885
#[derive(Debug, Clone, Copy, PartialEq)]
262
pub enum EnableBackground {
263
    Accumulate,
264
2
    New(Option<Rect>),
265
}
266

            
267
make_property!(
268
    EnableBackground,
269
    default: EnableBackground::Accumulate,
270
    inherits_automatically: false,
271

            
272
    parse_impl: {
273
        impl Parse for EnableBackground {
274
1170
            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
275
1170
                let loc = parser.current_source_location();
276

            
277
1170
                if parser
278
1170
                    .try_parse(|p| p.expect_ident_matching("accumulate"))
279
1170
                    .is_ok()
280
                {
281
939
                    return Ok(EnableBackground::Accumulate);
282
                }
283

            
284
462
                if parser.try_parse(|p| p.expect_ident_matching("new")).is_ok() {
285
690
                    parser.try_parse(|p| -> Result<_, ParseError<'_>> {
286
230
                        let x = f64::parse(p)?;
287
3
                        let y = f64::parse(p)?;
288
3
                        let w = f64::parse(p)?;
289
3
                        let h = f64::parse(p)?;
290

            
291
3
                        Ok(EnableBackground::New(Some(Rect::new(x, y, x + w, y + h))))
292
460
                    }).or(Ok(EnableBackground::New(None)))
293
                } else {
294
1
                    Err(loc.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'enable-background' property")))
295
                }
296
1170
            }
297
        }
298

            
299
    }
300
);
301

            
302
#[cfg(test)]
303
#[test]
304
2
fn parses_enable_background() {
305
1
    assert_eq!(
306
1
        EnableBackground::parse_str("accumulate").unwrap(),
307
        EnableBackground::Accumulate
308
    );
309

            
310
1
    assert_eq!(
311
1
        EnableBackground::parse_str("new").unwrap(),
312
        EnableBackground::New(None)
313
    );
314

            
315
1
    assert_eq!(
316
1
        EnableBackground::parse_str("new 1 2 3 4").unwrap(),
317
1
        EnableBackground::New(Some(Rect::new(1.0, 2.0, 4.0, 6.0)))
318
    );
319

            
320
1
    assert!(EnableBackground::parse_str("new foo").is_err());
321

            
322
1
    assert!(EnableBackground::parse_str("plonk").is_err());
323
2
}
324

            
325
make_property!(
326
    /// `fill` property.
327
    ///
328
    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillProperty>
329
    ///
330
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#FillProperty>
331
    Fill,
332
    default: PaintServer::parse_str("#000").unwrap(),
333
    inherits_automatically: true,
334
    newtype_parse: PaintServer,
335
);
336

            
337
make_property!(
338
    /// `fill-opacity` property.
339
    ///
340
    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty>
341
    ///
342
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#FillOpacity>
343
    FillOpacity,
344
    default: UnitInterval(1.0),
345
    inherits_automatically: true,
346
    newtype_parse: UnitInterval,
347
);
348

            
349
make_property!(
350
    /// `fill-rule` property.
351
    ///
352
    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty>
353
    ///
354
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#WindingRule>
355
    FillRule,
356
    default: NonZero,
357
    inherits_automatically: true,
358

            
359
    identifiers:
360
    "nonzero" => NonZero,
361
    "evenodd" => EvenOdd,
362
);
363

            
364
/// `filter` property.
365
///
366
/// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#FilterProperty>
367
///
368
/// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FilterProperty>
369
///
370
/// Note that in SVG2, the filters got offloaded to the [Filter Effects Module Level
371
/// 1](https://www.w3.org/TR/filter-effects/) specification.
372
6427977
#[derive(Debug, Clone, PartialEq)]
373
pub enum Filter {
374
    None,
375
2008
    List(FilterValueList),
376
}
377

            
378
make_property!(
379
    Filter,
380
    default: Filter::None,
381
    inherits_automatically: false,
382
    parse_impl: {
383
        impl Parse for Filter {
384
316
            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
385

            
386
316
                if parser
387
316
                    .try_parse(|p| p.expect_ident_matching("none"))
388
316
                    .is_ok()
389
                {
390
12
                    return Ok(Filter::None);
391
                }
392

            
393
304
                Ok(Filter::List(FilterValueList::parse(parser)?))
394
316
            }
395
        }
396
    }
397
);
398

            
399
make_property!(
400
    /// `flood-color` property, for `feFlood` and `feDropShadow` filter elements.
401
    ///
402
    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#feFloodElement>
403
    ///
404
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FloodColorProperty>
405
    FloodColor,
406
    default: cssparser::Color::Rgba(cssparser::RGBA::new(Some(0), Some(0), Some(0), Some(1.0))),
407
    inherits_automatically: false,
408
    newtype_parse: cssparser::Color,
409
);
410

            
411
make_property!(
412
    /// `flood-opacity` property, for `feFlood` and `feDropShadow` filter elements.
413
    ///
414
    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#feFloodElement>
415
    ///
416
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#FloodOpacityProperty>
417
    FloodOpacity,
418
    default: UnitInterval(1.0),
419
    inherits_automatically: false,
420
    newtype_parse: UnitInterval,
421
);
422

            
423
make_property!(
424
    // docs are in font_props.rs
425
    Font,
426
    default: Font::Spec(Default::default()),
427
    inherits_automatically: true,
428
);
429

            
430
make_property!(
431
    // docs are in font_props.rs
432
    FontFamily,
433
    default: FontFamily("Times New Roman".to_string()),
434
    inherits_automatically: true,
435
);
436

            
437
make_property!(
438
    // docs are in font_props.rs
439
    FontSize,
440
    default: FontSize::Value(Length::<Both>::parse_str("12.0").unwrap()),
441
    property_impl: {
442
        impl Property for FontSize {
443
1465679
            fn inherits_automatically() -> bool {
444
                true
445
1465679
            }
446

            
447
1467046
            fn compute(&self, v: &ComputedValues) -> Self {
448
1467046
                self.compute(v)
449
1467046
            }
450
        }
451
    }
452
);
453

            
454
make_property!(
455
    /// `font-stretch` property.
456
    ///
457
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontStretchProperty>
458
    ///
459
    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#font-size-propstret>
460
    FontStretch,
461
    default: Normal,
462
    inherits_automatically: true,
463

            
464
    identifiers:
465
    "normal" => Normal,
466
    "wider" => Wider,
467
    "narrower" => Narrower,
468
    "ultra-condensed" => UltraCondensed,
469
    "extra-condensed" => ExtraCondensed,
470
    "condensed" => Condensed,
471
    "semi-condensed" => SemiCondensed,
472
    "semi-expanded" => SemiExpanded,
473
    "expanded" => Expanded,
474
    "extra-expanded" => ExtraExpanded,
475
    "ultra-expanded" => UltraExpanded,
476
);
477

            
478
make_property!(
479
    /// `font-style` property.
480
    ///
481
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontStyleProperty>
482
    ///
483
    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#font-size-propstret>
484
    FontStyle,
485
    default: Normal,
486
    inherits_automatically: true,
487

            
488
    identifiers:
489
    "normal" => Normal,
490
    "italic" => Italic,
491
    "oblique" => Oblique,
492
);
493

            
494
make_property!(
495
    /// `font-variant` property.
496
    ///
497
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#FontVariantProperty>
498
    ///
499
    /// CSS Fonts 3: <https://www.w3.org/TR/css-fonts-3/#propdef-font-variant>
500
    ///
501
    /// Note that in CSS3, this is a lot more complex than CSS2.1 / SVG1.1.
502
    FontVariant,
503
    default: Normal,
504
    inherits_automatically: true,
505

            
506
    identifiers:
507
    "normal" => Normal,
508
    "small-caps" => SmallCaps,
509
);
510

            
511
make_property!(
512
    // docs are in font_props.rs
513
    FontWeight,
514
    default: FontWeight::Normal,
515
    property_impl: {
516
        impl Property for FontWeight {
517
1466399
            fn inherits_automatically() -> bool {
518
                true
519
1466399
            }
520

            
521
1467047
            fn compute(&self, v: &ComputedValues) -> Self {
522
1467047
                self.compute(&v.font_weight())
523
1467047
            }
524
        }
525
    }
526
);
527

            
528
make_property!(
529
    // docs are in font_props.rs
530
    //
531
    // Although https://www.w3.org/TR/css-writing-modes-3/#propdef-glyph-orientation-vertical specifies
532
    // "n/a" for both the initial value (default) and inheritance, we'll use Auto here for the default,
533
    // since it translates to TextOrientation::Mixed - which is text-orientation's initial value.
534
    GlyphOrientationVertical,
535
    default: GlyphOrientationVertical::Auto,
536
    inherits_automatically: false,
537
);
538

            
539
make_property!(
540
    /// `height` property.
541
    ///
542
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Sizing>
543
    ///
544
    /// Note that in SVG1.1, this was an attribute, not a property.
545
    Height,
546
    default: LengthOrAuto::<Vertical>::Auto,
547
    inherits_automatically: false,
548
    newtype_parse: LengthOrAuto<Vertical>,
549
);
550

            
551
make_property!(
552
    /// `image-rendering` property.
553
    ///
554
    /// CSS Images Module Level 3: <https://www.w3.org/TR/css-images-3/#the-image-rendering>
555
    ///
556
    /// Note that this property previously accepted the values optimizeSpeed and optimizeQuality.
557
    /// These are now deprecated; a user agent must accept them as valid values but must treat
558
    /// them as having the same behavior as crisp-edges and smooth respectively.
559
    ImageRendering,
560
    default: Auto,
561
    inherits_automatically: true,
562

            
563
    identifiers:
564
    "auto" => Auto,
565
    "smooth" => Smooth,
566
    "optimizeQuality" => OptimizeQuality,
567
    "high-quality" => HighQuality,
568
    "crisp-edges" => CrispEdges,
569
    "optimizeSpeed" => OptimizeSpeed,
570
    "pixelated" => Pixelated,
571
);
572

            
573
make_property!(
574
    /// `isolation` property.
575
    ///
576
    /// CSS Compositing and Blending 1: <https://www.w3.org/TR/compositing-1/#isolation>
577
    Isolation,
578
    default: Auto,
579
    inherits_automatically: false,
580

            
581
    identifiers:
582
    "auto" => Auto,
583
    "isolate" => Isolate,
584
);
585

            
586
make_property!(
587
    // docs are in font_props.rs
588
    LetterSpacing,
589
    default: LetterSpacing::Normal,
590
    property_impl: {
591
        impl Property for LetterSpacing {
592
1466490
            fn inherits_automatically() -> bool {
593
                true
594
1466490
            }
595

            
596
1467039
            fn compute(&self, _v: &ComputedValues) -> Self {
597
1467039
                self.compute()
598
1467039
            }
599
        }
600
    }
601
);
602

            
603
make_property!(
604
    // docs are in font_props.rs
605
    LineHeight,
606
    default: LineHeight::Normal,
607
    inherits_automatically: true,
608
);
609

            
610
make_property!(
611
    /// `lighting-color` property for `feDiffuseLighting` and `feSpecularLighting` filter elements.
612
    ///
613
    /// SVG1.1: <https://www.w3.org/TR/SVG11/filters.html#LightingColorProperty>
614
    ///
615
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#LightingColorProperty>
616
    LightingColor,
617
    default: cssparser::Color::Rgba(cssparser::RGBA::new(Some(255), Some(255), Some(255), Some(1.0))),
618
    inherits_automatically: false,
619
    newtype_parse: cssparser::Color,
620
);
621

            
622
make_property!(
623
    /// `marker` shorthand property.
624
    ///
625
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand>
626
    ///
627
    /// This is a shorthand, which expands to the `marker-start`, `marker-mid`,
628
    /// `marker-end` longhand properties.
629
    Marker,
630
    default: Iri::None,
631
    inherits_automatically: true,
632
    newtype_parse: Iri,
633
);
634

            
635
make_property!(
636
    /// `marker-end` property.
637
    ///
638
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
639
    MarkerEnd,
640
    default: Iri::None,
641
    inherits_automatically: true,
642
    newtype_parse: Iri,
643
);
644

            
645
make_property!(
646
    /// `marker-mid` property.
647
    ///
648
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
649
    MarkerMid,
650
    default: Iri::None,
651
    inherits_automatically: true,
652
    newtype_parse: Iri,
653
);
654

            
655
make_property!(
656
    /// `marker-start` property.
657
    ///
658
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties>
659
    MarkerStart,
660
    default: Iri::None,
661
    inherits_automatically: true,
662
    newtype_parse: Iri,
663
);
664

            
665
make_property!(
666
    /// `mask` shorthand property.
667
    ///
668
    /// SVG1.1: <https://www.w3.org/TR/SVG11/masking.html#MaskProperty>
669
    ///
670
    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-mask>
671
    ///
672
    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
673
    Mask,
674
    default: Iri::None,
675
    inherits_automatically: false,
676
    newtype_parse: Iri,
677
);
678

            
679
make_property!(
680
    /// `mask-type` property.
681
    ///
682
    /// CSS Masking 1: <https://www.w3.org/TR/css-masking-1/#the-mask-type>
683
    MaskType,
684
    default: Luminance,
685
    inherits_automatically: false,
686

            
687
    identifiers:
688
    "luminance" => Luminance,
689
    "alpha" => Alpha,
690
);
691

            
692
make_property!(
693
    /// `mix-blend-mode` property.
694
    ///
695
    /// Compositing and Blending 1: <https://www.w3.org/TR/compositing/#mix-blend-mode>
696
    MixBlendMode,
697
    default: Normal,
698
    inherits_automatically: false,
699

            
700
    identifiers:
701
    "normal" => Normal,
702
    "multiply" => Multiply,
703
    "screen" => Screen,
704
    "overlay" => Overlay,
705
    "darken" => Darken,
706
    "lighten" => Lighten,
707
    "color-dodge" => ColorDodge,
708
    "color-burn" => ColorBurn,
709
    "hard-light" => HardLight,
710
    "soft-light" => SoftLight,
711
    "difference" => Difference,
712
    "exclusion" => Exclusion,
713
    "hue" => Hue,
714
    "saturation" => Saturation,
715
    "color" => Color,
716
    "luminosity" => Luminosity,
717
);
718

            
719
make_property!(
720
    /// `opacity` property.
721
    ///
722
    /// CSS Color 3: <https://www.w3.org/TR/css-color-3/#opacity>
723
    Opacity,
724
    default: UnitInterval(1.0),
725
    inherits_automatically: false,
726
    newtype_parse: UnitInterval,
727
);
728

            
729
make_property!(
730
    /// `overflow` shorthand property.
731
    ///
732
    /// CSS2: <https://www.w3.org/TR/CSS2/visufx.html#overflow>
733
    ///
734
    /// CSS Overflow 3: <https://www.w3.org/TR/css-overflow-3/#propdef-overflow>
735
    ///
736
    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
737
    Overflow,
738
    default: Visible,
739
    inherits_automatically: false,
740

            
741
    identifiers:
742
    "visible" => Visible,
743
    "hidden" => Hidden,
744
    "scroll" => Scroll,
745
    "auto" => Auto,
746
);
747

            
748
/// One of the three operations for the `paint-order` property; see [`PaintOrder`].
749
#[repr(u8)]
750
1529
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
751
pub enum PaintTarget {
752
    Fill,
753
    Stroke,
754
    Markers,
755
}
756

            
757
make_property!(
758
    /// `paint-order` property.
759
    ///
760
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#PaintOrder>
761
    ///
762
    /// The `targets` field specifies the order in which graphic elements should be filled/stroked.
763
    /// Instead of hard-coding an order of fill/stroke/markers, use the order specified by the `targets`.
764
    PaintOrder,
765
    inherits_automatically: true,
766
    fields: {
767
        targets: [PaintTarget; 3], default: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers],
768
    }
769

            
770
    parse_impl: {
771
        impl Parse for PaintOrder {
772
639
            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<PaintOrder, ParseError<'i>> {
773
639
                let allowed_targets = 3;
774
639
                let mut targets = Vec::with_capacity(allowed_targets);
775

            
776
934
                if parser.try_parse(|p| p.expect_ident_matching("normal")).is_ok() {
777
123
                    return Ok(PaintOrder::default());
778
                }
779

            
780
509
                while !parser.is_exhausted() {
781
511
                    let loc = parser.current_source_location();
782
511
                    let token = parser.next()?;
783

            
784
511
                    let value = match token {
785
511
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("fill") && !targets.contains(&PaintTarget::Fill) => PaintTarget::Fill,
786
341
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("stroke") && !targets.contains(&PaintTarget::Stroke) => PaintTarget::Stroke,
787
170
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("markers") && !targets.contains(&PaintTarget::Markers) => PaintTarget::Markers,
788
2
                        _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
789
                    };
790

            
791
509
                    targets.push(value);
792
                };
793

            
794
                // any values which were not specfied should be painted in default order
795
                // (fill, stroke, markers) following the values which were explicitly specified.
796
680
                for &target in &[PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers] {
797
510
                    if !targets.contains(&target) {
798
5
                        targets.push(target);
799
                    }
800
                }
801
170
                Ok(PaintOrder {
802
170
                    targets: targets[..].try_into().expect("Incorrect number of targets in paint-order")
803
                })
804
295
            }
805
        }
806
    }
807
);
808

            
809
#[cfg(test)]
810
#[test]
811
2
fn parses_paint_order() {
812
1
    assert_eq!(
813
1
        PaintOrder::parse_str("normal").unwrap(),
814
        PaintOrder {
815
            targets: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers]
816
        }
817
    );
818

            
819
1
    assert_eq!(
820
1
        PaintOrder::parse_str("markers fill").unwrap(),
821
        PaintOrder {
822
            targets: [PaintTarget::Markers, PaintTarget::Fill, PaintTarget::Stroke]
823
        }
824
    );
825

            
826
1
    assert_eq!(
827
1
        PaintOrder::parse_str("stroke").unwrap(),
828
        PaintOrder {
829
            targets: [PaintTarget::Stroke, PaintTarget::Fill, PaintTarget::Markers]
830
        }
831
    );
832

            
833
1
    assert!(PaintOrder::parse_str("stroke stroke").is_err());
834
1
    assert!(PaintOrder::parse_str("markers stroke fill hello").is_err());
835
2
}
836

            
837
make_property!(
838
    /// `r` property.
839
    ///
840
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#R>
841
    ///
842
    /// Note that in SVG1.1, this was an attribute, not a property.
843
    R,
844
    default: ULength::<Both>::parse_str("0").unwrap(),
845
    inherits_automatically: false,
846
    newtype_parse: ULength<Both>,
847
);
848

            
849
make_property!(
850
    /// `rx` property.
851
    ///
852
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#RX>
853
    ///
854
    /// Note that in SVG1.1, this was an attribute, not a property.
855
    RX,
856
    default: LengthOrAuto::<Horizontal>::Auto,
857
    inherits_automatically: false,
858
    newtype_parse: LengthOrAuto<Horizontal>,
859
);
860

            
861
make_property!(
862
    /// `ry` property.
863
    ///
864
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#RY>
865
    ///
866
    /// Note that in SVG1.1, this was an attribute, not a property.
867
    RY,
868
    default: LengthOrAuto::<Vertical>::Auto,
869
    inherits_automatically: false,
870
    newtype_parse: LengthOrAuto<Vertical>,
871
);
872

            
873
make_property!(
874
    /// `shape-rendering` property.
875
    ///
876
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#ShapeRendering>
877
    ShapeRendering,
878
    default: Auto,
879
    inherits_automatically: true,
880

            
881
    identifiers:
882
    "auto" => Auto,
883
    "optimizeSpeed" => OptimizeSpeed,
884
    "geometricPrecision" => GeometricPrecision,
885
    "crispEdges" => CrispEdges,
886
);
887

            
888
make_property!(
889
    /// `stop-color` property for gradient stops.
890
    ///
891
    /// SVG2: <https://www.w3.org/TR/SVG2/pservers.html#StopColorProperty>
892
    StopColor,
893
    default: cssparser::Color::Rgba(cssparser::RGBA::new(Some(0), Some(0), Some(0), Some(1.0))),
894
    inherits_automatically: false,
895
    newtype_parse: cssparser::Color,
896
);
897

            
898
make_property!(
899
    /// `stop-opacity` property for gradient stops.
900
    ///
901
    /// SVG2: <https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty>
902
    StopOpacity,
903
    default: UnitInterval(1.0),
904
    inherits_automatically: false,
905
    newtype_parse: UnitInterval,
906
);
907

            
908
make_property!(
909
    /// `stroke` property.
910
    ///
911
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint>
912
    Stroke,
913
    default: PaintServer::None,
914
    inherits_automatically: true,
915
    newtype_parse: PaintServer,
916
);
917

            
918
make_property!(
919
    /// `stroke-dasharray` property.
920
    ///
921
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeDashing>
922
    StrokeDasharray,
923
    default: Dasharray::default(),
924
    inherits_automatically: true,
925
    newtype_parse: Dasharray,
926
);
927

            
928
make_property!(
929
    /// `stroke-dashoffset` property.
930
    ///
931
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeDashingdas>
932
    StrokeDashoffset,
933
    default: Length::<Both>::default(),
934
    inherits_automatically: true,
935
    newtype_parse: Length<Both>,
936
);
937

            
938
make_property!(
939
    /// `stroke-linecap` property.
940
    ///
941
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#LineCaps>
942
    StrokeLinecap,
943
    default: Butt,
944
    inherits_automatically: true,
945

            
946
    identifiers:
947
    "butt" => Butt,
948
    "round" => Round,
949
    "square" => Square,
950
);
951

            
952
make_property!(
953
    /// `stroke-linejoin` property.
954
    ///
955
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#LineJoin>
956
    StrokeLinejoin,
957
    default: Miter,
958
    inherits_automatically: true,
959

            
960
    identifiers:
961
    "miter" => Miter,
962
    "round" => Round,
963
    "bevel" => Bevel,
964
);
965

            
966
make_property!(
967
    /// `stroke-miterlimit` property.
968
    ///
969
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty>
970
    StrokeMiterlimit,
971
    default: 4f64,
972
    inherits_automatically: true,
973
    newtype_parse: f64,
974
);
975

            
976
make_property!(
977
    /// `stroke-opacity` property.
978
    ///
979
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeOpacity>
980
    StrokeOpacity,
981
    default: UnitInterval(1.0),
982
    inherits_automatically: true,
983
    newtype_parse: UnitInterval,
984
);
985

            
986
make_property!(
987
    /// `stroke-width` property.
988
    ///
989
    /// SVG2: <https://www.w3.org/TR/SVG2/painting.html#StrokeWidth>
990
    StrokeWidth,
991
    default: Length::<Both>::parse_str("1.0").unwrap(),
992
    inherits_automatically: true,
993
    newtype_parse: Length::<Both>,
994
);
995

            
996
make_property!(
997
    /// `text-anchor` property.
998
    ///
999
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty>
    TextAnchor,
    default: Start,
    inherits_automatically: true,
    identifiers:
    "start" => Start,
    "middle" => Middle,
    "end" => End,
);
make_property!(
    /// `text-decoration` shorthand property.
    ///
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#TextDecorationProperty>
    ///
    /// CSS Text Decoration 3: <https://www.w3.org/TR/css-text-decor-3/#text-decoration-property>
    ///
    /// Note that librsvg implements SVG1.1 semantics, where this is not a shorthand.
    TextDecoration,
    inherits_automatically: false,
    fields: {
        overline: bool, default: false,
        underline: bool, default: false,
        strike: bool, default: false,
    }
    parse_impl: {
        impl Parse for TextDecoration {
559
            fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TextDecoration, ParseError<'i>> {
559
                let mut overline = false;
559
                let mut underline = false;
559
                let mut strike = false;
1117
                if parser.try_parse(|p| p.expect_ident_matching("none")).is_ok() {
551
                    return Ok(TextDecoration::default());
                }
16
                while !parser.is_exhausted() {
9
                    let loc = parser.current_source_location();
9
                    let token = parser.next()?;
9
                    match token {
9
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("overline") => overline = true,
7
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("underline") => underline = true,
3
                        Token::Ident(ref cow) if cow.eq_ignore_ascii_case("line-through") => strike = true,
1
                        _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
                    }
                }
7
                Ok(TextDecoration {
7
                    overline,
7
                    underline,
7
                    strike,
                })
559
            }
        }
    }
);
#[cfg(test)]
#[test]
2
fn parses_text_decoration() {
1
    assert_eq!(
1
        TextDecoration::parse_str("none").unwrap(),
        TextDecoration {
            overline: false,
            underline: false,
            strike: false,
        }
    );
1
    assert_eq!(
1
        TextDecoration::parse_str("overline").unwrap(),
        TextDecoration {
            overline: true,
            underline: false,
            strike: false,
        }
    );
1
    assert_eq!(
1
        TextDecoration::parse_str("underline").unwrap(),
        TextDecoration {
            overline: false,
            underline: true,
            strike: false,
        }
    );
1
    assert_eq!(
1
        TextDecoration::parse_str("line-through").unwrap(),
        TextDecoration {
            overline: false,
            underline: false,
            strike: true,
        }
    );
1
    assert_eq!(
1
        TextDecoration::parse_str("underline overline").unwrap(),
        TextDecoration {
            overline: true,
            underline: true,
            strike: false,
        }
    );
1
    assert!(TextDecoration::parse_str("airline").is_err())
2
}
make_property!(
    /// `text-orientation` property.
    ///
    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#propdef-text-orientation>
    TextOrientation,
    default: Mixed,
    inherits_automatically: true,
    identifiers:
    "mixed" => Mixed,
    "upright" => Upright,
    "sideways" => Sideways,
);
impl From<GlyphOrientationVertical> for TextOrientation {
    /// Converts the `glyph-orientation-vertical` shorthand to a `text-orientation` longhand.
    ///
    /// See <https://www.w3.org/TR/css-writing-modes-3/#propdef-glyph-orientation-vertical> for the conversion table.
    fn from(o: GlyphOrientationVertical) -> TextOrientation {
        match o {
            GlyphOrientationVertical::Auto => TextOrientation::Mixed,
            GlyphOrientationVertical::Angle0 => TextOrientation::Upright,
            GlyphOrientationVertical::Angle90 => TextOrientation::Sideways,
        }
    }
}
make_property!(
    /// `text-rendering` property.
    ///
    /// SVG1.1: <https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty>
    TextRendering,
    default: Auto,
    inherits_automatically: true,
    identifiers:
    "auto" => Auto,
    "optimizeSpeed" => OptimizeSpeed,
    "optimizeLegibility" => OptimizeLegibility,
    "geometricPrecision" => GeometricPrecision,
);
make_property!(
    /// `transform` property.
    ///
    /// CSS Transforms 1: <https://www.w3.org/TR/css-transforms-1/#transform-property>
    Transform,
    default: TransformProperty::None,
    inherits_automatically: false,
    newtype_parse: TransformProperty,
);
make_property!(
    /// `unicode-bidi` property.
    ///
    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#unicode-bidi>
    UnicodeBidi,
    default: Normal,
    inherits_automatically: false,
    identifiers:
    "normal" => Normal,
    "embed" => Embed,
    "isolate" => Isolate,
    "bidi-override" => BidiOverride,
    "isolate-override" => IsolateOverride,
    "plaintext" => Plaintext,
);
make_property!(
    /// `vector-effect` property.
    ///
    /// SVG2: <https://svgwg.org/svg2-draft/coords.html#VectorEffectProperty>
    VectorEffect,
    default: None,
    inherits_automatically: false,
    identifiers:
    "none" => None,
    "non-scaling-stroke" => NonScalingStroke,
    // non-scaling-size, non-rotation, fixed-position not implemented
);
make_property!(
    /// `visibility` property.
    ///
    /// CSS2: <https://www.w3.org/TR/CSS2/visufx.html#visibility>
    Visibility,
    default: Visible,
    inherits_automatically: true,
    identifiers:
    "visible" => Visible,
    "hidden" => Hidden,
    "collapse" => Collapse,
);
make_property!(
    /// `width` property.
    ///
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Sizing>
    ///
    /// Note that in SVG1.1, this was an attribute, not a property.
    Width,
    default: LengthOrAuto::<Horizontal>::Auto,
    inherits_automatically: false,
    newtype_parse: LengthOrAuto<Horizontal>,
);
make_property!(
    /// `writing-mode` property.
    ///
    /// SVG1.1: <https://www.w3.org/TR/SVG11/text.html#WritingModeProperty>
    ///
    /// SVG2: <https://svgwg.org/svg2-draft/text.html#WritingModeProperty>
    ///
    /// CSS Writing Modes 3: <https://www.w3.org/TR/css-writing-modes-3/#block-flow>
    ///
    /// See the comments in the SVG2 spec for how the SVG1.1 values must be translated
    /// into CSS Writing Modes 3 values.
    WritingMode,
    default: HorizontalTb,
    identifiers: {
        "horizontal-tb" => HorizontalTb,
        "vertical-rl" => VerticalRl,
        "vertical-lr" => VerticalLr,
        "lr" => Lr,
        "lr-tb" => LrTb,
        "rl" => Rl,
        "rl-tb" => RlTb,
        "tb" => Tb,
        "tb-rl" => TbRl,
    },
    property_impl: {
        impl Property for WritingMode {
1466488
            fn inherits_automatically() -> bool {
                true
1466488
            }
1467043
            fn compute(&self, _v: &ComputedValues) -> Self {
                use WritingMode::*;
                // Translate SVG1.1 compatibility values to SVG2 / CSS Writing Modes 3.
1467043
                match *self {
555
                    Lr | LrTb | Rl | RlTb => HorizontalTb,
                    Tb | TbRl => VerticalRl,
1466488
                    _ => *self,
                }
1467043
            }
        }
    }
);
impl WritingMode {
3044
    pub fn is_horizontal(self) -> bool {
        use WritingMode::*;
3044
        matches!(self, HorizontalTb | Lr | LrTb | Rl | RlTb)
3044
    }
}
make_property!(
    /// `x` property.
    ///
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#X>
    ///
    /// Note that in SVG1.1, this was an attribute, not a property.
    X,
    default: Length::<Horizontal>::parse_str("0").unwrap(),
    inherits_automatically: false,
    newtype_parse: Length<Horizontal>,
);
make_property!(
    /// `xml:lang` attribute.
    ///
    /// XML1.0: <https://www.w3.org/TR/xml/#sec-lang-tag>
    ///
    /// Similar to `XmlSpace`, this is a hack in librsvg: the `xml:lang` attribute is
    /// supposed to apply to an element and all its children.  This more or less matches
    /// CSS property inheritance, so librsvg reuses the machinery for property inheritance
    /// to propagate down the value of the `xml:lang` attribute to an element's children.
    XmlLang,
    default: None,
    inherits_automatically: true,
    newtype: Option<Box<LanguageTag>>,
    parse_impl: {
        impl Parse for XmlLang {
13
            fn parse<'i>(
                parser: &mut Parser<'i, '_>,
            ) -> Result<XmlLang, ParseError<'i>> {
13
                let language_tag = parser.expect_ident()?;
12
                let language_tag = LanguageTag::from_str(language_tag).map_err(|_| {
                    parser.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'xml:lang' parameter"))
                })?;
12
                Ok(XmlLang(Some(Box::new(language_tag))))
13
            }
        }
    },
);
#[cfg(test)]
#[test]
2
fn parses_xml_lang() {
2
    assert_eq!(
1
        XmlLang::parse_str("es-MX").unwrap(),
1
        XmlLang(Some(Box::new(LanguageTag::from_str("es-MX").unwrap())))
    );
1
    assert!(XmlLang::parse_str("").is_err());
2
}
make_property!(
    /// `xml:space` attribute.
    ///
    /// XML1.0: <https://www.w3.org/TR/xml/#sec-white-space>
    ///
    /// Similar to `XmlLang`, this is a hack in librsvg.  The `xml:space` attribute is
    /// supposed to be applied to all the children of the element in which it appears, so
    /// it works more or less the same as CSS property inheritance.  Librsvg reuses the
    /// machinery for CSS property inheritance to propagate down the value of `xml:space`
    /// to an element's children.
    XmlSpace,
    default: Default,
    inherits_automatically: true,
    identifiers:
    "default" => Default,
    "preserve" => Preserve,
);
make_property!(
    /// `y` property.
    ///
    /// SVG2: <https://www.w3.org/TR/SVG2/geometry.html#Y>
    ///
    /// Note that in SVG1.1, this was an attribute, not a property.
    Y,
    default: Length::<Vertical>::parse_str("0").unwrap(),
    inherits_automatically: false,
    newtype_parse: Length<Vertical>,
);