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::{ClipMode, 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::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
5520
#[derive(Default)]
25
pub struct Group();
26

            
27
impl ElementTrait for Group {
28
505546
    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
505546
        let values = cascaded.get();
38

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

            
49
505544
        draw_ctx.with_discrete_layer(
50
            &stacking_ctx,
51
            acquired_nodes,
52
            viewport,
53
505544
            clipping,
54
1011086
            &mut |an, dc| node.draw_children(an, cascaded, viewport, dc, clipping),
55
        )
56
505544
    }
57
}
58

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

            
66
impl ElementTrait for NonRendering {}
67

            
68
/// The `<switch>` element.
69
10
#[derive(Default)]
70
pub struct Switch();
71

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

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

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

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

            
131
    /// Computed value of the `height` property.
132
    pub height: ULength<Vertical>,
133

            
134
    /// Contents of the `viewBox` attribute.
135
    pub vbox: Option<ViewBox>,
136
}
137

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

            
148
impl Svg {
149
1150
    pub fn get_intrinsic_dimensions(&self, values: &ComputedValues) -> IntrinsicDimensions {
150
1150
        let w = match values.width().0 {
151
44
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
152
1106
            LengthOrAuto::Length(l) => l,
153
        };
154

            
155
1150
        let h = match values.height().0 {
156
51
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
157
1099
            LengthOrAuto::Length(l) => l,
158
        };
159

            
160
1150
        IntrinsicDimensions {
161
1150
            width: w,
162
1150
            height: h,
163
1150
            vbox: self.vbox,
164
        }
165
1150
    }
166

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

            
175
35
        (x, y)
176
35
    }
177

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

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

            
209
1119
        let (w, h) = self.get_unnormalized_size(values);
210
1119
        let (nw, nh) = (w.to_user(params), h.to_user(params));
211

            
212
1119
        Rect::new(nx, ny, nx + nw, ny + nh)
213
1119
    }
214

            
215
1
    pub fn get_viewbox(&self) -> Option<ViewBox> {
216
1
        self.vbox
217
1
    }
218

            
219
1
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
220
1
        self.preserve_aspect_ratio
221
1
    }
222

            
223
1118
    fn make_svg_viewport(
224
        &self,
225
        node: &Node,
226
        cascaded: &CascadedValues<'_>,
227
        current_viewport: &Viewport,
228
        draw_ctx: &mut DrawingCtx,
229
    ) -> Option<Viewport> {
230
1118
        let values = cascaded.get();
231

            
232
1118
        let params = NormalizeParams::new(values, current_viewport);
233

            
234
1118
        let has_parent = node.parent().is_some();
235

            
236
        // FIXME: do we need to look at preserveAspectRatio.slice, like in DrawingCtx::draw_image()?
237
1118
        let clip_mode = if !values.is_overflow() && has_parent {
238
32
            ClipMode::ClipToViewport
239
        } else {
240
1086
            ClipMode::NoClip
241
        };
242

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

            
256
            // otherwise just use our specified preserveAspectRatio
257
1104
            _ => self.preserve_aspect_ratio,
258
        };
259

            
260
1118
        let svg_viewport = self.get_viewport(&params, values, !has_parent);
261

            
262
1118
        let is_measuring_toplevel_svg = !has_parent && draw_ctx.is_measuring();
263

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

            
288
1118
        draw_ctx.push_new_viewport(
289
            current_viewport,
290
            vbox,
291
            viewport,
292
1118
            preserve_aspect_ratio,
293
1118
            clip_mode,
294
        )
295
1118
    }
296
}
297

            
298
impl ElementTrait for Svg {
299
1119
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
300
7004
        for (attr, value) in attrs.iter() {
301
5887
            match attr.expanded() {
302
                expanded_name!("", "preserveAspectRatio") => {
303
18
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
304
                }
305
                expanded_name!("", "viewBox") => {
306
449
                    set_attribute(&mut self.vbox, attr.parse(value), session)
307
                }
308
                _ => (),
309
            }
310
5885
        }
311
1117
    }
312

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

            
324
1121
        let elt = node.borrow_element();
325
1123
        let stacking_ctx = StackingContext::new(
326
1121
            draw_ctx.session(),
327
            acquired_nodes,
328
1120
            &elt,
329
1122
            values.transform(),
330
1123
            None,
331
            values,
332
        );
333

            
334
1122
        draw_ctx.with_discrete_layer(
335
            &stacking_ctx,
336
            acquired_nodes,
337
            viewport, // FIXME: should this be the svg_viewport from below?
338
1122
            clipping,
339
2240
            &mut |an, dc| {
340
1118
                if let Some(svg_viewport) = self.make_svg_viewport(node, cascaded, viewport, dc) {
341
1116
                    node.draw_children(an, cascaded, &svg_viewport, dc, clipping)
342
                } else {
343
2
                    Ok(dc.empty_bbox())
344
                }
345
1118
            },
346
        )
347
1122
    }
348
}
349

            
350
/// The `<use>` element.
351
pub struct Use {
352
    link: Option<NodeId>,
353
    x: Length<Horizontal>,
354
    y: Length<Vertical>,
355
    width: ULength<Horizontal>,
356
    height: ULength<Vertical>,
357
}
358

            
359
impl Use {
360
500165
    fn get_rect(&self, params: &NormalizeParams) -> Rect {
361
500165
        let x = self.x.to_user(params);
362
500165
        let y = self.y.to_user(params);
363
500165
        let w = self.width.to_user(params);
364
500165
        let h = self.height.to_user(params);
365

            
366
500165
        Rect::new(x, y, x + w, y + h)
367
500165
    }
368
}
369

            
370
impl Default for Use {
371
406
    fn default() -> Use {
372
404
        Use {
373
406
            link: None,
374
406
            x: Default::default(),
375
406
            y: Default::default(),
376
406
            width: ULength::<Horizontal>::parse_str("100%").unwrap(),
377
405
            height: ULength::<Vertical>::parse_str("100%").unwrap(),
378
        }
379
404
    }
380
}
381

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

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

            
422
1000330
            let stroke_paint = values.stroke().0.resolve(
423
                acquired_nodes,
424
500165
                values.stroke_opacity().0,
425
500165
                values.color().0,
426
500165
                cascaded.context_fill.clone(),
427
500165
                cascaded.context_stroke.clone(),
428
500165
                draw_ctx.session(),
429
500165
            );
430

            
431
1000330
            let fill_paint = values.fill().0.resolve(
432
                acquired_nodes,
433
500165
                values.fill_opacity().0,
434
500165
                values.color().0,
435
500165
                cascaded.context_fill.clone(),
436
500165
                cascaded.context_stroke.clone(),
437
500165
                draw_ctx.session(),
438
500165
            );
439

            
440
500165
            draw_ctx.draw_from_use_node(
441
                node,
442
                acquired_nodes,
443
                values,
444
                rect,
445
                link,
446
                clipping,
447
                viewport,
448
500165
                fill_paint,
449
500165
                stroke_paint,
450
            )
451
500165
        } else {
452
            Ok(draw_ctx.empty_bbox())
453
        }
454
500165
    }
455
}
456

            
457
/// The `<symbol>` element.
458
22
#[derive(Default)]
459
pub struct Symbol {
460
11
    preserve_aspect_ratio: AspectRatio,
461
11
    vbox: Option<ViewBox>,
462
}
463

            
464
impl Symbol {
465
9
    pub fn get_viewbox(&self) -> Option<ViewBox> {
466
9
        self.vbox
467
9
    }
468

            
469
9
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
470
9
        self.preserve_aspect_ratio
471
9
    }
472
}
473

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

            
490
coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);
491

            
492
/// The `<clipPath>` element.
493
72
#[derive(Default)]
494
pub struct ClipPath {
495
36
    units: ClipPathUnits,
496
}
497

            
498
impl ClipPath {
499
90
    pub fn get_units(&self) -> CoordUnits {
500
90
        CoordUnits::from(self.units)
501
90
    }
502
}
503

            
504
impl ElementTrait for ClipPath {
505
36
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
506
93
        for (attr, value) in attrs.iter() {
507
57
            if attr.expanded() == expanded_name!("", "clipPathUnits") {
508
19
                set_attribute(&mut self.units, attr.parse(value), session);
509
            }
510
57
        }
511
36
    }
512
}
513

            
514
coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
515
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
516

            
517
/// The `<mask>` element.
518
pub struct Mask {
519
    x: Length<Horizontal>,
520
    y: Length<Vertical>,
521
    width: ULength<Horizontal>,
522
    height: ULength<Vertical>,
523

            
524
    units: MaskUnits,
525
    content_units: MaskContentUnits,
526
}
527

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

            
537
23
            units: MaskUnits::default(),
538
23
            content_units: MaskContentUnits::default(),
539
        }
540
23
    }
541
}
542

            
543
impl Mask {
544
59
    pub fn get_units(&self) -> CoordUnits {
545
59
        CoordUnits::from(self.units)
546
59
    }
547

            
548
117
    pub fn get_content_units(&self) -> CoordUnits {
549
117
        CoordUnits::from(self.content_units)
550
117
    }
551

            
552
59
    pub fn get_rect(&self, params: &NormalizeParams) -> Rect {
553
59
        let x = self.x.to_user(params);
554
59
        let y = self.y.to_user(params);
555
59
        let w = self.width.to_user(params);
556
59
        let h = self.height.to_user(params);
557

            
558
59
        Rect::new(x, y, x + w, y + h)
559
59
    }
560
}
561

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

            
586
/// The `<a>` element.
587
22
#[derive(Default)]
588
pub struct Link {
589
11
    pub link: Option<String>,
590
}
591

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

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

            
619
3
        let cascaded = CascadedValues::clone_with_node(cascaded, node);
620
3
        let values = cascaded.get();
621

            
622
3
        let elt = node.borrow_element();
623

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

            
626
5
        let link_target = if link_is_empty {
627
1
            None
628
        } else {
629
2
            self.link.clone()
630
        };
631

            
632
3
        let stacking_ctx = StackingContext::new_with_link(
633
3
            draw_ctx.session(),
634
            acquired_nodes,
635
3
            &elt,
636
3
            values.transform(),
637
            values,
638
3
            link_target,
639
        );
640

            
641
3
        draw_ctx.with_discrete_layer(
642
            &stacking_ctx,
643
            acquired_nodes,
644
            viewport,
645
3
            clipping,
646
6
            &mut |an, dc| node.draw_children(an, &cascaded, viewport, dc, clipping),
647
        )
648
3
    }
649
}