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

            
3
use markup5ever::{expanded_name, local_name, 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::{self, Layer, LayerKind, 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
#[derive(Default)]
25
pub struct Group();
26

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

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

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

            
61
2
    fn layout(
62
2
        &self,
63
2
        node: &Node,
64
2
        acquired_nodes: &mut AcquiredNodes<'_>,
65
2
        cascaded: &CascadedValues<'_>,
66
2
        viewport: &Viewport,
67
2
        draw_ctx: &mut DrawingCtx,
68
2
        clipping: bool,
69
2
    ) -> Result<Option<Layer>, InternalRenderingError> {
70
2
        let mut child_layers = Vec::new();
71

            
72
8
        for child in node.children().filter(|c| c.is_element()) {
73
3
            let elt = child.borrow_element();
74

            
75
3
            let layer = elt.layout(
76
3
                &child,
77
3
                acquired_nodes,
78
3
                &CascadedValues::clone_with_node(cascaded, &child),
79
3
                viewport,
80
3
                draw_ctx,
81
3
                clipping,
82
3
            )?;
83

            
84
3
            if let Some(layer) = layer {
85
3
                child_layers.push(layer);
86
3
            }
87
        }
88

            
89
2
        self.layout_with_children(
90
2
            draw_ctx.session(),
91
2
            node,
92
2
            acquired_nodes,
93
2
            cascaded,
94
2
            child_layers,
95
2
        )
96
2
    }
97
}
98

            
99
2
fn extents_of_transformed_children(layers: &[Layer]) -> Option<Rect> {
100
2
    let mut result_bbox = BoundingBox::new();
101

            
102
5
    for layer in layers {
103
3
        if let Some(extents) = layer.kind.local_extents() {
104
3
            let bbox = BoundingBox::new()
105
3
                .with_transform(layer.stacking_ctx.transform)
106
3
                .with_rect(extents);
107
3
            result_bbox.insert(&bbox);
108
3
        }
109
    }
110

            
111
2
    result_bbox.rect
112
2
}
113

            
114
impl Group {
115
2
    fn layout_with_children(
116
2
        &self,
117
2
        session: &Session,
118
2
        node: &Node,
119
2
        acquired_nodes: &mut AcquiredNodes<'_>,
120
2
        cascaded: &CascadedValues<'_>,
121
2
        child_layers: Vec<Layer>,
122
2
    ) -> Result<Option<Layer>, InternalRenderingError> {
123
2
        let values = cascaded.get();
124
2

            
125
2
        let extents = extents_of_transformed_children(&child_layers);
126
2

            
127
2
        let group = Box::new(layout::Group {
128
2
            children: child_layers,
129
2
            establish_viewport: None,
130
2
            extents,
131
2
        });
132
2

            
133
2
        let elt = node.borrow_element();
134
2
        let stacking_ctx = StackingContext::new(
135
2
            session,
136
2
            acquired_nodes,
137
2
            &elt,
138
2
            values.transform(),
139
2
            None,
140
2
            values,
141
2
        );
142
2

            
143
2
        Ok(Some(Layer {
144
2
            kind: LayerKind::Group(group),
145
2
            stacking_ctx,
146
2
        }))
147
2
    }
148
}
149

            
150
/// A no-op node that does not render anything
151
///
152
/// Sometimes we just need a node that can contain children, but doesn't
153
/// render itself or its children.  This is just that kind of node.
154
#[derive(Default)]
155
pub struct NonRendering;
156

            
157
impl ElementTrait for NonRendering {}
158

            
159
/// The `<switch>` element.
160
#[derive(Default)]
161
pub struct Switch();
162

            
163
impl ElementTrait for Switch {
164
190
    fn draw(
165
190
        &self,
166
190
        node: &Node,
167
190
        acquired_nodes: &mut AcquiredNodes<'_>,
168
190
        cascaded: &CascadedValues<'_>,
169
190
        viewport: &Viewport,
170
190
        draw_ctx: &mut DrawingCtx,
171
190
        clipping: bool,
172
190
    ) -> Result<BoundingBox, InternalRenderingError> {
173
190
        let values = cascaded.get();
174
190

            
175
190
        let elt = node.borrow_element();
176
190
        let stacking_ctx = Box::new(StackingContext::new(
177
190
            draw_ctx.session(),
178
190
            acquired_nodes,
179
190
            &elt,
180
190
            values.transform(),
181
190
            None,
182
190
            values,
183
190
        ));
184
190

            
185
190
        draw_ctx.with_discrete_layer(
186
190
            &stacking_ctx,
187
190
            acquired_nodes,
188
190
            viewport,
189
190
            None,
190
190
            clipping,
191
190
            &mut |an, dc, new_viewport| {
192
722
                if let Some(child) = node.children().filter(|c| c.is_element()).find(|c| {
193
361
                    let elt = c.borrow_element();
194
361
                    elt.get_cond(dc.user_language())
195
361
                }) {
196
190
                    child.draw(
197
190
                        an,
198
190
                        &CascadedValues::clone_with_node(cascaded, &child),
199
190
                        new_viewport,
200
190
                        dc,
201
190
                        clipping,
202
190
                    )
203
                } else {
204
                    Ok(new_viewport.empty_bbox())
205
                }
206
190
            },
207
190
        )
208
190
    }
209
}
210

            
211
/// Intrinsic dimensions of an SVG document fragment: its `width/height` properties and  `viewBox` attribute.
212
///
213
/// Note that in SVG2, `width` and `height` are properties, not
214
/// attributes.  If either is omitted, it defaults to `auto`. which
215
/// computes to `100%`.
216
///
217
/// The `viewBox` attribute can also be omitted, hence an `Option`.
218
#[derive(Debug, Copy, Clone, PartialEq)]
219
pub struct IntrinsicDimensions {
220
    /// Computed value of the `width` property.
221
    pub width: ULength<Horizontal>,
222

            
223
    /// Computed value of the `height` property.
224
    pub height: ULength<Vertical>,
225

            
226
    /// Contents of the `viewBox` attribute.
227
    pub vbox: Option<ViewBox>,
228
}
229

            
230
/// The `<svg>` element.
231
///
232
/// Note that its x/y/width/height are properties in SVG2, so they are
233
/// defined as part of [the properties machinery](properties.rs).
234
#[derive(Default)]
235
pub struct Svg {
236
    preserve_aspect_ratio: AspectRatio,
237
    vbox: Option<ViewBox>,
238
}
239

            
240
impl Svg {
241
20387
    pub fn get_intrinsic_dimensions(&self, values: &ComputedValues) -> IntrinsicDimensions {
242
20387
        let w = match values.width().0 {
243
627
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
244
19760
            LengthOrAuto::Length(l) => l,
245
        };
246

            
247
20387
        let h = match values.height().0 {
248
646
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
249
19741
            LengthOrAuto::Length(l) => l,
250
        };
251

            
252
20387
        IntrinsicDimensions {
253
20387
            width: w,
254
20387
            height: h,
255
20387
            vbox: self.vbox,
256
20387
        }
257
20387
    }
258

            
259
1273
    fn get_unnormalized_offset(
260
1273
        &self,
261
1273
        values: &ComputedValues,
262
1273
    ) -> (Length<Horizontal>, Length<Vertical>) {
263
1273
        // these defaults are per the spec
264
1273
        let x = values.x().0;
265
1273
        let y = values.y().0;
266
1273

            
267
1273
        (x, y)
268
1273
    }
269

            
270
21641
    fn get_unnormalized_size(
271
21641
        &self,
272
21641
        values: &ComputedValues,
273
21641
    ) -> (ULength<Horizontal>, ULength<Vertical>) {
274
        // these defaults are per the spec
275
21641
        let w = match values.width().0 {
276
1425
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
277
20216
            LengthOrAuto::Length(l) => l,
278
        };
279
21641
        let h = match values.height().0 {
280
1406
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
281
20235
            LengthOrAuto::Length(l) => l,
282
        };
283
21641
        (w, h)
284
21641
    }
285

            
286
21641
    fn get_viewport(
287
21641
        &self,
288
21641
        params: &NormalizeParams,
289
21641
        values: &ComputedValues,
290
21641
        outermost: bool,
291
21641
    ) -> Rect {
292
        // x & y attributes have no effect on outermost svg
293
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
294
21641
        let (nx, ny) = if outermost {
295
20368
            (0.0, 0.0)
296
        } else {
297
1273
            let (x, y) = self.get_unnormalized_offset(values);
298
1273
            (x.to_user(params), y.to_user(params))
299
        };
300

            
301
21641
        let (w, h) = self.get_unnormalized_size(values);
302
21641
        let (nw, nh) = (w.to_user(params), h.to_user(params));
303
21641

            
304
21641
        Rect::new(nx, ny, nx + nw, ny + nh)
305
21641
    }
306

            
307
19
    pub fn get_viewbox(&self) -> Option<ViewBox> {
308
19
        self.vbox
309
19
    }
310

            
311
19
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
312
19
        self.preserve_aspect_ratio
313
19
    }
314

            
315
21641
    fn make_svg_viewport(
316
21641
        &self,
317
21641
        node: &Node,
318
21641
        cascaded: &CascadedValues<'_>,
319
21641
        current_viewport: &Viewport,
320
21641
        draw_ctx: &mut DrawingCtx,
321
21641
    ) -> LayoutViewport {
322
21641
        let values = cascaded.get();
323
21641

            
324
21641
        let params = NormalizeParams::new(values, current_viewport);
325
21641

            
326
21641
        let has_parent = node.parent().is_some();
327

            
328
        // From https://www.w3.org/TR/SVG2/embedded.html#ImageElement:
329
        //
330
        // For `image` elements embedding an SVG image, the `preserveAspectRatio`
331
        // attribute on the root element in the referenced SVG image must be ignored,
332
        // and instead treated as if it had a value of `none`. (see
333
        // `preserveAspectRatio` for details).  This ensures that the
334
        // `preserveAspectRatio` attribute on the referencing `image` has its
335
        // intended effect, even if it is none.
336
        //
337
21641
        let preserve_aspect_ratio = match (has_parent, draw_ctx.svg_nesting()) {
338
            // we are a toplevel, and referenced from <image> => preserveAspectRatio=none
339
285
            (false, SvgNesting::ReferencedFromImageElement) => AspectRatio::none(),
340

            
341
            // otherwise just use our specified preserveAspectRatio
342
21356
            _ => self.preserve_aspect_ratio,
343
        };
344

            
345
21641
        let svg_viewport = self.get_viewport(&params, values, !has_parent);
346

            
347
21641
        let is_measuring_toplevel_svg = !has_parent && draw_ctx.is_measuring();
348

            
349
21641
        let (geometry, vbox) = if is_measuring_toplevel_svg {
350
            // We are obtaining the toplevel SVG's geometry.  This means, don't care about the
351
            // DrawingCtx's viewport, just use the SVG's intrinsic dimensions and see how far
352
            // it wants to extend.
353
1368
            (svg_viewport, self.vbox)
354
        } else {
355
            (
356
                // The client's viewport overrides the toplevel's x/y/w/h viewport
357
20273
                if has_parent {
358
1273
                    svg_viewport
359
                } else {
360
19000
                    draw_ctx.toplevel_viewport()
361
                },
362
                // Use our viewBox if available, or try to derive one from
363
                // the intrinsic dimensions.
364
20273
                self.vbox.or_else(|| {
365
12483
                    Some(ViewBox::from(Rect::from_size(
366
12483
                        svg_viewport.width(),
367
12483
                        svg_viewport.height(),
368
12483
                    )))
369
20273
                }),
370
20273
            )
371
        };
372

            
373
21641
        LayoutViewport {
374
21641
            geometry,
375
21641
            vbox,
376
21641
            preserve_aspect_ratio,
377
21641
            overflow: values.overflow(),
378
21641
        }
379
21641
    }
380
}
381

            
382
impl ElementTrait for Svg {
383
24597
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
384
109800
        for (attr, value) in attrs.iter() {
385
109800
            match attr.expanded() {
386
                expanded_name!("", "preserveAspectRatio") => {
387
342
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
388
                }
389
                expanded_name!("", "viewBox") => {
390
7942
                    set_attribute(&mut self.vbox, attr.parse(value), session)
391
                }
392
101516
                _ => (),
393
            }
394
        }
395
24597
    }
396

            
397
21641
    fn draw(
398
21641
        &self,
399
21641
        node: &Node,
400
21641
        acquired_nodes: &mut AcquiredNodes<'_>,
401
21641
        cascaded: &CascadedValues<'_>,
402
21641
        viewport: &Viewport,
403
21641
        draw_ctx: &mut DrawingCtx,
404
21641
        clipping: bool,
405
21641
    ) -> Result<BoundingBox, InternalRenderingError> {
406
21641
        let values = cascaded.get();
407
21641

            
408
21641
        let elt = node.borrow_element();
409
21641
        let stacking_ctx = Box::new(StackingContext::new(
410
21641
            draw_ctx.session(),
411
21641
            acquired_nodes,
412
21641
            &elt,
413
21641
            values.transform(),
414
21641
            None,
415
21641
            values,
416
21641
        ));
417
21641

            
418
21641
        let layout_viewport = self.make_svg_viewport(node, cascaded, viewport, draw_ctx);
419
21641

            
420
21641
        draw_ctx.with_discrete_layer(
421
21641
            &stacking_ctx,
422
21641
            acquired_nodes,
423
21641
            viewport,
424
21641
            Some(layout_viewport),
425
21641
            clipping,
426
21641
            &mut |an, dc, new_viewport| {
427
21584
                node.draw_children(an, cascaded, new_viewport, dc, clipping)
428
21641
            },
429
21641
        )
430
21641
    }
431
}
432

            
433
/// The `<use>` element.
434
pub struct Use {
435
    link: Option<NodeId>,
436
    x: Length<Horizontal>,
437
    y: Length<Vertical>,
438
    width: ULength<Horizontal>,
439
    height: ULength<Vertical>,
440
}
441

            
442
impl Use {
443
9507296
    fn get_rect(&self, params: &NormalizeParams) -> Rect {
444
9507296
        let x = self.x.to_user(params);
445
9507296
        let y = self.y.to_user(params);
446
9507296
        let w = self.width.to_user(params);
447
9507296
        let h = self.height.to_user(params);
448
9507296

            
449
9507296
        Rect::new(x, y, x + w, y + h)
450
9507296
    }
451
}
452

            
453
impl Default for Use {
454
27683
    fn default() -> Use {
455
27683
        Use {
456
27683
            link: None,
457
27683
            x: Default::default(),
458
27683
            y: Default::default(),
459
27683
            width: ULength::<Horizontal>::parse_str("100%").unwrap(),
460
27683
            height: ULength::<Vertical>::parse_str("100%").unwrap(),
461
27683
        }
462
27683
    }
463
}
464

            
465
impl ElementTrait for Use {
466
27683
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
467
63954
        for (attr, value) in attrs.iter() {
468
63954
            match attr.expanded() {
469
63954
                ref a if is_href(a) => {
470
27702
                    let mut href = None;
471
27702
                    set_attribute(
472
27702
                        &mut href,
473
27702
                        NodeId::parse(value).map(Some).attribute(attr.clone()),
474
27702
                        session,
475
27702
                    );
476
27702
                    set_href(a, &mut self.link, href);
477
27702
                }
478
1311
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
479
2356
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
480
                expanded_name!("", "width") => {
481
1349
                    set_attribute(&mut self.width, attr.parse(value), session)
482
                }
483
                expanded_name!("", "height") => {
484
1349
                    set_attribute(&mut self.height, attr.parse(value), session)
485
                }
486
29887
                _ => (),
487
            }
488
        }
489
27683
    }
490

            
491
9507296
    fn draw(
492
9507296
        &self,
493
9507296
        node: &Node,
494
9507296
        acquired_nodes: &mut AcquiredNodes<'_>,
495
9507296
        cascaded: &CascadedValues<'_>,
496
9507296
        viewport: &Viewport,
497
9507296
        draw_ctx: &mut DrawingCtx,
498
9507296
        clipping: bool,
499
9507296
    ) -> Result<BoundingBox, InternalRenderingError> {
500
9507296
        if let Some(link) = self.link.as_ref() {
501
9507296
            let values = cascaded.get();
502
9507296
            let params = NormalizeParams::new(values, viewport);
503
9507296
            let rect = self.get_rect(&params);
504
9507296

            
505
9507296
            let stroke_paint = values.stroke().0.resolve(
506
9507296
                acquired_nodes,
507
9507296
                values.stroke_opacity().0,
508
9507296
                values.color().0,
509
9507296
                cascaded.context_fill.clone(),
510
9507296
                cascaded.context_stroke.clone(),
511
9507296
                draw_ctx.session(),
512
9507296
            );
513
9507296

            
514
9507296
            let fill_paint = values.fill().0.resolve(
515
9507296
                acquired_nodes,
516
9507296
                values.fill_opacity().0,
517
9507296
                values.color().0,
518
9507296
                cascaded.context_fill.clone(),
519
9507296
                cascaded.context_stroke.clone(),
520
9507296
                draw_ctx.session(),
521
9507296
            );
522
9507296

            
523
9507296
            draw_ctx.draw_from_use_node(
524
9507296
                node,
525
9507296
                acquired_nodes,
526
9507296
                values,
527
9507296
                rect,
528
9507296
                link,
529
9507296
                clipping,
530
9507296
                viewport,
531
9507296
                fill_paint,
532
9507296
                stroke_paint,
533
9507296
            )
534
        } else {
535
            Ok(viewport.empty_bbox())
536
        }
537
9507296
    }
538
}
539

            
540
/// The `<symbol>` element.
541
#[derive(Default)]
542
pub struct Symbol {
543
    preserve_aspect_ratio: AspectRatio,
544
    vbox: Option<ViewBox>,
545
}
546

            
547
impl Symbol {
548
171
    pub fn get_viewbox(&self) -> Option<ViewBox> {
549
171
        self.vbox
550
171
    }
551

            
552
171
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
553
171
        self.preserve_aspect_ratio
554
171
    }
555
}
556

            
557
impl ElementTrait for Symbol {
558
209
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
559
418
        for (attr, value) in attrs.iter() {
560
418
            match attr.expanded() {
561
                expanded_name!("", "preserveAspectRatio") => {
562
38
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
563
                }
564
                expanded_name!("", "viewBox") => {
565
76
                    set_attribute(&mut self.vbox, attr.parse(value), session)
566
                }
567
304
                _ => (),
568
            }
569
        }
570
209
    }
571
}
572

            
573
coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);
574

            
575
/// The `<clipPath>` element.
576
#[derive(Default)]
577
pub struct ClipPath {
578
    units: ClipPathUnits,
579
}
580

            
581
impl ClipPath {
582
1710
    pub fn get_units(&self) -> CoordUnits {
583
1710
        CoordUnits::from(self.units)
584
1710
    }
585
}
586

            
587
impl ElementTrait for ClipPath {
588
665
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
589
1045
        for (attr, value) in attrs.iter() {
590
1045
            if attr.expanded() == expanded_name!("", "clipPathUnits") {
591
342
                set_attribute(&mut self.units, attr.parse(value), session);
592
703
            }
593
        }
594
665
    }
595
}
596

            
597
coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
598
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
599

            
600
/// The `<mask>` element.
601
pub struct Mask {
602
    x: Length<Horizontal>,
603
    y: Length<Vertical>,
604
    width: ULength<Horizontal>,
605
    height: ULength<Vertical>,
606

            
607
    units: MaskUnits,
608
    content_units: MaskContentUnits,
609
}
610

            
611
impl Default for Mask {
612
494
    fn default() -> Mask {
613
494
        Mask {
614
494
            // these values are per the spec
615
494
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
616
494
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
617
494
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
618
494
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
619
494

            
620
494
            units: MaskUnits::default(),
621
494
            content_units: MaskContentUnits::default(),
622
494
        }
623
494
    }
624
}
625

            
626
impl Mask {
627
1140
    pub fn get_units(&self) -> CoordUnits {
628
1140
        CoordUnits::from(self.units)
629
1140
    }
630

            
631
2242
    pub fn get_content_units(&self) -> CoordUnits {
632
2242
        CoordUnits::from(self.content_units)
633
2242
    }
634

            
635
1140
    pub fn get_rect(&self, params: &NormalizeParams) -> Rect {
636
1140
        let x = self.x.to_user(params);
637
1140
        let y = self.y.to_user(params);
638
1140
        let w = self.width.to_user(params);
639
1140
        let h = self.height.to_user(params);
640
1140

            
641
1140
        Rect::new(x, y, x + w, y + h)
642
1140
    }
643
}
644

            
645
impl ElementTrait for Mask {
646
494
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
647
2261
        for (attr, value) in attrs.iter() {
648
2261
            match attr.expanded() {
649
247
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
650
266
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
651
                expanded_name!("", "width") => {
652
247
                    set_attribute(&mut self.width, attr.parse(value), session)
653
                }
654
                expanded_name!("", "height") => {
655
266
                    set_attribute(&mut self.height, attr.parse(value), session)
656
                }
657
                expanded_name!("", "maskUnits") => {
658
304
                    set_attribute(&mut self.units, attr.parse(value), session)
659
                }
660
                expanded_name!("", "maskContentUnits") => {
661
114
                    set_attribute(&mut self.content_units, attr.parse(value), session)
662
                }
663
817
                _ => (),
664
            }
665
        }
666
494
    }
667
}
668

            
669
/// The `<a>` element.
670
#[derive(Default)]
671
pub struct Link {
672
    pub link: Option<String>,
673
}
674

            
675
impl ElementTrait for Link {
676
209
    fn set_attributes(&mut self, attrs: &Attributes, _session: &Session) {
677
209
        for (attr, value) in attrs.iter() {
678
190
            let expanded = attr.expanded();
679
190
            if is_href(&expanded) {
680
114
                set_href(&expanded, &mut self.link, Some(value.to_owned()));
681
114
            }
682
        }
683
209
    }
684

            
685
57
    fn draw(
686
57
        &self,
687
57
        node: &Node,
688
57
        acquired_nodes: &mut AcquiredNodes<'_>,
689
57
        cascaded: &CascadedValues<'_>,
690
57
        viewport: &Viewport,
691
57
        draw_ctx: &mut DrawingCtx,
692
57
        clipping: bool,
693
57
    ) -> Result<BoundingBox, InternalRenderingError> {
694
        // If this element is inside of <text>, do not draw it.
695
        // The <text> takes care of it.
696
152
        for an in node.ancestors() {
697
152
            if matches!(&*an.borrow_element_data(), ElementData::Text(_)) {
698
                return Ok(viewport.empty_bbox());
699
152
            }
700
        }
701

            
702
57
        let cascaded = CascadedValues::clone_with_node(cascaded, node);
703
57
        let values = cascaded.get();
704
57

            
705
57
        let elt = node.borrow_element();
706
57

            
707
57
        let link_is_empty = self.link.as_ref().map(|l| l.is_empty()).unwrap_or(true);
708

            
709
57
        let link_target = if link_is_empty {
710
19
            None
711
        } else {
712
38
            self.link.clone()
713
        };
714

            
715
57
        let stacking_ctx = Box::new(StackingContext::new_with_link(
716
57
            draw_ctx.session(),
717
57
            acquired_nodes,
718
57
            &elt,
719
57
            values.transform(),
720
57
            values,
721
57
            link_target,
722
57
        ));
723
57

            
724
57
        draw_ctx.with_discrete_layer(
725
57
            &stacking_ctx,
726
57
            acquired_nodes,
727
57
            viewport,
728
57
            None,
729
57
            clipping,
730
57
            &mut |an, dc, new_viewport| {
731
57
                node.draw_children(an, &cascaded, new_viewport, dc, clipping)
732
57
            },
733
57
        )
734
57
    }
735
}
736

            
737
#[cfg(test)]
738
mod tests {
739
    use super::*;
740

            
741
    use crate::accept_language::{LanguageTags, UserLanguage};
742
    use crate::document::Document;
743
    use crate::dpi::Dpi;
744
    use crate::drawing_ctx::{RenderingConfiguration, SvgNesting};
745

            
746
    #[test]
747
1
    fn computes_group_extents() {
748
1
        let document = Document::load_from_bytes(
749
1
            br#"<?xml version="1.0" encoding="UTF-8"?>
750
1
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
751
1
  <g id="a">
752
1
    <g transform="translate(10, 10) scale(2, 3)">
753
1
      <rect x="0" y="0" width="5" height="10"/>
754
1
    </g>
755
1
    <rect x="0" y="0" width="5" height="10" transform="scale(2) translate(-10, -20)"/>
756
1
  </g>
757
1
</svg>
758
1
"#,
759
1
        );
760
1

            
761
1
        let a = document.lookup_internal_node("a").unwrap();
762
1

            
763
1
        let elt = a.borrow_element();
764
1

            
765
1
        let mut acquired_nodes = AcquiredNodes::new(&document, None);
766
1
        let cascaded = CascadedValues::new_from_node(&a);
767
1

            
768
1
        let dpi = Dpi::new(96.0, 96.0);
769
1

            
770
1
        let viewport = Viewport::new(dpi.clone(), 100.0, 100.0);
771
1

            
772
1
        let surface = cairo::RecordingSurface::create(cairo::Content::ColorAlpha, None).unwrap();
773
1
        let cr = cairo::Context::new(&surface).unwrap();
774
1

            
775
1
        let config = RenderingConfiguration {
776
1
            dpi,
777
1
            cancellable: None,
778
1
            user_language: UserLanguage::LanguageTags(LanguageTags::empty()),
779
1
            svg_nesting: SvgNesting::Standalone,
780
1
            measuring: false,
781
1
            testing: true,
782
1
        };
783
1

            
784
1
        let mut draw_ctx = DrawingCtx::new(Session::default(), &cr, &viewport, config, Vec::new());
785
1

            
786
1
        let layout = elt.layout(
787
1
            &a,
788
1
            &mut acquired_nodes,
789
1
            &cascaded,
790
1
            &viewport,
791
1
            &mut draw_ctx,
792
1
            false,
793
1
        );
794

            
795
1
        match layout {
796
            Ok(Some(Layer {
797
1
                kind: LayerKind::Group(ref group),
798
1
                ..
799
1
            })) => {
800
1
                assert_eq!(group.extents, Some(Rect::new(-20.0, -40.0, 20.0, 40.0)));
801
            }
802

            
803
            Err(_) => panic!("layout should not produce an InternalRenderingError"),
804

            
805
            _ => panic!("layout object is not a LayerKind::Group"),
806
        }
807
1
    }
808
}