1
//! Structural elements in SVG: the `g`, `switch`, `svg`, `use`, `symbol`, `clip_path`, `mask`, `link` elements.
2

            
3
use markup5ever::{expanded_name, local_name, namespace_url, ns};
4

            
5
use crate::aspect_ratio::*;
6
use crate::bbox::BoundingBox;
7
use crate::coord_units;
8
use crate::coord_units::CoordUnits;
9
use crate::document::{AcquiredNodes, NodeId};
10
use crate::drawing_ctx::{DrawingCtx, SvgNesting, Viewport};
11
use crate::element::{set_attribute, ElementData, ElementTrait};
12
use crate::error::*;
13
use crate::href::{is_href, set_href};
14
use crate::layout::{LayoutViewport, StackingContext};
15
use crate::length::*;
16
use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
17
use crate::parsers::{Parse, ParseValue};
18
use crate::properties::ComputedValues;
19
use crate::rect::Rect;
20
use crate::session::Session;
21
use crate::viewbox::*;
22
use crate::xml::Attributes;
23

            
24
5521
#[derive(Default)]
25
pub struct Group();
26

            
27
impl ElementTrait for Group {
28
505545
    fn draw(
29
        &self,
30
        node: &Node,
31
        acquired_nodes: &mut AcquiredNodes<'_>,
32
        cascaded: &CascadedValues<'_>,
33
        viewport: &Viewport,
34
        draw_ctx: &mut DrawingCtx,
35
        clipping: bool,
36
    ) -> Result<BoundingBox, InternalRenderingError> {
37
505545
        let values = cascaded.get();
38

            
39
505545
        let elt = node.borrow_element();
40
505548
        let stacking_ctx = StackingContext::new(
41
505545
            draw_ctx.session(),
42
            acquired_nodes,
43
505544
            &elt,
44
505544
            values.transform(),
45
505548
            None,
46
            values,
47
        );
48

            
49
505546
        draw_ctx.with_discrete_layer(
50
            &stacking_ctx,
51
            acquired_nodes,
52
            viewport,
53
505546
            None,
54
505546
            clipping,
55
1011090
            &mut |an, dc, new_viewport| {
56
505544
                node.draw_children(an, cascaded, new_viewport, dc, clipping)
57
505544
            },
58
        )
59
505546
    }
60
}
61

            
62
/// A no-op node that does not render anything
63
///
64
/// Sometimes we just need a node that can contain children, but doesn't
65
/// render itself or its children.  This is just that kind of node.
66
7613
#[derive(Default)]
67
pub struct NonRendering;
68

            
69
impl ElementTrait for NonRendering {}
70

            
71
/// The `<switch>` element.
72
10
#[derive(Default)]
73
pub struct Switch();
74

            
75
impl ElementTrait for Switch {
76
10
    fn draw(
77
        &self,
78
        node: &Node,
79
        acquired_nodes: &mut AcquiredNodes<'_>,
80
        cascaded: &CascadedValues<'_>,
81
        viewport: &Viewport,
82
        draw_ctx: &mut DrawingCtx,
83
        clipping: bool,
84
    ) -> Result<BoundingBox, InternalRenderingError> {
85
10
        let values = cascaded.get();
86

            
87
10
        let elt = node.borrow_element();
88
10
        let stacking_ctx = StackingContext::new(
89
10
            draw_ctx.session(),
90
            acquired_nodes,
91
10
            &elt,
92
10
            values.transform(),
93
10
            None,
94
            values,
95
        );
96

            
97
10
        draw_ctx.with_discrete_layer(
98
            &stacking_ctx,
99
            acquired_nodes,
100
            viewport,
101
10
            None,
102
10
            clipping,
103
20
            &mut |an, dc, new_viewport| {
104
67
                if let Some(child) = node.children().filter(|c| c.is_element()).find(|c| {
105
19
                    let elt = c.borrow_element();
106
19
                    elt.get_cond(dc.user_language())
107
29
                }) {
108
10
                    child.draw(
109
                        an,
110
10
                        &CascadedValues::clone_with_node(cascaded, &child),
111
                        new_viewport,
112
                        dc,
113
10
                        clipping,
114
                    )
115
10
                } else {
116
                    Ok(dc.empty_bbox())
117
                }
118
10
            },
119
        )
120
10
    }
121
}
122

            
123
/// Intrinsic dimensions of an SVG document fragment: its `width/height` properties and  `viewBox` attribute.
124
///
125
/// Note that in SVG2, `width` and `height` are properties, not
126
/// attributes.  If either is omitted, it defaults to `auto`. which
127
/// computes to `100%`.
128
///
129
/// The `viewBox` attribute can also be omitted, hence an `Option`.
130
#[derive(Debug, Copy, Clone, PartialEq)]
131
pub struct IntrinsicDimensions {
132
    /// Computed value of the `width` property.
133
    pub width: ULength<Horizontal>,
134

            
135
    /// Computed value of the `height` property.
136
    pub height: ULength<Vertical>,
137

            
138
    /// Contents of the `viewBox` attribute.
139
    pub vbox: Option<ViewBox>,
140
}
141

            
142
/// The `<svg>` element.
143
///
144
/// Note that its x/y/width/height are properties in SVG2, so they are
145
/// defined as part of [the properties machinery](properties.rs).
146
2344
#[derive(Default)]
147
pub struct Svg {
148
1172
    preserve_aspect_ratio: AspectRatio,
149
1172
    vbox: Option<ViewBox>,
150
}
151

            
152
impl Svg {
153
1152
    pub fn get_intrinsic_dimensions(&self, values: &ComputedValues) -> IntrinsicDimensions {
154
1152
        let w = match values.width().0 {
155
44
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
156
1108
            LengthOrAuto::Length(l) => l,
157
        };
158

            
159
1152
        let h = match values.height().0 {
160
51
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
161
1101
            LengthOrAuto::Length(l) => l,
162
        };
163

            
164
1152
        IntrinsicDimensions {
165
1152
            width: w,
166
1152
            height: h,
167
1152
            vbox: self.vbox,
168
        }
169
1152
    }
170

            
171
35
    fn get_unnormalized_offset(
172
        &self,
173
        values: &ComputedValues,
174
    ) -> (Length<Horizontal>, Length<Vertical>) {
175
        // these defaults are per the spec
176
35
        let x = values.x().0;
177
35
        let y = values.y().0;
178

            
179
35
        (x, y)
180
35
    }
181

            
182
1121
    fn get_unnormalized_size(
183
        &self,
184
        values: &ComputedValues,
185
    ) -> (ULength<Horizontal>, ULength<Vertical>) {
186
        // these defaults are per the spec
187
1121
        let w = match values.width().0 {
188
56
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
189
1065
            LengthOrAuto::Length(l) => l,
190
        };
191
1121
        let h = match values.height().0 {
192
57
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
193
1064
            LengthOrAuto::Length(l) => l,
194
        };
195
1121
        (w, h)
196
1121
    }
197

            
198
1121
    fn get_viewport(
199
        &self,
200
        params: &NormalizeParams,
201
        values: &ComputedValues,
202
        outermost: bool,
203
    ) -> Rect {
204
        // x & y attributes have no effect on outermost svg
205
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
206
1121
        let (nx, ny) = if outermost {
207
1086
            (0.0, 0.0)
208
        } else {
209
35
            let (x, y) = self.get_unnormalized_offset(values);
210
35
            (x.to_user(params), y.to_user(params))
211
        };
212

            
213
1121
        let (w, h) = self.get_unnormalized_size(values);
214
1121
        let (nw, nh) = (w.to_user(params), h.to_user(params));
215

            
216
1121
        Rect::new(nx, ny, nx + nw, ny + nh)
217
1121
    }
218

            
219
1
    pub fn get_viewbox(&self) -> Option<ViewBox> {
220
1
        self.vbox
221
1
    }
222

            
223
1
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
224
1
        self.preserve_aspect_ratio
225
1
    }
226

            
227
1120
    fn make_svg_viewport(
228
        &self,
229
        node: &Node,
230
        cascaded: &CascadedValues<'_>,
231
        current_viewport: &Viewport,
232
        draw_ctx: &mut DrawingCtx,
233
    ) -> LayoutViewport {
234
1120
        let values = cascaded.get();
235

            
236
1120
        let params = NormalizeParams::new(values, current_viewport);
237

            
238
1120
        let has_parent = node.parent().is_some();
239

            
240
        // From https://www.w3.org/TR/SVG2/embedded.html#ImageElement:
241
        //
242
        // For `image` elements embedding an SVG image, the `preserveAspectRatio`
243
        // attribute on the root element in the referenced SVG image must be ignored,
244
        // and instead treated as if it had a value of `none`. (see
245
        // `preserveAspectRatio` for details).  This ensures that the
246
        // `preserveAspectRatio` attribute on the referencing `image` has its
247
        // intended effect, even if it is none.
248
        //
249
1120
        let preserve_aspect_ratio = match (has_parent, draw_ctx.svg_nesting()) {
250
            // we are a toplevel, and referenced from <image> => preserveAspectRatio=none
251
14
            (false, SvgNesting::ReferencedFromImageElement) => AspectRatio::none(),
252

            
253
            // otherwise just use our specified preserveAspectRatio
254
1106
            _ => self.preserve_aspect_ratio,
255
        };
256

            
257
1120
        let svg_viewport = self.get_viewport(&params, values, !has_parent);
258

            
259
1120
        let is_measuring_toplevel_svg = !has_parent && draw_ctx.is_measuring();
260

            
261
2156
        let (geometry, vbox) = if is_measuring_toplevel_svg {
262
            // We are obtaining the toplevel SVG's geometry.  This means, don't care about the
263
            // DrawingCtx's viewport, just use the SVG's intrinsic dimensions and see how far
264
            // it wants to extend.
265
84
            (svg_viewport, self.vbox)
266
        } else {
267
1036
            (
268
                // The client's viewport overrides the toplevel's x/y/w/h viewport
269
1036
                if has_parent {
270
35
                    svg_viewport
271
                } else {
272
1001
                    draw_ctx.toplevel_viewport()
273
                },
274
                // Use our viewBox if available, or try to derive one from
275
                // the intrinsic dimensions.
276
1672
                self.vbox.or_else(|| {
277
636
                    Some(ViewBox::from(Rect::from_size(
278
636
                        svg_viewport.width(),
279
636
                        svg_viewport.height(),
280
                    )))
281
636
                }),
282
            )
283
        };
284

            
285
1120
        LayoutViewport {
286
            geometry,
287
            vbox,
288
1120
            preserve_aspect_ratio,
289
1120
            overflow: values.overflow(),
290
        }
291
1120
    }
292
}
293

            
294
impl ElementTrait for Svg {
295
1134
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
296
7015
        for (attr, value) in attrs.iter() {
297
5895
            match attr.expanded() {
298
                expanded_name!("", "preserveAspectRatio") => {
299
18
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
300
                }
301
                expanded_name!("", "viewBox") => {
302
450
                    set_attribute(&mut self.vbox, attr.parse(value), session)
303
                }
304
                _ => (),
305
            }
306
5881
        }
307
1120
    }
308

            
309
1122
    fn draw(
310
        &self,
311
        node: &Node,
312
        acquired_nodes: &mut AcquiredNodes<'_>,
313
        cascaded: &CascadedValues<'_>,
314
        viewport: &Viewport,
315
        draw_ctx: &mut DrawingCtx,
316
        clipping: bool,
317
    ) -> Result<BoundingBox, InternalRenderingError> {
318
1122
        let values = cascaded.get();
319

            
320
1122
        let elt = node.borrow_element();
321
1122
        let stacking_ctx = StackingContext::new(
322
1122
            draw_ctx.session(),
323
            acquired_nodes,
324
1122
            &elt,
325
1122
            values.transform(),
326
1122
            None,
327
            values,
328
        );
329

            
330
1122
        let layout_viewport = self.make_svg_viewport(node, cascaded, viewport, draw_ctx);
331

            
332
1122
        draw_ctx.with_discrete_layer(
333
            &stacking_ctx,
334
            acquired_nodes,
335
            viewport,
336
1122
            Some(layout_viewport),
337
1122
            clipping,
338
2242
            &mut |an, dc, new_viewport| {
339
1120
                node.draw_children(an, cascaded, new_viewport, dc, clipping)
340
1120
            },
341
        )
342
1122
    }
343
}
344

            
345
/// The `<use>` element.
346
pub struct Use {
347
    link: Option<NodeId>,
348
    x: Length<Horizontal>,
349
    y: Length<Vertical>,
350
    width: ULength<Horizontal>,
351
    height: ULength<Vertical>,
352
}
353

            
354
impl Use {
355
500165
    fn get_rect(&self, params: &NormalizeParams) -> Rect {
356
500165
        let x = self.x.to_user(params);
357
500165
        let y = self.y.to_user(params);
358
500165
        let w = self.width.to_user(params);
359
500165
        let h = self.height.to_user(params);
360

            
361
500165
        Rect::new(x, y, x + w, y + h)
362
500165
    }
363
}
364

            
365
impl Default for Use {
366
406
    fn default() -> Use {
367
406
        Use {
368
406
            link: None,
369
406
            x: Default::default(),
370
406
            y: Default::default(),
371
406
            width: ULength::<Horizontal>::parse_str("100%").unwrap(),
372
406
            height: ULength::<Vertical>::parse_str("100%").unwrap(),
373
        }
374
406
    }
375
}
376

            
377
impl ElementTrait for Use {
378
406
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
379
1639
        for (attr, value) in attrs.iter() {
380
2059
            match attr.expanded() {
381
1233
                ref a if is_href(a) => {
382
407
                    let mut href = None;
383
407
                    set_attribute(
384
                        &mut href,
385
407
                        NodeId::parse(value).map(Some).attribute(attr.clone()),
386
                        session,
387
                    );
388
407
                    set_href(a, &mut self.link, href);
389
407
                }
390
87
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
391
142
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
392
                expanded_name!("", "width") => {
393
54
                    set_attribute(&mut self.width, attr.parse(value), session)
394
                }
395
                expanded_name!("", "height") => {
396
54
                    set_attribute(&mut self.height, attr.parse(value), session)
397
                }
398
                _ => (),
399
            }
400
1233
        }
401
406
    }
402

            
403
500165
    fn draw(
404
        &self,
405
        node: &Node,
406
        acquired_nodes: &mut AcquiredNodes<'_>,
407
        cascaded: &CascadedValues<'_>,
408
        viewport: &Viewport,
409
        draw_ctx: &mut DrawingCtx,
410
        clipping: bool,
411
    ) -> Result<BoundingBox, InternalRenderingError> {
412
1000330
        if let Some(link) = self.link.as_ref() {
413
500165
            let values = cascaded.get();
414
500165
            let params = NormalizeParams::new(values, viewport);
415
500165
            let rect = self.get_rect(&params);
416

            
417
1000330
            let stroke_paint = values.stroke().0.resolve(
418
                acquired_nodes,
419
500165
                values.stroke_opacity().0,
420
500165
                values.color().0,
421
500165
                cascaded.context_fill.clone(),
422
500165
                cascaded.context_stroke.clone(),
423
500165
                draw_ctx.session(),
424
500165
            );
425

            
426
1000330
            let fill_paint = values.fill().0.resolve(
427
                acquired_nodes,
428
500165
                values.fill_opacity().0,
429
500165
                values.color().0,
430
500165
                cascaded.context_fill.clone(),
431
500165
                cascaded.context_stroke.clone(),
432
500165
                draw_ctx.session(),
433
500165
            );
434

            
435
500165
            draw_ctx.draw_from_use_node(
436
                node,
437
                acquired_nodes,
438
                values,
439
                rect,
440
                link,
441
                clipping,
442
                viewport,
443
500165
                fill_paint,
444
500165
                stroke_paint,
445
            )
446
500165
        } else {
447
            Ok(draw_ctx.empty_bbox())
448
        }
449
500165
    }
450
}
451

            
452
/// The `<symbol>` element.
453
22
#[derive(Default)]
454
pub struct Symbol {
455
11
    preserve_aspect_ratio: AspectRatio,
456
11
    vbox: Option<ViewBox>,
457
}
458

            
459
impl Symbol {
460
9
    pub fn get_viewbox(&self) -> Option<ViewBox> {
461
9
        self.vbox
462
9
    }
463

            
464
9
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
465
9
        self.preserve_aspect_ratio
466
9
    }
467
}
468

            
469
impl ElementTrait for Symbol {
470
11
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
471
33
        for (attr, value) in attrs.iter() {
472
22
            match attr.expanded() {
473
                expanded_name!("", "preserveAspectRatio") => {
474
2
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
475
                }
476
                expanded_name!("", "viewBox") => {
477
4
                    set_attribute(&mut self.vbox, attr.parse(value), session)
478
                }
479
                _ => (),
480
            }
481
22
        }
482
11
    }
483
}
484

            
485
coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);
486

            
487
/// The `<clipPath>` element.
488
72
#[derive(Default)]
489
pub struct ClipPath {
490
36
    units: ClipPathUnits,
491
}
492

            
493
impl ClipPath {
494
90
    pub fn get_units(&self) -> CoordUnits {
495
90
        CoordUnits::from(self.units)
496
90
    }
497
}
498

            
499
impl ElementTrait for ClipPath {
500
36
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
501
93
        for (attr, value) in attrs.iter() {
502
59
            if attr.expanded() == expanded_name!("", "clipPathUnits") {
503
19
                set_attribute(&mut self.units, attr.parse(value), session);
504
            }
505
57
        }
506
34
    }
507
}
508

            
509
coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
510
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
511

            
512
/// The `<mask>` element.
513
pub struct Mask {
514
    x: Length<Horizontal>,
515
    y: Length<Vertical>,
516
    width: ULength<Horizontal>,
517
    height: ULength<Vertical>,
518

            
519
    units: MaskUnits,
520
    content_units: MaskContentUnits,
521
}
522

            
523
impl Default for Mask {
524
23
    fn default() -> Mask {
525
23
        Mask {
526
            // these values are per the spec
527
23
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
528
23
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
529
23
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
530
23
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
531

            
532
23
            units: MaskUnits::default(),
533
23
            content_units: MaskContentUnits::default(),
534
        }
535
23
    }
536
}
537

            
538
impl Mask {
539
59
    pub fn get_units(&self) -> CoordUnits {
540
59
        CoordUnits::from(self.units)
541
59
    }
542

            
543
117
    pub fn get_content_units(&self) -> CoordUnits {
544
117
        CoordUnits::from(self.content_units)
545
117
    }
546

            
547
59
    pub fn get_rect(&self, params: &NormalizeParams) -> Rect {
548
59
        let x = self.x.to_user(params);
549
59
        let y = self.y.to_user(params);
550
59
        let w = self.width.to_user(params);
551
59
        let h = self.height.to_user(params);
552

            
553
59
        Rect::new(x, y, x + w, y + h)
554
59
    }
555
}
556

            
557
impl ElementTrait for Mask {
558
23
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
559
127
        for (attr, value) in attrs.iter() {
560
104
            match attr.expanded() {
561
12
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
562
12
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
563
                expanded_name!("", "width") => {
564
12
                    set_attribute(&mut self.width, attr.parse(value), session)
565
                }
566
                expanded_name!("", "height") => {
567
12
                    set_attribute(&mut self.height, attr.parse(value), session)
568
                }
569
                expanded_name!("", "maskUnits") => {
570
14
                    set_attribute(&mut self.units, attr.parse(value), session)
571
                }
572
                expanded_name!("", "maskContentUnits") => {
573
6
                    set_attribute(&mut self.content_units, attr.parse(value), session)
574
                }
575
                _ => (),
576
            }
577
104
        }
578
23
    }
579
}
580

            
581
/// The `<a>` element.
582
22
#[derive(Default)]
583
pub struct Link {
584
11
    pub link: Option<String>,
585
}
586

            
587
impl ElementTrait for Link {
588
11
    fn set_attributes(&mut self, attrs: &Attributes, _session: &Session) {
589
21
        for (attr, value) in attrs.iter() {
590
10
            let expanded = attr.expanded();
591
10
            if is_href(&expanded) {
592
6
                set_href(&expanded, &mut self.link, Some(value.to_owned()));
593
            }
594
10
        }
595
11
    }
596

            
597
19
    fn draw(
598
        &self,
599
        node: &Node,
600
        acquired_nodes: &mut AcquiredNodes<'_>,
601
        cascaded: &CascadedValues<'_>,
602
        viewport: &Viewport,
603
        draw_ctx: &mut DrawingCtx,
604
        clipping: bool,
605
    ) -> Result<BoundingBox, InternalRenderingError> {
606
        // If this element is inside of <text>, do not draw it.
607
        // The <text> takes care of it.
608
19
        for an in node.ancestors() {
609
8
            if matches!(&*an.borrow_element_data(), ElementData::Text(_)) {
610
                return Ok(draw_ctx.empty_bbox());
611
            }
612
8
        }
613

            
614
3
        let cascaded = CascadedValues::clone_with_node(cascaded, node);
615
3
        let values = cascaded.get();
616

            
617
3
        let elt = node.borrow_element();
618

            
619
5
        let link_is_empty = self.link.as_ref().map(|l| l.is_empty()).unwrap_or(true);
620

            
621
5
        let link_target = if link_is_empty {
622
1
            None
623
        } else {
624
2
            self.link.clone()
625
        };
626

            
627
3
        let stacking_ctx = StackingContext::new_with_link(
628
3
            draw_ctx.session(),
629
            acquired_nodes,
630
3
            &elt,
631
3
            values.transform(),
632
            values,
633
3
            link_target,
634
        );
635

            
636
3
        draw_ctx.with_discrete_layer(
637
            &stacking_ctx,
638
            acquired_nodes,
639
            viewport,
640
3
            None,
641
3
            clipping,
642
6
            &mut |an, dc, new_viewport| {
643
3
                node.draw_children(an, &cascaded, new_viewport, dc, clipping)
644
3
            },
645
        )
646
3
    }
647
}