usvg_tree/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5/*!
6`usvg-tree` is an [SVG] tree representation used by [usvg].
7
8[SVG]: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
9[usvg]: https://github.com/RazrFalcon/resvg/tree/master/crates/usvg
10*/
11
12#![forbid(unsafe_code)]
13#![warn(missing_docs)]
14#![warn(missing_debug_implementations)]
15#![warn(missing_copy_implementations)]
16#![allow(clippy::collapsible_else_if)]
17#![allow(clippy::neg_cmp_op_on_partial_ord)]
18#![allow(clippy::too_many_arguments)]
19#![allow(clippy::derivable_impls)]
20
21pub mod filter;
22mod geom;
23mod text;
24
25use std::cell::RefCell;
26use std::rc::Rc;
27use std::sync::Arc;
28
29pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
30pub use svgtypes::{Align, AspectRatio};
31
32pub use tiny_skia_path;
33
34pub use crate::geom::*;
35pub use crate::text::*;
36
37/// An alias to `NormalizedF32`.
38pub type Opacity = NormalizedF32;
39
40/// A non-zero `f32`.
41///
42/// Just like `f32` but immutable and guarantee to never be zero.
43#[derive(Clone, Copy, Debug)]
44pub struct NonZeroF32(f32);
45
46impl NonZeroF32 {
47    /// Creates a new `NonZeroF32` value.
48    #[inline]
49    pub fn new(n: f32) -> Option<Self> {
50        if n.approx_eq_ulps(&0.0, 4) {
51            None
52        } else {
53            Some(NonZeroF32(n))
54        }
55    }
56
57    /// Returns an underlying value.
58    #[inline]
59    pub fn get(&self) -> f32 {
60        self.0
61    }
62}
63
64/// An element units.
65#[allow(missing_docs)]
66#[derive(Clone, Copy, PartialEq, Debug)]
67pub enum Units {
68    UserSpaceOnUse,
69    ObjectBoundingBox,
70}
71
72// `Units` cannot have a default value, because it changes depending on an element.
73
74/// A visibility property.
75///
76/// `visibility` attribute in the SVG.
77#[allow(missing_docs)]
78#[derive(Clone, Copy, PartialEq, Debug)]
79pub enum Visibility {
80    Visible,
81    Hidden,
82    Collapse,
83}
84
85impl Default for Visibility {
86    fn default() -> Self {
87        Self::Visible
88    }
89}
90
91/// A shape rendering method.
92///
93/// `shape-rendering` attribute in the SVG.
94#[derive(Clone, Copy, PartialEq, Debug)]
95#[allow(missing_docs)]
96pub enum ShapeRendering {
97    OptimizeSpeed,
98    CrispEdges,
99    GeometricPrecision,
100}
101
102impl ShapeRendering {
103    /// Checks if anti-aliasing should be enabled.
104    pub fn use_shape_antialiasing(self) -> bool {
105        match self {
106            ShapeRendering::OptimizeSpeed => false,
107            ShapeRendering::CrispEdges => false,
108            ShapeRendering::GeometricPrecision => true,
109        }
110    }
111}
112
113impl Default for ShapeRendering {
114    fn default() -> Self {
115        Self::GeometricPrecision
116    }
117}
118
119// TODO: remove?
120impl std::str::FromStr for ShapeRendering {
121    type Err = &'static str;
122
123    fn from_str(s: &str) -> Result<Self, Self::Err> {
124        match s {
125            "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
126            "crispEdges" => Ok(ShapeRendering::CrispEdges),
127            "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
128            _ => Err("invalid"),
129        }
130    }
131}
132
133/// A text rendering method.
134///
135/// `text-rendering` attribute in the SVG.
136#[allow(missing_docs)]
137#[derive(Clone, Copy, PartialEq, Debug)]
138pub enum TextRendering {
139    OptimizeSpeed,
140    OptimizeLegibility,
141    GeometricPrecision,
142}
143
144impl Default for TextRendering {
145    fn default() -> Self {
146        Self::OptimizeLegibility
147    }
148}
149
150impl std::str::FromStr for TextRendering {
151    type Err = &'static str;
152
153    fn from_str(s: &str) -> Result<Self, Self::Err> {
154        match s {
155            "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
156            "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
157            "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
158            _ => Err("invalid"),
159        }
160    }
161}
162
163/// An image rendering method.
164///
165/// `image-rendering` attribute in the SVG.
166#[allow(missing_docs)]
167#[derive(Clone, Copy, PartialEq, Debug)]
168pub enum ImageRendering {
169    OptimizeQuality,
170    OptimizeSpeed,
171}
172
173impl Default for ImageRendering {
174    fn default() -> Self {
175        Self::OptimizeQuality
176    }
177}
178
179impl std::str::FromStr for ImageRendering {
180    type Err = &'static str;
181
182    fn from_str(s: &str) -> Result<Self, Self::Err> {
183        match s {
184            "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
185            "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
186            _ => Err("invalid"),
187        }
188    }
189}
190
191/// A blending mode property.
192///
193/// `mix-blend-mode` attribute in the SVG.
194#[allow(missing_docs)]
195#[derive(Clone, Copy, PartialEq, Debug)]
196pub enum BlendMode {
197    Normal,
198    Multiply,
199    Screen,
200    Overlay,
201    Darken,
202    Lighten,
203    ColorDodge,
204    ColorBurn,
205    HardLight,
206    SoftLight,
207    Difference,
208    Exclusion,
209    Hue,
210    Saturation,
211    Color,
212    Luminosity,
213}
214
215impl Default for BlendMode {
216    fn default() -> Self {
217        Self::Normal
218    }
219}
220
221/// A spread method.
222///
223/// `spreadMethod` attribute in the SVG.
224#[allow(missing_docs)]
225#[derive(Clone, Copy, PartialEq, Debug)]
226pub enum SpreadMethod {
227    Pad,
228    Reflect,
229    Repeat,
230}
231
232impl Default for SpreadMethod {
233    fn default() -> Self {
234        Self::Pad
235    }
236}
237
238/// A generic gradient.
239#[derive(Clone, Debug)]
240pub struct BaseGradient {
241    /// Element's ID.
242    ///
243    /// Taken from the SVG itself.
244    /// Used only during SVG writing. `resvg` doesn't rely on this property.
245    pub id: String,
246
247    /// Coordinate system units.
248    ///
249    /// `gradientUnits` in SVG.
250    pub units: Units,
251
252    /// Gradient transform.
253    ///
254    /// `gradientTransform` in SVG.
255    pub transform: Transform,
256
257    /// Gradient spreading method.
258    ///
259    /// `spreadMethod` in SVG.
260    pub spread_method: SpreadMethod,
261
262    /// A list of `stop` elements.
263    pub stops: Vec<Stop>,
264}
265
266/// A linear gradient.
267///
268/// `linearGradient` element in SVG.
269#[allow(missing_docs)]
270#[derive(Clone, Debug)]
271pub struct LinearGradient {
272    /// Base gradient data.
273    pub base: BaseGradient,
274
275    pub x1: f32,
276    pub y1: f32,
277    pub x2: f32,
278    pub y2: f32,
279}
280
281impl std::ops::Deref for LinearGradient {
282    type Target = BaseGradient;
283
284    fn deref(&self) -> &Self::Target {
285        &self.base
286    }
287}
288
289/// A radial gradient.
290///
291/// `radialGradient` element in SVG.
292#[allow(missing_docs)]
293#[derive(Clone, Debug)]
294pub struct RadialGradient {
295    /// Base gradient data.
296    pub base: BaseGradient,
297
298    pub cx: f32,
299    pub cy: f32,
300    pub r: PositiveF32,
301    pub fx: f32,
302    pub fy: f32,
303}
304
305impl std::ops::Deref for RadialGradient {
306    type Target = BaseGradient;
307
308    fn deref(&self) -> &Self::Target {
309        &self.base
310    }
311}
312
313/// An alias to `NormalizedF32`.
314pub type StopOffset = NormalizedF32;
315
316/// Gradient's stop element.
317///
318/// `stop` element in SVG.
319#[derive(Clone, Copy, Debug)]
320pub struct Stop {
321    /// Gradient stop offset.
322    ///
323    /// `offset` in SVG.
324    pub offset: StopOffset,
325
326    /// Gradient stop color.
327    ///
328    /// `stop-color` in SVG.
329    pub color: Color,
330
331    /// Gradient stop opacity.
332    ///
333    /// `stop-opacity` in SVG.
334    pub opacity: Opacity,
335}
336
337/// A pattern element.
338///
339/// `pattern` element in SVG.
340#[derive(Clone, Debug)]
341pub struct Pattern {
342    /// Element's ID.
343    ///
344    /// Taken from the SVG itself.
345    /// Used only during SVG writing. `resvg` doesn't rely on this property.
346    pub id: String,
347
348    /// Coordinate system units.
349    ///
350    /// `patternUnits` in SVG.
351    pub units: Units,
352
353    // TODO: should not be accessible when `viewBox` is present.
354    /// Content coordinate system units.
355    ///
356    /// `patternContentUnits` in SVG.
357    pub content_units: Units,
358
359    /// Pattern transform.
360    ///
361    /// `patternTransform` in SVG.
362    pub transform: Transform,
363
364    /// Pattern rectangle.
365    ///
366    /// `x`, `y`, `width` and `height` in SVG.
367    pub rect: NonZeroRect,
368
369    /// Pattern viewbox.
370    pub view_box: Option<ViewBox>,
371
372    /// Pattern children.
373    pub root: Group,
374}
375
376/// An alias to `NonZeroPositiveF32`.
377pub type StrokeWidth = NonZeroPositiveF32;
378
379/// A `stroke-miterlimit` value.
380///
381/// Just like `f32` but immutable and guarantee to be >=1.0.
382#[derive(Clone, Copy, Debug)]
383pub struct StrokeMiterlimit(f32);
384
385impl StrokeMiterlimit {
386    /// Creates a new `StrokeMiterlimit` value.
387    #[inline]
388    pub fn new(n: f32) -> Self {
389        debug_assert!(n.is_finite());
390        debug_assert!(n >= 1.0);
391
392        let n = if !(n >= 1.0) { 1.0 } else { n };
393
394        StrokeMiterlimit(n)
395    }
396
397    /// Returns an underlying value.
398    #[inline]
399    pub fn get(&self) -> f32 {
400        self.0
401    }
402}
403
404impl Default for StrokeMiterlimit {
405    #[inline]
406    fn default() -> Self {
407        StrokeMiterlimit::new(4.0)
408    }
409}
410
411impl From<f32> for StrokeMiterlimit {
412    #[inline]
413    fn from(n: f32) -> Self {
414        Self::new(n)
415    }
416}
417
418impl PartialEq for StrokeMiterlimit {
419    #[inline]
420    fn eq(&self, other: &Self) -> bool {
421        self.0.approx_eq_ulps(&other.0, 4)
422    }
423}
424
425/// A line cap.
426///
427/// `stroke-linecap` attribute in the SVG.
428#[allow(missing_docs)]
429#[derive(Clone, Copy, PartialEq, Debug)]
430pub enum LineCap {
431    Butt,
432    Round,
433    Square,
434}
435
436impl Default for LineCap {
437    fn default() -> Self {
438        Self::Butt
439    }
440}
441
442/// A line join.
443///
444/// `stroke-linejoin` attribute in the SVG.
445#[allow(missing_docs)]
446#[derive(Clone, Copy, PartialEq, Debug)]
447pub enum LineJoin {
448    Miter,
449    MiterClip,
450    Round,
451    Bevel,
452}
453
454impl Default for LineJoin {
455    fn default() -> Self {
456        Self::Miter
457    }
458}
459
460/// A stroke style.
461#[allow(missing_docs)]
462#[derive(Clone, Debug)]
463pub struct Stroke {
464    pub paint: Paint,
465    pub dasharray: Option<Vec<f32>>,
466    pub dashoffset: f32,
467    pub miterlimit: StrokeMiterlimit,
468    pub opacity: Opacity,
469    pub width: StrokeWidth,
470    pub linecap: LineCap,
471    pub linejoin: LineJoin,
472}
473
474impl Default for Stroke {
475    fn default() -> Self {
476        Stroke {
477            // The actual default color is `none`,
478            // but to simplify the `Stroke` object creation we use `black`.
479            paint: Paint::Color(Color::black()),
480            dasharray: None,
481            dashoffset: 0.0,
482            miterlimit: StrokeMiterlimit::default(),
483            opacity: Opacity::ONE,
484            width: StrokeWidth::new(1.0).unwrap(),
485            linecap: LineCap::default(),
486            linejoin: LineJoin::default(),
487        }
488    }
489}
490
491impl Stroke {
492    /// Converts into a `tiny_skia_path::Stroke` type.
493    pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
494        let mut stroke = tiny_skia_path::Stroke {
495            width: self.width.get(),
496            miter_limit: self.miterlimit.get(),
497            line_cap: match self.linecap {
498                LineCap::Butt => tiny_skia_path::LineCap::Butt,
499                LineCap::Round => tiny_skia_path::LineCap::Round,
500                LineCap::Square => tiny_skia_path::LineCap::Square,
501            },
502            line_join: match self.linejoin {
503                LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
504                LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
505                LineJoin::Round => tiny_skia_path::LineJoin::Round,
506                LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
507            },
508            // According to the spec, dash should not be accounted during
509            // bbox calculation.
510            dash: None,
511        };
512
513        if let Some(ref list) = self.dasharray {
514            stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
515        }
516
517        stroke
518    }
519}
520
521/// A fill rule.
522///
523/// `fill-rule` attribute in the SVG.
524#[allow(missing_docs)]
525#[derive(Clone, Copy, PartialEq, Debug)]
526pub enum FillRule {
527    NonZero,
528    EvenOdd,
529}
530
531impl Default for FillRule {
532    fn default() -> Self {
533        Self::NonZero
534    }
535}
536
537/// A fill style.
538#[allow(missing_docs)]
539#[derive(Clone, Debug)]
540pub struct Fill {
541    pub paint: Paint,
542    pub opacity: Opacity,
543    pub rule: FillRule,
544}
545
546impl Fill {
547    /// Creates a `Fill` from `Paint`.
548    ///
549    /// `opacity` and `rule` will be set to default values.
550    pub fn from_paint(paint: Paint) -> Self {
551        Fill {
552            paint,
553            ..Fill::default()
554        }
555    }
556}
557
558impl Default for Fill {
559    fn default() -> Self {
560        Fill {
561            paint: Paint::Color(Color::black()),
562            opacity: Opacity::ONE,
563            rule: FillRule::default(),
564        }
565    }
566}
567
568/// A 8-bit RGB color.
569#[derive(Clone, Copy, PartialEq, Debug)]
570#[allow(missing_docs)]
571pub struct Color {
572    pub red: u8,
573    pub green: u8,
574    pub blue: u8,
575}
576
577impl Color {
578    /// Constructs a new `Color` from RGB values.
579    #[inline]
580    pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
581        Color { red, green, blue }
582    }
583
584    /// Constructs a new `Color` set to black.
585    #[inline]
586    pub fn black() -> Color {
587        Color::new_rgb(0, 0, 0)
588    }
589
590    /// Constructs a new `Color` set to white.
591    #[inline]
592    pub fn white() -> Color {
593        Color::new_rgb(255, 255, 255)
594    }
595}
596
597/// A paint style.
598///
599/// `paint` value type in the SVG.
600#[allow(missing_docs)]
601#[derive(Clone, Debug)]
602pub enum Paint {
603    Color(Color),
604    LinearGradient(Rc<LinearGradient>),
605    RadialGradient(Rc<RadialGradient>),
606    Pattern(Rc<RefCell<Pattern>>),
607}
608
609impl Paint {
610    /// Returns paint server units.
611    ///
612    /// Returns `None` for `Color`.
613    #[inline]
614    pub fn units(&self) -> Option<Units> {
615        match self {
616            Self::Color(_) => None,
617            Self::LinearGradient(ref lg) => Some(lg.units),
618            Self::RadialGradient(ref rg) => Some(rg.units),
619            Self::Pattern(ref patt) => Some(patt.borrow().units),
620        }
621    }
622}
623
624impl PartialEq for Paint {
625    #[inline]
626    fn eq(&self, other: &Self) -> bool {
627        match (self, other) {
628            (Self::Color(lc), Self::Color(rc)) => lc == rc,
629            (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Rc::ptr_eq(lg1, lg2),
630            (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Rc::ptr_eq(rg1, rg2),
631            (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Rc::ptr_eq(p1, p2),
632            _ => false,
633        }
634    }
635}
636
637/// A clip-path element.
638///
639/// `clipPath` element in SVG.
640#[derive(Clone, Debug)]
641pub struct ClipPath {
642    /// Element's ID.
643    ///
644    /// Taken from the SVG itself.
645    /// Used only during SVG writing. `resvg` doesn't rely on this property.
646    pub id: String,
647
648    /// Coordinate system units.
649    ///
650    /// `clipPathUnits` in SVG.
651    pub units: Units,
652
653    /// Clip path transform.
654    ///
655    /// `transform` in SVG.
656    pub transform: Transform,
657
658    /// Additional clip path.
659    ///
660    /// `clip-path` in SVG.
661    pub clip_path: Option<SharedClipPath>,
662
663    /// Clip path children.
664    pub root: Group,
665}
666
667/// An alias for a shared `ClipPath`.
668pub type SharedClipPath = Rc<RefCell<ClipPath>>;
669
670impl Default for ClipPath {
671    fn default() -> Self {
672        ClipPath {
673            id: String::new(),
674            units: Units::UserSpaceOnUse,
675            transform: Transform::default(),
676            clip_path: None,
677            root: Group::default(),
678        }
679    }
680}
681
682/// A mask type.
683#[derive(Clone, Copy, PartialEq, Debug)]
684pub enum MaskType {
685    /// Indicates that the luminance values of the mask should be used.
686    Luminance,
687    /// Indicates that the alpha values of the mask should be used.
688    Alpha,
689}
690
691impl Default for MaskType {
692    fn default() -> Self {
693        Self::Luminance
694    }
695}
696
697/// A mask element.
698///
699/// `mask` element in SVG.
700#[derive(Clone, Debug)]
701pub struct Mask {
702    /// Element's ID.
703    ///
704    /// Taken from the SVG itself.
705    /// Used only during SVG writing. `resvg` doesn't rely on this property.
706    pub id: String,
707
708    /// Coordinate system units.
709    ///
710    /// `maskUnits` in SVG.
711    pub units: Units,
712
713    /// Content coordinate system units.
714    ///
715    /// `maskContentUnits` in SVG.
716    pub content_units: Units,
717
718    /// Mask rectangle.
719    ///
720    /// `x`, `y`, `width` and `height` in SVG.
721    pub rect: NonZeroRect,
722
723    /// Mask type.
724    ///
725    /// `mask-type` in SVG.
726    pub kind: MaskType,
727
728    /// Additional mask.
729    ///
730    /// `mask` in SVG.
731    pub mask: Option<SharedMask>,
732
733    /// Mask children.
734    pub root: Group,
735}
736
737/// An alias for a shared `Mask`.
738pub type SharedMask = Rc<RefCell<Mask>>;
739
740/// Node's kind.
741#[allow(missing_docs)]
742#[derive(Clone, Debug)]
743pub enum Node {
744    Group(Box<Group>),
745    Path(Box<Path>),
746    Image(Box<Image>),
747    Text(Box<Text>),
748}
749
750impl Node {
751    /// Returns node's ID.
752    pub fn id(&self) -> &str {
753        match self {
754            Node::Group(ref e) => e.id.as_str(),
755            Node::Path(ref e) => e.id.as_str(),
756            Node::Image(ref e) => e.id.as_str(),
757            Node::Text(ref e) => e.id.as_str(),
758        }
759    }
760
761    /// Returns node's absolute transform.
762    ///
763    /// If a current node doesn't support transformation - a default
764    /// transform will be returned.
765    ///
766    /// This method is cheap since absolute transforms are already resolved.
767    pub fn abs_transform(&self) -> Transform {
768        match self {
769            Node::Group(ref group) => group.abs_transform,
770            Node::Path(ref path) => path.abs_transform,
771            Node::Image(ref image) => image.abs_transform,
772            Node::Text(ref text) => text.abs_transform,
773        }
774    }
775
776    /// Returns node's bounding box in object coordinates, if any.
777    ///
778    /// This method is cheap since bounding boxes are already calculated.
779    pub fn bounding_box(&self) -> Option<Rect> {
780        match self {
781            Node::Group(ref group) => group.bounding_box,
782            Node::Path(ref path) => path.bounding_box,
783            Node::Image(ref image) => image.bounding_box.map(|r| r.to_rect()),
784            Node::Text(ref text) => text.bounding_box.map(|r| r.to_rect()),
785        }
786    }
787
788    /// Returns node's bounding box in canvas coordinates, if any.
789    ///
790    /// This method is cheap since bounding boxes are already calculated.
791    pub fn abs_bounding_box(&self) -> Option<Rect> {
792        self.bounding_box()?.transform(self.abs_transform())
793    }
794
795    /// Returns node's bounding box, including stroke, in object coordinates, if any.
796    ///
797    /// This method is cheap since bounding boxes are already calculated.
798    pub fn stroke_bounding_box(&self) -> Option<NonZeroRect> {
799        match self {
800            Node::Group(ref group) => group.stroke_bounding_box,
801            Node::Path(ref path) => path.stroke_bounding_box,
802            // Image cannot be stroked.
803            Node::Image(ref image) => image.bounding_box,
804            Node::Text(ref text) => text.stroke_bounding_box,
805        }
806    }
807
808    /// Returns node's bounding box, including stroke, in canvas coordinates, if any.
809    ///
810    /// This method is cheap since bounding boxes are already calculated.
811    pub fn abs_stroke_bounding_box(&self) -> Option<NonZeroRect> {
812        self.stroke_bounding_box()?.transform(self.abs_transform())
813    }
814
815    /// Calls a closure for each subroot this `Node` has.
816    ///
817    /// The [`Tree::root`](Tree::root) field contain only render-able SVG elements.
818    /// But some elements, specifically clip paths, masks, patterns and feImage
819    /// can store their own SVG subtrees.
820    /// And while one can access them manually, it's pretty verbose.
821    /// This methods allows looping over _all_ SVG elements present in the `Tree`.
822    ///
823    /// # Example
824    ///
825    /// ```no_run
826    /// fn all_nodes(parent: &usvg_tree::Group) {
827    ///     for node in &parent.children {
828    ///         // do stuff...
829    ///
830    ///         if let usvg_tree::Node::Group(ref g) = node {
831    ///             all_nodes(g);
832    ///         }
833    ///
834    ///         // handle subroots as well
835    ///         node.subroots(|subroot| all_nodes(subroot));
836    ///     }
837    /// }
838    /// ```
839    pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
840        match self {
841            Node::Group(ref group) => group.subroots(&mut f),
842            Node::Path(ref path) => path.subroots(&mut f),
843            Node::Image(ref image) => image.subroots(&mut f),
844            Node::Text(ref text) => text.subroots(&mut f),
845        }
846    }
847
848    /// Calls a closure for each subroot this `Node` has.
849    ///
850    /// A mutable version of `subroots()`.
851    pub fn subroots_mut<F: FnMut(&mut Group)>(&mut self, mut f: F) {
852        match self {
853            Node::Group(ref mut group) => group.subroots_mut(&mut f),
854            Node::Path(ref mut path) => path.subroots_mut(&mut f),
855            Node::Image(ref mut image) => image.subroots_mut(&mut f),
856            Node::Text(ref mut text) => text.subroots_mut(&mut f),
857        }
858    }
859}
860
861/// A group container.
862///
863/// The preprocessor will remove all groups that don't impact rendering.
864/// Those that left is just an indicator that a new canvas should be created.
865///
866/// `g` element in SVG.
867#[derive(Clone, Debug)]
868pub struct Group {
869    /// Element's ID.
870    ///
871    /// Taken from the SVG itself.
872    /// Isn't automatically generated.
873    /// Can be empty.
874    pub id: String,
875
876    /// Element's transform.
877    ///
878    /// This is a relative transform. The one that is set via the `transform` attribute in SVG.
879    pub transform: Transform,
880
881    /// Element's absolute transform.
882    ///
883    /// Contains all ancestors transforms.
884    ///
885    /// Will be set after calling `usvg::Tree::postprocess`.
886    ///
887    /// Note that subroots, like clipPaths, masks and patterns, have their own root transform,
888    /// which isn't affected by the node that references this subroot.
889    pub abs_transform: Transform,
890
891    /// Group opacity.
892    ///
893    /// After the group is rendered we should combine
894    /// it with a parent group using the specified opacity.
895    pub opacity: Opacity,
896
897    /// Group blend mode.
898    ///
899    /// `mix-blend-mode` in SVG.
900    pub blend_mode: BlendMode,
901
902    /// Group isolation.
903    ///
904    /// `isolation` in SVG.
905    pub isolate: bool,
906
907    /// Element's clip path.
908    pub clip_path: Option<SharedClipPath>,
909
910    /// Element's mask.
911    pub mask: Option<SharedMask>,
912
913    /// Element's filters.
914    pub filters: Vec<filter::SharedFilter>,
915
916    /// Element's object bounding box.
917    ///
918    /// `objectBoundingBox` in SVG terms. Meaning it doesn't affected by parent transforms.
919    ///
920    /// Can be set to `None` in case of an empty group.
921    ///
922    /// Will be set after calling `usvg::Tree::postprocess`.
923    pub bounding_box: Option<Rect>,
924
925    /// Element's object bounding box including stroke.
926    ///
927    /// Similar to `bounding_box`, but includes stroke.
928    pub stroke_bounding_box: Option<NonZeroRect>,
929
930    /// Element's "layer" bounding box in object units.
931    ///
932    /// Conceptually, this is `stroke_bounding_box` expanded and/or clipped
933    /// by `filters_bounding_box`, but also including all the children.
934    /// This is the bounding box `resvg` will later use to allocate layers/pixmaps
935    /// during isolated groups rendering.
936    ///
937    /// Only groups have it, because only groups can have filters.
938    /// For other nodes layer bounding box is the same as stroke bounding box.
939    ///
940    /// Unlike other bounding boxes, cannot have zero size.
941    ///
942    /// Will be set after calling `usvg::Tree::postprocess`.
943    pub layer_bounding_box: Option<NonZeroRect>,
944
945    /// Group's children.
946    pub children: Vec<Node>,
947}
948
949impl Default for Group {
950    fn default() -> Self {
951        Group {
952            id: String::new(),
953            transform: Transform::default(),
954            abs_transform: Transform::default(),
955            opacity: Opacity::ONE,
956            blend_mode: BlendMode::Normal,
957            isolate: false,
958            clip_path: None,
959            mask: None,
960            filters: Vec::new(),
961            bounding_box: None,
962            stroke_bounding_box: None,
963            layer_bounding_box: None,
964            children: Vec::new(),
965        }
966    }
967}
968
969impl Group {
970    /// Checks if this group should be isolated during rendering.
971    pub fn should_isolate(&self) -> bool {
972        self.isolate
973            || self.opacity != Opacity::ONE
974            || self.clip_path.is_some()
975            || self.mask.is_some()
976            || !self.filters.is_empty()
977            || self.blend_mode != BlendMode::Normal // TODO: probably not needed?
978    }
979
980    /// Returns `true` if the group has any children.
981    pub fn has_children(&self) -> bool {
982        !self.children.is_empty()
983    }
984
985    /// Returns node's bounding box in canvas coordinates.
986    pub fn abs_bounding_box(&self) -> Option<Rect> {
987        self.bounding_box?.transform(self.abs_transform)
988    }
989
990    /// Calculates a node's filter bounding box.
991    ///
992    /// Filters with `objectBoundingBox` and missing or zero `bounding_box` would be ignored.
993    ///
994    /// Note that a filter region can act like a clipping rectangle,
995    /// therefore this function can produce a bounding box smaller than `bounding_box`.
996    ///
997    /// Returns `None` when then group has no filters.
998    ///
999    /// This function is very fast, that's why we do not store this bbox as a `Group` field.
1000    pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1001        let mut full_region = BBox::default();
1002
1003        for filter in &self.filters {
1004            let mut region = filter.borrow().rect;
1005
1006            if filter.borrow().units == Units::ObjectBoundingBox {
1007                let object_bbox = self.bounding_box.and_then(|bbox| bbox.to_non_zero_rect());
1008                if let Some(object_bbox) = object_bbox {
1009                    region = region.bbox_transform(object_bbox);
1010                } else {
1011                    // Skip filters with `objectBoundingBox` on nodes without a bbox.
1012                    continue;
1013                }
1014            }
1015
1016            full_region = full_region.expand(BBox::from(region));
1017        }
1018
1019        full_region.to_non_zero_rect()
1020    }
1021
1022    /// Calculates a node's filter bounding box in canvas coordinates.
1023    pub fn abs_filters_bounding_box(&self) -> Option<NonZeroRect> {
1024        self.filters_bounding_box()?.transform(self.abs_transform)
1025    }
1026
1027    fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1028        if let Some(ref clip) = self.clip_path {
1029            f(&clip.borrow().root);
1030
1031            if let Some(ref sub_clip) = clip.borrow().clip_path {
1032                f(&sub_clip.borrow().root);
1033            }
1034        }
1035
1036        if let Some(ref mask) = self.mask {
1037            f(&mask.borrow().root);
1038
1039            if let Some(ref sub_mask) = mask.borrow().mask {
1040                f(&sub_mask.borrow().root);
1041            }
1042        }
1043
1044        for filter in &self.filters {
1045            for primitive in &filter.borrow().primitives {
1046                if let filter::Kind::Image(ref image) = primitive.kind {
1047                    if let filter::ImageKind::Use(ref use_node) = image.data {
1048                        f(use_node);
1049                    }
1050                }
1051            }
1052        }
1053    }
1054
1055    fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1056        if let Some(ref clip) = self.clip_path {
1057            f(&mut clip.borrow_mut().root);
1058
1059            if let Some(ref sub_clip) = clip.borrow().clip_path {
1060                f(&mut sub_clip.borrow_mut().root);
1061            }
1062        }
1063
1064        if let Some(ref mask) = self.mask {
1065            f(&mut mask.borrow_mut().root);
1066
1067            if let Some(ref sub_mask) = mask.borrow_mut().mask {
1068                f(&mut sub_mask.borrow_mut().root);
1069            }
1070        }
1071
1072        for filter in &mut self.filters {
1073            for primitive in &mut filter.borrow_mut().primitives {
1074                if let filter::Kind::Image(ref mut image) = primitive.kind {
1075                    if let filter::ImageKind::Use(ref mut use_node) = image.data {
1076                        f(use_node);
1077                    }
1078                }
1079            }
1080        }
1081    }
1082}
1083
1084/// Representation of the [`paint-order`] property.
1085///
1086/// `usvg` will handle `markers` automatically,
1087/// therefore we provide only `fill` and `stroke` variants.
1088///
1089/// [`paint-order`]: https://www.w3.org/TR/SVG2/painting.html#PaintOrder
1090#[derive(Clone, Copy, PartialEq, Debug)]
1091#[allow(missing_docs)]
1092pub enum PaintOrder {
1093    FillAndStroke,
1094    StrokeAndFill,
1095}
1096
1097impl Default for PaintOrder {
1098    fn default() -> Self {
1099        Self::FillAndStroke
1100    }
1101}
1102
1103/// A path element.
1104#[derive(Clone, Debug)]
1105pub struct Path {
1106    /// Element's ID.
1107    ///
1108    /// Taken from the SVG itself.
1109    /// Isn't automatically generated.
1110    /// Can be empty.
1111    pub id: String,
1112
1113    /// Element visibility.
1114    pub visibility: Visibility,
1115
1116    /// Fill style.
1117    pub fill: Option<Fill>,
1118
1119    /// Stroke style.
1120    pub stroke: Option<Stroke>,
1121
1122    /// Fill and stroke paint order.
1123    ///
1124    /// Since markers will be replaced with regular nodes automatically,
1125    /// `usvg` doesn't provide the `markers` order type. It's was already done.
1126    ///
1127    /// `paint-order` in SVG.
1128    pub paint_order: PaintOrder,
1129
1130    /// Rendering mode.
1131    ///
1132    /// `shape-rendering` in SVG.
1133    pub rendering_mode: ShapeRendering,
1134
1135    /// Segments list.
1136    ///
1137    /// All segments are in absolute coordinates.
1138    pub data: Rc<tiny_skia_path::Path>,
1139
1140    /// Element's absolute transform.
1141    ///
1142    /// Contains all ancestors transforms.
1143    ///
1144    /// Will be set after calling `usvg::Tree::postprocess`.
1145    ///
1146    /// Note that this is not the relative transform present in SVG.
1147    /// The SVG one would be set only on groups.
1148    pub abs_transform: Transform,
1149
1150    /// Element's object bounding box.
1151    ///
1152    /// `objectBoundingBox` in SVG terms. Meaning it doesn't affected by parent transforms.
1153    ///
1154    /// Will be set after calling `usvg::Tree::postprocess`.
1155    pub bounding_box: Option<Rect>,
1156
1157    /// Element's object bounding box including stroke.
1158    ///
1159    /// Similar to `bounding_box`, but includes stroke.
1160    ///
1161    /// Will have the same value as `bounding_box` when path has no stroke.
1162    pub stroke_bounding_box: Option<NonZeroRect>,
1163}
1164
1165impl Path {
1166    /// Creates a new `Path` with default values.
1167    pub fn new(data: Rc<tiny_skia_path::Path>) -> Self {
1168        Path {
1169            id: String::new(),
1170            visibility: Visibility::Visible,
1171            fill: None,
1172            stroke: None,
1173            paint_order: PaintOrder::default(),
1174            rendering_mode: ShapeRendering::default(),
1175            data,
1176            abs_transform: Transform::default(),
1177            bounding_box: None,
1178            stroke_bounding_box: None,
1179        }
1180    }
1181
1182    /// Calculates and sets path's stroke bounding box.
1183    ///
1184    /// This operation is expensive.
1185    pub fn calculate_stroke_bounding_box(&self) -> Option<NonZeroRect> {
1186        let stroke = self.stroke.as_ref()?;
1187        let mut stroke = stroke.to_tiny_skia();
1188        // According to the spec, dash should not be accounted during bbox calculation.
1189        stroke.dash = None;
1190
1191        // Expensive, but there is not much we can do about it.
1192        if let Some(stroked_path) = self.data.stroke(&stroke, 1.0) {
1193            // A stroked path cannot have zero width or height,
1194            // therefore we use `NonZeroRect` here.
1195            return stroked_path
1196                .compute_tight_bounds()
1197                .and_then(|r| r.to_non_zero_rect());
1198        }
1199
1200        None
1201    }
1202
1203    fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1204        if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1205            f(&patt.borrow().root)
1206        }
1207        if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1208            f(&patt.borrow().root)
1209        }
1210    }
1211
1212    fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1213        if let Some(Paint::Pattern(ref mut patt)) = self.fill.as_mut().map(|f| &mut f.paint) {
1214            f(&mut patt.borrow_mut().root)
1215        }
1216        if let Some(Paint::Pattern(ref mut patt)) = self.stroke.as_mut().map(|f| &mut f.paint) {
1217            f(&mut patt.borrow_mut().root)
1218        }
1219    }
1220}
1221
1222/// An embedded image kind.
1223#[derive(Clone)]
1224pub enum ImageKind {
1225    /// A reference to raw JPEG data. Should be decoded by the caller.
1226    JPEG(Arc<Vec<u8>>),
1227    /// A reference to raw PNG data. Should be decoded by the caller.
1228    PNG(Arc<Vec<u8>>),
1229    /// A reference to raw GIF data. Should be decoded by the caller.
1230    GIF(Arc<Vec<u8>>),
1231    /// A preprocessed SVG tree. Can be rendered as is.
1232    SVG(Tree),
1233}
1234
1235impl std::fmt::Debug for ImageKind {
1236    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1237        match self {
1238            ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1239            ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1240            ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1241            ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1242        }
1243    }
1244}
1245
1246/// A raster image element.
1247///
1248/// `image` element in SVG.
1249#[derive(Clone, Debug)]
1250pub struct Image {
1251    /// Element's ID.
1252    ///
1253    /// Taken from the SVG itself.
1254    /// Isn't automatically generated.
1255    /// Can be empty.
1256    pub id: String,
1257
1258    /// Element visibility.
1259    pub visibility: Visibility,
1260
1261    /// An image rectangle in which it should be fit.
1262    ///
1263    /// Combination of the `x`, `y`, `width`, `height` and `preserveAspectRatio`
1264    /// attributes.
1265    pub view_box: ViewBox,
1266
1267    /// Rendering mode.
1268    ///
1269    /// `image-rendering` in SVG.
1270    pub rendering_mode: ImageRendering,
1271
1272    /// Image data.
1273    pub kind: ImageKind,
1274
1275    /// Element's absolute transform.
1276    ///
1277    /// Contains all ancestors transforms.
1278    ///
1279    /// Will be set after calling `usvg::Tree::postprocess`.
1280    ///
1281    /// Note that this is not the relative transform present in SVG.
1282    /// The SVG one would be set only on groups.
1283    pub abs_transform: Transform,
1284
1285    /// Element's object bounding box.
1286    ///
1287    /// `objectBoundingBox` in SVG terms. Meaning it doesn't affected by parent transforms.
1288    ///
1289    /// Will be set after calling `usvg::Tree::postprocess`.
1290    pub bounding_box: Option<NonZeroRect>,
1291}
1292
1293impl Image {
1294    fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1295        if let ImageKind::SVG(ref tree) = self.kind {
1296            f(&tree.root)
1297        }
1298    }
1299
1300    fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1301        if let ImageKind::SVG(ref mut tree) = self.kind {
1302            f(&mut tree.root)
1303        }
1304    }
1305}
1306
1307/// A nodes tree container.
1308#[allow(missing_debug_implementations)]
1309#[derive(Clone, Debug)]
1310pub struct Tree {
1311    /// Image size.
1312    ///
1313    /// Size of an image that should be created to fit the SVG.
1314    ///
1315    /// `width` and `height` in SVG.
1316    pub size: Size,
1317
1318    /// SVG viewbox.
1319    ///
1320    /// Specifies which part of the SVG image should be rendered.
1321    ///
1322    /// `viewBox` and `preserveAspectRatio` in SVG.
1323    pub view_box: ViewBox,
1324
1325    /// The root element of the SVG tree.
1326    pub root: Group,
1327}
1328
1329impl Tree {
1330    /// Returns a renderable node by ID.
1331    ///
1332    /// If an empty ID is provided, than this method will always return `None`.
1333    pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1334        if id.is_empty() {
1335            return None;
1336        }
1337
1338        node_by_id(&self.root, id)
1339    }
1340
1341    /// Checks if the current tree has any text nodes.
1342    pub fn has_text_nodes(&self) -> bool {
1343        has_text_nodes(&self.root)
1344    }
1345
1346    /// Calls a closure for each [`Paint`] in the tree.
1347    ///
1348    /// Doesn't guarantee to have unique paint servers. A caller must deduplicate them manually.
1349    pub fn paint_servers<F: FnMut(&Paint)>(&self, mut f: F) {
1350        loop_over_paint_servers(&self.root, &mut f)
1351    }
1352
1353    /// Calls a closure for each [`ClipPath`] in the tree.
1354    ///
1355    /// Doesn't guarantee to have unique clip paths. A caller must deduplicate them manually.
1356    pub fn clip_paths<F: FnMut(SharedClipPath)>(&self, mut f: F) {
1357        loop_over_clip_paths(&self.root, &mut f)
1358    }
1359
1360    /// Calls a closure for each [`Mask`] in the tree.
1361    ///
1362    /// Doesn't guarantee to have unique masks. A caller must deduplicate them manually.
1363    pub fn masks<F: FnMut(SharedMask)>(&self, mut f: F) {
1364        loop_over_masks(&self.root, &mut f)
1365    }
1366
1367    /// Calls a closure for each [`Filter`](filter::Filter) in the tree.
1368    ///
1369    /// Doesn't guarantee to have unique filters. A caller must deduplicate them manually.
1370    pub fn filters<F: FnMut(filter::SharedFilter)>(&self, mut f: F) {
1371        loop_over_filters(&self.root, &mut f)
1372    }
1373
1374    /// Calculates absolute transforms for all nodes in the tree.
1375    ///
1376    /// A low-level methods. Prefer `usvg::Tree::postprocess` instead.
1377    pub fn calculate_abs_transforms(&mut self) {
1378        self.root.calculate_abs_transforms(Transform::identity());
1379    }
1380
1381    /// Calculates bounding boxes for all nodes in the tree.
1382    ///
1383    /// A low-level methods. Prefer `usvg::Tree::postprocess` instead.
1384    pub fn calculate_bounding_boxes(&mut self) {
1385        self.root.calculate_bounding_boxes();
1386    }
1387}
1388
1389fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1390    for child in &parent.children {
1391        if child.id() == id {
1392            return Some(child);
1393        }
1394
1395        if let Node::Group(ref g) = child {
1396            if let Some(n) = node_by_id(g, id) {
1397                return Some(n);
1398            }
1399        }
1400    }
1401
1402    None
1403}
1404
1405fn has_text_nodes(root: &Group) -> bool {
1406    for node in &root.children {
1407        if let Node::Text(_) = node {
1408            return true;
1409        }
1410
1411        let mut has_text = false;
1412
1413        if let Node::Image(ref image) = node {
1414            if let ImageKind::SVG(ref tree) = image.kind {
1415                if has_text_nodes(&tree.root) {
1416                    has_text = true;
1417                }
1418            }
1419        }
1420
1421        node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1422
1423        if has_text {
1424            return true;
1425        }
1426    }
1427
1428    true
1429}
1430
1431fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1432    fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1433        if let Some(paint) = paint {
1434            f(paint);
1435        }
1436    }
1437
1438    for node in &parent.children {
1439        match node {
1440            Node::Group(ref group) => loop_over_paint_servers(group, f),
1441            Node::Path(ref path) => {
1442                push(path.fill.as_ref().map(|f| &f.paint), f);
1443                push(path.stroke.as_ref().map(|f| &f.paint), f);
1444            }
1445            Node::Image(_) => {}
1446            Node::Text(ref text) => {
1447                // A flattened text should be ignored, otherwise we would have duplicates.
1448                if text.flattened.is_none() {
1449                    for chunk in &text.chunks {
1450                        for span in &chunk.spans {
1451                            push(span.fill.as_ref().map(|f| &f.paint), f);
1452                            push(span.stroke.as_ref().map(|f| &f.paint), f);
1453
1454                            if let Some(ref underline) = span.decoration.underline {
1455                                push(underline.fill.as_ref().map(|f| &f.paint), f);
1456                                push(underline.stroke.as_ref().map(|f| &f.paint), f);
1457                            }
1458
1459                            if let Some(ref overline) = span.decoration.overline {
1460                                push(overline.fill.as_ref().map(|f| &f.paint), f);
1461                                push(overline.stroke.as_ref().map(|f| &f.paint), f);
1462                            }
1463
1464                            if let Some(ref line_through) = span.decoration.line_through {
1465                                push(line_through.fill.as_ref().map(|f| &f.paint), f);
1466                                push(line_through.stroke.as_ref().map(|f| &f.paint), f);
1467                            }
1468                        }
1469                    }
1470                }
1471            }
1472        }
1473
1474        node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1475    }
1476}
1477
1478fn loop_over_clip_paths(parent: &Group, f: &mut dyn FnMut(SharedClipPath)) {
1479    for node in &parent.children {
1480        if let Node::Group(ref g) = node {
1481            if let Some(ref clip) = g.clip_path {
1482                f(clip.clone());
1483
1484                if let Some(ref sub_clip) = clip.borrow().clip_path {
1485                    f(sub_clip.clone());
1486                }
1487            }
1488        }
1489
1490        node.subroots(|subroot| loop_over_clip_paths(subroot, f));
1491
1492        if let Node::Group(ref g) = node {
1493            loop_over_clip_paths(g, f);
1494        }
1495    }
1496}
1497
1498fn loop_over_masks(parent: &Group, f: &mut dyn FnMut(SharedMask)) {
1499    for node in &parent.children {
1500        if let Node::Group(ref g) = node {
1501            if let Some(ref mask) = g.mask {
1502                f(mask.clone());
1503
1504                if let Some(ref sub_mask) = mask.borrow().mask {
1505                    f(sub_mask.clone());
1506                }
1507            }
1508
1509            loop_over_masks(g, f);
1510        }
1511
1512        node.subroots(|subroot| loop_over_masks(subroot, f));
1513
1514        if let Node::Group(ref g) = node {
1515            loop_over_masks(g, f);
1516        }
1517    }
1518}
1519
1520fn loop_over_filters(parent: &Group, f: &mut dyn FnMut(filter::SharedFilter)) {
1521    for node in &parent.children {
1522        if let Node::Group(ref g) = node {
1523            for filter in &g.filters {
1524                f(filter.clone());
1525            }
1526        }
1527
1528        node.subroots(|subroot| loop_over_filters(subroot, f));
1529
1530        if let Node::Group(ref g) = node {
1531            loop_over_filters(g, f);
1532        }
1533    }
1534}
1535
1536impl Group {
1537    /// Calculates absolute transforms for all children of this group.
1538    ///
1539    /// A low-level methods. Prefer `usvg::Tree::postprocess` instead.
1540    pub fn calculate_abs_transforms(&mut self, transform: Transform) {
1541        for node in &mut self.children {
1542            match node {
1543                Node::Group(ref mut group) => {
1544                    let abs_ts = transform.pre_concat(group.transform);
1545                    group.abs_transform = abs_ts;
1546                    group.calculate_abs_transforms(abs_ts);
1547                }
1548                Node::Path(ref mut path) => path.abs_transform = transform,
1549                Node::Image(ref mut image) => image.abs_transform = transform,
1550                Node::Text(ref mut text) => text.abs_transform = transform,
1551            }
1552
1553            // Yes, subroots are not affected by the node's transform.
1554            node.subroots_mut(|root| root.calculate_abs_transforms(Transform::identity()));
1555        }
1556    }
1557
1558    /// Calculates bounding boxes for all children of this group.
1559    ///
1560    /// A low-level methods. Prefer `usvg::Tree::postprocess` instead.
1561    pub fn calculate_bounding_boxes(&mut self) {
1562        for node in &mut self.children {
1563            match node {
1564                Node::Path(ref mut path) => {
1565                    path.bounding_box = path.data.compute_tight_bounds();
1566                    path.stroke_bounding_box = path.calculate_stroke_bounding_box();
1567                    if path.stroke_bounding_box.is_none() {
1568                        path.stroke_bounding_box =
1569                            path.bounding_box.and_then(|r| r.to_non_zero_rect());
1570                    }
1571                }
1572                // TODO: should we account for `preserveAspectRatio`?
1573                Node::Image(ref mut image) => image.bounding_box = Some(image.view_box.rect),
1574                // Have to be handled separately to prevent multiple mutable reference to the tree.
1575                Node::Group(ref mut group) => {
1576                    group.calculate_bounding_boxes();
1577                }
1578                // Will be set only during text-to-path conversion.
1579                Node::Text(_) => {}
1580            }
1581
1582            // Yes, subroots are not affected by the node's transform.
1583            node.subroots_mut(|root| root.calculate_bounding_boxes());
1584        }
1585
1586        let mut bbox = BBox::default();
1587        let mut stroke_bbox = BBox::default();
1588        let mut layer_bbox = BBox::default();
1589        for child in &self.children {
1590            if let Some(mut c_bbox) = child.bounding_box() {
1591                if let Node::Group(ref group) = child {
1592                    if let Some(r) = c_bbox.transform(group.transform) {
1593                        c_bbox = r;
1594                    }
1595                }
1596
1597                bbox = bbox.expand(c_bbox);
1598            }
1599
1600            if let Some(mut c_bbox) = child.stroke_bounding_box() {
1601                if let Node::Group(ref group) = child {
1602                    if let Some(r) = c_bbox.transform(group.transform) {
1603                        c_bbox = r;
1604                    }
1605                }
1606
1607                stroke_bbox = stroke_bbox.expand(c_bbox);
1608            }
1609
1610            if let Node::Group(ref group) = child {
1611                if let Some(r) = group.layer_bounding_box {
1612                    if let Some(r) = r.transform(group.transform) {
1613                        layer_bbox = layer_bbox.expand(r);
1614                    }
1615                }
1616            } else if let Some(c_bbox) = child.stroke_bounding_box() {
1617                // Not a group - no need to transform.
1618                layer_bbox = layer_bbox.expand(c_bbox);
1619            }
1620        }
1621
1622        self.bounding_box = bbox.to_rect();
1623        self.stroke_bounding_box = stroke_bbox.to_non_zero_rect();
1624
1625        // Filter bbox has a higher priority than layers bbox.
1626        if let Some(filter_bbox) = self.filters_bounding_box() {
1627            self.layer_bounding_box = Some(filter_bbox);
1628        } else {
1629            self.layer_bounding_box = layer_bbox.to_non_zero_rect();
1630        }
1631    }
1632}