1pub mod filter;
6mod geom;
7mod text;
8
9use std::sync::Arc;
10pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
11pub use svgrtypes::{Align, AspectRatio};
12
13pub use tiny_skia_path;
14
15use crate::hashers::CustomHash;
16use crate::PreloadedImageData;
17
18pub use self::geom::*;
19pub use self::text::*;
20
21pub type Opacity = NormalizedF32;
23
24#[derive(Debug, Hash)]
26pub(crate) struct NonEmptyString(String);
27
28impl NonEmptyString {
29 pub(crate) fn new(string: String) -> Option<Self> {
30 if string.trim().is_empty() {
31 return None;
32 }
33
34 Some(NonEmptyString(string))
35 }
36
37 pub(crate) fn get(&self) -> &str {
38 &self.0
39 }
40}
41
42#[derive(Clone, Copy, Debug)]
46pub struct NonZeroF32(f32);
47
48impl std::hash::Hash for NonZeroF32 {
49 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50 self.0.to_bits().hash(state);
51 }
52}
53
54impl NonZeroF32 {
55 #[inline]
57 pub fn new(n: f32) -> Option<Self> {
58 if n.approx_eq_ulps(&0.0, 4) {
59 None
60 } else {
61 Some(NonZeroF32(n))
62 }
63 }
64
65 #[inline]
67 pub fn get(&self) -> f32 {
68 self.0
69 }
70}
71
72#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
73pub(crate) enum Units {
74 UserSpaceOnUse,
75 ObjectBoundingBox,
76}
77
78#[allow(missing_docs)]
84#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
85pub enum Visibility {
86 Visible,
87 Hidden,
88 Collapse,
89}
90
91impl Default for Visibility {
92 fn default() -> Self {
93 Self::Visible
94 }
95}
96
97#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
101#[allow(missing_docs)]
102pub enum ShapeRendering {
103 OptimizeSpeed,
104 CrispEdges,
105 GeometricPrecision,
106}
107
108impl ShapeRendering {
109 pub fn use_shape_antialiasing(self) -> bool {
111 match self {
112 ShapeRendering::OptimizeSpeed => false,
113 ShapeRendering::CrispEdges => false,
114 ShapeRendering::GeometricPrecision => true,
115 }
116 }
117}
118
119impl Default for ShapeRendering {
120 fn default() -> Self {
121 Self::GeometricPrecision
122 }
123}
124
125impl std::str::FromStr for ShapeRendering {
126 type Err = &'static str;
127
128 fn from_str(s: &str) -> Result<Self, Self::Err> {
129 match s {
130 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
131 "crispEdges" => Ok(ShapeRendering::CrispEdges),
132 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
133 _ => Err("invalid"),
134 }
135 }
136}
137
138#[allow(missing_docs)]
142#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
143pub enum TextRendering {
144 OptimizeSpeed,
145 OptimizeLegibility,
146 GeometricPrecision,
147}
148
149impl Default for TextRendering {
150 fn default() -> Self {
151 Self::OptimizeLegibility
152 }
153}
154
155impl std::str::FromStr for TextRendering {
156 type Err = &'static str;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 match s {
160 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
161 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
162 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
163 _ => Err("invalid"),
164 }
165 }
166}
167
168#[allow(missing_docs)]
172#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
173pub enum ImageRendering {
174 OptimizeQuality,
175 OptimizeSpeed,
176}
177
178impl Default for ImageRendering {
179 fn default() -> Self {
180 Self::OptimizeQuality
181 }
182}
183
184impl std::str::FromStr for ImageRendering {
185 type Err = &'static str;
186
187 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 match s {
189 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
190 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
191 _ => Err("invalid"),
192 }
193 }
194}
195
196#[allow(missing_docs)]
200#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
201pub enum BlendMode {
202 Normal,
203 Multiply,
204 Screen,
205 Overlay,
206 Darken,
207 Lighten,
208 ColorDodge,
209 ColorBurn,
210 HardLight,
211 SoftLight,
212 Difference,
213 Exclusion,
214 Hue,
215 Saturation,
216 Color,
217 Luminosity,
218}
219
220impl Default for BlendMode {
221 fn default() -> Self {
222 Self::Normal
223 }
224}
225
226#[allow(missing_docs)]
230#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
231pub enum SpreadMethod {
232 Pad,
233 Reflect,
234 Repeat,
235}
236
237impl Default for SpreadMethod {
238 fn default() -> Self {
239 Self::Pad
240 }
241}
242
243#[derive(Debug)]
245pub struct BaseGradient {
246 pub(crate) id: NonEmptyString,
247 pub(crate) units: Units, pub(crate) transform: Transform,
249 pub(crate) spread_method: SpreadMethod,
250 pub(crate) stops: Vec<Stop>,
251}
252
253impl std::hash::Hash for BaseGradient {
254 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
255 self.id.hash(state);
256 self.transform.custom_hash(state);
257 self.spread_method.hash(state);
258 self.stops.hash(state);
259 }
260}
261
262impl BaseGradient {
263 pub fn id(&self) -> &str {
268 self.id.get()
269 }
270
271 pub fn transform(&self) -> Transform {
275 self.transform
276 }
277
278 pub fn spread_method(&self) -> SpreadMethod {
282 self.spread_method
283 }
284
285 pub fn stops(&self) -> &[Stop] {
287 &self.stops
288 }
289}
290
291#[derive(Debug)]
295pub struct LinearGradient {
296 pub(crate) base: BaseGradient,
297 pub(crate) x1: f32,
298 pub(crate) y1: f32,
299 pub(crate) x2: f32,
300 pub(crate) y2: f32,
301}
302
303impl std::hash::Hash for LinearGradient {
304 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
305 self.id.hash(state);
306 self.x1.to_bits().hash(state);
307 self.y1.to_bits().hash(state);
308 self.x2.to_bits().hash(state);
309 self.y2.to_bits().hash(state);
310 self.base.hash(state);
311 }
312}
313
314impl LinearGradient {
315 pub fn x1(&self) -> f32 {
317 self.x1
318 }
319
320 pub fn y1(&self) -> f32 {
322 self.y1
323 }
324
325 pub fn x2(&self) -> f32 {
327 self.x2
328 }
329
330 pub fn y2(&self) -> f32 {
332 self.y2
333 }
334}
335
336impl std::ops::Deref for LinearGradient {
337 type Target = BaseGradient;
338
339 fn deref(&self) -> &Self::Target {
340 &self.base
341 }
342}
343
344#[derive(Debug)]
348pub struct RadialGradient {
349 pub(crate) base: BaseGradient,
350 pub(crate) cx: f32,
351 pub(crate) cy: f32,
352 pub(crate) r: PositiveF32,
353 pub(crate) fx: f32,
354 pub(crate) fy: f32,
355}
356
357impl std::hash::Hash for RadialGradient {
358 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
359 self.id.hash(state);
360 self.cx.to_bits().hash(state);
361 self.cy.to_bits().hash(state);
362 self.r.hash(state);
363 self.fx.to_bits().hash(state);
364 self.fy.to_bits().hash(state);
365 self.base.hash(state);
366 }
367}
368
369impl RadialGradient {
370 pub fn cx(&self) -> f32 {
372 self.cx
373 }
374
375 pub fn cy(&self) -> f32 {
377 self.cy
378 }
379
380 pub fn r(&self) -> PositiveF32 {
382 self.r
383 }
384
385 pub fn fx(&self) -> f32 {
387 self.fx
388 }
389
390 pub fn fy(&self) -> f32 {
392 self.fy
393 }
394}
395
396impl std::ops::Deref for RadialGradient {
397 type Target = BaseGradient;
398
399 fn deref(&self) -> &Self::Target {
400 &self.base
401 }
402}
403
404pub type StopOffset = NormalizedF32;
406
407#[derive(Clone, Copy, Debug, Hash)]
411pub struct Stop {
412 pub(crate) offset: StopOffset,
413 pub(crate) color: Color,
414 pub(crate) opacity: Opacity,
415}
416
417impl Stop {
418 pub fn offset(&self) -> StopOffset {
422 self.offset
423 }
424
425 pub fn color(&self) -> Color {
429 self.color
430 }
431
432 pub fn opacity(&self) -> Opacity {
436 self.opacity
437 }
438}
439
440#[derive(Debug)]
444pub struct Pattern {
445 pub(crate) id: NonEmptyString,
446 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
449 pub(crate) rect: NonZeroRect,
450 pub(crate) view_box: Option<ViewBox>,
451 pub(crate) root: Group,
452}
453
454impl std::hash::Hash for Pattern {
455 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
456 self.id.hash(state);
457 self.transform.custom_hash(state);
458 self.rect.custom_hash(state);
459 self.view_box.hash(state);
460 self.root.hash(state);
461 }
462}
463
464impl Pattern {
465 pub fn id(&self) -> &str {
470 self.id.get()
471 }
472
473 pub fn transform(&self) -> Transform {
477 self.transform
478 }
479
480 pub fn rect(&self) -> NonZeroRect {
484 self.rect
485 }
486
487 pub fn view_box(&self) -> Option<ViewBox> {
489 self.view_box
490 }
491
492 pub fn root(&self) -> &Group {
494 &self.root
495 }
496}
497
498pub type StrokeWidth = NonZeroPositiveF32;
500
501#[derive(Clone, Copy, Debug)]
505pub struct StrokeMiterlimit(f32);
506
507impl StrokeMiterlimit {
508 #[inline]
510 pub fn new(n: f32) -> Self {
511 debug_assert!(n.is_finite());
512 debug_assert!(n >= 1.0);
513
514 let n = if !(n >= 1.0) { 1.0 } else { n };
515
516 StrokeMiterlimit(n)
517 }
518
519 #[inline]
521 pub fn get(&self) -> f32 {
522 self.0
523 }
524}
525
526impl Default for StrokeMiterlimit {
527 #[inline]
528 fn default() -> Self {
529 StrokeMiterlimit::new(4.0)
530 }
531}
532
533impl From<f32> for StrokeMiterlimit {
534 #[inline]
535 fn from(n: f32) -> Self {
536 Self::new(n)
537 }
538}
539
540impl PartialEq for StrokeMiterlimit {
541 #[inline]
542 fn eq(&self, other: &Self) -> bool {
543 self.0.approx_eq_ulps(&other.0, 4)
544 }
545}
546
547#[allow(missing_docs)]
551#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
552pub enum LineCap {
553 Butt,
554 Round,
555 Square,
556}
557
558impl Default for LineCap {
559 fn default() -> Self {
560 Self::Butt
561 }
562}
563
564#[allow(missing_docs)]
568#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
569pub enum LineJoin {
570 Miter,
571 MiterClip,
572 Round,
573 Bevel,
574}
575
576impl Default for LineJoin {
577 fn default() -> Self {
578 Self::Miter
579 }
580}
581
582#[derive(Clone, Debug)]
584pub struct Stroke {
585 pub(crate) paint: Paint,
586 pub(crate) dasharray: Option<Vec<f32>>,
587 pub(crate) dashoffset: f32,
588 pub(crate) miterlimit: StrokeMiterlimit,
589 pub(crate) opacity: Opacity,
590 pub(crate) width: StrokeWidth,
591 pub(crate) linecap: LineCap,
592 pub(crate) linejoin: LineJoin,
593 pub(crate) context_element: Option<ContextElement>,
596}
597
598impl std::hash::Hash for Stroke {
599 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
600 self.paint.hash(state);
601 self.dasharray
602 .as_ref()
603 .map(|vec| vec.iter().map(|v| v.to_bits()).collect::<Vec<_>>())
604 .hash(state);
605 self.dashoffset.to_bits().hash(state);
606
607 self.miterlimit.0.to_bits().hash(state);
608 self.opacity.hash(state);
609 self.width.hash(state);
610 self.linecap.hash(state);
611 self.linejoin.hash(state);
612 }
613}
614
615impl Stroke {
616 pub fn paint(&self) -> &Paint {
618 &self.paint
619 }
620
621 pub fn dasharray(&self) -> Option<&[f32]> {
623 self.dasharray.as_deref()
624 }
625
626 pub fn dashoffset(&self) -> f32 {
628 self.dashoffset
629 }
630
631 pub fn miterlimit(&self) -> StrokeMiterlimit {
633 self.miterlimit
634 }
635
636 pub fn opacity(&self) -> Opacity {
638 self.opacity
639 }
640
641 pub fn width(&self) -> StrokeWidth {
643 self.width
644 }
645
646 pub fn linecap(&self) -> LineCap {
648 self.linecap
649 }
650
651 pub fn linejoin(&self) -> LineJoin {
653 self.linejoin
654 }
655
656 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
658 let mut stroke = tiny_skia_path::Stroke {
659 width: self.width.get(),
660 miter_limit: self.miterlimit.get(),
661 line_cap: match self.linecap {
662 LineCap::Butt => tiny_skia_path::LineCap::Butt,
663 LineCap::Round => tiny_skia_path::LineCap::Round,
664 LineCap::Square => tiny_skia_path::LineCap::Square,
665 },
666 line_join: match self.linejoin {
667 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
668 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
669 LineJoin::Round => tiny_skia_path::LineJoin::Round,
670 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
671 },
672 dash: None,
675 };
676
677 if let Some(ref list) = self.dasharray {
678 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
679 }
680
681 stroke
682 }
683}
684
685#[allow(missing_docs)]
689#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
690pub enum FillRule {
691 NonZero,
692 EvenOdd,
693}
694
695impl Default for FillRule {
696 fn default() -> Self {
697 Self::NonZero
698 }
699}
700
701#[derive(Clone, Copy, Debug)]
702pub(crate) enum ContextElement {
703 UseNode,
708 PathNode(Transform, Option<NonZeroRect>),
713}
714
715impl std::hash::Hash for ContextElement {
716 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
717 match self {
718 ContextElement::UseNode => 0.hash(state),
719 ContextElement::PathNode(transform, rect) => {
720 1.hash(state);
721 transform.custom_hash(state);
722
723 if let Some(rect) = rect {
724 rect.custom_hash(state);
725 }
726 }
727 }
728 }
729}
730
731#[derive(Clone, Debug, Hash)]
733pub struct Fill {
734 pub(crate) paint: Paint,
735 pub(crate) opacity: Opacity,
736 pub(crate) rule: FillRule,
737 pub(crate) context_element: Option<ContextElement>,
740}
741
742impl Fill {
743 pub fn paint(&self) -> &Paint {
745 &self.paint
746 }
747
748 pub fn opacity(&self) -> Opacity {
750 self.opacity
751 }
752
753 pub fn rule(&self) -> FillRule {
755 self.rule
756 }
757}
758
759impl Default for Fill {
760 fn default() -> Self {
761 Fill {
762 paint: Paint::Color(Color::black()),
763 opacity: Opacity::ONE,
764 rule: FillRule::default(),
765 context_element: None,
766 }
767 }
768}
769
770#[allow(missing_docs)]
772#[derive(Clone, Copy, PartialEq, Debug, Hash)]
773pub struct Color {
774 pub red: u8,
775 pub green: u8,
776 pub blue: u8,
777}
778
779impl Color {
780 #[inline]
782 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
783 Color { red, green, blue }
784 }
785
786 #[inline]
788 pub fn black() -> Color {
789 Color::new_rgb(0, 0, 0)
790 }
791
792 #[inline]
794 pub fn white() -> Color {
795 Color::new_rgb(255, 255, 255)
796 }
797}
798
799#[allow(missing_docs)]
803#[derive(Clone, Debug)]
804pub enum Paint {
805 Color(Color),
806 LinearGradient(Arc<LinearGradient>),
807 RadialGradient(Arc<RadialGradient>),
808 Pattern(Arc<Pattern>),
809}
810
811impl PartialEq for Paint {
812 #[inline]
813 fn eq(&self, other: &Self) -> bool {
814 match (self, other) {
815 (Self::Color(lc), Self::Color(rc)) => lc == rc,
816 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Arc::ptr_eq(lg1, lg2),
817 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Arc::ptr_eq(rg1, rg2),
818 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Arc::ptr_eq(p1, p2),
819 _ => false,
820 }
821 }
822}
823
824impl std::hash::Hash for Paint {
825 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
826 match self {
827 Paint::Color(color) => color.hash(state),
828 Paint::LinearGradient(gradient) => gradient.id.hash(state),
829 Paint::RadialGradient(gradient) => gradient.id.hash(state),
830 Paint::Pattern(pattern) => pattern.id.hash(state),
831 }
832 }
833}
834
835#[derive(Debug)]
839pub struct ClipPath {
840 pub(crate) id: NonEmptyString,
841 pub(crate) transform: Transform,
842 pub(crate) clip_path: Option<Arc<ClipPath>>,
843 pub(crate) root: Group,
844}
845
846impl std::hash::Hash for ClipPath {
847 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
848 self.id.hash(state);
849 self.transform.custom_hash(state);
850 self.clip_path.hash(state);
851 self.root.hash(state);
852 }
853}
854
855impl ClipPath {
856 pub(crate) fn empty(id: NonEmptyString) -> Self {
857 ClipPath {
858 id,
859 transform: Transform::default(),
860 clip_path: None,
861 root: Group::empty(),
862 }
863 }
864
865 pub fn id(&self) -> &str {
870 self.id.get()
871 }
872
873 pub fn transform(&self) -> Transform {
877 self.transform
878 }
879
880 pub fn clip_path(&self) -> Option<&ClipPath> {
884 self.clip_path.as_deref()
885 }
886
887 pub fn root(&self) -> &Group {
889 &self.root
890 }
891}
892
893#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
895pub enum MaskType {
896 Luminance,
898 Alpha,
900}
901
902impl Default for MaskType {
903 fn default() -> Self {
904 Self::Luminance
905 }
906}
907
908#[derive(Debug)]
912pub struct Mask {
913 pub(crate) id: NonEmptyString,
914 pub(crate) rect: NonZeroRect,
915 pub(crate) kind: MaskType,
916 pub(crate) mask: Option<Arc<Mask>>,
917 pub(crate) root: Group,
918}
919
920impl std::hash::Hash for Mask {
921 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
922 self.id.hash(state);
923 self.rect.custom_hash(state);
924 self.kind.hash(state);
925 self.mask.hash(state);
926 self.root.hash(state);
927 }
928}
929
930impl Mask {
931 pub fn id(&self) -> &str {
936 self.id.get()
937 }
938
939 pub fn rect(&self) -> NonZeroRect {
943 self.rect
944 }
945
946 pub fn kind(&self) -> MaskType {
950 self.kind
951 }
952
953 pub fn mask(&self) -> Option<&Mask> {
957 self.mask.as_deref()
958 }
959
960 pub fn root(&self) -> &Group {
964 &self.root
965 }
966}
967
968#[allow(missing_docs)]
970#[derive(Clone, Debug, Hash)]
971pub enum Node {
972 Group(Box<Group>),
973 Path(Box<Path>),
974 Image(Box<Image>),
975 Text(Box<Text>),
976}
977
978impl Node {
979 pub fn id(&self) -> &str {
981 match self {
982 Node::Group(ref e) => e.id.as_str(),
983 Node::Path(ref e) => e.id.as_str(),
984 Node::Image(ref e) => e.id.as_str(),
985 Node::Text(ref e) => e.id.as_str(),
986 }
987 }
988
989 pub fn abs_transform(&self) -> Transform {
996 match self {
997 Node::Group(ref group) => group.abs_transform(),
998 Node::Path(ref path) => path.abs_transform(),
999 Node::Image(ref image) => image.abs_transform(),
1000 Node::Text(ref text) => text.abs_transform(),
1001 }
1002 }
1003
1004 pub fn bounding_box(&self) -> Rect {
1006 match self {
1007 Node::Group(ref group) => group.bounding_box(),
1008 Node::Path(ref path) => path.bounding_box(),
1009 Node::Image(ref image) => image.bounding_box(),
1010 Node::Text(ref text) => text.bounding_box(),
1011 }
1012 }
1013
1014 pub fn abs_bounding_box(&self) -> Rect {
1016 match self {
1017 Node::Group(ref group) => group.abs_bounding_box(),
1018 Node::Path(ref path) => path.abs_bounding_box(),
1019 Node::Image(ref image) => image.abs_bounding_box(),
1020 Node::Text(ref text) => text.abs_bounding_box(),
1021 }
1022 }
1023
1024 pub fn stroke_bounding_box(&self) -> Rect {
1026 match self {
1027 Node::Group(ref group) => group.stroke_bounding_box(),
1028 Node::Path(ref path) => path.stroke_bounding_box(),
1029 Node::Image(ref image) => image.bounding_box(),
1031 Node::Text(ref text) => text.stroke_bounding_box(),
1032 }
1033 }
1034
1035 pub fn abs_stroke_bounding_box(&self) -> Rect {
1037 match self {
1038 Node::Group(ref group) => group.abs_stroke_bounding_box(),
1039 Node::Path(ref path) => path.abs_stroke_bounding_box(),
1040 Node::Image(ref image) => image.abs_bounding_box(),
1042 Node::Text(ref text) => text.abs_stroke_bounding_box(),
1043 }
1044 }
1045
1046 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
1053 match self {
1054 Node::Group(ref group) => Some(group.abs_layer_bounding_box()),
1055 Node::Path(ref path) => path.abs_bounding_box().to_non_zero_rect(),
1057 Node::Image(ref image) => image.abs_bounding_box().to_non_zero_rect(),
1058 Node::Text(ref text) => text.abs_bounding_box().to_non_zero_rect(),
1059 }
1060 }
1061
1062 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1087 match self {
1088 Node::Group(ref group) => group.subroots(&mut f),
1089 Node::Path(ref path) => path.subroots(&mut f),
1090 Node::Image(ref image) => image.subroots(&mut f),
1091 Node::Text(ref text) => text.subroots(&mut f),
1092 }
1093 }
1094}
1095
1096#[derive(Clone, Debug)]
1103pub struct Group {
1104 pub(crate) id: String,
1105 pub(crate) transform: Transform,
1106 pub(crate) abs_transform: Transform,
1107 pub(crate) opacity: Opacity,
1108 pub(crate) blend_mode: BlendMode,
1109 pub(crate) isolate: bool,
1110 pub(crate) clip_path: Option<Arc<ClipPath>>,
1111 pub(crate) is_context_element: bool,
1113 pub(crate) mask: Option<Arc<Mask>>,
1114 pub(crate) filters: Vec<Arc<filter::Filter>>,
1115 pub(crate) bounding_box: Rect,
1116 pub(crate) abs_bounding_box: Rect,
1117 pub(crate) stroke_bounding_box: Rect,
1118 pub(crate) abs_stroke_bounding_box: Rect,
1119 pub(crate) layer_bounding_box: NonZeroRect,
1120 pub(crate) abs_layer_bounding_box: NonZeroRect,
1121 pub(crate) children: Vec<Node>,
1122 pub(crate) static_hash: Option<u64>,
1125}
1126
1127impl std::hash::Hash for Group {
1128 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1129 self.id.hash(state);
1130 self.transform.custom_hash(state);
1131 self.abs_transform.custom_hash(state);
1132 self.isolate.hash(state);
1136 self.clip_path.hash(state);
1137 self.mask.hash(state);
1138 self.filters.hash(state);
1139 self.bounding_box.custom_hash(state);
1140 self.abs_bounding_box.custom_hash(state);
1141 self.stroke_bounding_box.custom_hash(state);
1142 self.abs_stroke_bounding_box.custom_hash(state);
1143 self.layer_bounding_box.custom_hash(state);
1144 self.abs_layer_bounding_box.custom_hash(state);
1145 self.children.hash(state);
1146 }
1147}
1148
1149impl Group {
1150 pub(crate) fn empty() -> Self {
1151 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1152 Group {
1153 id: String::new(),
1154 transform: Transform::default(),
1155 abs_transform: Transform::default(),
1156 opacity: Opacity::ONE,
1157 blend_mode: BlendMode::Normal,
1158 isolate: false,
1159 clip_path: None,
1160 mask: None,
1161 filters: Vec::new(),
1162 is_context_element: false,
1163 bounding_box: dummy,
1164 abs_bounding_box: dummy,
1165 stroke_bounding_box: dummy,
1166 abs_stroke_bounding_box: dummy,
1167 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1168 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1169 children: Vec::new(),
1170 static_hash: None,
1171 }
1172 }
1173
1174 pub fn id(&self) -> &str {
1180 &self.id
1181 }
1182
1183 pub fn transform(&self) -> Transform {
1187 self.transform
1188 }
1189
1190 pub fn abs_transform(&self) -> Transform {
1197 self.abs_transform
1198 }
1199
1200 pub fn opacity(&self) -> Opacity {
1205 self.opacity
1206 }
1207
1208 pub fn blend_mode(&self) -> BlendMode {
1212 self.blend_mode
1213 }
1214
1215 pub fn isolate(&self) -> bool {
1219 self.isolate
1220 }
1221
1222 pub fn clip_path(&self) -> Option<&ClipPath> {
1224 self.clip_path.as_deref()
1225 }
1226
1227 pub fn mask(&self) -> Option<&Mask> {
1229 self.mask.as_deref()
1230 }
1231
1232 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1234 &self.filters
1235 }
1236
1237 pub fn bounding_box(&self) -> Rect {
1243 self.bounding_box
1244 }
1245
1246 pub fn abs_bounding_box(&self) -> Rect {
1250 self.abs_bounding_box
1251 }
1252
1253 pub fn stroke_bounding_box(&self) -> Rect {
1257 self.stroke_bounding_box
1258 }
1259
1260 pub fn abs_stroke_bounding_box(&self) -> Rect {
1264 self.abs_stroke_bounding_box
1265 }
1266
1267 pub fn layer_bounding_box(&self) -> NonZeroRect {
1279 self.layer_bounding_box
1280 }
1281
1282 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1284 self.abs_layer_bounding_box
1285 }
1286
1287 pub fn children(&self) -> &[Node] {
1289 &self.children
1290 }
1291
1292 pub fn static_hash(&self) -> Option<u64> {
1297 self.static_hash
1298 }
1299
1300 pub fn should_isolate(&self) -> bool {
1302 self.isolate
1303 || self.opacity != Opacity::ONE
1304 || self.clip_path.is_some()
1305 || self.mask.is_some()
1306 || !self.filters.is_empty()
1307 || self.blend_mode != BlendMode::Normal }
1309
1310 pub fn has_children(&self) -> bool {
1312 !self.children.is_empty()
1313 }
1314
1315 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1326 let mut full_region = BBox::default();
1327 for filter in &self.filters {
1328 full_region = full_region.expand(filter.rect);
1329 }
1330
1331 full_region.to_non_zero_rect()
1332 }
1333
1334 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1335 if let Some(ref clip) = self.clip_path {
1336 f(&clip.root);
1337
1338 if let Some(ref sub_clip) = clip.clip_path {
1339 f(&sub_clip.root);
1340 }
1341 }
1342
1343 if let Some(ref mask) = self.mask {
1344 f(&mask.root);
1345
1346 if let Some(ref sub_mask) = mask.mask {
1347 f(&sub_mask.root);
1348 }
1349 }
1350
1351 for filter in &self.filters {
1352 for primitive in &filter.primitives {
1353 if let filter::Kind::Image(ref image) = primitive.kind {
1354 if let filter::ImageKind::Use(ref use_node) = image.data {
1355 f(use_node);
1356 }
1357 }
1358 }
1359 }
1360 }
1361}
1362
1363#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
1370#[allow(missing_docs)]
1371pub enum PaintOrder {
1372 FillAndStroke,
1373 StrokeAndFill,
1374}
1375
1376impl Default for PaintOrder {
1377 fn default() -> Self {
1378 Self::FillAndStroke
1379 }
1380}
1381
1382#[derive(Clone, Debug)]
1384pub struct Path {
1385 pub(crate) id: String,
1386 pub(crate) visibility: Visibility,
1387 pub(crate) fill: Option<Fill>,
1388 pub(crate) stroke: Option<Stroke>,
1389 pub(crate) paint_order: PaintOrder,
1390 pub(crate) rendering_mode: ShapeRendering,
1391 pub(crate) data: Arc<tiny_skia_path::Path>,
1392 pub(crate) abs_transform: Transform,
1393 pub(crate) bounding_box: Rect,
1394 pub(crate) abs_bounding_box: Rect,
1395 pub(crate) stroke_bounding_box: Rect,
1396 pub(crate) abs_stroke_bounding_box: Rect,
1397 pub(crate) static_hash: Option<u64>,
1400}
1401
1402impl std::hash::Hash for Path {
1403 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1404 self.id.hash(state);
1405 self.visibility.hash(state);
1406 self.fill.hash(state);
1407 self.stroke.hash(state);
1408 self.paint_order.hash(state);
1409 self.rendering_mode.hash(state);
1410 self.data.custom_hash(state);
1411 self.abs_transform.custom_hash(state);
1412 self.bounding_box.custom_hash(state);
1413 self.abs_bounding_box.custom_hash(state);
1414 self.stroke_bounding_box.custom_hash(state);
1415 self.abs_stroke_bounding_box.custom_hash(state);
1416 }
1417}
1418
1419impl Path {
1420 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1421 Self::new(
1422 String::new(),
1423 Visibility::default(),
1424 None,
1425 None,
1426 PaintOrder::default(),
1427 ShapeRendering::default(),
1428 data,
1429 Transform::default(),
1430 None, )
1432 }
1433
1434 pub(crate) fn new(
1435 id: String,
1436 visibility: Visibility,
1437 fill: Option<Fill>,
1438 stroke: Option<Stroke>,
1439 paint_order: PaintOrder,
1440 rendering_mode: ShapeRendering,
1441 data: Arc<tiny_skia_path::Path>,
1442 abs_transform: Transform,
1443 static_hash: Option<u64>,
1444 ) -> Option<Self> {
1445 let bounding_box = data.compute_tight_bounds()?;
1446 let stroke_bounding_box =
1447 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1448
1449 let abs_bounding_box: Rect;
1450 let abs_stroke_bounding_box: Rect;
1451 if abs_transform.has_skew() {
1452 let path2 = data.as_ref().clone();
1454 let path2 = path2.transform(abs_transform)?;
1455 abs_bounding_box = path2.compute_tight_bounds()?;
1456 abs_stroke_bounding_box =
1457 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1458 } else {
1459 abs_bounding_box = bounding_box.transform(abs_transform)?;
1461 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1462 }
1463
1464 Some(Path {
1465 id,
1466 visibility,
1467 fill,
1468 stroke,
1469 paint_order,
1470 rendering_mode,
1471 data,
1472 abs_transform,
1473 bounding_box,
1474 abs_bounding_box,
1475 stroke_bounding_box,
1476 abs_stroke_bounding_box,
1477 static_hash,
1478 })
1479 }
1480
1481 pub fn id(&self) -> &str {
1487 &self.id
1488 }
1489
1490 pub fn visibility(&self) -> Visibility {
1492 self.visibility
1493 }
1494
1495 pub fn fill(&self) -> Option<&Fill> {
1497 self.fill.as_ref()
1498 }
1499
1500 pub fn stroke(&self) -> Option<&Stroke> {
1502 self.stroke.as_ref()
1503 }
1504
1505 pub fn paint_order(&self) -> PaintOrder {
1512 self.paint_order
1513 }
1514
1515 pub fn rendering_mode(&self) -> ShapeRendering {
1519 self.rendering_mode
1520 }
1521
1522 pub fn data(&self) -> &tiny_skia_path::Path {
1527 self.data.as_ref()
1528 }
1529
1530 pub fn abs_transform(&self) -> Transform {
1537 self.abs_transform
1538 }
1539
1540 pub fn bounding_box(&self) -> Rect {
1544 self.bounding_box
1545 }
1546
1547 pub fn abs_bounding_box(&self) -> Rect {
1551 self.abs_bounding_box
1552 }
1553
1554 pub fn stroke_bounding_box(&self) -> Rect {
1558 self.stroke_bounding_box
1559 }
1560
1561 pub fn abs_stroke_bounding_box(&self) -> Rect {
1565 self.abs_stroke_bounding_box
1566 }
1567
1568 pub fn static_hash(&self) -> Option<u64> {
1571 self.static_hash
1572 }
1573
1574 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1575 let mut stroke = stroke?.to_tiny_skia();
1576 stroke.dash = None;
1578
1579 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1583 return stroked_path.compute_tight_bounds();
1584 }
1585
1586 None
1587 }
1588
1589 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1590 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1591 f(patt.root())
1592 }
1593 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1594 f(patt.root())
1595 }
1596 }
1597}
1598
1599#[derive(Clone)]
1601pub enum ImageKind {
1602 DATA(Arc<PreloadedImageData>),
1604 SVG {
1606 original_href: String,
1608 tree: Arc<Tree>,
1610 },
1611}
1612
1613impl std::fmt::Debug for ImageKind {
1614 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1615 match self {
1616 ImageKind::DATA(_) => f.write_str("ImageKind::DATA(..)"),
1617 ImageKind::SVG { .. } => f.write_str("ImageKind::SVG(..)"),
1618 }
1619 }
1620}
1621
1622#[derive(Clone, Debug)]
1626pub struct Image {
1627 pub(crate) id: String,
1628 pub(crate) visibility: Visibility,
1629 pub(crate) view_box: ViewBox,
1630 pub(crate) rendering_mode: ImageRendering,
1631 pub(crate) abs_transform: Transform,
1632 pub(crate) abs_bounding_box: NonZeroRect,
1633 pub(crate) origin_href: String,
1635 pub(crate) kind: ImageKind,
1636}
1637
1638impl std::hash::Hash for Image {
1639 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1640 self.visibility.hash(state);
1642 self.view_box.hash(state);
1643 self.rendering_mode.hash(state);
1644 self.abs_transform.custom_hash(state);
1645 self.abs_bounding_box.custom_hash(state);
1646 self.origin_href.hash(state);
1647 }
1648}
1649
1650impl Image {
1651 pub fn id(&self) -> &str {
1657 &self.id
1658 }
1659
1660 pub fn visibility(&self) -> Visibility {
1662 self.visibility
1663 }
1664
1665 pub fn view_box(&self) -> ViewBox {
1670 self.view_box
1671 }
1672
1673 pub fn rendering_mode(&self) -> ImageRendering {
1677 self.rendering_mode
1678 }
1679
1680 pub fn kind(&self) -> &ImageKind {
1682 &self.kind
1683 }
1684
1685 pub fn abs_transform(&self) -> Transform {
1692 self.abs_transform
1693 }
1694
1695 pub fn bounding_box(&self) -> Rect {
1699 self.view_box.rect.to_rect()
1700 }
1701
1702 pub fn abs_bounding_box(&self) -> Rect {
1706 self.abs_bounding_box.to_rect()
1707 }
1708
1709 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1710 if let ImageKind::SVG { ref tree, .. } = self.kind {
1711 f(&tree.root)
1712 }
1713 }
1714}
1715
1716#[allow(missing_debug_implementations)]
1718#[derive(Clone, Debug)]
1719pub struct Tree {
1720 pub(crate) size: Size,
1721 pub(crate) view_box: ViewBox,
1722 pub(crate) root: Group,
1723 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1724 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1725 pub(crate) patterns: Vec<Arc<Pattern>>,
1726 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1727 pub(crate) masks: Vec<Arc<Mask>>,
1728 pub(crate) filters: Vec<Arc<filter::Filter>>,
1729}
1730
1731impl Tree {
1732 pub fn size(&self) -> Size {
1738 self.size
1739 }
1740
1741 pub fn view_box(&self) -> ViewBox {
1747 self.view_box
1748 }
1749
1750 pub fn root(&self) -> &Group {
1752 &self.root
1753 }
1754
1755 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1759 if id.is_empty() {
1760 return None;
1761 }
1762
1763 node_by_id(&self.root, id)
1764 }
1765
1766 pub fn has_text_nodes(&self) -> bool {
1768 has_text_nodes(&self.root)
1769 }
1770
1771 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1773 &self.linear_gradients
1774 }
1775
1776 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1778 &self.radial_gradients
1779 }
1780
1781 pub fn patterns(&self) -> &[Arc<Pattern>] {
1783 &self.patterns
1784 }
1785
1786 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1788 &self.clip_paths
1789 }
1790
1791 pub fn masks(&self) -> &[Arc<Mask>] {
1793 &self.masks
1794 }
1795
1796 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1798 &self.filters
1799 }
1800
1801 pub(crate) fn collect_paint_servers(&mut self) {
1802 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1803 Paint::Color(_) => {}
1804 Paint::LinearGradient(lg) => {
1805 if !self
1806 .linear_gradients
1807 .iter()
1808 .any(|other| Arc::ptr_eq(&lg, other))
1809 {
1810 self.linear_gradients.push(lg.clone());
1811 }
1812 }
1813 Paint::RadialGradient(rg) => {
1814 if !self
1815 .radial_gradients
1816 .iter()
1817 .any(|other| Arc::ptr_eq(&rg, other))
1818 {
1819 self.radial_gradients.push(rg.clone());
1820 }
1821 }
1822 Paint::Pattern(patt) => {
1823 if !self.patterns.iter().any(|other| Arc::ptr_eq(&patt, other)) {
1824 self.patterns.push(patt.clone());
1825 }
1826 }
1827 });
1828 }
1829}
1830
1831fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1832 for child in &parent.children {
1833 if child.id() == id {
1834 return Some(child);
1835 }
1836
1837 if let Node::Group(ref g) = child {
1838 if let Some(n) = node_by_id(g, id) {
1839 return Some(n);
1840 }
1841 }
1842 }
1843
1844 None
1845}
1846
1847fn has_text_nodes(root: &Group) -> bool {
1848 for node in &root.children {
1849 if let Node::Text(_) = node {
1850 return true;
1851 }
1852
1853 let mut has_text = false;
1854
1855 if let Node::Image(ref image) = node {
1856 if let ImageKind::SVG { ref tree, .. } = image.kind {
1857 if has_text_nodes(&tree.root) {
1858 has_text = true;
1859 }
1860 }
1861 }
1862
1863 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1864
1865 if has_text {
1866 return true;
1867 }
1868 }
1869
1870 true
1871}
1872
1873fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1874 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1875 if let Some(paint) = paint {
1876 f(paint);
1877 }
1878 }
1879
1880 for node in &parent.children {
1881 match node {
1882 Node::Group(ref group) => loop_over_paint_servers(group, f),
1883 Node::Path(ref path) => {
1884 push(path.fill.as_ref().map(|f| &f.paint), f);
1885 push(path.stroke.as_ref().map(|f| &f.paint), f);
1886 }
1887 Node::Image(_) => {}
1888 Node::Text(_) => {}
1890 }
1891
1892 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1893 }
1894}
1895
1896impl Group {
1897 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1898 for node in self.children() {
1899 if let Node::Group(ref g) = node {
1900 if let Some(ref clip) = g.clip_path {
1901 if !clip_paths.iter().any(|other| Arc::ptr_eq(&clip, other)) {
1902 clip_paths.push(clip.clone());
1903 }
1904
1905 if let Some(ref sub_clip) = clip.clip_path {
1906 if !clip_paths.iter().any(|other| Arc::ptr_eq(&sub_clip, other)) {
1907 clip_paths.push(sub_clip.clone());
1908 }
1909 }
1910 }
1911 }
1912
1913 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1914
1915 if let Node::Group(ref g) = node {
1916 g.collect_clip_paths(clip_paths);
1917 }
1918 }
1919 }
1920
1921 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1922 for node in self.children() {
1923 if let Node::Group(ref g) = node {
1924 if let Some(ref mask) = g.mask {
1925 if !masks.iter().any(|other| Arc::ptr_eq(&mask, other)) {
1926 masks.push(mask.clone());
1927 }
1928
1929 if let Some(ref sub_mask) = mask.mask {
1930 if !masks.iter().any(|other| Arc::ptr_eq(&sub_mask, other)) {
1931 masks.push(sub_mask.clone());
1932 }
1933 }
1934 }
1935 }
1936
1937 node.subroots(|subroot| subroot.collect_masks(masks));
1938
1939 if let Node::Group(ref g) = node {
1940 g.collect_masks(masks);
1941 }
1942 }
1943 }
1944
1945 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1946 for node in self.children() {
1947 if let Node::Group(ref g) = node {
1948 for filter in g.filters() {
1949 if !filters.iter().any(|other| Arc::ptr_eq(&filter, other)) {
1950 filters.push(filter.clone());
1951 }
1952 }
1953 }
1954
1955 node.subroots(|subroot| subroot.collect_filters(filters));
1956
1957 if let Node::Group(ref g) = node {
1958 g.collect_filters(filters);
1959 }
1960 }
1961 }
1962
1963 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1964 let mut bbox = BBox::default();
1965 for child in &self.children {
1966 let mut c_bbox = child.bounding_box();
1967 if let Node::Group(ref group) = child {
1968 if let Some(r) = c_bbox.transform(group.transform) {
1969 c_bbox = r;
1970 }
1971 }
1972
1973 bbox = bbox.expand(c_bbox);
1974 }
1975
1976 bbox.to_non_zero_rect()
1977 }
1978
1979 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1980 let mut bbox = BBox::default();
1981 let mut abs_bbox = BBox::default();
1982 let mut stroke_bbox = BBox::default();
1983 let mut abs_stroke_bbox = BBox::default();
1984 let mut layer_bbox = BBox::default();
1985 for child in &self.children {
1986 {
1987 let mut c_bbox = child.bounding_box();
1988 if let Node::Group(ref group) = child {
1989 if let Some(r) = c_bbox.transform(group.transform) {
1990 c_bbox = r;
1991 }
1992 }
1993
1994 bbox = bbox.expand(c_bbox);
1995 }
1996
1997 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1998
1999 {
2000 let mut c_bbox = child.stroke_bounding_box();
2001 if let Node::Group(ref group) = child {
2002 if let Some(r) = c_bbox.transform(group.transform) {
2003 c_bbox = r;
2004 }
2005 }
2006
2007 stroke_bbox = stroke_bbox.expand(c_bbox);
2008 }
2009
2010 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
2011
2012 if let Node::Group(ref group) = child {
2013 let r = group.layer_bounding_box;
2014 if let Some(r) = r.transform(group.transform) {
2015 layer_bbox = layer_bbox.expand(r);
2016 }
2017 } else {
2018 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
2020 }
2021 }
2022
2023 if let Some(bbox) = bbox.to_rect() {
2026 self.bounding_box = bbox;
2027 self.abs_bounding_box = abs_bbox.to_rect()?;
2028 self.stroke_bounding_box = stroke_bbox.to_rect()?;
2029 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
2030 }
2031
2032 if let Some(filter_bbox) = self.filters_bounding_box() {
2034 self.layer_bounding_box = filter_bbox;
2035 } else {
2036 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
2037 }
2038
2039 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
2040
2041 Some(())
2042 }
2043}