1use cssparser::{Color, Parser};
4use markup5ever::{expanded_name, local_name, ns, ExpandedName, LocalName, Namespace};
5
6use crate::coord_units;
7use crate::coord_units::CoordUnits;
8use crate::document::{AcquiredNodes, NodeId, NodeStack};
9use crate::drawing_ctx::Viewport;
10use crate::element::{set_attribute, ElementData, ElementTrait};
11use crate::error::*;
12use crate::href::{is_href, set_href};
13use crate::length::*;
14use crate::node::{CascadedValues, Node, NodeBorrow};
15use crate::paint_server::resolve_color;
16use crate::parse_identifiers;
17use crate::parsers::{Parse, ParseValue};
18use crate::rect::{rect_to_transform, Rect};
19use crate::session::Session;
20use crate::transform::{Transform, TransformAttribute};
21use crate::unit_interval::UnitInterval;
22use crate::xml::Attributes;
23
24#[derive(Copy, Clone)]
26pub struct ColorStop {
27 pub offset: UnitInterval,
29
30 pub color: Color,
32}
33
34coord_units!(GradientUnits, CoordUnits::ObjectBoundingBox);
36
37#[derive(Debug, Default, Copy, Clone, PartialEq)]
39pub enum SpreadMethod {
40 #[default]
41 Pad,
42 Reflect,
43 Repeat,
44}
45
46impl Parse for SpreadMethod {
47 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<SpreadMethod, ParseError<'i>> {
48 Ok(parse_identifiers!(
49 parser,
50 "pad" => SpreadMethod::Pad,
51 "reflect" => SpreadMethod::Reflect,
52 "repeat" => SpreadMethod::Repeat,
53 )?)
54 }
55}
56
57#[derive(Default)]
59pub struct Stop {
60 offset: UnitInterval,
62 }
65
66impl ElementTrait for Stop {
67 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
68 for (attr, value) in attrs.iter() {
69 if attr.expanded() == expanded_name!("", "offset") {
70 set_attribute(&mut self.offset, attr.parse(value), session);
71 }
72 }
73 }
74}
75
76#[derive(Copy, Clone)]
81enum UnresolvedVariant {
82 Linear {
83 x1: Option<Length<Horizontal>>,
84 y1: Option<Length<Vertical>>,
85 x2: Option<Length<Horizontal>>,
86 y2: Option<Length<Vertical>>,
87 },
88
89 Radial {
90 cx: Option<Length<Horizontal>>,
91 cy: Option<Length<Vertical>>,
92 r: Option<Length<Both>>,
93 fx: Option<Length<Horizontal>>,
94 fy: Option<Length<Vertical>>,
95 fr: Option<Length<Both>>,
96 },
97}
98
99#[derive(Clone)]
101enum ResolvedGradientVariant {
102 Linear {
103 x1: Length<Horizontal>,
104 y1: Length<Vertical>,
105 x2: Length<Horizontal>,
106 y2: Length<Vertical>,
107 },
108
109 Radial {
110 cx: Length<Horizontal>,
111 cy: Length<Vertical>,
112 r: Length<Both>,
113 fx: Length<Horizontal>,
114 fy: Length<Vertical>,
115 fr: Length<Both>,
116 },
117}
118
119pub enum GradientVariant {
121 Linear {
122 x1: f64,
123 y1: f64,
124 x2: f64,
125 y2: f64,
126 },
127
128 Radial {
129 cx: f64,
130 cy: f64,
131 r: f64,
132 fx: f64,
133 fy: f64,
134 fr: f64,
135 },
136}
137
138impl UnresolvedVariant {
139 fn into_resolved(self) -> ResolvedGradientVariant {
140 assert!(self.is_resolved());
141
142 match self {
143 UnresolvedVariant::Linear { x1, y1, x2, y2 } => ResolvedGradientVariant::Linear {
144 x1: x1.unwrap(),
145 y1: y1.unwrap(),
146 x2: x2.unwrap(),
147 y2: y2.unwrap(),
148 },
149
150 UnresolvedVariant::Radial {
151 cx,
152 cy,
153 r,
154 fx,
155 fy,
156 fr,
157 } => ResolvedGradientVariant::Radial {
158 cx: cx.unwrap(),
159 cy: cy.unwrap(),
160 r: r.unwrap(),
161 fx: fx.unwrap(),
162 fy: fy.unwrap(),
163 fr: fr.unwrap(),
164 },
165 }
166 }
167
168 fn is_resolved(&self) -> bool {
169 match *self {
170 UnresolvedVariant::Linear { x1, y1, x2, y2 } => {
171 x1.is_some() && y1.is_some() && x2.is_some() && y2.is_some()
172 }
173
174 UnresolvedVariant::Radial {
175 cx,
176 cy,
177 r,
178 fx,
179 fy,
180 fr,
181 } => {
182 cx.is_some()
183 && cy.is_some()
184 && r.is_some()
185 && fx.is_some()
186 && fy.is_some()
187 && fr.is_some()
188 }
189 }
190 }
191
192 fn resolve_from_fallback(&self, fallback: &UnresolvedVariant) -> UnresolvedVariant {
193 match (*self, *fallback) {
194 (
195 UnresolvedVariant::Linear { x1, y1, x2, y2 },
196 UnresolvedVariant::Linear {
197 x1: fx1,
198 y1: fy1,
199 x2: fx2,
200 y2: fy2,
201 },
202 ) => UnresolvedVariant::Linear {
203 x1: x1.or(fx1),
204 y1: y1.or(fy1),
205 x2: x2.or(fx2),
206 y2: y2.or(fy2),
207 },
208
209 (
210 UnresolvedVariant::Radial {
211 cx,
212 cy,
213 r,
214 fx,
215 fy,
216 fr,
217 },
218 UnresolvedVariant::Radial {
219 cx: f_cx,
220 cy: f_cy,
221 r: f_r,
222 fx: f_fx,
223 fy: f_fy,
224 fr: f_fr,
225 },
226 ) => UnresolvedVariant::Radial {
227 cx: cx.or(f_cx),
228 cy: cy.or(f_cy),
229 r: r.or(f_r),
230 fx: fx.or(f_fx),
231 fy: fy.or(f_fy),
232 fr: fr.or(f_fr),
233 },
234
235 _ => *self, }
237 }
238
239 fn resolve_from_defaults(&self) -> UnresolvedVariant {
242 match self {
243 UnresolvedVariant::Linear { x1, y1, x2, y2 } => UnresolvedVariant::Linear {
244 x1: x1.or_else(|| Some(Length::<Horizontal>::parse_str("0%").unwrap())),
245 y1: y1.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
246 x2: x2.or_else(|| Some(Length::<Horizontal>::parse_str("100%").unwrap())),
247 y2: y2.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
248 },
249
250 UnresolvedVariant::Radial {
251 cx,
252 cy,
253 r,
254 fx,
255 fy,
256 fr,
257 } => {
258 let cx = cx.or_else(|| Some(Length::<Horizontal>::parse_str("50%").unwrap()));
259 let cy = cy.or_else(|| Some(Length::<Vertical>::parse_str("50%").unwrap()));
260 let r = r.or_else(|| Some(Length::<Both>::parse_str("50%").unwrap()));
261
262 let fx = fx.or(cx);
264 let fy = fy.or(cy);
265 let fr = fr.or_else(|| Some(Length::<Both>::parse_str("0%").unwrap()));
266
267 UnresolvedVariant::Radial {
268 cx,
269 cy,
270 r,
271 fx,
272 fy,
273 fr,
274 }
275 }
276 }
277 }
278}
279
280#[derive(Default)]
282struct Common {
283 units: Option<GradientUnits>,
284 transform: Option<TransformAttribute>,
285 spread: Option<SpreadMethod>,
286
287 fallback: Option<NodeId>,
288}
289
290#[derive(Default)]
292pub struct LinearGradient {
293 common: Common,
294
295 x1: Option<Length<Horizontal>>,
296 y1: Option<Length<Vertical>>,
297 x2: Option<Length<Horizontal>>,
298 y2: Option<Length<Vertical>>,
299}
300
301#[derive(Default)]
303pub struct RadialGradient {
304 common: Common,
305
306 cx: Option<Length<Horizontal>>,
307 cy: Option<Length<Vertical>>,
308 r: Option<Length<Both>>,
309 fx: Option<Length<Horizontal>>,
310 fy: Option<Length<Vertical>>,
311 fr: Option<Length<Both>>,
312}
313
314struct UnresolvedGradient {
319 units: Option<GradientUnits>,
320 transform: Option<TransformAttribute>,
321 spread: Option<SpreadMethod>,
322 stops: Option<Vec<ColorStop>>,
323
324 variant: UnresolvedVariant,
325}
326
327#[derive(Clone)]
329pub struct ResolvedGradient {
330 units: GradientUnits,
331 transform: TransformAttribute,
332 spread: SpreadMethod,
333 stops: Vec<ColorStop>,
334
335 variant: ResolvedGradientVariant,
336}
337
338pub struct UserSpaceGradient {
340 pub transform: Transform,
341 pub spread: SpreadMethod,
342 pub stops: Vec<ColorStop>,
343
344 pub variant: GradientVariant,
345}
346
347impl UnresolvedGradient {
348 fn into_resolved(self) -> ResolvedGradient {
349 assert!(self.is_resolved());
350
351 let UnresolvedGradient {
352 units,
353 transform,
354 spread,
355 stops,
356 variant,
357 } = self;
358
359 match variant {
360 UnresolvedVariant::Linear { .. } => ResolvedGradient {
361 units: units.unwrap(),
362 transform: transform.unwrap(),
363 spread: spread.unwrap(),
364 stops: stops.unwrap(),
365
366 variant: variant.into_resolved(),
367 },
368
369 UnresolvedVariant::Radial { .. } => ResolvedGradient {
370 units: units.unwrap(),
371 transform: transform.unwrap(),
372 spread: spread.unwrap(),
373 stops: stops.unwrap(),
374
375 variant: variant.into_resolved(),
376 },
377 }
378 }
379
380 fn add_color_stop(&mut self, offset: UnitInterval, color: Color) {
382 if self.stops.is_none() {
383 self.stops = Some(Vec::<ColorStop>::new());
384 }
385
386 if let Some(ref mut stops) = self.stops {
387 let last_offset = if !stops.is_empty() {
388 stops[stops.len() - 1].offset
389 } else {
390 UnitInterval(0.0)
391 };
392
393 let offset = if offset > last_offset {
394 offset
395 } else {
396 last_offset
397 };
398
399 stops.push(ColorStop { offset, color });
400 } else {
401 unreachable!();
402 }
403 }
404
405 fn add_color_stops_from_node(&mut self, node: &Node, opacity: UnitInterval) {
408 assert!(matches!(
409 *node.borrow_element_data(),
410 ElementData::LinearGradient(_) | ElementData::RadialGradient(_)
411 ));
412
413 for child in node.children().filter(|c| c.is_element()) {
414 if let ElementData::Stop(ref stop) = &*child.borrow_element_data() {
415 let cascaded = CascadedValues::new_from_node(&child);
416 let values = cascaded.get();
417
418 let UnitInterval(stop_opacity) = values.stop_opacity().0;
419 let UnitInterval(o) = opacity;
420
421 let composed_opacity = UnitInterval(stop_opacity * o);
422
423 let stop_color =
424 resolve_color(&values.stop_color().0, composed_opacity, &values.color().0);
425
426 self.add_color_stop(stop.offset, stop_color);
427 }
428 }
429 }
430
431 fn is_resolved(&self) -> bool {
432 self.units.is_some()
433 && self.transform.is_some()
434 && self.spread.is_some()
435 && self.stops.is_some()
436 && self.variant.is_resolved()
437 }
438
439 fn resolve_from_fallback(&self, fallback: &UnresolvedGradient) -> UnresolvedGradient {
440 let units = self.units.or(fallback.units);
441 let transform = self.transform.or(fallback.transform);
442 let spread = self.spread.or(fallback.spread);
443 let stops = self.stops.clone().or_else(|| fallback.stops.clone());
444 let variant = self.variant.resolve_from_fallback(&fallback.variant);
445
446 UnresolvedGradient {
447 units,
448 transform,
449 spread,
450 stops,
451 variant,
452 }
453 }
454
455 fn resolve_from_defaults(&self) -> UnresolvedGradient {
456 let units = self.units.or_else(|| Some(GradientUnits::default()));
457 let transform = self
458 .transform
459 .or_else(|| Some(TransformAttribute::default()));
460 let spread = self.spread.or_else(|| Some(SpreadMethod::default()));
461 let stops = self.stops.clone().or_else(|| Some(Vec::<ColorStop>::new()));
462 let variant = self.variant.resolve_from_defaults();
463
464 UnresolvedGradient {
465 units,
466 transform,
467 spread,
468 stops,
469 variant,
470 }
471 }
472}
473
474struct Unresolved {
480 gradient: UnresolvedGradient,
481 fallback: Option<NodeId>,
482}
483
484impl LinearGradient {
485 fn get_unresolved_variant(&self) -> UnresolvedVariant {
486 UnresolvedVariant::Linear {
487 x1: self.x1,
488 y1: self.y1,
489 x2: self.x2,
490 y2: self.y2,
491 }
492 }
493}
494
495impl RadialGradient {
496 fn get_unresolved_variant(&self) -> UnresolvedVariant {
497 UnresolvedVariant::Radial {
498 cx: self.cx,
499 cy: self.cy,
500 r: self.r,
501 fx: self.fx,
502 fy: self.fy,
503 fr: self.fr,
504 }
505 }
506}
507
508impl Common {
509 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
510 for (attr, value) in attrs.iter() {
511 match attr.expanded() {
512 expanded_name!("", "gradientUnits") => {
513 set_attribute(&mut self.units, attr.parse(value), session)
514 }
515 expanded_name!("", "gradientTransform") => {
516 set_attribute(&mut self.transform, attr.parse(value), session);
517 }
518 expanded_name!("", "spreadMethod") => {
519 set_attribute(&mut self.spread, attr.parse(value), session)
520 }
521 ref a if is_href(a) => {
522 let mut href = None;
523 set_attribute(
524 &mut href,
525 NodeId::parse(value).map(Some).attribute(attr.clone()),
526 session,
527 );
528 set_href(a, &mut self.fallback, href);
529 }
530 _ => (),
531 }
532 }
533 }
534}
535
536impl ElementTrait for LinearGradient {
537 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
538 self.common.set_attributes(attrs, session);
539
540 for (attr, value) in attrs.iter() {
541 match attr.expanded() {
542 expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
543 expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
544 expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
545 expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
546
547 _ => (),
548 }
549 }
550 }
551}
552
553macro_rules! impl_gradient {
554 ($gradient_type:ident, $other_type:ident) => {
555 impl $gradient_type {
556 fn get_unresolved(&self, node: &Node, opacity: UnitInterval) -> Unresolved {
557 let mut gradient = UnresolvedGradient {
558 units: self.common.units,
559 transform: self.common.transform,
560 spread: self.common.spread,
561 stops: None,
562 variant: self.get_unresolved_variant(),
563 };
564
565 gradient.add_color_stops_from_node(node, opacity);
566
567 Unresolved {
568 gradient,
569 fallback: self.common.fallback.clone(),
570 }
571 }
572
573 pub fn resolve(
574 &self,
575 node: &Node,
576 acquired_nodes: &mut AcquiredNodes<'_>,
577 opacity: UnitInterval,
578 ) -> Result<ResolvedGradient, AcquireError> {
579 let Unresolved {
580 mut gradient,
581 mut fallback,
582 } = self.get_unresolved(node, opacity);
583
584 let mut stack = NodeStack::new();
585
586 while !gradient.is_resolved() {
587 if let Some(node_id) = fallback {
588 let acquired = acquired_nodes.acquire(&node_id)?;
589 let acquired_node = acquired.get();
590
591 if stack.contains(acquired_node) {
592 return Err(AcquireError::CircularReference(acquired_node.clone()));
593 }
594
595 let unresolved = match *acquired_node.borrow_element_data() {
596 ElementData::$gradient_type(ref g) => {
597 g.get_unresolved(&acquired_node, opacity)
598 }
599 ElementData::$other_type(ref g) => {
600 g.get_unresolved(&acquired_node, opacity)
601 }
602 _ => return Err(AcquireError::InvalidLinkType(node_id.clone())),
603 };
604
605 gradient = gradient.resolve_from_fallback(&unresolved.gradient);
606 fallback = unresolved.fallback;
607
608 stack.push(acquired_node);
609 } else {
610 gradient = gradient.resolve_from_defaults();
611 break;
612 }
613 }
614
615 Ok(gradient.into_resolved())
616 }
617 }
618 };
619}
620
621impl_gradient!(LinearGradient, RadialGradient);
622impl_gradient!(RadialGradient, LinearGradient);
623
624impl ElementTrait for RadialGradient {
625 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
626 self.common.set_attributes(attrs, session);
627
628 let expanded_name_fr = ExpandedName {
630 ns: &Namespace::from(""),
631 local: &LocalName::from("fr"),
632 };
633
634 for (attr, value) in attrs.iter() {
635 let attr_expanded = attr.expanded();
636 match attr_expanded {
637 expanded_name!("", "cx") => set_attribute(&mut self.cx, attr.parse(value), session),
638 expanded_name!("", "cy") => set_attribute(&mut self.cy, attr.parse(value), session),
639 expanded_name!("", "r") => set_attribute(&mut self.r, attr.parse(value), session),
640 expanded_name!("", "fx") => set_attribute(&mut self.fx, attr.parse(value), session),
641 expanded_name!("", "fy") => set_attribute(&mut self.fy, attr.parse(value), session),
642 a if a == expanded_name_fr => {
643 set_attribute(&mut self.fr, attr.parse(value), session)
644 }
645
646 _ => (),
647 }
648 }
649 }
650}
651
652impl ResolvedGradient {
653 pub fn to_user_space(
654 &self,
655 object_bbox: &Option<Rect>,
656 viewport: &Viewport,
657 values: &NormalizeValues,
658 ) -> Option<UserSpaceGradient> {
659 let units = self.units.0;
660 let transform = rect_to_transform(object_bbox, units).ok()?;
661 let viewport = viewport.with_units(units);
662 let params = NormalizeParams::from_values(values, &viewport);
663
664 let gradient_transform = self.transform.to_transform();
665 let transform = transform.pre_transform(&gradient_transform).invert()?;
666
667 let variant = match self.variant {
668 ResolvedGradientVariant::Linear { x1, y1, x2, y2 } => GradientVariant::Linear {
669 x1: x1.to_user(¶ms),
670 y1: y1.to_user(¶ms),
671 x2: x2.to_user(¶ms),
672 y2: y2.to_user(¶ms),
673 },
674
675 ResolvedGradientVariant::Radial {
676 cx,
677 cy,
678 r,
679 fx,
680 fy,
681 fr,
682 } => GradientVariant::Radial {
683 cx: cx.to_user(¶ms),
684 cy: cy.to_user(¶ms),
685 r: r.to_user(¶ms),
686 fx: fx.to_user(¶ms),
687 fy: fy.to_user(¶ms),
688 fr: fr.to_user(¶ms),
689 },
690 };
691
692 Some(UserSpaceGradient {
693 transform,
694 spread: self.spread,
695 stops: self.stops.clone(),
696 variant,
697 })
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 use markup5ever::{ns, QualName};
706
707 use crate::borrow_element_as;
708 use crate::node::{Node, NodeData};
709
710 #[test]
711 fn parses_spread_method() {
712 assert_eq!(SpreadMethod::parse_str("pad").unwrap(), SpreadMethod::Pad);
713 assert_eq!(
714 SpreadMethod::parse_str("reflect").unwrap(),
715 SpreadMethod::Reflect
716 );
717 assert_eq!(
718 SpreadMethod::parse_str("repeat").unwrap(),
719 SpreadMethod::Repeat
720 );
721 assert!(SpreadMethod::parse_str("foobar").is_err());
722 }
723
724 #[test]
725 fn gradient_resolved_from_defaults_is_really_resolved() {
726 let session = Session::default();
727
728 let node = Node::new(NodeData::new_element(
729 &session,
730 &QualName::new(None, ns!(svg), local_name!("linearGradient")),
731 Attributes::new(),
732 ));
733
734 let unresolved = borrow_element_as!(node, LinearGradient)
735 .get_unresolved(&node, UnitInterval::clamp(1.0));
736 let gradient = unresolved.gradient.resolve_from_defaults();
737 assert!(gradient.is_resolved());
738
739 let node = Node::new(NodeData::new_element(
740 &session,
741 &QualName::new(None, ns!(svg), local_name!("radialGradient")),
742 Attributes::new(),
743 ));
744
745 let unresolved = borrow_element_as!(node, RadialGradient)
746 .get_unresolved(&node, UnitInterval::clamp(1.0));
747 let gradient = unresolved.gradient.resolve_from_defaults();
748 assert!(gradient.is_resolved());
749 }
750}