Coverage Report

Created: 2026-06-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/librsvg/rsvg/src/structure.rs
Line
Count
Source
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::{DrawResult, ElementData, ElementTrait, set_attribute};
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
16.9k
    fn draw(
29
16.9k
        &self,
30
16.9k
        node: &Node,
31
16.9k
        acquired_nodes: &mut AcquiredNodes<'_>,
32
16.9k
        cascaded: &CascadedValues<'_>,
33
16.9k
        viewport: &Viewport,
34
16.9k
        draw_ctx: &mut DrawingCtx,
35
16.9k
        _clipping: bool,
36
16.9k
    ) -> DrawResult {
37
16.9k
        let values = cascaded.get();
38
39
16.9k
        let elt = node.borrow_element();
40
16.9k
        let stacking_ctx = Box::new(StackingContext::new(
41
16.9k
            draw_ctx,
42
16.9k
            acquired_nodes,
43
16.9k
            &elt,
44
16.9k
            values.transform(),
45
16.9k
            None,
46
16.9k
            values,
47
16.9k
            viewport,
48
        ));
49
50
16.9k
        draw_ctx.with_discrete_layer(
51
16.9k
            &stacking_ctx,
52
16.9k
            acquired_nodes,
53
16.9k
            viewport,
54
16.9k
            None,
55
            false,
56
16.8k
            &mut |an, dc, new_viewport| node.draw_children(an, cascaded, new_viewport, dc),
57
        )
58
16.9k
    }
59
60
0
    fn layout(
61
0
        &self,
62
0
        node: &Node,
63
0
        acquired_nodes: &mut AcquiredNodes<'_>,
64
0
        cascaded: &CascadedValues<'_>,
65
0
        viewport: &Viewport,
66
0
        draw_ctx: &mut DrawingCtx,
67
0
    ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
68
0
        let mut child_layers = Vec::new();
69
70
0
        for child in node.children().filter(|c| c.is_element()) {
71
0
            let elt = child.borrow_element();
72
73
0
            let layer = elt.layout(
74
0
                &child,
75
0
                acquired_nodes,
76
0
                &CascadedValues::clone_with_node(cascaded, &child),
77
0
                viewport,
78
0
                draw_ctx,
79
0
            )?;
80
81
0
            if let Some(layer) = layer {
82
0
                child_layers.push(layer);
83
0
            }
84
        }
85
86
0
        self.layout_with_children(
87
0
            draw_ctx,
88
0
            node,
89
0
            acquired_nodes,
90
0
            cascaded,
91
0
            child_layers,
92
0
            viewport,
93
        )
94
0
    }
95
}
96
97
0
fn extents_of_transformed_children(layers: &[Layer]) -> Option<Rect> {
98
0
    let mut result_bbox = BoundingBox::new();
99
100
0
    for layer in layers {
101
0
        if let Some(extents) = layer.kind.local_extents() {
102
0
            let bbox = BoundingBox::new()
103
0
                .with_transform(layer.stacking_ctx.transform)
104
0
                .with_rect(extents);
105
0
            result_bbox.insert(&bbox);
106
0
        }
107
    }
108
109
0
    result_bbox.rect
110
0
}
111
112
impl Group {
113
0
    fn layout_with_children(
114
0
        &self,
115
0
        draw_ctx: &DrawingCtx,
116
0
        node: &Node,
117
0
        acquired_nodes: &mut AcquiredNodes<'_>,
118
0
        cascaded: &CascadedValues<'_>,
119
0
        child_layers: Vec<Layer>,
120
0
        viewport: &Viewport,
121
0
    ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
122
0
        let values = cascaded.get();
123
124
0
        let extents = extents_of_transformed_children(&child_layers);
125
126
0
        let group = Box::new(layout::Group {
127
0
            children: child_layers,
128
0
            establish_viewport: None,
129
0
            extents,
130
0
        });
131
132
0
        let elt = node.borrow_element();
133
0
        let stacking_ctx = StackingContext::new(
134
0
            draw_ctx,
135
0
            acquired_nodes,
136
0
            &elt,
137
0
            values.transform(),
138
0
            None,
139
0
            values,
140
0
            viewport,
141
        );
142
143
0
        Ok(Some(Layer {
144
0
            kind: LayerKind::Group(group),
145
0
            stacking_ctx,
146
0
        }))
147
0
    }
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
0
    fn draw(
165
0
        &self,
166
0
        node: &Node,
167
0
        acquired_nodes: &mut AcquiredNodes<'_>,
168
0
        cascaded: &CascadedValues<'_>,
169
0
        viewport: &Viewport,
170
0
        draw_ctx: &mut DrawingCtx,
171
0
        _clipping: bool,
172
0
    ) -> DrawResult {
173
0
        let values = cascaded.get();
174
175
0
        let switch_elt = node.borrow_element();
176
0
        let child_that_matches = node.children().filter(|c| c.is_element()).find(|c| {
177
0
            let elt = c.borrow_element();
178
0
            elt.get_cond(draw_ctx.user_language(), draw_ctx.session())
179
0
        });
180
181
0
        if let Some(child) = child_that_matches {
182
0
            let stacking_ctx = Box::new(StackingContext::new(
183
0
                draw_ctx,
184
0
                acquired_nodes,
185
0
                &switch_elt,
186
0
                values.transform(),
187
0
                None,
188
0
                values,
189
0
                viewport,
190
            ));
191
192
0
            draw_ctx.with_discrete_layer(
193
0
                &stacking_ctx,
194
0
                acquired_nodes,
195
0
                viewport,
196
0
                None,
197
                false,
198
0
                &mut |an, dc, new_viewport| {
199
0
                    child.draw(
200
0
                        an,
201
0
                        &CascadedValues::clone_with_node(cascaded, &child),
202
0
                        new_viewport,
203
0
                        dc,
204
                        false,
205
                    )
206
0
                },
207
            )
208
        } else {
209
0
            Ok(viewport.empty_bbox())
210
        }
211
0
    }
212
}
213
214
/// Intrinsic dimensions of an SVG document fragment: its `width/height` properties and  `viewBox` attribute.
215
///
216
/// Note that in SVG2, `width` and `height` are properties, not
217
/// attributes.  If either is omitted, it defaults to `auto`. which
218
/// computes to `100%`.
219
///
220
/// The `viewBox` attribute can also be omitted, hence an `Option`.
221
#[derive(Debug, Copy, Clone, PartialEq)]
222
pub struct IntrinsicDimensions {
223
    /// Computed value of the `width` property.
224
    pub width: ULength<Horizontal>,
225
226
    /// Computed value of the `height` property.
227
    pub height: ULength<Vertical>,
228
229
    /// Contents of the `viewBox` attribute.
230
    pub vbox: Option<ViewBox>,
231
}
232
233
/// The `<svg>` element.
234
///
235
/// Note that its x/y/width/height are properties in SVG2, so they are
236
/// defined as part of [the properties machinery](properties.rs).
237
#[derive(Default)]
238
pub struct Svg {
239
    preserve_aspect_ratio: AspectRatio,
240
    vbox: Option<ViewBox>,
241
}
242
243
impl Svg {
244
0
    pub fn get_intrinsic_dimensions(&self, values: &ComputedValues) -> IntrinsicDimensions {
245
0
        let w = match values.width().0 {
246
0
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
247
0
            LengthOrAuto::Length(l) => l,
248
        };
249
250
0
        let h = match values.height().0 {
251
0
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
252
0
            LengthOrAuto::Length(l) => l,
253
        };
254
255
0
        IntrinsicDimensions {
256
0
            width: w,
257
0
            height: h,
258
0
            vbox: self.vbox,
259
0
        }
260
0
    }
261
262
47.3k
    fn get_unnormalized_offset(
263
47.3k
        &self,
264
47.3k
        values: &ComputedValues,
265
47.3k
    ) -> (Length<Horizontal>, Length<Vertical>) {
266
        // these defaults are per the spec
267
47.3k
        let x = values.x().0;
268
47.3k
        let y = values.y().0;
269
270
47.3k
        (x, y)
271
47.3k
    }
272
273
47.4k
    fn get_unnormalized_size(
274
47.4k
        &self,
275
47.4k
        values: &ComputedValues,
276
47.4k
    ) -> (ULength<Horizontal>, ULength<Vertical>) {
277
        // these defaults are per the spec
278
47.4k
        let w = match values.width().0 {
279
46.5k
            LengthOrAuto::Auto => ULength::<Horizontal>::parse_str("100%").unwrap(),
280
975
            LengthOrAuto::Length(l) => l,
281
        };
282
47.4k
        let h = match values.height().0 {
283
47.4k
            LengthOrAuto::Auto => ULength::<Vertical>::parse_str("100%").unwrap(),
284
71
            LengthOrAuto::Length(l) => l,
285
        };
286
47.4k
        (w, h)
287
47.4k
    }
288
289
47.4k
    fn get_viewport(
290
47.4k
        &self,
291
47.4k
        params: &NormalizeParams,
292
47.4k
        values: &ComputedValues,
293
47.4k
        outermost: bool,
294
47.4k
    ) -> Rect {
295
        // x & y attributes have no effect on outermost svg
296
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
297
47.4k
        let (nx, ny) = if outermost {
298
188
            (0.0, 0.0)
299
        } else {
300
47.3k
            let (x, y) = self.get_unnormalized_offset(values);
301
47.3k
            (x.to_user(params), y.to_user(params))
302
        };
303
304
47.4k
        let (w, h) = self.get_unnormalized_size(values);
305
47.4k
        let (nw, nh) = (w.to_user(params), h.to_user(params));
306
307
47.4k
        Rect::new(nx, ny, nx + nw, ny + nh)
308
47.4k
    }
309
310
0
    pub fn get_viewbox(&self) -> Option<ViewBox> {
311
0
        self.vbox
312
0
    }
313
314
0
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
315
0
        self.preserve_aspect_ratio
316
0
    }
317
318
47.4k
    fn make_svg_viewport(
319
47.4k
        &self,
320
47.4k
        node: &Node,
321
47.4k
        cascaded: &CascadedValues<'_>,
322
47.4k
        current_viewport: &Viewport,
323
47.4k
        draw_ctx: &mut DrawingCtx,
324
47.4k
    ) -> LayoutViewport {
325
47.4k
        let values = cascaded.get();
326
327
47.4k
        let params = NormalizeParams::new(values, current_viewport);
328
329
47.4k
        let has_parent = node.parent().is_some();
330
331
        // From https://www.w3.org/TR/SVG2/embedded.html#ImageElement:
332
        //
333
        // For `image` elements embedding an SVG image, the `preserveAspectRatio`
334
        // attribute on the root element in the referenced SVG image must be ignored,
335
        // and instead treated as if it had a value of `none`. (see
336
        // `preserveAspectRatio` for details).  This ensures that the
337
        // `preserveAspectRatio` attribute on the referencing `image` has its
338
        // intended effect, even if it is none.
339
        //
340
47.4k
        let preserve_aspect_ratio = match (has_parent, draw_ctx.svg_nesting()) {
341
            // we are a toplevel, and referenced from <image> => preserveAspectRatio=none
342
0
            (false, SvgNesting::ReferencedFromImageElement) => AspectRatio::none(),
343
344
            // otherwise just use our specified preserveAspectRatio
345
47.4k
            _ => self.preserve_aspect_ratio,
346
        };
347
348
47.4k
        let svg_viewport = self.get_viewport(&params, values, !has_parent);
349
350
47.4k
        let is_measuring_toplevel_svg = !has_parent && draw_ctx.is_measuring();
351
352
47.4k
        let (geometry, vbox) = if is_measuring_toplevel_svg {
353
            // We are obtaining the toplevel SVG's geometry.  This means, don't care about the
354
            // DrawingCtx's viewport, just use the SVG's intrinsic dimensions and see how far
355
            // it wants to extend.
356
0
            (svg_viewport, self.vbox)
357
        } else {
358
            (
359
                // The client's viewport overrides the toplevel's x/y/w/h viewport
360
47.4k
                if has_parent {
361
47.3k
                    svg_viewport
362
                } else {
363
188
                    draw_ctx.toplevel_viewport()
364
                },
365
                // Use our viewBox if available, or try to derive one from
366
                // the intrinsic dimensions.
367
47.4k
                self.vbox.or_else(|| {
368
47.4k
                    Some(ViewBox::from(Rect::from_size(
369
47.4k
                        svg_viewport.width(),
370
47.4k
                        svg_viewport.height(),
371
47.4k
                    )))
372
47.4k
                }),
373
            )
374
        };
375
376
47.4k
        LayoutViewport {
377
47.4k
            geometry,
378
47.4k
            vbox,
379
47.4k
            preserve_aspect_ratio,
380
47.4k
            overflow: values.overflow(),
381
47.4k
        }
382
47.4k
    }
383
}
384
385
impl ElementTrait for Svg {
386
46.8k
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
387
58.2k
        for (attr, value) in attrs.iter() {
388
58.2k
            match attr.expanded() {
389
                expanded_name!("", "preserveAspectRatio") => {
390
1.25k
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
391
                }
392
                expanded_name!("", "viewBox") => {
393
89
                    set_attribute(&mut self.vbox, attr.parse(value), session)
394
                }
395
56.9k
                _ => (),
396
            }
397
        }
398
46.8k
    }
399
400
47.4k
    fn draw(
401
47.4k
        &self,
402
47.4k
        node: &Node,
403
47.4k
        acquired_nodes: &mut AcquiredNodes<'_>,
404
47.4k
        cascaded: &CascadedValues<'_>,
405
47.4k
        viewport: &Viewport,
406
47.4k
        draw_ctx: &mut DrawingCtx,
407
47.4k
        _clipping: bool,
408
47.4k
    ) -> DrawResult {
409
47.4k
        let values = cascaded.get();
410
411
47.4k
        let elt = node.borrow_element();
412
47.4k
        let stacking_ctx = Box::new(StackingContext::new(
413
47.4k
            draw_ctx,
414
47.4k
            acquired_nodes,
415
47.4k
            &elt,
416
47.4k
            values.transform(),
417
47.4k
            None,
418
47.4k
            values,
419
47.4k
            viewport,
420
        ));
421
422
47.4k
        let layout_viewport = self.make_svg_viewport(node, cascaded, viewport, draw_ctx);
423
424
47.4k
        draw_ctx.with_discrete_layer(
425
47.4k
            &stacking_ctx,
426
47.4k
            acquired_nodes,
427
47.4k
            viewport,
428
47.4k
            Some(layout_viewport),
429
            false,
430
47.4k
            &mut |an, dc, new_viewport| node.draw_children(an, cascaded, new_viewport, dc),
431
        )
432
47.4k
    }
433
434
0
    fn layout(
435
0
        &self,
436
0
        node: &Node,
437
0
        acquired_nodes: &mut AcquiredNodes<'_>,
438
0
        cascaded: &CascadedValues<'_>,
439
0
        viewport: &Viewport,
440
0
        draw_ctx: &mut DrawingCtx,
441
0
    ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
442
0
        let mut child_layers = Vec::new();
443
444
0
        for child in node.children().filter(|c| c.is_element()) {
445
0
            let elt = child.borrow_element();
446
447
0
            let layer = elt.layout(
448
0
                &child,
449
0
                acquired_nodes,
450
0
                &CascadedValues::clone_with_node(cascaded, &child),
451
0
                viewport,
452
0
                draw_ctx,
453
0
            )?;
454
455
0
            if let Some(layer) = layer {
456
0
                child_layers.push(layer);
457
0
            }
458
        }
459
460
0
        let values = cascaded.get();
461
0
        let extents = extents_of_transformed_children(&child_layers);
462
463
0
        let layout_viewport = self.make_svg_viewport(node, cascaded, viewport, draw_ctx);
464
465
0
        let group = Box::new(layout::Group {
466
0
            children: child_layers,
467
0
            establish_viewport: Some(layout_viewport),
468
0
            extents,
469
0
        });
470
471
0
        let elt = node.borrow_element();
472
0
        let stacking_ctx = StackingContext::new(
473
0
            draw_ctx,
474
0
            acquired_nodes,
475
0
            &elt,
476
0
            values.transform(),
477
0
            None,
478
0
            values,
479
0
            viewport,
480
        );
481
482
0
        Ok(Some(Layer {
483
0
            kind: LayerKind::Group(group),
484
0
            stacking_ctx,
485
0
        }))
486
0
    }
487
}
488
489
/// The `<use>` element.
490
pub struct Use {
491
    link: Option<NodeId>,
492
    x: Length<Horizontal>,
493
    y: Length<Vertical>,
494
    width: ULength<Horizontal>,
495
    height: ULength<Vertical>,
496
}
497
498
impl Use {
499
1.36k
    pub fn get_rect(&self, params: &NormalizeParams) -> Rect {
500
1.36k
        let x = self.x.to_user(params);
501
1.36k
        let y = self.y.to_user(params);
502
1.36k
        let w = self.width.to_user(params);
503
1.36k
        let h = self.height.to_user(params);
504
505
1.36k
        Rect::new(x, y, x + w, y + h)
506
1.36k
    }
507
508
21
    pub fn get_link(&self) -> Option<NodeId> {
509
21
        self.link.clone()
510
21
    }
511
}
512
513
impl Default for Use {
514
1.49k
    fn default() -> Use {
515
1.49k
        Use {
516
1.49k
            link: None,
517
1.49k
            x: Default::default(),
518
1.49k
            y: Default::default(),
519
1.49k
            width: ULength::<Horizontal>::parse_str("100%").unwrap(),
520
1.49k
            height: ULength::<Vertical>::parse_str("100%").unwrap(),
521
1.49k
        }
522
1.49k
    }
523
}
524
525
impl ElementTrait for Use {
526
1.49k
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
527
2.93k
        for (attr, value) in attrs.iter() {
528
2.93k
            match attr.expanded() {
529
2.93k
                ref a if is_href(a) => {
530
1.39k
                    let mut href = None;
531
1.39k
                    set_attribute(
532
1.39k
                        &mut href,
533
1.39k
                        NodeId::parse(value).map(Some).attribute(attr.clone()),
534
1.39k
                        session,
535
1.39k
                    );
536
1.39k
                    set_href(a, &mut self.link, href);
537
1.39k
                }
538
0
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
539
0
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
540
                expanded_name!("", "width") => {
541
0
                    set_attribute(&mut self.width, attr.parse(value), session)
542
                }
543
                expanded_name!("", "height") => {
544
517
                    set_attribute(&mut self.height, attr.parse(value), session)
545
                }
546
1.02k
                _ => (),
547
            }
548
        }
549
1.49k
    }
550
551
1.49k
    fn draw(
552
1.49k
        &self,
553
1.49k
        node: &Node,
554
1.49k
        acquired_nodes: &mut AcquiredNodes<'_>,
555
1.49k
        cascaded: &CascadedValues<'_>,
556
1.49k
        viewport: &Viewport,
557
1.49k
        draw_ctx: &mut DrawingCtx,
558
1.49k
        clipping: bool,
559
1.49k
    ) -> DrawResult {
560
1.49k
        if let Some(link) = self.link.as_ref() {
561
1.34k
            let values = cascaded.get();
562
1.34k
            let params = NormalizeParams::new(values, viewport);
563
1.34k
            let rect = self.get_rect(&params);
564
565
1.34k
            let use_node_name = format!("{node}");
566
567
1.34k
            let stroke_paint = values.stroke().0.resolve(
568
1.34k
                acquired_nodes,
569
1.34k
                &use_node_name,
570
1.34k
                values.stroke_opacity().0,
571
1.34k
                values.color().0,
572
1.34k
                cascaded.context_fill.clone(),
573
1.34k
                cascaded.context_stroke.clone(),
574
1.34k
                draw_ctx.session(),
575
            );
576
577
1.34k
            let fill_paint = values.fill().0.resolve(
578
1.34k
                acquired_nodes,
579
1.34k
                &use_node_name,
580
1.34k
                values.fill_opacity().0,
581
1.34k
                values.color().0,
582
1.34k
                cascaded.context_fill.clone(),
583
1.34k
                cascaded.context_stroke.clone(),
584
1.34k
                draw_ctx.session(),
585
            );
586
587
1.34k
            draw_ctx.draw_from_use_node(
588
1.34k
                node,
589
1.34k
                acquired_nodes,
590
1.34k
                values,
591
1.34k
                rect,
592
1.34k
                link,
593
1.34k
                clipping,
594
1.34k
                viewport,
595
1.34k
                fill_paint,
596
1.34k
                stroke_paint,
597
            )
598
        } else {
599
144
            Ok(viewport.empty_bbox())
600
        }
601
1.49k
    }
602
}
603
604
/// The `<symbol>` element.
605
#[derive(Default)]
606
pub struct Symbol {
607
    preserve_aspect_ratio: AspectRatio,
608
    vbox: Option<ViewBox>,
609
}
610
611
impl Symbol {
612
0
    pub fn get_viewbox(&self) -> Option<ViewBox> {
613
0
        self.vbox
614
0
    }
615
616
0
    pub fn get_preserve_aspect_ratio(&self) -> AspectRatio {
617
0
        self.preserve_aspect_ratio
618
0
    }
619
}
620
621
impl ElementTrait for Symbol {
622
0
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
623
0
        for (attr, value) in attrs.iter() {
624
0
            match attr.expanded() {
625
                expanded_name!("", "preserveAspectRatio") => {
626
0
                    set_attribute(&mut self.preserve_aspect_ratio, attr.parse(value), session)
627
                }
628
                expanded_name!("", "viewBox") => {
629
0
                    set_attribute(&mut self.vbox, attr.parse(value), session)
630
                }
631
0
                _ => (),
632
            }
633
        }
634
0
    }
635
}
636
637
coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);
638
639
/// The `<clipPath>` element.
640
#[derive(Default)]
641
pub struct ClipPath {
642
    units: ClipPathUnits,
643
}
644
645
impl ClipPath {
646
126
    pub fn get_units(&self) -> CoordUnits {
647
126
        CoordUnits::from(self.units)
648
126
    }
649
}
650
651
impl ElementTrait for ClipPath {
652
124
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
653
239
        for (attr, value) in attrs.iter() {
654
239
            if attr.expanded() == expanded_name!("", "clipPathUnits") {
655
115
                set_attribute(&mut self.units, attr.parse(value), session);
656
124
            }
657
        }
658
124
    }
659
}
660
661
coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
662
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);
663
664
/// The `<mask>` element.
665
pub struct Mask {
666
    x: Length<Horizontal>,
667
    y: Length<Vertical>,
668
    width: ULength<Horizontal>,
669
    height: ULength<Vertical>,
670
671
    units: MaskUnits,
672
    content_units: MaskContentUnits,
673
}
674
675
impl Default for Mask {
676
5.01k
    fn default() -> Mask {
677
5.01k
        Mask {
678
5.01k
            // these values are per the spec
679
5.01k
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
680
5.01k
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
681
5.01k
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
682
5.01k
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
683
5.01k
684
5.01k
            units: MaskUnits::default(),
685
5.01k
            content_units: MaskContentUnits::default(),
686
5.01k
        }
687
5.01k
    }
688
}
689
690
impl Mask {
691
15
    pub fn get_units(&self) -> CoordUnits {
692
15
        CoordUnits::from(self.units)
693
15
    }
694
695
0
    pub fn get_content_units(&self) -> CoordUnits {
696
0
        CoordUnits::from(self.content_units)
697
0
    }
698
699
15
    pub fn get_rect(&self, params: &NormalizeParams) -> Rect {
700
15
        let x = self.x.to_user(params);
701
15
        let y = self.y.to_user(params);
702
15
        let w = self.width.to_user(params);
703
15
        let h = self.height.to_user(params);
704
705
15
        Rect::new(x, y, x + w, y + h)
706
15
    }
707
}
708
709
impl ElementTrait for Mask {
710
5.01k
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
711
9.80k
        for (attr, value) in attrs.iter() {
712
9.80k
            match attr.expanded() {
713
0
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
714
0
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
715
                expanded_name!("", "width") => {
716
92
                    set_attribute(&mut self.width, attr.parse(value), session)
717
                }
718
                expanded_name!("", "height") => {
719
0
                    set_attribute(&mut self.height, attr.parse(value), session)
720
                }
721
                expanded_name!("", "maskUnits") => {
722
337
                    set_attribute(&mut self.units, attr.parse(value), session)
723
                }
724
                expanded_name!("", "maskContentUnits") => {
725
0
                    set_attribute(&mut self.content_units, attr.parse(value), session)
726
                }
727
9.37k
                _ => (),
728
            }
729
        }
730
5.01k
    }
731
}
732
733
/// The `<a>` element.
734
#[derive(Default)]
735
pub struct Link {
736
    pub link: Option<String>,
737
}
738
739
impl ElementTrait for Link {
740
539
    fn set_attributes(&mut self, attrs: &Attributes, _session: &Session) {
741
1.09k
        for (attr, value) in attrs.iter() {
742
1.09k
            let expanded = attr.expanded();
743
1.09k
            if is_href(&expanded) {
744
530
                set_href(&expanded, &mut self.link, Some(value.to_owned()));
745
566
            }
746
        }
747
539
    }
748
749
539
    fn draw(
750
539
        &self,
751
539
        node: &Node,
752
539
        acquired_nodes: &mut AcquiredNodes<'_>,
753
539
        cascaded: &CascadedValues<'_>,
754
539
        viewport: &Viewport,
755
539
        draw_ctx: &mut DrawingCtx,
756
539
        _clipping: bool,
757
539
    ) -> DrawResult {
758
        // If this element is inside of <text>, do not draw it.
759
        // The <text> takes care of it.
760
1.07k
        for an in node.ancestors() {
761
1.07k
            if matches!(&*an.borrow_element_data(), ElementData::Text(_)) {
762
0
                return Ok(viewport.empty_bbox());
763
1.07k
            }
764
        }
765
766
539
        let cascaded = CascadedValues::clone_with_node(cascaded, node);
767
539
        let values = cascaded.get();
768
769
539
        let elt = node.borrow_element();
770
771
539
        let link_is_empty = self.link.as_ref().map(|l| l.is_empty()).unwrap_or(true);
772
773
539
        let link_target = if link_is_empty {
774
35
            None
775
        } else {
776
504
            self.link.clone()
777
        };
778
779
539
        let stacking_ctx = Box::new(StackingContext::new_with_link(
780
539
            draw_ctx,
781
539
            acquired_nodes,
782
539
            &elt,
783
539
            values.transform(),
784
539
            values,
785
539
            viewport,
786
539
            link_target,
787
        ));
788
789
539
        draw_ctx.with_discrete_layer(
790
539
            &stacking_ctx,
791
539
            acquired_nodes,
792
539
            viewport,
793
539
            None,
794
            false,
795
539
            &mut |an, dc, new_viewport| node.draw_children(an, &cascaded, new_viewport, dc),
796
        )
797
539
    }
798
}
799
800
#[cfg(test)]
801
mod tests {
802
    use super::*;
803
804
    use crate::accept_language::UserLanguage;
805
    use crate::document::Document;
806
    use crate::dpi::Dpi;
807
    use crate::drawing_ctx::{RenderingConfiguration, SvgNesting};
808
809
    #[test]
810
    fn computes_group_extents() {
811
        let document = Document::load_from_bytes(
812
            br#"<?xml version="1.0" encoding="UTF-8"?>
813
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
814
  <g id="a">
815
    <g transform="translate(10, 10) scale(2, 3)">
816
      <rect x="0" y="0" width="5" height="10"/>
817
    </g>
818
    <rect x="0" y="0" width="5" height="10" transform="scale(2) translate(-10, -20)"/>
819
  </g>
820
</svg>
821
"#,
822
        );
823
824
        let a = document.lookup_internal_node("a").unwrap();
825
826
        let elt = a.borrow_element();
827
828
        let mut acquired_nodes = AcquiredNodes::new(&document, None);
829
        let cascaded = CascadedValues::new_from_node(&a);
830
831
        let dpi = Dpi::new(96.0, 96.0);
832
833
        let viewport = Viewport::new(dpi.clone(), 100.0, 100.0);
834
835
        let surface = cairo::RecordingSurface::create(cairo::Content::ColorAlpha, None).unwrap();
836
        let cr = cairo::Context::new(&surface).unwrap();
837
838
        let config = RenderingConfiguration {
839
            dpi,
840
            cancellable: None,
841
            user_language: UserLanguage::LanguageTags(crate::accept_language::LanguageTags::empty()),
842
            svg_nesting: SvgNesting::Standalone,
843
            measuring: false,
844
            testing: true,
845
        };
846
847
        let mut draw_ctx = DrawingCtx::new(Session::default(), &cr, &viewport, config, Vec::new());
848
849
        let layout = elt.layout(&a, &mut acquired_nodes, &cascaded, &viewport, &mut draw_ctx);
850
851
        match layout {
852
            Ok(Some(Layer {
853
                kind: LayerKind::Group(ref group),
854
                ..
855
            })) => {
856
                assert_eq!(group.extents, Some(Rect::new(-20.0, -40.0, 20.0, 40.0)));
857
            }
858
859
            Err(_) => panic!("layout should not produce an InternalRenderingError"),
860
861
            _ => panic!("layout object is not a LayerKind::Group"),
862
        }
863
    }
864
}