1pub mod filter;
5mod geom;
6mod text;
7
8use alloc::boxed::Box;
9use alloc::string::String;
10use alloc::sync::Arc;
11use alloc::vec::Vec;
12use core::fmt::Display;
13
14pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
15
16pub use tiny_skia_path;
17
18pub use self::geom::*;
19pub use self::text::*;
20
21use crate::OptionLog;
22
23pub type Opacity = NormalizedF32;
25
26#[derive(Debug)]
28pub(crate) struct NonEmptyString(String);
29
30impl NonEmptyString {
31 pub(crate) fn new(string: String) -> Option<Self> {
32 if string.trim().is_empty() {
33 return None;
34 }
35
36 Some(NonEmptyString(string))
37 }
38
39 pub(crate) fn get(&self) -> &str {
40 &self.0
41 }
42
43 pub(crate) fn take(self) -> String {
44 self.0
45 }
46}
47
48#[derive(Clone, Copy, Debug)]
52pub struct NonZeroF32(f32);
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)]
73pub(crate) enum Units {
74 UserSpaceOnUse,
75 ObjectBoundingBox,
76}
77
78#[allow(missing_docs)]
84#[derive(Clone, Copy, PartialEq, Debug)]
85pub(crate) enum Visibility {
86 Visible,
87 Hidden,
88 Collapse,
89}
90
91impl Default for Visibility {
92 fn default() -> Self {
93 Self::Visible
94 }
95}
96
97#[derive(Clone, Copy, PartialEq, Debug)]
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 core::str::FromStr for ShapeRendering {
126 type Err = &'static str;
127
128 fn from_str(s: &str) -> Result<Self, Self::Err> {
129 match s {
130 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
131 "crispEdges" => Ok(ShapeRendering::CrispEdges),
132 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
133 _ => Err("invalid"),
134 }
135 }
136}
137
138#[allow(missing_docs)]
142#[derive(Clone, Copy, PartialEq, Debug)]
143pub enum TextRendering {
144 OptimizeSpeed,
145 OptimizeLegibility,
146 GeometricPrecision,
147}
148
149impl Default for TextRendering {
150 fn default() -> Self {
151 Self::OptimizeLegibility
152 }
153}
154
155impl core::str::FromStr for TextRendering {
156 type Err = &'static str;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 match s {
160 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
161 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
162 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
163 _ => Err("invalid"),
164 }
165 }
166}
167
168#[allow(missing_docs)]
172#[derive(Clone, Copy, PartialEq, Debug)]
173pub enum ImageRendering {
174 OptimizeQuality,
175 OptimizeSpeed,
176 Smooth,
178 HighQuality,
179 CrispEdges,
180 Pixelated,
181}
182
183impl Default for ImageRendering {
184 fn default() -> Self {
185 Self::OptimizeQuality
186 }
187}
188
189impl core::str::FromStr for ImageRendering {
190 type Err = &'static str;
191
192 fn from_str(s: &str) -> Result<Self, Self::Err> {
193 match s {
194 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
195 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
196 "smooth" => Ok(ImageRendering::Smooth),
197 "high-quality" => Ok(ImageRendering::HighQuality),
198 "crisp-edges" => Ok(ImageRendering::CrispEdges),
199 "pixelated" => Ok(ImageRendering::Pixelated),
200 _ => Err("invalid"),
201 }
202 }
203}
204
205#[allow(missing_docs)]
209#[derive(Clone, Copy, PartialEq, Debug)]
210pub enum BlendMode {
211 Normal,
212 Multiply,
213 Screen,
214 Overlay,
215 Darken,
216 Lighten,
217 ColorDodge,
218 ColorBurn,
219 HardLight,
220 SoftLight,
221 Difference,
222 Exclusion,
223 Hue,
224 Saturation,
225 Color,
226 Luminosity,
227}
228
229impl Default for BlendMode {
230 fn default() -> Self {
231 Self::Normal
232 }
233}
234
235impl Display for BlendMode {
236 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237 let blend_mode = match self {
238 BlendMode::Normal => "normal",
239 BlendMode::Multiply => "multiply",
240 BlendMode::Screen => "screen",
241 BlendMode::Overlay => "overlay",
242 BlendMode::Darken => "darken",
243 BlendMode::Lighten => "lighten",
244 BlendMode::ColorDodge => "color-dodge",
245 BlendMode::ColorBurn => "color-burn",
246 BlendMode::HardLight => "hard-light",
247 BlendMode::SoftLight => "soft-light",
248 BlendMode::Difference => "difference",
249 BlendMode::Exclusion => "exclusion",
250 BlendMode::Hue => "hue",
251 BlendMode::Saturation => "saturation",
252 BlendMode::Color => "color",
253 BlendMode::Luminosity => "luminosity",
254 };
255 write!(f, "{blend_mode}")
256 }
257}
258
259#[allow(missing_docs)]
263#[derive(Clone, Copy, PartialEq, Debug)]
264pub enum SpreadMethod {
265 Pad,
266 Reflect,
267 Repeat,
268}
269
270impl Default for SpreadMethod {
271 fn default() -> Self {
272 Self::Pad
273 }
274}
275
276#[derive(Debug)]
278pub struct BaseGradient {
279 pub(crate) id: NonEmptyString,
280 pub(crate) units: Units, pub(crate) transform: Transform,
282 pub(crate) spread_method: SpreadMethod,
283 pub(crate) stops: Vec<Stop>,
284}
285
286impl BaseGradient {
287 pub fn id(&self) -> &str {
292 self.id.get()
293 }
294
295 pub fn transform(&self) -> Transform {
299 self.transform
300 }
301
302 pub fn spread_method(&self) -> SpreadMethod {
306 self.spread_method
307 }
308
309 pub fn stops(&self) -> &[Stop] {
311 &self.stops
312 }
313}
314
315#[derive(Debug)]
319pub struct LinearGradient {
320 pub(crate) base: BaseGradient,
321 pub(crate) x1: f32,
322 pub(crate) y1: f32,
323 pub(crate) x2: f32,
324 pub(crate) y2: f32,
325}
326
327impl LinearGradient {
328 pub fn x1(&self) -> f32 {
330 self.x1
331 }
332
333 pub fn y1(&self) -> f32 {
335 self.y1
336 }
337
338 pub fn x2(&self) -> f32 {
340 self.x2
341 }
342
343 pub fn y2(&self) -> f32 {
345 self.y2
346 }
347}
348
349impl core::ops::Deref for LinearGradient {
350 type Target = BaseGradient;
351
352 fn deref(&self) -> &Self::Target {
353 &self.base
354 }
355}
356
357#[derive(Debug)]
361pub struct RadialGradient {
362 pub(crate) base: BaseGradient,
363 pub(crate) cx: f32,
364 pub(crate) cy: f32,
365 pub(crate) r: PositiveF32,
366 pub(crate) fx: f32,
367 pub(crate) fy: f32,
368 pub(crate) fr: PositiveF32,
369}
370
371impl RadialGradient {
372 pub fn cx(&self) -> f32 {
374 self.cx
375 }
376
377 pub fn cy(&self) -> f32 {
379 self.cy
380 }
381
382 pub fn r(&self) -> PositiveF32 {
384 self.r
385 }
386
387 pub fn fx(&self) -> f32 {
389 self.fx
390 }
391
392 pub fn fy(&self) -> f32 {
394 self.fy
395 }
396
397 pub fn fr(&self) -> PositiveF32 {
399 self.fr
400 }
401}
402
403impl core::ops::Deref for RadialGradient {
404 type Target = BaseGradient;
405
406 fn deref(&self) -> &Self::Target {
407 &self.base
408 }
409}
410
411pub type StopOffset = NormalizedF32;
413
414#[derive(Clone, Copy, Debug)]
418pub struct Stop {
419 pub(crate) offset: StopOffset,
420 pub(crate) color: Color,
421 pub(crate) opacity: Opacity,
422}
423
424impl Stop {
425 pub fn offset(&self) -> StopOffset {
429 self.offset
430 }
431
432 pub fn color(&self) -> Color {
436 self.color
437 }
438
439 pub fn opacity(&self) -> Opacity {
443 self.opacity
444 }
445}
446
447#[derive(Debug)]
451pub struct Pattern {
452 pub(crate) id: NonEmptyString,
453 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
456 pub(crate) rect: NonZeroRect,
457 pub(crate) view_box: Option<ViewBox>,
458 pub(crate) root: Group,
459}
460
461impl Pattern {
462 pub fn id(&self) -> &str {
467 self.id.get()
468 }
469
470 pub fn transform(&self) -> Transform {
474 self.transform
475 }
476
477 pub fn rect(&self) -> NonZeroRect {
481 self.rect
482 }
483
484 pub fn root(&self) -> &Group {
486 &self.root
487 }
488}
489
490pub type StrokeWidth = NonZeroPositiveF32;
492
493#[derive(Clone, Copy, Debug)]
497pub struct StrokeMiterlimit(f32);
498
499impl StrokeMiterlimit {
500 #[inline]
502 pub fn new(n: f32) -> Self {
503 debug_assert!(n.is_finite());
504 debug_assert!(n >= 1.0);
505
506 let n = if !(n >= 1.0) { 1.0 } else { n };
507
508 StrokeMiterlimit(n)
509 }
510
511 #[inline]
513 pub fn get(&self) -> f32 {
514 self.0
515 }
516}
517
518impl Default for StrokeMiterlimit {
519 #[inline]
520 fn default() -> Self {
521 StrokeMiterlimit::new(4.0)
522 }
523}
524
525impl From<f32> for StrokeMiterlimit {
526 #[inline]
527 fn from(n: f32) -> Self {
528 Self::new(n)
529 }
530}
531
532impl PartialEq for StrokeMiterlimit {
533 #[inline]
534 fn eq(&self, other: &Self) -> bool {
535 self.0.approx_eq_ulps(&other.0, 4)
536 }
537}
538
539#[allow(missing_docs)]
543#[derive(Clone, Copy, PartialEq, Debug)]
544pub enum LineCap {
545 Butt,
546 Round,
547 Square,
548}
549
550impl Default for LineCap {
551 fn default() -> Self {
552 Self::Butt
553 }
554}
555
556#[allow(missing_docs)]
560#[derive(Clone, Copy, PartialEq, Debug)]
561pub enum LineJoin {
562 Miter,
563 MiterClip,
564 Round,
565 Bevel,
566}
567
568impl Default for LineJoin {
569 fn default() -> Self {
570 Self::Miter
571 }
572}
573
574#[derive(Clone, Debug)]
576pub struct Stroke {
577 pub(crate) paint: Paint,
578 pub(crate) dasharray: Option<Vec<f32>>,
579 pub(crate) dashoffset: f32,
580 pub(crate) miterlimit: StrokeMiterlimit,
581 pub(crate) opacity: Opacity,
582 pub(crate) width: StrokeWidth,
583 pub(crate) linecap: LineCap,
584 pub(crate) linejoin: LineJoin,
585 pub(crate) context_element: Option<ContextElement>,
588}
589
590impl Stroke {
591 pub fn paint(&self) -> &Paint {
593 &self.paint
594 }
595
596 pub fn dasharray(&self) -> Option<&[f32]> {
598 self.dasharray.as_deref()
599 }
600
601 pub fn dashoffset(&self) -> f32 {
603 self.dashoffset
604 }
605
606 pub fn miterlimit(&self) -> StrokeMiterlimit {
608 self.miterlimit
609 }
610
611 pub fn opacity(&self) -> Opacity {
613 self.opacity
614 }
615
616 pub fn width(&self) -> StrokeWidth {
618 self.width
619 }
620
621 pub fn linecap(&self) -> LineCap {
623 self.linecap
624 }
625
626 pub fn linejoin(&self) -> LineJoin {
628 self.linejoin
629 }
630
631 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
633 let mut stroke = tiny_skia_path::Stroke {
634 width: self.width.get(),
635 miter_limit: self.miterlimit.get(),
636 line_cap: match self.linecap {
637 LineCap::Butt => tiny_skia_path::LineCap::Butt,
638 LineCap::Round => tiny_skia_path::LineCap::Round,
639 LineCap::Square => tiny_skia_path::LineCap::Square,
640 },
641 line_join: match self.linejoin {
642 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
643 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
644 LineJoin::Round => tiny_skia_path::LineJoin::Round,
645 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
646 },
647 dash: None,
650 };
651
652 if let Some(ref list) = self.dasharray {
653 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
654 }
655
656 stroke
657 }
658}
659
660#[allow(missing_docs)]
664#[derive(Clone, Copy, PartialEq, Debug)]
665pub enum FillRule {
666 NonZero,
667 EvenOdd,
668}
669
670impl Default for FillRule {
671 fn default() -> Self {
672 Self::NonZero
673 }
674}
675
676#[derive(Clone, Copy, Debug)]
677pub(crate) enum ContextElement {
678 UseNode,
683 PathNode(Transform, Option<NonZeroRect>),
688}
689
690#[derive(Clone, Debug)]
692pub struct Fill {
693 pub(crate) paint: Paint,
694 pub(crate) opacity: Opacity,
695 pub(crate) rule: FillRule,
696 pub(crate) context_element: Option<ContextElement>,
699}
700
701impl Fill {
702 pub fn paint(&self) -> &Paint {
704 &self.paint
705 }
706
707 pub fn opacity(&self) -> Opacity {
709 self.opacity
710 }
711
712 pub fn rule(&self) -> FillRule {
714 self.rule
715 }
716}
717
718impl Default for Fill {
719 fn default() -> Self {
720 Fill {
721 paint: Paint::Color(Color::black()),
722 opacity: Opacity::ONE,
723 rule: FillRule::default(),
724 context_element: None,
725 }
726 }
727}
728
729#[derive(Clone, Copy, PartialEq, Debug)]
731#[allow(missing_docs)]
732pub struct Color {
733 pub red: u8,
734 pub green: u8,
735 pub blue: u8,
736}
737
738impl Color {
739 #[inline]
741 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
742 Color { red, green, blue }
743 }
744
745 #[inline]
747 pub fn black() -> Color {
748 Color::new_rgb(0, 0, 0)
749 }
750
751 #[inline]
753 pub fn white() -> Color {
754 Color::new_rgb(255, 255, 255)
755 }
756}
757
758#[allow(missing_docs)]
762#[derive(Clone, Debug)]
763pub enum Paint {
764 Color(Color),
765 LinearGradient(Arc<LinearGradient>),
766 RadialGradient(Arc<RadialGradient>),
767 Pattern(Arc<Pattern>),
768}
769
770impl PartialEq for Paint {
771 #[inline]
772 fn eq(&self, other: &Self) -> bool {
773 match (self, other) {
774 (Self::Color(lc), Self::Color(rc)) => lc == rc,
775 (Self::LinearGradient(lg1), Self::LinearGradient(lg2)) => Arc::ptr_eq(lg1, lg2),
776 (Self::RadialGradient(rg1), Self::RadialGradient(rg2)) => Arc::ptr_eq(rg1, rg2),
777 (Self::Pattern(p1), Self::Pattern(p2)) => Arc::ptr_eq(p1, p2),
778 _ => false,
779 }
780 }
781}
782
783#[derive(Debug)]
787pub struct ClipPath {
788 pub(crate) id: NonEmptyString,
789 pub(crate) transform: Transform,
790 pub(crate) clip_path: Option<Arc<ClipPath>>,
791 pub(crate) root: Group,
792}
793
794impl ClipPath {
795 pub(crate) fn empty(id: NonEmptyString) -> Self {
796 ClipPath {
797 id,
798 transform: Transform::default(),
799 clip_path: None,
800 root: Group::empty(),
801 }
802 }
803
804 pub fn id(&self) -> &str {
809 self.id.get()
810 }
811
812 pub fn transform(&self) -> Transform {
816 self.transform
817 }
818
819 pub fn clip_path(&self) -> Option<&ClipPath> {
823 self.clip_path.as_deref()
824 }
825
826 pub fn root(&self) -> &Group {
828 &self.root
829 }
830}
831
832#[derive(Clone, Copy, PartialEq, Debug)]
834pub enum MaskType {
835 Luminance,
837 Alpha,
839}
840
841impl Default for MaskType {
842 fn default() -> Self {
843 Self::Luminance
844 }
845}
846
847#[derive(Debug)]
851pub struct Mask {
852 pub(crate) id: NonEmptyString,
853 pub(crate) rect: NonZeroRect,
854 pub(crate) kind: MaskType,
855 pub(crate) mask: Option<Arc<Mask>>,
856 pub(crate) root: Group,
857}
858
859impl Mask {
860 pub fn id(&self) -> &str {
865 self.id.get()
866 }
867
868 pub fn rect(&self) -> NonZeroRect {
872 self.rect
873 }
874
875 pub fn kind(&self) -> MaskType {
879 self.kind
880 }
881
882 pub fn mask(&self) -> Option<&Mask> {
886 self.mask.as_deref()
887 }
888
889 pub fn root(&self) -> &Group {
893 &self.root
894 }
895}
896
897#[allow(missing_docs)]
899#[derive(Clone, Debug)]
900pub enum Node {
901 Group(Box<Group>),
902 Path(Box<Path>),
903 Image(Box<Image>),
904 Text(Box<Text>),
905}
906
907impl Node {
908 pub fn id(&self) -> &str {
910 match self {
911 Node::Group(e) => e.id.as_str(),
912 Node::Path(e) => e.id.as_str(),
913 Node::Image(e) => e.id.as_str(),
914 Node::Text(e) => e.id.as_str(),
915 }
916 }
917
918 pub fn abs_transform(&self) -> Transform {
922 match self {
923 Node::Group(group) => group.abs_transform(),
924 Node::Path(path) => path.abs_transform(),
925 Node::Image(image) => image.abs_transform(),
926 Node::Text(text) => text.abs_transform(),
927 }
928 }
929
930 pub fn bounding_box(&self) -> Rect {
932 match self {
933 Node::Group(group) => group.bounding_box(),
934 Node::Path(path) => path.bounding_box(),
935 Node::Image(image) => image.bounding_box(),
936 Node::Text(text) => text.bounding_box(),
937 }
938 }
939
940 pub fn abs_bounding_box(&self) -> Rect {
942 match self {
943 Node::Group(group) => group.abs_bounding_box(),
944 Node::Path(path) => path.abs_bounding_box(),
945 Node::Image(image) => image.abs_bounding_box(),
946 Node::Text(text) => text.abs_bounding_box(),
947 }
948 }
949
950 pub fn stroke_bounding_box(&self) -> Rect {
952 match self {
953 Node::Group(group) => group.stroke_bounding_box(),
954 Node::Path(path) => path.stroke_bounding_box(),
955 Node::Image(image) => image.bounding_box(),
957 Node::Text(text) => text.stroke_bounding_box(),
958 }
959 }
960
961 pub fn abs_stroke_bounding_box(&self) -> Rect {
963 match self {
964 Node::Group(group) => group.abs_stroke_bounding_box(),
965 Node::Path(path) => path.abs_stroke_bounding_box(),
966 Node::Image(image) => image.abs_bounding_box(),
968 Node::Text(text) => text.abs_stroke_bounding_box(),
969 }
970 }
971
972 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
979 match self {
980 Node::Group(group) => Some(group.abs_layer_bounding_box()),
981 Node::Path(path) => path.abs_bounding_box().to_non_zero_rect(),
983 Node::Image(image) => image.abs_bounding_box().to_non_zero_rect(),
984 Node::Text(text) => text.abs_bounding_box().to_non_zero_rect(),
985 }
986 }
987
988 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1013 match self {
1014 Node::Group(group) => group.subroots(&mut f),
1015 Node::Path(path) => path.subroots(&mut f),
1016 Node::Image(image) => image.subroots(&mut f),
1017 Node::Text(text) => text.subroots(&mut f),
1018 }
1019 }
1020}
1021
1022#[derive(Clone, Debug)]
1029pub struct Group {
1030 pub(crate) id: String,
1031 pub(crate) transform: Transform,
1032 pub(crate) abs_transform: Transform,
1033 pub(crate) opacity: Opacity,
1034 pub(crate) blend_mode: BlendMode,
1035 pub(crate) isolate: bool,
1036 pub(crate) clip_path: Option<Arc<ClipPath>>,
1037 pub(crate) is_context_element: bool,
1039 pub(crate) mask: Option<Arc<Mask>>,
1040 pub(crate) filters: Vec<Arc<filter::Filter>>,
1041 pub(crate) bounding_box: Rect,
1042 pub(crate) abs_bounding_box: Rect,
1043 pub(crate) stroke_bounding_box: Rect,
1044 pub(crate) abs_stroke_bounding_box: Rect,
1045 pub(crate) layer_bounding_box: NonZeroRect,
1046 pub(crate) abs_layer_bounding_box: NonZeroRect,
1047 pub(crate) children: Vec<Node>,
1048}
1049
1050impl Group {
1051 pub(crate) fn empty() -> Self {
1052 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1053 Group {
1054 id: String::new(),
1055 transform: Transform::default(),
1056 abs_transform: Transform::default(),
1057 opacity: Opacity::ONE,
1058 blend_mode: BlendMode::Normal,
1059 isolate: false,
1060 clip_path: None,
1061 mask: None,
1062 filters: Vec::new(),
1063 is_context_element: false,
1064 bounding_box: dummy,
1065 abs_bounding_box: dummy,
1066 stroke_bounding_box: dummy,
1067 abs_stroke_bounding_box: dummy,
1068 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1069 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1070 children: Vec::new(),
1071 }
1072 }
1073
1074 pub fn id(&self) -> &str {
1080 &self.id
1081 }
1082
1083 pub fn transform(&self) -> Transform {
1087 self.transform
1088 }
1089
1090 pub fn abs_transform(&self) -> Transform {
1097 self.abs_transform
1098 }
1099
1100 pub fn opacity(&self) -> Opacity {
1105 self.opacity
1106 }
1107
1108 pub fn blend_mode(&self) -> BlendMode {
1112 self.blend_mode
1113 }
1114
1115 pub fn isolate(&self) -> bool {
1119 self.isolate
1120 }
1121
1122 pub fn clip_path(&self) -> Option<&ClipPath> {
1124 self.clip_path.as_deref()
1125 }
1126
1127 pub fn mask(&self) -> Option<&Mask> {
1129 self.mask.as_deref()
1130 }
1131
1132 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1134 &self.filters
1135 }
1136
1137 pub fn bounding_box(&self) -> Rect {
1143 self.bounding_box
1144 }
1145
1146 pub fn abs_bounding_box(&self) -> Rect {
1150 self.abs_bounding_box
1151 }
1152
1153 pub fn stroke_bounding_box(&self) -> Rect {
1157 self.stroke_bounding_box
1158 }
1159
1160 pub fn abs_stroke_bounding_box(&self) -> Rect {
1164 self.abs_stroke_bounding_box
1165 }
1166
1167 pub fn layer_bounding_box(&self) -> NonZeroRect {
1181 self.layer_bounding_box
1182 }
1183
1184 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1186 self.abs_layer_bounding_box
1187 }
1188
1189 pub fn children(&self) -> &[Node] {
1191 &self.children
1192 }
1193
1194 pub fn should_isolate(&self) -> bool {
1196 self.isolate
1197 || self.opacity != Opacity::ONE
1198 || self.clip_path.is_some()
1199 || self.mask.is_some()
1200 || !self.filters.is_empty()
1201 || self.blend_mode != BlendMode::Normal }
1203
1204 pub fn has_children(&self) -> bool {
1206 !self.children.is_empty()
1207 }
1208
1209 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1220 let mut full_region = BBox::default();
1221 for filter in &self.filters {
1222 full_region = full_region.expand(filter.rect);
1223 }
1224
1225 full_region.to_non_zero_rect()
1226 }
1227
1228 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1229 if let Some(ref clip) = self.clip_path {
1230 f(&clip.root);
1231
1232 if let Some(ref sub_clip) = clip.clip_path {
1233 f(&sub_clip.root);
1234 }
1235 }
1236
1237 if let Some(ref mask) = self.mask {
1238 f(&mask.root);
1239
1240 if let Some(ref sub_mask) = mask.mask {
1241 f(&sub_mask.root);
1242 }
1243 }
1244
1245 for filter in &self.filters {
1246 for primitive in &filter.primitives {
1247 if let filter::Kind::Image(ref image) = primitive.kind {
1248 f(image.root());
1249 }
1250 }
1251 }
1252 }
1253}
1254
1255#[derive(Clone, Copy, PartialEq, Debug)]
1262#[allow(missing_docs)]
1263pub enum PaintOrder {
1264 FillAndStroke,
1265 StrokeAndFill,
1266}
1267
1268impl Default for PaintOrder {
1269 fn default() -> Self {
1270 Self::FillAndStroke
1271 }
1272}
1273
1274#[derive(Clone, Debug)]
1276pub struct Path {
1277 pub(crate) id: String,
1278 pub(crate) visible: bool,
1279 pub(crate) fill: Option<Fill>,
1280 pub(crate) stroke: Option<Stroke>,
1281 pub(crate) paint_order: PaintOrder,
1282 pub(crate) rendering_mode: ShapeRendering,
1283 pub(crate) data: Arc<tiny_skia_path::Path>,
1284 pub(crate) abs_transform: Transform,
1285 pub(crate) bounding_box: Rect,
1286 pub(crate) abs_bounding_box: Rect,
1287 pub(crate) stroke_bounding_box: Rect,
1288 pub(crate) abs_stroke_bounding_box: Rect,
1289}
1290
1291impl Path {
1292 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1293 Self::new(
1294 String::new(),
1295 true,
1296 None,
1297 None,
1298 PaintOrder::default(),
1299 ShapeRendering::default(),
1300 data,
1301 Transform::default(),
1302 )
1303 }
1304
1305 pub(crate) fn new(
1306 id: String,
1307 visible: bool,
1308 fill: Option<Fill>,
1309 stroke: Option<Stroke>,
1310 paint_order: PaintOrder,
1311 rendering_mode: ShapeRendering,
1312 data: Arc<tiny_skia_path::Path>,
1313 abs_transform: Transform,
1314 ) -> Option<Self> {
1315 let bounding_box = data.compute_tight_bounds()?;
1316 let stroke_bounding_box =
1317 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1318
1319 let abs_bounding_box: Rect;
1320 let abs_stroke_bounding_box: Rect;
1321 if abs_transform.has_skew() {
1322 let path2 = data.as_ref().clone();
1324 let path2 = path2.transform(abs_transform)?;
1325 abs_bounding_box = path2.compute_tight_bounds()?;
1326 abs_stroke_bounding_box =
1327 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1328 } else {
1329 abs_bounding_box = bounding_box.transform(abs_transform)?;
1331 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1332 }
1333
1334 Some(Path {
1335 id,
1336 visible,
1337 fill,
1338 stroke,
1339 paint_order,
1340 rendering_mode,
1341 data,
1342 abs_transform,
1343 bounding_box,
1344 abs_bounding_box,
1345 stroke_bounding_box,
1346 abs_stroke_bounding_box,
1347 })
1348 }
1349
1350 pub fn id(&self) -> &str {
1356 &self.id
1357 }
1358
1359 pub fn is_visible(&self) -> bool {
1361 self.visible
1362 }
1363
1364 pub fn fill(&self) -> Option<&Fill> {
1366 self.fill.as_ref()
1367 }
1368
1369 pub fn stroke(&self) -> Option<&Stroke> {
1371 self.stroke.as_ref()
1372 }
1373
1374 pub fn paint_order(&self) -> PaintOrder {
1381 self.paint_order
1382 }
1383
1384 pub fn rendering_mode(&self) -> ShapeRendering {
1388 self.rendering_mode
1389 }
1390
1391 pub fn data(&self) -> &tiny_skia_path::Path {
1396 self.data.as_ref()
1397 }
1398
1399 pub fn abs_transform(&self) -> Transform {
1406 self.abs_transform
1407 }
1408
1409 pub fn bounding_box(&self) -> Rect {
1413 self.bounding_box
1414 }
1415
1416 pub fn abs_bounding_box(&self) -> Rect {
1420 self.abs_bounding_box
1421 }
1422
1423 pub fn stroke_bounding_box(&self) -> Rect {
1427 self.stroke_bounding_box
1428 }
1429
1430 pub fn abs_stroke_bounding_box(&self) -> Rect {
1434 self.abs_stroke_bounding_box
1435 }
1436
1437 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1438 let mut stroke = stroke?.to_tiny_skia();
1439 stroke.dash = None;
1441
1442 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1446 return stroked_path.compute_tight_bounds();
1447 }
1448
1449 None
1450 }
1451
1452 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1453 if let Some(Paint::Pattern(patt)) = self.fill.as_ref().map(|f| &f.paint) {
1454 f(patt.root());
1455 }
1456 if let Some(Paint::Pattern(patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1457 f(patt.root());
1458 }
1459 }
1460}
1461
1462#[derive(Clone)]
1464pub enum ImageKind {
1465 JPEG(Arc<Vec<u8>>),
1467 PNG(Arc<Vec<u8>>),
1469 GIF(Arc<Vec<u8>>),
1471 WEBP(Arc<Vec<u8>>),
1473 SVG(Tree),
1475}
1476
1477impl ImageKind {
1478 pub(crate) fn actual_size(&self) -> Option<Size> {
1479 match self {
1480 ImageKind::JPEG(data)
1481 | ImageKind::PNG(data)
1482 | ImageKind::GIF(data)
1483 | ImageKind::WEBP(data) => imagesize::blob_size(data)
1484 .ok()
1485 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1486 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1487 ImageKind::SVG(svg) => Some(svg.size),
1488 }
1489 }
1490}
1491
1492impl core::fmt::Debug for ImageKind {
1493 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1494 match self {
1495 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1496 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1497 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1498 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1499 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1500 }
1501 }
1502}
1503
1504#[derive(Clone, Debug)]
1508pub struct Image {
1509 pub(crate) id: String,
1510 pub(crate) visible: bool,
1511 pub(crate) size: Size,
1512 pub(crate) rendering_mode: ImageRendering,
1513 pub(crate) kind: ImageKind,
1514 pub(crate) abs_transform: Transform,
1515 pub(crate) abs_bounding_box: NonZeroRect,
1516}
1517
1518impl Image {
1519 pub fn id(&self) -> &str {
1525 &self.id
1526 }
1527
1528 pub fn is_visible(&self) -> bool {
1530 self.visible
1531 }
1532
1533 pub fn size(&self) -> Size {
1538 self.size
1539 }
1540
1541 pub fn rendering_mode(&self) -> ImageRendering {
1545 self.rendering_mode
1546 }
1547
1548 pub fn kind(&self) -> &ImageKind {
1550 &self.kind
1551 }
1552
1553 pub fn abs_transform(&self) -> Transform {
1560 self.abs_transform
1561 }
1562
1563 pub fn bounding_box(&self) -> Rect {
1567 self.size.to_rect(0.0, 0.0).unwrap()
1568 }
1569
1570 pub fn abs_bounding_box(&self) -> Rect {
1574 self.abs_bounding_box.to_rect()
1575 }
1576
1577 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1578 if let ImageKind::SVG(ref tree) = self.kind {
1579 f(&tree.root);
1580 }
1581 }
1582}
1583
1584#[allow(missing_debug_implementations)]
1586#[derive(Clone, Debug)]
1587pub struct Tree {
1588 pub(crate) size: Size,
1589 pub(crate) root: Group,
1590 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1591 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1592 pub(crate) patterns: Vec<Arc<Pattern>>,
1593 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1594 pub(crate) masks: Vec<Arc<Mask>>,
1595 pub(crate) filters: Vec<Arc<filter::Filter>>,
1596 #[cfg(feature = "text")]
1597 pub(crate) fontdb: Arc<fontdb::Database>,
1598}
1599
1600impl Tree {
1601 pub fn size(&self) -> Size {
1607 self.size
1608 }
1609
1610 pub fn root(&self) -> &Group {
1612 &self.root
1613 }
1614
1615 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1619 if id.is_empty() {
1620 return None;
1621 }
1622
1623 node_by_id(&self.root, id)
1624 }
1625
1626 pub fn has_text_nodes(&self) -> bool {
1628 has_text_nodes(&self.root)
1629 }
1630
1631 pub fn has_defs_nodes(&self) -> bool {
1633 !self.linear_gradients().is_empty()
1634 || !self.radial_gradients().is_empty()
1635 || !self.patterns().is_empty()
1636 || !self.filters().is_empty()
1637 || !self.clip_paths().is_empty()
1638 || !self.masks().is_empty()
1639 }
1640
1641 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1643 &self.linear_gradients
1644 }
1645
1646 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1648 &self.radial_gradients
1649 }
1650
1651 pub fn patterns(&self) -> &[Arc<Pattern>] {
1653 &self.patterns
1654 }
1655
1656 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1658 &self.clip_paths
1659 }
1660
1661 pub fn masks(&self) -> &[Arc<Mask>] {
1663 &self.masks
1664 }
1665
1666 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1668 &self.filters
1669 }
1670
1671 #[cfg(feature = "text")]
1673 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1674 &self.fontdb
1675 }
1676
1677 pub(crate) fn collect_paint_servers(&mut self) {
1678 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1679 Paint::Color(_) => {}
1680 Paint::LinearGradient(lg) => {
1681 if !self
1682 .linear_gradients
1683 .iter()
1684 .any(|other| Arc::ptr_eq(lg, other))
1685 {
1686 self.linear_gradients.push(lg.clone());
1687 }
1688 }
1689 Paint::RadialGradient(rg) => {
1690 if !self
1691 .radial_gradients
1692 .iter()
1693 .any(|other| Arc::ptr_eq(rg, other))
1694 {
1695 self.radial_gradients.push(rg.clone());
1696 }
1697 }
1698 Paint::Pattern(patt) => {
1699 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1700 self.patterns.push(patt.clone());
1701 }
1702 }
1703 });
1704 }
1705}
1706
1707fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1708 for child in &parent.children {
1709 if child.id() == id {
1710 return Some(child);
1711 }
1712
1713 if let Node::Group(g) = child {
1714 if let Some(n) = node_by_id(g, id) {
1715 return Some(n);
1716 }
1717 }
1718 }
1719
1720 None
1721}
1722
1723fn has_text_nodes(root: &Group) -> bool {
1724 for node in &root.children {
1725 if let Node::Text(_) = node {
1726 return true;
1727 }
1728
1729 let mut has_text = false;
1730
1731 if let Node::Image(image) = node {
1732 if let ImageKind::SVG(tree) = &image.kind {
1733 if has_text_nodes(&tree.root) {
1734 has_text = true;
1735 }
1736 }
1737 }
1738
1739 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1740
1741 if has_text {
1742 return true;
1743 }
1744 }
1745
1746 false
1747}
1748
1749fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1750 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1751 if let Some(paint) = paint {
1752 f(paint);
1753 }
1754 }
1755
1756 for node in &parent.children {
1757 match node {
1758 Node::Group(group) => loop_over_paint_servers(group, f),
1759 Node::Path(path) => {
1760 push(path.fill.as_ref().map(|f| &f.paint), f);
1761 push(path.stroke.as_ref().map(|f| &f.paint), f);
1762 }
1763 Node::Image(_) => {}
1764 Node::Text(_) => {}
1766 }
1767
1768 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1769 }
1770}
1771
1772impl Group {
1773 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1774 for node in self.children() {
1775 if let Node::Group(g) = node {
1776 if let Some(clip) = &g.clip_path {
1777 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1778 clip_paths.push(clip.clone());
1779 }
1780
1781 if let Some(sub_clip) = &clip.clip_path {
1782 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1783 clip_paths.push(sub_clip.clone());
1784 }
1785 }
1786 }
1787 }
1788
1789 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1790
1791 if let Node::Group(g) = node {
1792 g.collect_clip_paths(clip_paths);
1793 }
1794 }
1795 }
1796
1797 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1798 for node in self.children() {
1799 if let Node::Group(g) = node {
1800 if let Some(mask) = &g.mask {
1801 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1802 masks.push(mask.clone());
1803 }
1804
1805 if let Some(sub_mask) = &mask.mask {
1806 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1807 masks.push(sub_mask.clone());
1808 }
1809 }
1810 }
1811 }
1812
1813 node.subroots(|subroot| subroot.collect_masks(masks));
1814
1815 if let Node::Group(g) = node {
1816 g.collect_masks(masks);
1817 }
1818 }
1819 }
1820
1821 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1822 for node in self.children() {
1823 if let Node::Group(g) = node {
1824 for filter in g.filters() {
1825 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1826 filters.push(filter.clone());
1827 }
1828 }
1829 }
1830
1831 node.subroots(|subroot| subroot.collect_filters(filters));
1832
1833 if let Node::Group(g) = node {
1834 g.collect_filters(filters);
1835 }
1836 }
1837 }
1838
1839 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1840 let mut bbox = BBox::default();
1841 for child in &self.children {
1842 let mut c_bbox = child.bounding_box();
1843 if let Node::Group(group) = child {
1844 if let Some(r) = c_bbox.transform(group.transform) {
1845 c_bbox = r;
1846 }
1847 }
1848
1849 bbox = bbox.expand(c_bbox);
1850 }
1851
1852 bbox.to_non_zero_rect()
1853 }
1854
1855 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1856 let mut bbox = BBox::default();
1857 let mut abs_bbox = BBox::default();
1858 let mut stroke_bbox = BBox::default();
1859 let mut abs_stroke_bbox = BBox::default();
1860 let mut layer_bbox = BBox::default();
1861 for child in &self.children {
1862 {
1863 let mut c_bbox = child.bounding_box();
1864 if let Node::Group(group) = child {
1865 if let Some(r) = c_bbox.transform(group.transform) {
1866 c_bbox = r;
1867 }
1868 }
1869
1870 bbox = bbox.expand(c_bbox);
1871 }
1872
1873 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1874
1875 {
1876 let mut c_bbox = child.stroke_bounding_box();
1877 if let Node::Group(group) = child {
1878 if let Some(r) = c_bbox.transform(group.transform) {
1879 c_bbox = r;
1880 }
1881 }
1882
1883 stroke_bbox = stroke_bbox.expand(c_bbox);
1884 }
1885
1886 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1887
1888 if let Node::Group(group) = child {
1889 let r = group.layer_bounding_box;
1890 if let Some(r) = r.transform(group.transform) {
1891 layer_bbox = layer_bbox.expand(r);
1892 }
1893 } else {
1894 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1896 }
1897 }
1898
1899 if let Some(bbox) = bbox.to_rect() {
1902 self.bounding_box = bbox;
1903 self.abs_bounding_box = abs_bbox.to_rect()?;
1904 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1905 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1906 }
1907
1908 if let Some(filter_bbox) = self.filters_bounding_box() {
1910 self.layer_bounding_box = filter_bbox;
1911 } else {
1912 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1913 }
1914
1915 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1916
1917 Some(())
1918 }
1919}