Skip to main content

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