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