1use std::convert::TryInto;
38use std::str::FromStr;
39
40use cssparser::{Parser, Token};
41use language_tags::LanguageTag;
42
43use crate::dasharray::Dasharray;
44use crate::error::*;
45use crate::filter::FilterValueList;
46use crate::font_props::{
47 Font, FontFamily, FontSize, FontWeight, GlyphOrientationVertical, LetterSpacing, LineHeight,
48};
49use crate::iri::Iri;
50use crate::length::*;
51use crate::paint_server::PaintServer;
52use crate::parse_identifiers;
53use crate::parsers::Parse;
54use crate::properties::ComputedValues;
55use crate::property_macros::Property;
56use crate::rect::Rect;
57use crate::transform::TransformProperty;
58use crate::unit_interval::UnitInterval;
59use crate::{impl_default, impl_property, make_property};
60
61make_property!(
62 BaselineShift,
68 default: Length::<Both>::default(),
69 newtype: Length<Both>,
70 property_impl: {
71 impl Property for BaselineShift {
72 fn inherits_automatically() -> bool {
73 false
74 }
75
76 fn compute(&self, v: &ComputedValues) -> Self {
77 let font_size = v.font_size().value();
78 let parent = v.baseline_shift();
79
80 match (self.0.unit, parent.0.unit) {
81 (LengthUnit::Percent, _) => {
82 BaselineShift(Length::<Both>::new(self.0.length * font_size.length + parent.0.length, font_size.unit))
83 }
84
85 (x, y) if x == y || parent.0.length == 0.0 => {
86 BaselineShift(Length::<Both>::new(self.0.length + parent.0.length, self.0.unit))
87 }
88
89 _ => {
90 parent
99 }
100 }
101 }
102 }
103 },
104 parse_impl: {
105 impl Parse for BaselineShift {
106 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<BaselineShift, crate::error::ParseError<'i>> {
109 parser.try_parse(|p| Ok(BaselineShift(Length::<Both>::parse(p)?)))
110 .or_else(|_: ParseError<'_>| {
111 Ok(parse_identifiers!(
112 parser,
113 "baseline" => BaselineShift(Length::<Both>::new(0.0, LengthUnit::Percent)),
114 "sub" => BaselineShift(Length::<Both>::new(-0.2, LengthUnit::Percent)),
115
116 "super" => BaselineShift(Length::<Both>::new(0.4, LengthUnit::Percent)),
117 )?)
118 })
119 }
120 }
121 }
122);
123
124make_property!(
125 ClipPath,
131 default: Iri::None,
132 inherits_automatically: false,
133 newtype_parse: Iri,
134);
135
136make_property!(
137 ClipRule,
143 default: NonZero,
144 inherits_automatically: true,
145
146 identifiers:
147 "nonzero" => NonZero,
148 "evenodd" => EvenOdd,
149);
150
151make_property!(
152 Color,
165 default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
166 inherits_automatically: true,
167 newtype_parse: crate::color::Color,
168);
169
170make_property!(
171 ColorInterpolationFilters,
177 default: LinearRgb,
178 inherits_automatically: true,
179
180 identifiers:
181 "auto" => Auto,
182 "linearRGB" => LinearRgb,
183 "sRGB" => Srgb,
184);
185
186make_property!(
187 CX,
193 default: Length::<Horizontal>::default(),
194 inherits_automatically: false,
195 newtype_parse: Length<Horizontal>,
196);
197
198make_property!(
199 CY,
205 default: Length::<Vertical>::default(),
206 inherits_automatically: false,
207 newtype_parse: Length<Vertical>,
208);
209
210make_property!(
211 Direction,
217 default: Ltr,
218 inherits_automatically: true,
219
220 identifiers:
221 "ltr" => Ltr,
222 "rtl" => Rtl,
223);
224
225make_property!(
226 Display,
232 default: Inline,
233 inherits_automatically: false,
234
235 identifiers:
236 "inline" => Inline,
237 "block" => Block,
238 "list-item" => ListItem,
239 "run-in" => RunIn,
240 "compact" => Compact,
241 "marker" => Marker,
242 "table" => Table,
243 "inline-table" => InlineTable,
244 "table-row-group" => TableRowGroup,
245 "table-header-group" => TableHeaderGroup,
246 "table-footer-group" => TableFooterGroup,
247 "table-row" => TableRow,
248 "table-column-group" => TableColumnGroup,
249 "table-column" => TableColumn,
250 "table-cell" => TableCell,
251 "table-caption" => TableCaption,
252 "none" => None,
253);
254
255make_property!(
256 DominantBaseline,
260 default: Auto,
261 inherits_automatically: true,
262
263 identifiers:
264 "auto" => Auto,
265 "ideographic" => Ideographic,
266 "alphabetic" => Alphabetic,
267 "hanging" => Hanging,
268 "mathematical" => Mathematical,
269 "central" => Central,
270 "middle" => Middle,
271 "text-after-edge" => TextAfterEdge,
272 "text-before-edge" => TextBeforeEdge,
273 "text-top" => TextTop,
275 "text-bottom" => TextBottom,
276 );
279
280#[derive(Debug, Clone, Copy, PartialEq)]
287pub enum EnableBackground {
288 Accumulate,
289 New(Option<Rect>),
290}
291
292make_property!(
293 EnableBackground,
294 default: EnableBackground::Accumulate,
295 inherits_automatically: false,
296
297 parse_impl: {
298 impl Parse for EnableBackground {
299 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
300 let loc = parser.current_source_location();
301
302 if parser
303 .try_parse(|p| p.expect_ident_matching("accumulate"))
304 .is_ok()
305 {
306 return Ok(EnableBackground::Accumulate);
307 }
308
309 if parser.try_parse(|p| p.expect_ident_matching("new")).is_ok() {
310 parser.try_parse(|p| -> Result<_, ParseError<'_>> {
311 let x = f64::parse(p)?;
312 let y = f64::parse(p)?;
313 let w = f64::parse(p)?;
314 let h = f64::parse(p)?;
315
316 Ok(EnableBackground::New(Some(Rect::new(x, y, x + w, y + h))))
317 }).or(Ok(EnableBackground::New(None)))
318 } else {
319 Err(loc.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'enable-background' property")))
320 }
321 }
322 }
323
324 }
325);
326
327#[cfg(test)]
328#[test]
329fn parses_enable_background() {
330 assert_eq!(
331 EnableBackground::parse_str("accumulate").unwrap(),
332 EnableBackground::Accumulate
333 );
334
335 assert_eq!(
336 EnableBackground::parse_str("new").unwrap(),
337 EnableBackground::New(None)
338 );
339
340 assert_eq!(
341 EnableBackground::parse_str("new 1 2 3 4").unwrap(),
342 EnableBackground::New(Some(Rect::new(1.0, 2.0, 4.0, 6.0)))
343 );
344
345 assert!(EnableBackground::parse_str("new foo").is_err());
346
347 assert!(EnableBackground::parse_str("plonk").is_err());
348}
349
350make_property!(
351 Fill,
357 default: PaintServer::SolidColor(crate::color::Color::Rgba(
358 crate::color::RGBA::new(0, 0, 0, 1.0)
359 )),
360 inherits_automatically: true,
361 newtype_parse: PaintServer,
362);
363
364make_property!(
365 FillOpacity,
371 default: UnitInterval(1.0),
372 inherits_automatically: true,
373 newtype_parse: UnitInterval,
374);
375
376make_property!(
377 FillRule,
383 default: NonZero,
384 inherits_automatically: true,
385
386 identifiers:
387 "nonzero" => NonZero,
388 "evenodd" => EvenOdd,
389);
390
391#[derive(Debug, Clone, PartialEq)]
400pub enum Filter {
401 None,
402 List(FilterValueList),
403}
404
405make_property!(
406 Filter,
407 default: Filter::None,
408 inherits_automatically: false,
409 parse_impl: {
410 impl Parse for Filter {
411 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
412
413 if parser
414 .try_parse(|p| p.expect_ident_matching("none"))
415 .is_ok()
416 {
417 return Ok(Filter::None);
418 }
419
420 Ok(Filter::List(FilterValueList::parse(parser)?))
421 }
422 }
423 }
424);
425
426make_property!(
427 FloodColor,
433 default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
434 inherits_automatically: false,
435 newtype_parse: crate::color::Color,
436);
437
438make_property!(
439 FloodOpacity,
445 default: UnitInterval(1.0),
446 inherits_automatically: false,
447 newtype_parse: UnitInterval,
448);
449
450make_property!(
451 Font,
453 default: Font::Spec(Default::default()),
454 inherits_automatically: true,
455);
456
457make_property!(
458 FontFamily,
460 default: FontFamily("Times New Roman".to_string()),
461 inherits_automatically: true,
462);
463
464make_property!(
465 FontSize,
467 default: FontSize::Value(Length::<Both>::new(12.0, LengthUnit::Px)),
468 property_impl: {
469 impl Property for FontSize {
470 fn inherits_automatically() -> bool {
471 true
472 }
473
474 fn compute(&self, v: &ComputedValues) -> Self {
475 self.compute(v)
476 }
477 }
478 }
479);
480
481make_property!(
482 FontStretch,
488 default: Normal,
489 inherits_automatically: true,
490
491 identifiers:
492 "normal" => Normal,
493 "wider" => Wider,
494 "narrower" => Narrower,
495 "ultra-condensed" => UltraCondensed,
496 "extra-condensed" => ExtraCondensed,
497 "condensed" => Condensed,
498 "semi-condensed" => SemiCondensed,
499 "semi-expanded" => SemiExpanded,
500 "expanded" => Expanded,
501 "extra-expanded" => ExtraExpanded,
502 "ultra-expanded" => UltraExpanded,
503);
504
505make_property!(
506 FontStyle,
512 default: Normal,
513 inherits_automatically: true,
514
515 identifiers:
516 "normal" => Normal,
517 "italic" => Italic,
518 "oblique" => Oblique,
519);
520
521make_property!(
522 FontVariant,
530 default: Normal,
531 inherits_automatically: true,
532
533 identifiers:
534 "normal" => Normal,
535 "small-caps" => SmallCaps,
536);
537
538make_property!(
539 FontWeight,
541 default: FontWeight::Normal,
542 property_impl: {
543 impl Property for FontWeight {
544 fn inherits_automatically() -> bool {
545 true
546 }
547
548 fn compute(&self, v: &ComputedValues) -> Self {
549 self.compute(&v.font_weight())
550 }
551 }
552 }
553);
554
555make_property!(
556 GlyphOrientationVertical,
562 default: GlyphOrientationVertical::Auto,
563 inherits_automatically: false,
564);
565
566make_property!(
567 Height,
573 default: LengthOrAuto::<Vertical>::Auto,
574 inherits_automatically: false,
575 newtype_parse: LengthOrAuto<Vertical>,
576);
577
578make_property!(
579 ImageRendering,
587 default: Auto,
588 inherits_automatically: true,
589
590 identifiers:
591 "auto" => Auto,
592 "smooth" => Smooth,
593 "optimizeQuality" => OptimizeQuality,
594 "high-quality" => HighQuality,
595 "crisp-edges" => CrispEdges,
596 "optimizeSpeed" => OptimizeSpeed,
597 "pixelated" => Pixelated,
598);
599
600make_property!(
601 Isolation,
605 default: Auto,
606 inherits_automatically: false,
607
608 identifiers:
609 "auto" => Auto,
610 "isolate" => Isolate,
611);
612
613make_property!(
614 LetterSpacing,
616 default: LetterSpacing::Normal,
617 property_impl: {
618 impl Property for LetterSpacing {
619 fn inherits_automatically() -> bool {
620 true
621 }
622
623 fn compute(&self, _v: &ComputedValues) -> Self {
624 self.compute()
625 }
626 }
627 }
628);
629
630make_property!(
631 LineHeight,
633 default: LineHeight::Normal,
634 inherits_automatically: true,
635);
636
637make_property!(
638 LightingColor,
644 default: crate::color::Color::Rgba(crate::color::RGBA::new(255, 255, 255, 1.0)),
645 inherits_automatically: false,
646 newtype_parse: crate::color::Color,
647);
648
649make_property!(
650 Marker,
657 default: Iri::None,
658 inherits_automatically: true,
659 newtype_parse: Iri,
660);
661
662make_property!(
663 MarkerEnd,
667 default: Iri::None,
668 inherits_automatically: true,
669 newtype_parse: Iri,
670);
671
672make_property!(
673 MarkerMid,
677 default: Iri::None,
678 inherits_automatically: true,
679 newtype_parse: Iri,
680);
681
682make_property!(
683 MarkerStart,
687 default: Iri::None,
688 inherits_automatically: true,
689 newtype_parse: Iri,
690);
691
692make_property!(
693 Mask,
701 default: Iri::None,
702 inherits_automatically: false,
703 newtype_parse: Iri,
704);
705
706make_property!(
707 MaskType,
711 default: Luminance,
712 inherits_automatically: false,
713
714 identifiers:
715 "luminance" => Luminance,
716 "alpha" => Alpha,
717);
718
719make_property!(
720 MixBlendMode,
724 default: Normal,
725 inherits_automatically: false,
726
727 identifiers:
728 "normal" => Normal,
729 "multiply" => Multiply,
730 "screen" => Screen,
731 "overlay" => Overlay,
732 "darken" => Darken,
733 "lighten" => Lighten,
734 "color-dodge" => ColorDodge,
735 "color-burn" => ColorBurn,
736 "hard-light" => HardLight,
737 "soft-light" => SoftLight,
738 "difference" => Difference,
739 "exclusion" => Exclusion,
740 "hue" => Hue,
741 "saturation" => Saturation,
742 "color" => Color,
743 "luminosity" => Luminosity,
744);
745
746make_property!(
747 Opacity,
751 default: UnitInterval(1.0),
752 inherits_automatically: false,
753 newtype_parse: UnitInterval,
754);
755
756make_property!(
757 Overflow,
765 default: Visible,
766 inherits_automatically: false,
767
768 identifiers:
769 "visible" => Visible,
770 "hidden" => Hidden,
771 "scroll" => Scroll,
772 "auto" => Auto,
773);
774
775impl Overflow {
776 pub fn overflow_allowed(&self) -> bool {
777 matches!(*self, Overflow::Auto | Overflow::Visible)
778 }
779}
780
781#[repr(u8)]
783#[derive(Debug, Clone, Copy, Eq, PartialEq)]
784pub enum PaintTarget {
785 Fill,
786 Stroke,
787 Markers,
788}
789
790make_property!(
791 PaintOrder,
798 inherits_automatically: true,
799 fields: {
800 targets: [PaintTarget; 3], default: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers],
801 }
802
803 parse_impl: {
804 impl Parse for PaintOrder {
805 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<PaintOrder, ParseError<'i>> {
806 let allowed_targets = 3;
807 let mut targets = Vec::with_capacity(allowed_targets);
808
809 if parser.try_parse(|p| p.expect_ident_matching("normal")).is_ok() {
810 return Ok(PaintOrder::default());
811 }
812
813 while !parser.is_exhausted() {
814 let loc = parser.current_source_location();
815 let token = parser.next()?;
816
817 let value = match token {
818 Token::Ident(cow) if cow.eq_ignore_ascii_case("fill") && !targets.contains(&PaintTarget::Fill) => PaintTarget::Fill,
819 Token::Ident(cow) if cow.eq_ignore_ascii_case("stroke") && !targets.contains(&PaintTarget::Stroke) => PaintTarget::Stroke,
820 Token::Ident(cow) if cow.eq_ignore_ascii_case("markers") && !targets.contains(&PaintTarget::Markers) => PaintTarget::Markers,
821 _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
822 };
823
824 targets.push(value);
825 };
826
827 for &target in &[PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers] {
830 if !targets.contains(&target) {
831 targets.push(target);
832 }
833 }
834 Ok(PaintOrder {
835 targets: targets[..].try_into().expect("Incorrect number of targets in paint-order")
836 })
837 }
838 }
839 }
840);
841
842#[cfg(test)]
843#[test]
844fn parses_paint_order() {
845 assert_eq!(
846 PaintOrder::parse_str("normal").unwrap(),
847 PaintOrder {
848 targets: [PaintTarget::Fill, PaintTarget::Stroke, PaintTarget::Markers]
849 }
850 );
851
852 assert_eq!(
853 PaintOrder::parse_str("markers fill").unwrap(),
854 PaintOrder {
855 targets: [PaintTarget::Markers, PaintTarget::Fill, PaintTarget::Stroke]
856 }
857 );
858
859 assert_eq!(
860 PaintOrder::parse_str("stroke").unwrap(),
861 PaintOrder {
862 targets: [PaintTarget::Stroke, PaintTarget::Fill, PaintTarget::Markers]
863 }
864 );
865
866 assert!(PaintOrder::parse_str("stroke stroke").is_err());
867 assert!(PaintOrder::parse_str("markers stroke fill hello").is_err());
868}
869
870make_property!(
871 R,
877 default: ULength::<Both>::default(),
878 inherits_automatically: false,
879 newtype_parse: ULength<Both>,
880);
881
882make_property!(
883 RX,
889 default: LengthOrAuto::<Horizontal>::Auto,
890 inherits_automatically: false,
891 newtype_parse: LengthOrAuto<Horizontal>,
892);
893
894make_property!(
895 RY,
901 default: LengthOrAuto::<Vertical>::Auto,
902 inherits_automatically: false,
903 newtype_parse: LengthOrAuto<Vertical>,
904);
905
906make_property!(
907 ShapeRendering,
911 default: Auto,
912 inherits_automatically: true,
913
914 identifiers:
915 "auto" => Auto,
916 "optimizeSpeed" => OptimizeSpeed,
917 "geometricPrecision" => GeometricPrecision,
918 "crispEdges" => CrispEdges,
919);
920
921make_property!(
922 StopColor,
926 default: crate::color::Color::Rgba(crate::color::RGBA::new(0, 0, 0, 1.0)),
927 inherits_automatically: false,
928 newtype_parse: crate::color::Color,
929);
930
931make_property!(
932 StopOpacity,
936 default: UnitInterval(1.0),
937 inherits_automatically: false,
938 newtype_parse: UnitInterval,
939);
940
941make_property!(
942 Stroke,
946 default: PaintServer::None,
947 inherits_automatically: true,
948 newtype_parse: PaintServer,
949);
950
951make_property!(
952 StrokeDasharray,
956 default: Dasharray::default(),
957 inherits_automatically: true,
958 newtype_parse: Dasharray,
959);
960
961make_property!(
962 StrokeDashoffset,
966 default: Length::<Both>::default(),
967 inherits_automatically: true,
968 newtype_parse: Length<Both>,
969);
970
971make_property!(
972 StrokeLinecap,
976 default: Butt,
977 inherits_automatically: true,
978
979 identifiers:
980 "butt" => Butt,
981 "round" => Round,
982 "square" => Square,
983);
984
985make_property!(
986 StrokeLinejoin,
990 default: Miter,
991 inherits_automatically: true,
992
993 identifiers:
994 "miter" => Miter,
995 "round" => Round,
996 "bevel" => Bevel,
997);
998
999make_property!(
1000 StrokeMiterlimit,
1004 default: 4f64,
1005 inherits_automatically: true,
1006 newtype_parse: f64,
1007);
1008
1009make_property!(
1010 StrokeOpacity,
1014 default: UnitInterval(1.0),
1015 inherits_automatically: true,
1016 newtype_parse: UnitInterval,
1017);
1018
1019make_property!(
1020 StrokeWidth,
1024 default: Length::<Both>::new(1.0, LengthUnit::Px),
1025 inherits_automatically: true,
1026 newtype_parse: Length::<Both>,
1027);
1028
1029make_property!(
1030 TextAnchor,
1034 default: Start,
1035 inherits_automatically: true,
1036
1037 identifiers:
1038 "start" => Start,
1039 "middle" => Middle,
1040 "end" => End,
1041);
1042
1043make_property!(
1044 TextDecoration,
1052 inherits_automatically: false,
1053
1054 fields: {
1055 overline: bool, default: false,
1056 underline: bool, default: false,
1057 strike: bool, default: false,
1058 }
1059
1060 parse_impl: {
1061 impl Parse for TextDecoration {
1062 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TextDecoration, ParseError<'i>> {
1063 let mut overline = false;
1064 let mut underline = false;
1065 let mut strike = false;
1066
1067 if parser.try_parse(|p| p.expect_ident_matching("none")).is_ok() {
1068 return Ok(TextDecoration::default());
1069 }
1070
1071 while !parser.is_exhausted() {
1072 let loc = parser.current_source_location();
1073 let token = parser.next()?;
1074
1075 match token {
1076 Token::Ident(cow) if cow.eq_ignore_ascii_case("overline") => overline = true,
1077 Token::Ident(cow) if cow.eq_ignore_ascii_case("underline") => underline = true,
1078 Token::Ident(cow) if cow.eq_ignore_ascii_case("line-through") => strike = true,
1079 _ => return Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
1080 }
1081 }
1082
1083 Ok(TextDecoration {
1084 overline,
1085 underline,
1086 strike,
1087 })
1088 }
1089 }
1090 }
1091);
1092
1093#[cfg(test)]
1094#[test]
1095fn parses_text_decoration() {
1096 assert_eq!(
1097 TextDecoration::parse_str("none").unwrap(),
1098 TextDecoration {
1099 overline: false,
1100 underline: false,
1101 strike: false,
1102 }
1103 );
1104
1105 assert_eq!(
1106 TextDecoration::parse_str("overline").unwrap(),
1107 TextDecoration {
1108 overline: true,
1109 underline: false,
1110 strike: false,
1111 }
1112 );
1113
1114 assert_eq!(
1115 TextDecoration::parse_str("underline").unwrap(),
1116 TextDecoration {
1117 overline: false,
1118 underline: true,
1119 strike: false,
1120 }
1121 );
1122
1123 assert_eq!(
1124 TextDecoration::parse_str("line-through").unwrap(),
1125 TextDecoration {
1126 overline: false,
1127 underline: false,
1128 strike: true,
1129 }
1130 );
1131
1132 assert_eq!(
1133 TextDecoration::parse_str("underline overline").unwrap(),
1134 TextDecoration {
1135 overline: true,
1136 underline: true,
1137 strike: false,
1138 }
1139 );
1140
1141 assert!(TextDecoration::parse_str("airline").is_err())
1142}
1143
1144make_property!(
1145 TextOrientation,
1149 default: Mixed,
1150 inherits_automatically: true,
1151
1152 identifiers:
1153 "mixed" => Mixed,
1154 "upright" => Upright,
1155 "sideways" => Sideways,
1156);
1157
1158impl From<GlyphOrientationVertical> for TextOrientation {
1159 fn from(o: GlyphOrientationVertical) -> TextOrientation {
1163 match o {
1164 GlyphOrientationVertical::Auto => TextOrientation::Mixed,
1165 GlyphOrientationVertical::Angle0 => TextOrientation::Upright,
1166 GlyphOrientationVertical::Angle90 => TextOrientation::Sideways,
1167 }
1168 }
1169}
1170
1171make_property!(
1172 TextRendering,
1176 default: Auto,
1177 inherits_automatically: true,
1178
1179 identifiers:
1180 "auto" => Auto,
1181 "optimizeSpeed" => OptimizeSpeed,
1182 "optimizeLegibility" => OptimizeLegibility,
1183 "geometricPrecision" => GeometricPrecision,
1184);
1185
1186make_property!(
1196 #[allow(unused)]
1200 Transform,
1201 default: TransformProperty::None,
1202 inherits_automatically: false,
1203 newtype_parse: TransformProperty,
1204);
1205
1206make_property!(
1207 UnicodeBidi,
1211 default: Normal,
1212 inherits_automatically: false,
1213
1214 identifiers:
1215 "normal" => Normal,
1216 "embed" => Embed,
1217 "isolate" => Isolate,
1218 "bidi-override" => BidiOverride,
1219 "isolate-override" => IsolateOverride,
1220 "plaintext" => Plaintext,
1221);
1222
1223make_property!(
1224 VectorEffect,
1228 default: None,
1229 inherits_automatically: false,
1230
1231 identifiers:
1232 "none" => None,
1233 "non-scaling-stroke" => NonScalingStroke,
1234 );
1236
1237make_property!(
1238 Visibility,
1242 default: Visible,
1243 inherits_automatically: true,
1244
1245 identifiers:
1246 "visible" => Visible,
1247 "hidden" => Hidden,
1248 "collapse" => Collapse,
1249);
1250
1251make_property!(
1252 Width,
1258 default: LengthOrAuto::<Horizontal>::Auto,
1259 inherits_automatically: false,
1260 newtype_parse: LengthOrAuto<Horizontal>,
1261);
1262
1263make_property!(
1264 WritingMode,
1275 default: HorizontalTb,
1276 identifiers: {
1277 "horizontal-tb" => HorizontalTb,
1278 "vertical-rl" => VerticalRl,
1279 "vertical-lr" => VerticalLr,
1280 "lr" => Lr,
1281 "lr-tb" => LrTb,
1282 "rl" => Rl,
1283 "rl-tb" => RlTb,
1284 "tb" => Tb,
1285 "tb-rl" => TbRl,
1286 },
1287 property_impl: {
1288 impl Property for WritingMode {
1289 fn inherits_automatically() -> bool {
1290 true
1291 }
1292
1293 fn compute(&self, _v: &ComputedValues) -> Self {
1294 use WritingMode::*;
1295
1296 match *self {
1298 Lr | LrTb | Rl | RlTb => HorizontalTb,
1299 Tb | TbRl => VerticalRl,
1300 _ => *self,
1301 }
1302 }
1303 }
1304 }
1305);
1306
1307impl WritingMode {
1308 pub fn is_horizontal(self) -> bool {
1309 use WritingMode::*;
1310
1311 matches!(self, HorizontalTb | Lr | LrTb | Rl | RlTb)
1312 }
1313}
1314
1315make_property!(
1316 X,
1322 default: Length::<Horizontal>::default(),
1323 inherits_automatically: false,
1324 newtype_parse: Length<Horizontal>,
1325);
1326
1327make_property!(
1328 XmlLang,
1337 default: None,
1338 inherits_automatically: true,
1339 newtype: Option<Box<LanguageTag>>,
1340 parse_impl: {
1341 impl Parse for XmlLang {
1342 fn parse<'i>(
1343 parser: &mut Parser<'i, '_>,
1344 ) -> Result<XmlLang, ParseError<'i>> {
1345 let language_tag = parser.expect_ident()?;
1346 let language_tag = LanguageTag::from_str(language_tag).map_err(|_| {
1347 parser.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'xml:lang' parameter"))
1348 })?;
1349 Ok(XmlLang(Some(Box::new(language_tag))))
1350 }
1351 }
1352 },
1353);
1354
1355#[cfg(test)]
1356#[test]
1357fn parses_xml_lang() {
1358 assert_eq!(
1359 XmlLang::parse_str("es-MX").unwrap(),
1360 XmlLang(Some(Box::new(LanguageTag::from_str("es-MX").unwrap())))
1361 );
1362
1363 assert!(XmlLang::parse_str("").is_err());
1364}
1365
1366make_property!(
1367 XmlSpace,
1377 default: Default,
1378 inherits_automatically: true,
1379
1380 identifiers:
1381 "default" => Default,
1382 "preserve" => Preserve,
1383);
1384
1385make_property!(
1386 Y,
1392 default: Length::<Vertical>::default(),
1393 inherits_automatically: false,
1394 newtype_parse: Length<Vertical>,
1395);
1396
1397make_property!(
1398 WhiteSpace,
1402
1403 default: Normal,
1404 inherits_automatically: true,
1405
1406 identifiers:
1407 "normal" => Normal,
1408 "pre" => Pre,
1409 "nowrap" => NoWrap,
1410 "pre-wrap" => PreWrap,
1411 "break-spaces" => BreakSpaces,
1412 "pre-line" => PreLine,
1413);