Skip to main content

ai_usvg/tree/
mod.rs

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