1
//! Gradient paint servers; the `linearGradient` and `radialGradient` elements.
2

            
3
use cssparser::{Color, Parser};
4
use markup5ever::{
5
    expanded_name, local_name, namespace_url, ns, ExpandedName, LocalName, Namespace,
6
};
7

            
8
use crate::coord_units;
9
use crate::coord_units::CoordUnits;
10
use crate::document::{AcquiredNodes, NodeId, NodeStack};
11
use crate::drawing_ctx::Viewport;
12
use crate::element::{set_attribute, ElementData, ElementTrait};
13
use crate::error::*;
14
use crate::href::{is_href, set_href};
15
use crate::length::*;
16
use crate::node::{CascadedValues, Node, NodeBorrow};
17
use crate::paint_server::resolve_color;
18
use crate::parse_identifiers;
19
use crate::parsers::{Parse, ParseValue};
20
use crate::rect::{rect_to_transform, Rect};
21
use crate::session::Session;
22
use crate::transform::{Transform, TransformAttribute};
23
use crate::unit_interval::UnitInterval;
24
use crate::xml::Attributes;
25

            
26
/// Contents of a `<stop>` element for gradient color stops
27
#[derive(Copy, Clone)]
28
pub struct ColorStop {
29
    /// `<stop offset="..."/>`
30
    pub offset: UnitInterval,
31

            
32
    /// `<stop stop-color="..." stop-opacity="..."/>`
33
    pub color: Color,
34
}
35

            
36
// gradientUnits attribute; its default is objectBoundingBox
37
coord_units!(GradientUnits, CoordUnits::ObjectBoundingBox);
38

            
39
/// spreadMethod attribute for gradients
40
387
#[derive(Debug, Default, Copy, Clone, PartialEq)]
41
pub enum SpreadMethod {
42
    #[default]
43
192
    Pad,
44
    Reflect,
45
    Repeat,
46
}
47

            
48
impl Parse for SpreadMethod {
49
13
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<SpreadMethod, ParseError<'i>> {
50
26
        Ok(parse_identifiers!(
51
            parser,
52
            "pad" => SpreadMethod::Pad,
53
            "reflect" => SpreadMethod::Reflect,
54
            "repeat" => SpreadMethod::Repeat,
55
1
        )?)
56
13
    }
57
}
58

            
59
/// Node for the `<stop>` element
60
854
#[derive(Default)]
61
pub struct Stop {
62
    /// `<stop offset="..."/>`
63
427
    offset: UnitInterval,
64
    /* stop-color and stop-opacity are not attributes; they are properties, so
65
     * they go into property_defs.rs */
66
}
67

            
68
impl ElementTrait for Stop {
69
427
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
70
1525
        for (attr, value) in attrs.iter() {
71
1098
            if attr.expanded() == expanded_name!("", "offset") {
72
427
                set_attribute(&mut self.offset, attr.parse(value), session);
73
            }
74
1098
        }
75
427
    }
76
}
77

            
78
/// Parameters specific to each gradient type, before being resolved.
79
/// These will be composed together with UnreseolvedVariant from fallback
80
/// nodes (referenced with e.g. `<linearGradient xlink:href="#fallback">`) to form
81
/// a final, resolved Variant.
82
#[derive(Copy, Clone)]
83
enum UnresolvedVariant {
84
    Linear {
85
        x1: Option<Length<Horizontal>>,
86
        y1: Option<Length<Vertical>>,
87
        x2: Option<Length<Horizontal>>,
88
        y2: Option<Length<Vertical>>,
89
    },
90

            
91
    Radial {
92
        cx: Option<Length<Horizontal>>,
93
        cy: Option<Length<Vertical>>,
94
        r: Option<Length<Both>>,
95
        fx: Option<Length<Horizontal>>,
96
        fy: Option<Length<Vertical>>,
97
        fr: Option<Length<Both>>,
98
    },
99
}
100

            
101
/// Parameters specific to each gradient type, after resolving.
102
#[derive(Clone)]
103
enum ResolvedGradientVariant {
104
    Linear {
105
        x1: Length<Horizontal>,
106
        y1: Length<Vertical>,
107
        x2: Length<Horizontal>,
108
        y2: Length<Vertical>,
109
    },
110

            
111
    Radial {
112
        cx: Length<Horizontal>,
113
        cy: Length<Vertical>,
114
        r: Length<Both>,
115
        fx: Length<Horizontal>,
116
        fy: Length<Vertical>,
117
        fr: Length<Both>,
118
    },
119
}
120

            
121
/// Parameters specific to each gradient type, after normalizing to user-space units.
122
pub enum GradientVariant {
123
    Linear {
124
        x1: f64,
125
        y1: f64,
126
        x2: f64,
127
        y2: f64,
128
    },
129

            
130
    Radial {
131
        cx: f64,
132
        cy: f64,
133
        r: f64,
134
        fx: f64,
135
        fy: f64,
136
        fr: f64,
137
    },
138
}
139

            
140
impl UnresolvedVariant {
141
199
    fn into_resolved(self) -> ResolvedGradientVariant {
142
199
        assert!(self.is_resolved());
143

            
144
199
        match self {
145
324
            UnresolvedVariant::Linear { x1, y1, x2, y2 } => ResolvedGradientVariant::Linear {
146
162
                x1: x1.unwrap(),
147
162
                y1: y1.unwrap(),
148
162
                x2: x2.unwrap(),
149
162
                y2: y2.unwrap(),
150
162
            },
151

            
152
            UnresolvedVariant::Radial {
153
37
                cx,
154
37
                cy,
155
37
                r,
156
37
                fx,
157
37
                fy,
158
37
                fr,
159
37
            } => ResolvedGradientVariant::Radial {
160
37
                cx: cx.unwrap(),
161
37
                cy: cy.unwrap(),
162
37
                r: r.unwrap(),
163
37
                fx: fx.unwrap(),
164
37
                fy: fy.unwrap(),
165
37
                fr: fr.unwrap(),
166
37
            },
167
        }
168
199
    }
169

            
170
400
    fn is_resolved(&self) -> bool {
171
400
        match *self {
172
325
            UnresolvedVariant::Linear { x1, y1, x2, y2 } => {
173
325
                x1.is_some() && y1.is_some() && x2.is_some() && y2.is_some()
174
            }
175

            
176
            UnresolvedVariant::Radial {
177
75
                cx,
178
75
                cy,
179
75
                r,
180
75
                fx,
181
75
                fy,
182
75
                fr,
183
            } => {
184
75
                cx.is_some()
185
75
                    && cy.is_some()
186
75
                    && r.is_some()
187
75
                    && fx.is_some()
188
75
                    && fy.is_some()
189
75
                    && fr.is_some()
190
            }
191
        }
192
400
    }
193

            
194
45
    fn resolve_from_fallback(&self, fallback: &UnresolvedVariant) -> UnresolvedVariant {
195
45
        match (*self, *fallback) {
196
            (
197
31
                UnresolvedVariant::Linear { x1, y1, x2, y2 },
198
                UnresolvedVariant::Linear {
199
31
                    x1: fx1,
200
31
                    y1: fy1,
201
31
                    x2: fx2,
202
31
                    y2: fy2,
203
                },
204
31
            ) => UnresolvedVariant::Linear {
205
31
                x1: x1.or(fx1),
206
31
                y1: y1.or(fy1),
207
31
                x2: x2.or(fx2),
208
31
                y2: y2.or(fy2),
209
31
            },
210

            
211
            (
212
                UnresolvedVariant::Radial {
213
1
                    cx,
214
1
                    cy,
215
1
                    r,
216
1
                    fx,
217
1
                    fy,
218
1
                    fr,
219
                },
220
                UnresolvedVariant::Radial {
221
1
                    cx: f_cx,
222
1
                    cy: f_cy,
223
1
                    r: f_r,
224
1
                    fx: f_fx,
225
1
                    fy: f_fy,
226
1
                    fr: f_fr,
227
                },
228
1
            ) => UnresolvedVariant::Radial {
229
1
                cx: cx.or(f_cx),
230
1
                cy: cy.or(f_cy),
231
1
                r: r.or(f_r),
232
1
                fx: fx.or(f_fx),
233
1
                fy: fy.or(f_fy),
234
1
                fr: fr.or(f_fr),
235
1
            },
236

            
237
13
            _ => *self, // If variants are of different types, then nothing to resolve
238
        }
239
45
    }
240

            
241
    // https://www.w3.org/TR/SVG/pservers.html#LinearGradients
242
    // https://www.w3.org/TR/SVG/pservers.html#RadialGradients
243
201
    fn resolve_from_defaults(&self) -> UnresolvedVariant {
244
201
        match self {
245
326
            UnresolvedVariant::Linear { x1, y1, x2, y2 } => UnresolvedVariant::Linear {
246
188
                x1: x1.or_else(|| Some(Length::<Horizontal>::parse_str("0%").unwrap())),
247
188
                y1: y1.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
248
188
                x2: x2.or_else(|| Some(Length::<Horizontal>::parse_str("100%").unwrap())),
249
188
                y2: y2.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
250
163
            },
251

            
252
            UnresolvedVariant::Radial {
253
38
                cx,
254
38
                cy,
255
38
                r,
256
38
                fx,
257
38
                fy,
258
38
                fr,
259
            } => {
260
43
                let cx = cx.or_else(|| Some(Length::<Horizontal>::parse_str("50%").unwrap()));
261
43
                let cy = cy.or_else(|| Some(Length::<Vertical>::parse_str("50%").unwrap()));
262
43
                let r = r.or_else(|| Some(Length::<Both>::parse_str("50%").unwrap()));
263

            
264
                // fx and fy fall back to the presentational value of cx and cy
265
38
                let fx = fx.or(cx);
266
38
                let fy = fy.or(cy);
267
74
                let fr = fr.or_else(|| Some(Length::<Both>::parse_str("0%").unwrap()));
268

            
269
38
                UnresolvedVariant::Radial {
270
                    cx,
271
                    cy,
272
                    r,
273
                    fx,
274
                    fy,
275
                    fr,
276
                }
277
38
            }
278
        }
279
201
    }
280
}
281

            
282
/// Fields shared by all gradient nodes
283
646
#[derive(Default)]
284
struct Common {
285
323
    units: Option<GradientUnits>,
286
323
    transform: Option<TransformAttribute>,
287
323
    spread: Option<SpreadMethod>,
288

            
289
323
    fallback: Option<NodeId>,
290
}
291

            
292
/// Node for the `<linearGradient>` element
293
524
#[derive(Default)]
294
pub struct LinearGradient {
295
262
    common: Common,
296

            
297
262
    x1: Option<Length<Horizontal>>,
298
262
    y1: Option<Length<Vertical>>,
299
262
    x2: Option<Length<Horizontal>>,
300
262
    y2: Option<Length<Vertical>>,
301
}
302

            
303
/// Node for the `<radialGradient>` element
304
122
#[derive(Default)]
305
pub struct RadialGradient {
306
61
    common: Common,
307

            
308
61
    cx: Option<Length<Horizontal>>,
309
61
    cy: Option<Length<Vertical>>,
310
61
    r: Option<Length<Both>>,
311
61
    fx: Option<Length<Horizontal>>,
312
61
    fy: Option<Length<Vertical>>,
313
61
    fr: Option<Length<Both>>,
314
}
315

            
316
/// Main structure used during gradient resolution.  For unresolved
317
/// gradients, we store all fields as `Option<T>` - if `None`, it means
318
/// that the field is not specified; if `Some(T)`, it means that the
319
/// field was specified.
320
struct UnresolvedGradient {
321
    units: Option<GradientUnits>,
322
    transform: Option<TransformAttribute>,
323
    spread: Option<SpreadMethod>,
324
    stops: Option<Vec<ColorStop>>,
325

            
326
    variant: UnresolvedVariant,
327
}
328

            
329
/// Resolved gradient; this is memoizable after the initial resolution.
330
#[derive(Clone)]
331
pub struct ResolvedGradient {
332
    units: GradientUnits,
333
    transform: TransformAttribute,
334
    spread: SpreadMethod,
335
    stops: Vec<ColorStop>,
336

            
337
    variant: ResolvedGradientVariant,
338
}
339

            
340
/// Gradient normalized to user-space units.
341
pub struct UserSpaceGradient {
342
    pub transform: Transform,
343
    pub spread: SpreadMethod,
344
    pub stops: Vec<ColorStop>,
345

            
346
    pub variant: GradientVariant,
347
}
348

            
349
impl UnresolvedGradient {
350
199
    fn into_resolved(self) -> ResolvedGradient {
351
199
        assert!(self.is_resolved());
352

            
353
        let UnresolvedGradient {
354
199
            units,
355
199
            transform,
356
199
            spread,
357
199
            stops,
358
199
            variant,
359
        } = self;
360

            
361
199
        match variant {
362
162
            UnresolvedVariant::Linear { .. } => ResolvedGradient {
363
162
                units: units.unwrap(),
364
162
                transform: transform.unwrap(),
365
162
                spread: spread.unwrap(),
366
162
                stops: stops.unwrap(),
367

            
368
162
                variant: variant.into_resolved(),
369
162
            },
370

            
371
37
            UnresolvedVariant::Radial { .. } => ResolvedGradient {
372
37
                units: units.unwrap(),
373
37
                transform: transform.unwrap(),
374
37
                spread: spread.unwrap(),
375
37
                stops: stops.unwrap(),
376

            
377
37
                variant: variant.into_resolved(),
378
37
            },
379
        }
380
199
    }
381

            
382
    /// Helper for add_color_stops_from_node()
383
572
    fn add_color_stop(&mut self, offset: UnitInterval, color: Color) {
384
765
        if self.stops.is_none() {
385
193
            self.stops = Some(Vec::<ColorStop>::new());
386
        }
387

            
388
572
        if let Some(ref mut stops) = self.stops {
389
572
            let last_offset = if !stops.is_empty() {
390
379
                stops[stops.len() - 1].offset
391
            } else {
392
193
                UnitInterval(0.0)
393
            };
394

            
395
572
            let offset = if offset > last_offset {
396
357
                offset
397
            } else {
398
215
                last_offset
399
            };
400

            
401
572
            stops.push(ColorStop { offset, color });
402
        } else {
403
            unreachable!();
404
        }
405
572
    }
406

            
407
    /// Looks for `<stop>` children inside a linearGradient or radialGradient node,
408
    /// and adds their info to the UnresolvedGradient &self.
409
248
    fn add_color_stops_from_node(&mut self, node: &Node, opacity: UnitInterval) {
410
248
        assert!(matches!(
411
248
            *node.borrow_element_data(),
412
            ElementData::LinearGradient(_) | ElementData::RadialGradient(_)
413
        ));
414

            
415
1589
        for child in node.children().filter(|c| c.is_element()) {
416
572
            if let ElementData::Stop(ref stop) = &*child.borrow_element_data() {
417
572
                let cascaded = CascadedValues::new_from_node(&child);
418
572
                let values = cascaded.get();
419

            
420
572
                let UnitInterval(stop_opacity) = values.stop_opacity().0;
421
572
                let UnitInterval(o) = opacity;
422

            
423
572
                let composed_opacity = UnitInterval(stop_opacity * o);
424

            
425
                let stop_color =
426
572
                    resolve_color(&values.stop_color().0, composed_opacity, &values.color().0);
427

            
428
572
                self.add_color_stop(stop.offset, stop_color);
429
572
            }
430
572
        }
431
248
    }
432

            
433
447
    fn is_resolved(&self) -> bool {
434
662
        self.units.is_some()
435
416
            && self.transform.is_some()
436
268
            && self.spread.is_some()
437
201
            && self.stops.is_some()
438
201
            && self.variant.is_resolved()
439
447
    }
440

            
441
45
    fn resolve_from_fallback(&self, fallback: &UnresolvedGradient) -> UnresolvedGradient {
442
45
        let units = self.units.or(fallback.units);
443
45
        let transform = self.transform.or(fallback.transform);
444
45
        let spread = self.spread.or(fallback.spread);
445
90
        let stops = self.stops.clone().or_else(|| fallback.stops.clone());
446
45
        let variant = self.variant.resolve_from_fallback(&fallback.variant);
447

            
448
45
        UnresolvedGradient {
449
            units,
450
            transform,
451
            spread,
452
45
            stops,
453
            variant,
454
        }
455
45
    }
456

            
457
201
    fn resolve_from_defaults(&self) -> UnresolvedGradient {
458
229
        let units = self.units.or_else(|| Some(GradientUnits::default()));
459
201
        let transform = self
460
            .transform
461
165
            .or_else(|| Some(TransformAttribute::default()));
462
393
        let spread = self.spread.or_else(|| Some(SpreadMethod::default()));
463
209
        let stops = self.stops.clone().or_else(|| Some(Vec::<ColorStop>::new()));
464
201
        let variant = self.variant.resolve_from_defaults();
465

            
466
201
        UnresolvedGradient {
467
            units,
468
            transform,
469
            spread,
470
201
            stops,
471
            variant,
472
        }
473
201
    }
474
}
475

            
476
/// State used during the gradient resolution process
477
///
478
/// This is the current node's gradient information, plus the fallback
479
/// that should be used in case that information is not complete for a
480
/// resolved gradient yet.
481
struct Unresolved {
482
    gradient: UnresolvedGradient,
483
    fallback: Option<NodeId>,
484
}
485

            
486
impl LinearGradient {
487
207
    fn get_unresolved_variant(&self) -> UnresolvedVariant {
488
207
        UnresolvedVariant::Linear {
489
207
            x1: self.x1,
490
207
            y1: self.y1,
491
207
            x2: self.x2,
492
207
            y2: self.y2,
493
        }
494
207
    }
495
}
496

            
497
impl RadialGradient {
498
41
    fn get_unresolved_variant(&self) -> UnresolvedVariant {
499
41
        UnresolvedVariant::Radial {
500
41
            cx: self.cx,
501
41
            cy: self.cy,
502
41
            r: self.r,
503
41
            fx: self.fx,
504
41
            fy: self.fy,
505
41
            fr: self.fr,
506
        }
507
41
    }
508
}
509

            
510
impl Common {
511
323
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
512
1906
        for (attr, value) in attrs.iter() {
513
1583
            match attr.expanded() {
514
                expanded_name!("", "gradientUnits") => {
515
148
                    set_attribute(&mut self.units, attr.parse(value), session)
516
                }
517
                expanded_name!("", "gradientTransform") => {
518
170
                    set_attribute(&mut self.transform, attr.parse(value), session);
519
                }
520
                expanded_name!("", "spreadMethod") => {
521
9
                    set_attribute(&mut self.spread, attr.parse(value), session)
522
                }
523
1256
                ref a if is_href(a) => {
524
88
                    let mut href = None;
525
88
                    set_attribute(
526
                        &mut href,
527
88
                        NodeId::parse(value).map(Some).attribute(attr.clone()),
528
                        session,
529
                    );
530
88
                    set_href(a, &mut self.fallback, href);
531
88
                }
532
                _ => (),
533
            }
534
1583
        }
535
323
    }
536
}
537

            
538
impl ElementTrait for LinearGradient {
539
262
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
540
262
        self.common.set_attributes(attrs, session);
541

            
542
1378
        for (attr, value) in attrs.iter() {
543
1116
            match attr.expanded() {
544
98
                expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
545
98
                expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
546
98
                expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
547
98
                expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
548

            
549
                _ => (),
550
            }
551
1116
        }
552
262
    }
553
}
554

            
555
macro_rules! impl_gradient {
556
    ($gradient_type:ident, $other_type:ident) => {
557
        impl $gradient_type {
558
248
            fn get_unresolved(&self, node: &Node, opacity: UnitInterval) -> Unresolved {
559
248
                let mut gradient = UnresolvedGradient {
560
248
                    units: self.common.units,
561
248
                    transform: self.common.transform,
562
248
                    spread: self.common.spread,
563
248
                    stops: None,
564
248
                    variant: self.get_unresolved_variant(),
565
                };
566

            
567
248
                gradient.add_color_stops_from_node(node, opacity);
568

            
569
248
                Unresolved {
570
248
                    gradient,
571
248
                    fallback: self.common.fallback.clone(),
572
                }
573
248
            }
574

            
575
291
            pub fn resolve(
576
                &self,
577
                node: &Node,
578
                acquired_nodes: &mut AcquiredNodes<'_>,
579
                opacity: UnitInterval,
580
            ) -> Result<ResolvedGradient, AcquireError> {
581
                let Unresolved {
582
291
                    mut gradient,
583
291
                    mut fallback,
584
291
                } = self.get_unresolved(node, opacity);
585

            
586
291
                let mut stack = NodeStack::new();
587

            
588
291
                while !gradient.is_resolved() {
589
246
                    if let Some(node_id) = fallback {
590
47
                        let acquired = acquired_nodes.acquire(&node_id)?;
591
45
                        let acquired_node = acquired.get();
592

            
593
45
                        if stack.contains(acquired_node) {
594
                            return Err(AcquireError::CircularReference(acquired_node.clone()));
595
                        }
596

            
597
45
                        let unresolved = match *acquired_node.borrow_element_data() {
598
32
                            ElementData::$gradient_type(ref g) => {
599
32
                                g.get_unresolved(&acquired_node, opacity)
600
32
                            }
601
13
                            ElementData::$other_type(ref g) => {
602
13
                                g.get_unresolved(&acquired_node, opacity)
603
13
                            }
604
                            _ => return Err(AcquireError::InvalidLinkType(node_id.clone())),
605
45
                        };
606

            
607
45
                        gradient = gradient.resolve_from_fallback(&unresolved.gradient);
608
45
                        fallback = unresolved.fallback;
609

            
610
45
                        stack.push(acquired_node);
611
47
                    } else {
612
199
                        gradient = gradient.resolve_from_defaults();
613
199
                        break;
614
                    }
615
                }
616

            
617
199
                Ok(gradient.into_resolved())
618
201
            }
619
        }
620
    };
621
}
622

            
623
impl_gradient!(LinearGradient, RadialGradient);
624
impl_gradient!(RadialGradient, LinearGradient);
625

            
626
impl ElementTrait for RadialGradient {
627
61
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
628
61
        self.common.set_attributes(attrs, session);
629

            
630
        // Create a local expanded name for "fr" because markup5ever doesn't have built-in
631
61
        let expanded_name_fr = ExpandedName {
632
61
            ns: &Namespace::from(""),
633
61
            local: &LocalName::from("fr"),
634
        };
635

            
636
61
        for (attr, value) in attrs.iter() {
637
467
            let attr_expanded = attr.expanded();
638
467
            match attr_expanded {
639
53
                expanded_name!("", "cx") => set_attribute(&mut self.cx, attr.parse(value), session),
640
53
                expanded_name!("", "cy") => set_attribute(&mut self.cy, attr.parse(value), session),
641
53
                expanded_name!("", "r") => set_attribute(&mut self.r, attr.parse(value), session),
642
47
                expanded_name!("", "fx") => set_attribute(&mut self.fx, attr.parse(value), session),
643
47
                expanded_name!("", "fy") => set_attribute(&mut self.fy, attr.parse(value), session),
644
214
                a if a == expanded_name_fr => {
645
2
                    set_attribute(&mut self.fr, attr.parse(value), session)
646
                }
647

            
648
                _ => (),
649
            }
650
467
        }
651
61
    }
652
}
653

            
654
impl ResolvedGradient {
655
198
    pub fn to_user_space(
656
        &self,
657
        object_bbox: &Option<Rect>,
658
        viewport: &Viewport,
659
        values: &NormalizeValues,
660
    ) -> Option<UserSpaceGradient> {
661
198
        let units = self.units.0;
662
198
        let transform = rect_to_transform(object_bbox, units).ok()?;
663
195
        let viewport = viewport.with_units(units);
664
195
        let params = NormalizeParams::from_values(values, &viewport);
665

            
666
195
        let gradient_transform = self.transform.to_transform();
667
198
        let transform = transform.pre_transform(&gradient_transform).invert()?;
668

            
669
194
        let variant = match self.variant {
670
316
            ResolvedGradientVariant::Linear { x1, y1, x2, y2 } => GradientVariant::Linear {
671
158
                x1: x1.to_user(&params),
672
158
                y1: y1.to_user(&params),
673
158
                x2: x2.to_user(&params),
674
158
                y2: y2.to_user(&params),
675
158
            },
676

            
677
            ResolvedGradientVariant::Radial {
678
36
                cx,
679
36
                cy,
680
36
                r,
681
36
                fx,
682
36
                fy,
683
36
                fr,
684
36
            } => GradientVariant::Radial {
685
36
                cx: cx.to_user(&params),
686
36
                cy: cy.to_user(&params),
687
36
                r: r.to_user(&params),
688
36
                fx: fx.to_user(&params),
689
36
                fy: fy.to_user(&params),
690
36
                fr: fr.to_user(&params),
691
36
            },
692
        };
693

            
694
194
        Some(UserSpaceGradient {
695
            transform,
696
194
            spread: self.spread,
697
194
            stops: self.stops.clone(),
698
194
            variant,
699
        })
700
198
    }
701
}
702

            
703
#[cfg(test)]
704
mod tests {
705
    use super::*;
706

            
707
    use markup5ever::{namespace_url, ns, QualName};
708

            
709
    use crate::borrow_element_as;
710
    use crate::node::{Node, NodeData};
711

            
712
    #[test]
713
2
    fn parses_spread_method() {
714
1
        assert_eq!(SpreadMethod::parse_str("pad").unwrap(), SpreadMethod::Pad);
715
1
        assert_eq!(
716
1
            SpreadMethod::parse_str("reflect").unwrap(),
717
            SpreadMethod::Reflect
718
        );
719
1
        assert_eq!(
720
1
            SpreadMethod::parse_str("repeat").unwrap(),
721
            SpreadMethod::Repeat
722
        );
723
1
        assert!(SpreadMethod::parse_str("foobar").is_err());
724
2
    }
725

            
726
    #[test]
727
2
    fn gradient_resolved_from_defaults_is_really_resolved() {
728
1
        let session = Session::default();
729

            
730
1
        let node = Node::new(NodeData::new_element(
731
            &session,
732
1
            &QualName::new(None, ns!(svg), local_name!("linearGradient")),
733
1
            Attributes::new(),
734
1
        ));
735

            
736
1
        let unresolved = borrow_element_as!(node, LinearGradient)
737
2
            .get_unresolved(&node, UnitInterval::clamp(1.0));
738
1
        let gradient = unresolved.gradient.resolve_from_defaults();
739
1
        assert!(gradient.is_resolved());
740

            
741
1
        let node = Node::new(NodeData::new_element(
742
            &session,
743
1
            &QualName::new(None, ns!(svg), local_name!("radialGradient")),
744
1
            Attributes::new(),
745
1
        ));
746

            
747
1
        let unresolved = borrow_element_as!(node, RadialGradient)
748
2
            .get_unresolved(&node, UnitInterval::clamp(1.0));
749
1
        let gradient = unresolved.gradient.resolve_from_defaults();
750
1
        assert!(gradient.is_resolved());
751
2
    }
752
}