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

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

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

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

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

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

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

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

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

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

            
76
/// Parameters specific to each gradient type, before being resolved.
77
/// These will be composed together with UnreseolvedVariant from fallback
78
/// nodes (referenced with e.g. `<linearGradient xlink:href="#fallback">`) to form
79
/// a final, resolved Variant.
80
#[derive(Copy, Clone)]
81
enum 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
/// Parameters specific to each gradient type, after resolving.
100
#[derive(Clone)]
101
enum 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

            
119
/// Parameters specific to each gradient type, after normalizing to user-space units.
120
pub 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

            
138
impl UnresolvedVariant {
139
3363
    fn into_resolved(self) -> ResolvedGradientVariant {
140
3363
        assert!(self.is_resolved());
141

            
142
3363
        match self {
143
2717
            UnresolvedVariant::Linear { x1, y1, x2, y2 } => ResolvedGradientVariant::Linear {
144
2717
                x1: x1.unwrap(),
145
2717
                y1: y1.unwrap(),
146
2717
                x2: x2.unwrap(),
147
2717
                y2: y2.unwrap(),
148
2717
            },
149

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

            
168
6728
    fn is_resolved(&self) -> bool {
169
6728
        match *self {
170
5435
            UnresolvedVariant::Linear { x1, y1, x2, y2 } => {
171
5435
                x1.is_some() && y1.is_some() && x2.is_some() && y2.is_some()
172
            }
173

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

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

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

            
235
171
            _ => *self, // If variants are of different types, then nothing to resolve
236
        }
237
418
    }
238

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

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

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

            
267
647
                UnresolvedVariant::Radial {
268
647
                    cx,
269
647
                    cy,
270
647
                    r,
271
647
                    fx,
272
647
                    fy,
273
647
                    fr,
274
647
                }
275
            }
276
        }
277
3365
    }
278
}
279

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

            
287
    fallback: Option<NodeId>,
288
}
289

            
290
/// Node for the `<linearGradient>` element
291
#[derive(Default)]
292
pub 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
/// Node for the `<radialGradient>` element
302
#[derive(Default)]
303
pub 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

            
314
/// Main structure used during gradient resolution.  For unresolved
315
/// gradients, we store all fields as `Option<T>` - if `None`, it means
316
/// that the field is not specified; if `Some(T)`, it means that the
317
/// field was specified.
318
struct 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
/// Resolved gradient; this is memoizable after the initial resolution.
328
#[derive(Clone)]
329
pub struct ResolvedGradient {
330
    units: GradientUnits,
331
    transform: TransformAttribute,
332
    spread: SpreadMethod,
333
    stops: Vec<ColorStop>,
334

            
335
    variant: ResolvedGradientVariant,
336
}
337

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

            
344
    pub variant: GradientVariant,
345
}
346

            
347
impl UnresolvedGradient {
348
3363
    fn into_resolved(self) -> ResolvedGradient {
349
3363
        assert!(self.is_resolved());
350

            
351
        let UnresolvedGradient {
352
3363
            units,
353
3363
            transform,
354
3363
            spread,
355
3363
            stops,
356
3363
            variant,
357
3363
        } = self;
358
3363

            
359
3363
        match variant {
360
2717
            UnresolvedVariant::Linear { .. } => ResolvedGradient {
361
2717
                units: units.unwrap(),
362
2717
                transform: transform.unwrap(),
363
2717
                spread: spread.unwrap(),
364
2717
                stops: stops.unwrap(),
365
2717

            
366
2717
                variant: variant.into_resolved(),
367
2717
            },
368

            
369
646
            UnresolvedVariant::Radial { .. } => ResolvedGradient {
370
646
                units: units.unwrap(),
371
646
                transform: transform.unwrap(),
372
646
                spread: spread.unwrap(),
373
646
                stops: stops.unwrap(),
374
646

            
375
646
                variant: variant.into_resolved(),
376
646
            },
377
        }
378
3363
    }
379

            
380
    /// Helper for add_color_stops_from_node()
381
10032
    fn add_color_stop(&mut self, offset: UnitInterval, color: Color) {
382
10032
        if self.stops.is_none() {
383
3249
            self.stops = Some(Vec::<ColorStop>::new());
384
6783
        }
385

            
386
10032
        if let Some(ref mut stops) = self.stops {
387
10032
            let last_offset = if !stops.is_empty() {
388
6783
                stops[stops.len() - 1].offset
389
            } else {
390
3249
                UnitInterval(0.0)
391
            };
392

            
393
10032
            let offset = if offset > last_offset {
394
6365
                offset
395
            } else {
396
3667
                last_offset
397
            };
398

            
399
10032
            stops.push(ColorStop { offset, color });
400
        } else {
401
            unreachable!();
402
        }
403
10032
    }
404

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

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

            
418
10032
                let UnitInterval(stop_opacity) = values.stop_opacity().0;
419
10032
                let UnitInterval(o) = opacity;
420
10032

            
421
10032
                let composed_opacity = UnitInterval(stop_opacity * o);
422
10032

            
423
10032
                let stop_color =
424
10032
                    resolve_color(&values.stop_color().0, composed_opacity, &values.color().0);
425
10032

            
426
10032
                self.add_color_stop(stop.offset, stop_color);
427
10032
            }
428
        }
429
3802
    }
430

            
431
7165
    fn is_resolved(&self) -> bool {
432
7165
        self.units.is_some()
433
6614
            && self.transform.is_some()
434
3992
            && self.spread.is_some()
435
3365
            && self.stops.is_some()
436
3365
            && self.variant.is_resolved()
437
7165
    }
438

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

            
446
418
        UnresolvedGradient {
447
418
            units,
448
418
            transform,
449
418
            spread,
450
418
            stops,
451
418
            variant,
452
418
        }
453
418
    }
454

            
455
3365
    fn resolve_from_defaults(&self) -> UnresolvedGradient {
456
3365
        let units = self.units.or_else(|| Some(GradientUnits::default()));
457
3365
        let transform = self
458
3365
            .transform
459
3365
            .or_else(|| Some(TransformAttribute::default()));
460
3365
        let spread = self.spread.or_else(|| Some(SpreadMethod::default()));
461
3365
        let stops = self.stops.clone().or_else(|| Some(Vec::<ColorStop>::new()));
462
3365
        let variant = self.variant.resolve_from_defaults();
463
3365

            
464
3365
        UnresolvedGradient {
465
3365
            units,
466
3365
            transform,
467
3365
            spread,
468
3365
            stops,
469
3365
            variant,
470
3365
        }
471
3365
    }
472
}
473

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

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

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

            
508
impl Common {
509
5265
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
510
25044
        for (attr, value) in attrs.iter() {
511
25042
            match attr.expanded() {
512
                expanded_name!("", "gradientUnits") => {
513
2299
                    set_attribute(&mut self.units, attr.parse(value), session)
514
                }
515
2812
                expanded_name!("", "gradientTransform") => {
516
2812
                    set_attribute(&mut self.transform, attr.parse(value), session);
517
2812
                }
518
                expanded_name!("", "spreadMethod") => {
519
171
                    set_attribute(&mut self.spread, attr.parse(value), session)
520
                }
521
19760
                ref a if is_href(a) => {
522
1159
                    let mut href = None;
523
1159
                    set_attribute(
524
1159
                        &mut href,
525
1159
                        NodeId::parse(value).map(Some).attribute(attr.clone()),
526
1159
                        session,
527
1159
                    );
528
1159
                    set_href(a, &mut self.fallback, href);
529
1159
                }
530
18601
                _ => (),
531
            }
532
        }
533
5265
    }
534
}
535

            
536
impl ElementTrait for LinearGradient {
537
4181
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
538
4181
        self.common.set_attributes(attrs, session);
539

            
540
16740
        for (attr, value) in attrs.iter() {
541
16739
            match attr.expanded() {
542
1406
                expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
543
1406
                expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
544
1406
                expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
545
1406
                expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
546

            
547
11115
                _ => (),
548
            }
549
        }
550
4181
    }
551
}
552

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

            
565
3802
                gradient.add_color_stops_from_node(node, opacity);
566
3802

            
567
3802
                Unresolved {
568
3802
                    gradient,
569
3802
                    fallback: self.common.fallback.clone(),
570
3802
                }
571
3802
            }
572

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

            
584
3382
                let mut stack = NodeStack::new();
585

            
586
3800
                while !gradient.is_resolved() {
587
3800
                    if let Some(node_id) = fallback {
588
437
                        let acquired = acquired_nodes.acquire(&node_id)?;
589
418
                        let acquired_node = acquired.get();
590
418

            
591
418
                        if stack.contains(acquired_node) {
592
                            return Err(AcquireError::CircularReference(acquired_node.clone()));
593
418
                        }
594

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

            
605
418
                        gradient = gradient.resolve_from_fallback(&unresolved.gradient);
606
418
                        fallback = unresolved.fallback;
607
418

            
608
418
                        stack.push(acquired_node);
609
                    } else {
610
3363
                        gradient = gradient.resolve_from_defaults();
611
3363
                        break;
612
                    }
613
                }
614

            
615
3363
                Ok(gradient.into_resolved())
616
3382
            }
617
        }
618
    };
619
}
620

            
621
impl_gradient!(LinearGradient, RadialGradient);
622
impl_gradient!(RadialGradient, LinearGradient);
623

            
624
impl ElementTrait for RadialGradient {
625
1084
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
626
1084
        self.common.set_attributes(attrs, session);
627
1084

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

            
634
8304
        for (attr, value) in attrs.iter() {
635
8303
            let attr_expanded = attr.expanded();
636
3781
            match attr_expanded {
637
950
                expanded_name!("", "cx") => set_attribute(&mut self.cx, attr.parse(value), session),
638
950
                expanded_name!("", "cy") => set_attribute(&mut self.cy, attr.parse(value), session),
639
950
                expanded_name!("", "r") => set_attribute(&mut self.r, attr.parse(value), session),
640
836
                expanded_name!("", "fx") => set_attribute(&mut self.fx, attr.parse(value), session),
641
836
                expanded_name!("", "fy") => set_attribute(&mut self.fy, attr.parse(value), session),
642
3781
                a if a == expanded_name_fr => {
643
38
                    set_attribute(&mut self.fr, attr.parse(value), session)
644
                }
645

            
646
3743
                _ => (),
647
            }
648
        }
649
1084
    }
650
}
651

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

            
664
3287
        let gradient_transform = self.transform.to_transform();
665
3287
        let transform = transform.pre_transform(&gradient_transform).invert()?;
666

            
667
3268
        let variant = match self.variant {
668
2641
            ResolvedGradientVariant::Linear { x1, y1, x2, y2 } => GradientVariant::Linear {
669
2641
                x1: x1.to_user(&params),
670
2641
                y1: y1.to_user(&params),
671
2641
                x2: x2.to_user(&params),
672
2641
                y2: y2.to_user(&params),
673
2641
            },
674

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

            
692
3268
        Some(UserSpaceGradient {
693
3268
            transform,
694
3268
            spread: self.spread,
695
3268
            stops: self.stops.clone(),
696
3268
            variant,
697
3268
        })
698
3344
    }
699
}
700

            
701
#[cfg(test)]
702
mod 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
1
    fn parses_spread_method() {
712
1
        assert_eq!(SpreadMethod::parse_str("pad").unwrap(), SpreadMethod::Pad);
713
1
        assert_eq!(
714
1
            SpreadMethod::parse_str("reflect").unwrap(),
715
1
            SpreadMethod::Reflect
716
1
        );
717
1
        assert_eq!(
718
1
            SpreadMethod::parse_str("repeat").unwrap(),
719
1
            SpreadMethod::Repeat
720
1
        );
721
1
        assert!(SpreadMethod::parse_str("foobar").is_err());
722
1
    }
723

            
724
    #[test]
725
1
    fn gradient_resolved_from_defaults_is_really_resolved() {
726
1
        let session = Session::default();
727
1

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

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

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

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