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