1pub mod filter;
5mod geom;
6mod text;
7
8use std::fmt::Display;
9use std::sync::Arc;
10
11pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
12
13pub use tiny_skia_path;
14
15pub use self::geom::*;
16pub use self::text::*;
17
18use crate::OptionLog;
19
20pub type Opacity = NormalizedF32;
22
23#[allow(missing_docs)]
25#[derive(Debug)]
26pub struct NonEmptyString(String);
27
28#[allow(missing_docs)]
29impl NonEmptyString {
30 pub fn new(string: String) -> Option<Self> {
31 if string.trim().is_empty() {
32 return None;
33 }
34
35 Some(NonEmptyString(string))
36 }
37
38 pub fn get(&self) -> &str {
39 &self.0
40 }
41
42 pub fn take(self) -> String {
43 self.0
44 }
45}
46
47#[derive(Clone, Copy, Debug)]
51pub struct NonZeroF32(f32);
52
53impl NonZeroF32 {
54 #[inline]
56 pub fn new(n: f32) -> Option<Self> {
57 if n.approx_eq_ulps(&0.0, 4) {
58 None
59 } else {
60 Some(NonZeroF32(n))
61 }
62 }
63
64 #[inline]
66 pub fn get(&self) -> f32 {
67 self.0
68 }
69}
70
71#[allow(missing_docs)]
72#[derive(Clone, Copy, PartialEq, Debug)]
73pub 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 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)]
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)]
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 std::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 std::fmt::Formatter<'_>) -> std::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#[allow(missing_docs)]
278#[derive(Debug)]
279pub struct BaseGradient {
280 pub id: NonEmptyString,
281 pub units: Units, pub transform: Transform,
283 pub spread_method: SpreadMethod,
284 pub stops: Vec<Stop>,
285}
286
287impl BaseGradient {
288 pub fn id(&self) -> &str {
293 self.id.get()
294 }
295
296 pub fn transform(&self) -> Transform {
300 self.transform
301 }
302
303 pub fn spread_method(&self) -> SpreadMethod {
307 self.spread_method
308 }
309
310 pub fn stops(&self) -> &[Stop] {
312 &self.stops
313 }
314}
315
316#[allow(missing_docs)]
320#[derive(Debug)]
321pub struct LinearGradient {
322 pub base: BaseGradient,
323 pub x1: f32,
324 pub y1: f32,
325 pub x2: f32,
326 pub y2: f32,
327}
328
329impl LinearGradient {
330 pub fn x1(&self) -> f32 {
332 self.x1
333 }
334
335 pub fn y1(&self) -> f32 {
337 self.y1
338 }
339
340 pub fn x2(&self) -> f32 {
342 self.x2
343 }
344
345 pub fn y2(&self) -> f32 {
347 self.y2
348 }
349}
350
351impl std::ops::Deref for LinearGradient {
352 type Target = BaseGradient;
353
354 fn deref(&self) -> &Self::Target {
355 &self.base
356 }
357}
358
359#[allow(missing_docs)]
363#[derive(Debug)]
364pub struct RadialGradient {
365 pub base: BaseGradient,
366 pub cx: f32,
367 pub cy: f32,
368 pub r: PositiveF32,
369 pub fx: f32,
370 pub fy: f32,
371 pub fr: PositiveF32,
372}
373
374impl RadialGradient {
375 pub fn cx(&self) -> f32 {
377 self.cx
378 }
379
380 pub fn cy(&self) -> f32 {
382 self.cy
383 }
384
385 pub fn r(&self) -> PositiveF32 {
387 self.r
388 }
389
390 pub fn fx(&self) -> f32 {
392 self.fx
393 }
394
395 pub fn fy(&self) -> f32 {
397 self.fy
398 }
399
400 pub fn fr(&self) -> PositiveF32 {
402 self.fr
403 }
404}
405
406impl std::ops::Deref for RadialGradient {
407 type Target = BaseGradient;
408
409 fn deref(&self) -> &Self::Target {
410 &self.base
411 }
412}
413
414pub type StopOffset = NormalizedF32;
416
417#[allow(missing_docs)]
421#[derive(Clone, Copy, Debug)]
422pub struct Stop {
423 pub offset: StopOffset,
424 pub color: Color,
425 pub opacity: Opacity,
426}
427
428impl Stop {
429 pub fn offset(&self) -> StopOffset {
433 self.offset
434 }
435
436 pub fn color(&self) -> Color {
440 self.color
441 }
442
443 pub fn opacity(&self) -> Opacity {
447 self.opacity
448 }
449}
450
451#[allow(missing_docs)]
455#[derive(Debug)]
456pub struct Pattern {
457 pub id: NonEmptyString,
458 pub units: Units, pub content_units: Units, pub transform: Transform,
461 pub rect: NonZeroRect,
462 pub view_box: Option<ViewBox>,
463 pub root: Group,
464}
465
466impl Pattern {
467 pub fn id(&self) -> &str {
472 self.id.get()
473 }
474
475 pub fn transform(&self) -> Transform {
479 self.transform
480 }
481
482 pub fn rect(&self) -> NonZeroRect {
486 self.rect
487 }
488
489 pub fn root(&self) -> &Group {
491 &self.root
492 }
493}
494
495pub type StrokeWidth = NonZeroPositiveF32;
497
498#[derive(Clone, Copy, Debug)]
502pub struct StrokeMiterlimit(f32);
503
504impl StrokeMiterlimit {
505 #[inline]
507 pub fn new(n: f32) -> Self {
508 debug_assert!(n.is_finite());
509 debug_assert!(n >= 1.0);
510
511 let n = if !(n >= 1.0) { 1.0 } else { n };
512
513 StrokeMiterlimit(n)
514 }
515
516 #[inline]
518 pub fn get(&self) -> f32 {
519 self.0
520 }
521}
522
523impl Default for StrokeMiterlimit {
524 #[inline]
525 fn default() -> Self {
526 StrokeMiterlimit::new(4.0)
527 }
528}
529
530impl From<f32> for StrokeMiterlimit {
531 #[inline]
532 fn from(n: f32) -> Self {
533 Self::new(n)
534 }
535}
536
537impl PartialEq for StrokeMiterlimit {
538 #[inline]
539 fn eq(&self, other: &Self) -> bool {
540 self.0.approx_eq_ulps(&other.0, 4)
541 }
542}
543
544#[allow(missing_docs)]
548#[derive(Clone, Copy, PartialEq, Debug)]
549pub enum LineCap {
550 Butt,
551 Round,
552 Square,
553}
554
555impl Default for LineCap {
556 fn default() -> Self {
557 Self::Butt
558 }
559}
560
561#[allow(missing_docs)]
565#[derive(Clone, Copy, PartialEq, Debug)]
566pub enum LineJoin {
567 Miter,
568 MiterClip,
569 Round,
570 Bevel,
571}
572
573impl Default for LineJoin {
574 fn default() -> Self {
575 Self::Miter
576 }
577}
578
579#[allow(missing_docs)]
581#[derive(Clone, Debug)]
582pub struct Stroke {
583 pub paint: Paint,
584 pub dasharray: Option<Vec<f32>>,
585 pub dashoffset: f32,
586 pub miterlimit: StrokeMiterlimit,
587 pub opacity: Opacity,
588 pub width: StrokeWidth,
589 pub linecap: LineCap,
590 pub linejoin: LineJoin,
591 pub context_element: Option<ContextElement>,
594}
595
596impl Stroke {
597 pub fn paint(&self) -> &Paint {
599 &self.paint
600 }
601
602 pub fn dasharray(&self) -> Option<&[f32]> {
604 self.dasharray.as_deref()
605 }
606
607 pub fn dashoffset(&self) -> f32 {
609 self.dashoffset
610 }
611
612 pub fn miterlimit(&self) -> StrokeMiterlimit {
614 self.miterlimit
615 }
616
617 pub fn opacity(&self) -> Opacity {
619 self.opacity
620 }
621
622 pub fn width(&self) -> StrokeWidth {
624 self.width
625 }
626
627 pub fn linecap(&self) -> LineCap {
629 self.linecap
630 }
631
632 pub fn linejoin(&self) -> LineJoin {
634 self.linejoin
635 }
636
637 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
639 let mut stroke = tiny_skia_path::Stroke {
640 width: self.width.get(),
641 miter_limit: self.miterlimit.get(),
642 line_cap: match self.linecap {
643 LineCap::Butt => tiny_skia_path::LineCap::Butt,
644 LineCap::Round => tiny_skia_path::LineCap::Round,
645 LineCap::Square => tiny_skia_path::LineCap::Square,
646 },
647 line_join: match self.linejoin {
648 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
649 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
650 LineJoin::Round => tiny_skia_path::LineJoin::Round,
651 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
652 },
653 dash: None,
656 };
657
658 if let Some(ref list) = self.dasharray {
659 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
660 }
661
662 stroke
663 }
664}
665
666#[allow(missing_docs)]
670#[derive(Clone, Copy, PartialEq, Debug)]
671pub enum FillRule {
672 NonZero,
673 EvenOdd,
674}
675
676impl Default for FillRule {
677 fn default() -> Self {
678 Self::NonZero
679 }
680}
681
682#[allow(missing_docs)]
683#[derive(Clone, Copy, Debug)]
684pub enum ContextElement {
685 UseNode,
690 PathNode(Transform, Option<NonZeroRect>),
695}
696
697#[allow(missing_docs)]
699#[derive(Clone, Debug)]
700pub struct Fill {
701 pub paint: Paint,
702 pub opacity: Opacity,
703 pub rule: FillRule,
704 pub context_element: Option<ContextElement>,
707}
708
709impl Fill {
710 pub fn paint(&self) -> &Paint {
712 &self.paint
713 }
714
715 pub fn opacity(&self) -> Opacity {
717 self.opacity
718 }
719
720 pub fn rule(&self) -> FillRule {
722 self.rule
723 }
724}
725
726impl Default for Fill {
727 fn default() -> Self {
728 Fill {
729 paint: Paint::Color(Color::black()),
730 opacity: Opacity::ONE,
731 rule: FillRule::default(),
732 context_element: None,
733 }
734 }
735}
736
737#[derive(Clone, Copy, PartialEq, Debug)]
739#[allow(missing_docs)]
740pub struct Color {
741 pub red: u8,
742 pub green: u8,
743 pub blue: u8,
744}
745
746impl Color {
747 #[inline]
749 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
750 Color { red, green, blue }
751 }
752
753 #[inline]
755 pub fn black() -> Color {
756 Color::new_rgb(0, 0, 0)
757 }
758
759 #[inline]
761 pub fn white() -> Color {
762 Color::new_rgb(255, 255, 255)
763 }
764}
765
766#[allow(missing_docs)]
770#[derive(Clone, Debug)]
771pub enum Paint {
772 Color(Color),
773 LinearGradient(Arc<LinearGradient>),
774 RadialGradient(Arc<RadialGradient>),
775 Pattern(Arc<Pattern>),
776}
777
778impl PartialEq for Paint {
779 #[inline]
780 fn eq(&self, other: &Self) -> bool {
781 match (self, other) {
782 (Self::Color(lc), Self::Color(rc)) => lc == rc,
783 (Self::LinearGradient(lg1), Self::LinearGradient(lg2)) => Arc::ptr_eq(lg1, lg2),
784 (Self::RadialGradient(rg1), Self::RadialGradient(rg2)) => Arc::ptr_eq(rg1, rg2),
785 (Self::Pattern(p1), Self::Pattern(p2)) => Arc::ptr_eq(p1, p2),
786 _ => false,
787 }
788 }
789}
790
791#[allow(missing_docs)]
795#[derive(Debug)]
796pub struct ClipPath {
797 pub id: NonEmptyString,
798 pub transform: Transform,
799 pub clip_path: Option<Arc<ClipPath>>,
800 pub root: Group,
801}
802
803impl ClipPath {
804 pub(crate) fn empty(id: NonEmptyString) -> Self {
805 ClipPath {
806 id,
807 transform: Transform::default(),
808 clip_path: None,
809 root: Group::empty(),
810 }
811 }
812
813 pub fn id(&self) -> &str {
818 self.id.get()
819 }
820
821 pub fn transform(&self) -> Transform {
825 self.transform
826 }
827
828 pub fn clip_path(&self) -> Option<&ClipPath> {
832 self.clip_path.as_deref()
833 }
834
835 pub fn root(&self) -> &Group {
837 &self.root
838 }
839}
840
841#[derive(Clone, Copy, PartialEq, Debug)]
843pub enum MaskType {
844 Luminance,
846 Alpha,
848}
849
850impl Default for MaskType {
851 fn default() -> Self {
852 Self::Luminance
853 }
854}
855
856#[allow(missing_docs)]
860#[derive(Debug)]
861pub struct Mask {
862 pub id: NonEmptyString,
863 pub rect: NonZeroRect,
864 pub kind: MaskType,
865 pub mask: Option<Arc<Mask>>,
866 pub root: Group,
867}
868
869impl Mask {
870 pub fn id(&self) -> &str {
875 self.id.get()
876 }
877
878 pub fn rect(&self) -> NonZeroRect {
882 self.rect
883 }
884
885 pub fn kind(&self) -> MaskType {
889 self.kind
890 }
891
892 pub fn mask(&self) -> Option<&Mask> {
896 self.mask.as_deref()
897 }
898
899 pub fn root(&self) -> &Group {
903 &self.root
904 }
905}
906
907#[allow(missing_docs)]
909#[derive(Clone, Debug)]
910pub enum Node {
911 Group(Box<Group>),
912 Path(Box<Path>),
913 Image(Box<Image>),
914 Text(Box<Text>),
915}
916
917impl Node {
918 pub fn id(&self) -> &str {
920 match self {
921 Node::Group(e) => e.id.as_str(),
922 Node::Path(e) => e.id.as_str(),
923 Node::Image(e) => e.id.as_str(),
924 Node::Text(e) => e.id.as_str(),
925 }
926 }
927
928 pub fn abs_transform(&self) -> Transform {
932 match self {
933 Node::Group(group) => group.abs_transform(),
934 Node::Path(path) => path.abs_transform(),
935 Node::Image(image) => image.abs_transform(),
936 Node::Text(text) => text.abs_transform(),
937 }
938 }
939
940 pub fn bounding_box(&self) -> Rect {
942 match self {
943 Node::Group(group) => group.bounding_box(),
944 Node::Path(path) => path.bounding_box(),
945 Node::Image(image) => image.bounding_box(),
946 Node::Text(text) => text.bounding_box(),
947 }
948 }
949
950 pub fn abs_bounding_box(&self) -> Rect {
952 match self {
953 Node::Group(group) => group.abs_bounding_box(),
954 Node::Path(path) => path.abs_bounding_box(),
955 Node::Image(image) => image.abs_bounding_box(),
956 Node::Text(text) => text.abs_bounding_box(),
957 }
958 }
959
960 pub fn stroke_bounding_box(&self) -> Rect {
962 match self {
963 Node::Group(group) => group.stroke_bounding_box(),
964 Node::Path(path) => path.stroke_bounding_box(),
965 Node::Image(image) => image.bounding_box(),
967 Node::Text(text) => text.stroke_bounding_box(),
968 }
969 }
970
971 pub fn abs_stroke_bounding_box(&self) -> Rect {
973 match self {
974 Node::Group(group) => group.abs_stroke_bounding_box(),
975 Node::Path(path) => path.abs_stroke_bounding_box(),
976 Node::Image(image) => image.abs_bounding_box(),
978 Node::Text(text) => text.abs_stroke_bounding_box(),
979 }
980 }
981
982 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
989 match self {
990 Node::Group(group) => Some(group.abs_layer_bounding_box()),
991 Node::Path(path) => path.abs_bounding_box().to_non_zero_rect(),
993 Node::Image(image) => image.abs_bounding_box().to_non_zero_rect(),
994 Node::Text(text) => text.abs_bounding_box().to_non_zero_rect(),
995 }
996 }
997
998 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1023 match self {
1024 Node::Group(group) => group.subroots(&mut f),
1025 Node::Path(path) => path.subroots(&mut f),
1026 Node::Image(image) => image.subroots(&mut f),
1027 Node::Text(text) => text.subroots(&mut f),
1028 }
1029 }
1030}
1031
1032#[allow(missing_docs)]
1039#[derive(Clone, Debug)]
1040pub struct Group {
1041 pub id: String,
1042 pub transform: Transform,
1043 pub abs_transform: Transform,
1044 pub opacity: Opacity,
1045 pub blend_mode: BlendMode,
1046 pub isolate: bool,
1047 pub clip_path: Option<Arc<ClipPath>>,
1048 pub is_context_element: bool,
1050 pub mask: Option<Arc<Mask>>,
1051 pub filters: Vec<Arc<filter::Filter>>,
1052 pub bounding_box: Rect,
1053 pub abs_bounding_box: Rect,
1054 pub stroke_bounding_box: Rect,
1055 pub abs_stroke_bounding_box: Rect,
1056 pub layer_bounding_box: NonZeroRect,
1057 pub abs_layer_bounding_box: NonZeroRect,
1058 pub children: Vec<Node>,
1059}
1060
1061impl Group {
1062 pub fn empty() -> Self {
1063 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1064 Group {
1065 id: String::new(),
1066 transform: Transform::default(),
1067 abs_transform: Transform::default(),
1068 opacity: Opacity::ONE,
1069 blend_mode: BlendMode::Normal,
1070 isolate: false,
1071 clip_path: None,
1072 mask: None,
1073 filters: Vec::new(),
1074 is_context_element: false,
1075 bounding_box: dummy,
1076 abs_bounding_box: dummy,
1077 stroke_bounding_box: dummy,
1078 abs_stroke_bounding_box: dummy,
1079 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1080 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1081 children: Vec::new(),
1082 }
1083 }
1084
1085 pub fn id(&self) -> &str {
1091 &self.id
1092 }
1093
1094 pub fn transform(&self) -> Transform {
1098 self.transform
1099 }
1100
1101 pub fn abs_transform(&self) -> Transform {
1108 self.abs_transform
1109 }
1110
1111 pub fn opacity(&self) -> Opacity {
1116 self.opacity
1117 }
1118
1119 pub fn blend_mode(&self) -> BlendMode {
1123 self.blend_mode
1124 }
1125
1126 pub fn isolate(&self) -> bool {
1130 self.isolate
1131 }
1132
1133 pub fn clip_path(&self) -> Option<&ClipPath> {
1135 self.clip_path.as_deref()
1136 }
1137
1138 pub fn mask(&self) -> Option<&Mask> {
1140 self.mask.as_deref()
1141 }
1142
1143 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1145 &self.filters
1146 }
1147
1148 pub fn bounding_box(&self) -> Rect {
1154 self.bounding_box
1155 }
1156
1157 pub fn abs_bounding_box(&self) -> Rect {
1161 self.abs_bounding_box
1162 }
1163
1164 pub fn stroke_bounding_box(&self) -> Rect {
1168 self.stroke_bounding_box
1169 }
1170
1171 pub fn abs_stroke_bounding_box(&self) -> Rect {
1175 self.abs_stroke_bounding_box
1176 }
1177
1178 pub fn layer_bounding_box(&self) -> NonZeroRect {
1192 self.layer_bounding_box
1193 }
1194
1195 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1197 self.abs_layer_bounding_box
1198 }
1199
1200 pub fn children(&self) -> &[Node] {
1202 &self.children
1203 }
1204
1205 pub fn should_isolate(&self) -> bool {
1207 self.isolate
1208 || self.opacity != Opacity::ONE
1209 || self.clip_path.is_some()
1210 || self.mask.is_some()
1211 || !self.filters.is_empty()
1212 || self.blend_mode != BlendMode::Normal }
1214
1215 pub fn has_children(&self) -> bool {
1217 !self.children.is_empty()
1218 }
1219
1220 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1231 let mut full_region = BBox::default();
1232 for filter in &self.filters {
1233 full_region = full_region.expand(filter.rect);
1234 }
1235
1236 full_region.to_non_zero_rect()
1237 }
1238
1239 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1240 if let Some(ref clip) = self.clip_path {
1241 f(&clip.root);
1242
1243 if let Some(ref sub_clip) = clip.clip_path {
1244 f(&sub_clip.root);
1245 }
1246 }
1247
1248 if let Some(ref mask) = self.mask {
1249 f(&mask.root);
1250
1251 if let Some(ref sub_mask) = mask.mask {
1252 f(&sub_mask.root);
1253 }
1254 }
1255
1256 for filter in &self.filters {
1257 for primitive in &filter.primitives {
1258 if let filter::Kind::Image(ref image) = primitive.kind {
1259 f(image.root());
1260 }
1261 }
1262 }
1263 }
1264}
1265
1266#[derive(Clone, Copy, PartialEq, Debug)]
1273#[allow(missing_docs)]
1274pub enum PaintOrder {
1275 FillAndStroke,
1276 StrokeAndFill,
1277}
1278
1279impl Default for PaintOrder {
1280 fn default() -> Self {
1281 Self::FillAndStroke
1282 }
1283}
1284
1285#[allow(missing_docs)]
1287#[derive(Clone, Debug)]
1288pub struct Path {
1289 pub id: String,
1290 pub visible: bool,
1291 pub fill: Option<Fill>,
1292 pub stroke: Option<Stroke>,
1293 pub paint_order: PaintOrder,
1294 pub rendering_mode: ShapeRendering,
1295 pub data: Arc<tiny_skia_path::Path>,
1296 pub abs_transform: Transform,
1297 pub bounding_box: Rect,
1298 pub abs_bounding_box: Rect,
1299 pub stroke_bounding_box: Rect,
1300 pub abs_stroke_bounding_box: Rect,
1301}
1302
1303impl Path {
1304 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1305 Self::new(
1306 String::new(),
1307 true,
1308 None,
1309 None,
1310 PaintOrder::default(),
1311 ShapeRendering::default(),
1312 data,
1313 Transform::default(),
1314 )
1315 }
1316
1317 pub(crate) fn new(
1318 id: String,
1319 visible: bool,
1320 fill: Option<Fill>,
1321 stroke: Option<Stroke>,
1322 paint_order: PaintOrder,
1323 rendering_mode: ShapeRendering,
1324 data: Arc<tiny_skia_path::Path>,
1325 abs_transform: Transform,
1326 ) -> Option<Self> {
1327 let bounding_box = data.compute_tight_bounds()?;
1328 let stroke_bounding_box =
1329 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1330
1331 let abs_bounding_box: Rect;
1332 let abs_stroke_bounding_box: Rect;
1333 if abs_transform.has_skew() {
1334 let path2 = data.as_ref().clone();
1336 let path2 = path2.transform(abs_transform)?;
1337 abs_bounding_box = path2.compute_tight_bounds()?;
1338 abs_stroke_bounding_box =
1339 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1340 } else {
1341 abs_bounding_box = bounding_box.transform(abs_transform)?;
1343 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1344 }
1345
1346 Some(Path {
1347 id,
1348 visible,
1349 fill,
1350 stroke,
1351 paint_order,
1352 rendering_mode,
1353 data,
1354 abs_transform,
1355 bounding_box,
1356 abs_bounding_box,
1357 stroke_bounding_box,
1358 abs_stroke_bounding_box,
1359 })
1360 }
1361
1362 pub fn id(&self) -> &str {
1368 &self.id
1369 }
1370
1371 pub fn is_visible(&self) -> bool {
1373 self.visible
1374 }
1375
1376 pub fn fill(&self) -> Option<&Fill> {
1378 self.fill.as_ref()
1379 }
1380
1381 pub fn stroke(&self) -> Option<&Stroke> {
1383 self.stroke.as_ref()
1384 }
1385
1386 pub fn paint_order(&self) -> PaintOrder {
1393 self.paint_order
1394 }
1395
1396 pub fn rendering_mode(&self) -> ShapeRendering {
1400 self.rendering_mode
1401 }
1402
1403 pub fn data(&self) -> &tiny_skia_path::Path {
1408 self.data.as_ref()
1409 }
1410
1411 pub fn abs_transform(&self) -> Transform {
1418 self.abs_transform
1419 }
1420
1421 pub fn bounding_box(&self) -> Rect {
1425 self.bounding_box
1426 }
1427
1428 pub fn abs_bounding_box(&self) -> Rect {
1432 self.abs_bounding_box
1433 }
1434
1435 pub fn stroke_bounding_box(&self) -> Rect {
1439 self.stroke_bounding_box
1440 }
1441
1442 pub fn abs_stroke_bounding_box(&self) -> Rect {
1446 self.abs_stroke_bounding_box
1447 }
1448
1449 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1450 let mut stroke = stroke?.to_tiny_skia();
1451 stroke.dash = None;
1453
1454 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1458 return stroked_path.compute_tight_bounds();
1459 }
1460
1461 None
1462 }
1463
1464 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1465 if let Some(Paint::Pattern(patt)) = self.fill.as_ref().map(|f| &f.paint) {
1466 f(patt.root());
1467 }
1468 if let Some(Paint::Pattern(patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1469 f(patt.root());
1470 }
1471 }
1472}
1473
1474#[derive(Clone)]
1476pub enum ImageKind {
1477 JPEG(Arc<Vec<u8>>),
1479 PNG(Arc<Vec<u8>>),
1481 GIF(Arc<Vec<u8>>),
1483 WEBP(Arc<Vec<u8>>),
1485 SVG(Tree),
1487}
1488
1489impl ImageKind {
1490 pub(crate) fn actual_size(&self) -> Option<Size> {
1491 match self {
1492 ImageKind::JPEG(data)
1493 | ImageKind::PNG(data)
1494 | ImageKind::GIF(data)
1495 | ImageKind::WEBP(data) => imagesize::blob_size(data)
1496 .ok()
1497 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1498 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1499 ImageKind::SVG(svg) => Some(svg.size),
1500 }
1501 }
1502}
1503
1504impl std::fmt::Debug for ImageKind {
1505 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1506 match self {
1507 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1508 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1509 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1510 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1511 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1512 }
1513 }
1514}
1515
1516#[allow(missing_docs)]
1520#[derive(Clone, Debug)]
1521pub struct Image {
1522 pub id: String,
1523 pub visible: bool,
1524 pub size: Size,
1525 pub rendering_mode: ImageRendering,
1526 pub kind: ImageKind,
1527 pub abs_transform: Transform,
1528 pub abs_bounding_box: NonZeroRect,
1529}
1530
1531impl Image {
1532 pub fn id(&self) -> &str {
1538 &self.id
1539 }
1540
1541 pub fn is_visible(&self) -> bool {
1543 self.visible
1544 }
1545
1546 pub fn size(&self) -> Size {
1551 self.size
1552 }
1553
1554 pub fn rendering_mode(&self) -> ImageRendering {
1558 self.rendering_mode
1559 }
1560
1561 pub fn kind(&self) -> &ImageKind {
1563 &self.kind
1564 }
1565
1566 pub fn abs_transform(&self) -> Transform {
1573 self.abs_transform
1574 }
1575
1576 pub fn bounding_box(&self) -> Rect {
1580 self.size.to_rect(0.0, 0.0).unwrap()
1581 }
1582
1583 pub fn abs_bounding_box(&self) -> Rect {
1587 self.abs_bounding_box.to_rect()
1588 }
1589
1590 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1591 if let ImageKind::SVG(ref tree) = self.kind {
1592 f(&tree.root);
1593 }
1594 }
1595}
1596
1597#[allow(missing_docs)]
1599#[allow(missing_debug_implementations)]
1600#[derive(Clone, Debug)]
1601pub struct Tree {
1602 pub size: Size,
1603 pub root: Group,
1604 pub linear_gradients: Vec<Arc<LinearGradient>>,
1605 pub radial_gradients: Vec<Arc<RadialGradient>>,
1606 pub patterns: Vec<Arc<Pattern>>,
1607 pub clip_paths: Vec<Arc<ClipPath>>,
1608 pub masks: Vec<Arc<Mask>>,
1609 pub filters: Vec<Arc<filter::Filter>>,
1610 #[cfg(feature = "text")]
1611 pub fontdb: Arc<fontdb::Database>,
1612}
1613
1614impl Tree {
1615 pub fn size(&self) -> Size {
1621 self.size
1622 }
1623
1624 pub fn root(&self) -> &Group {
1626 &self.root
1627 }
1628
1629 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1633 if id.is_empty() {
1634 return None;
1635 }
1636
1637 node_by_id(&self.root, id)
1638 }
1639
1640 pub fn has_text_nodes(&self) -> bool {
1642 has_text_nodes(&self.root)
1643 }
1644
1645 pub fn has_defs_nodes(&self) -> bool {
1647 !self.linear_gradients().is_empty()
1648 || !self.radial_gradients().is_empty()
1649 || !self.patterns().is_empty()
1650 || !self.filters().is_empty()
1651 || !self.clip_paths().is_empty()
1652 || !self.masks().is_empty()
1653 }
1654
1655 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1657 &self.linear_gradients
1658 }
1659
1660 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1662 &self.radial_gradients
1663 }
1664
1665 pub fn patterns(&self) -> &[Arc<Pattern>] {
1667 &self.patterns
1668 }
1669
1670 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1672 &self.clip_paths
1673 }
1674
1675 pub fn masks(&self) -> &[Arc<Mask>] {
1677 &self.masks
1678 }
1679
1680 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1682 &self.filters
1683 }
1684
1685 #[cfg(feature = "text")]
1687 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1688 &self.fontdb
1689 }
1690
1691 pub(crate) fn collect_paint_servers(&mut self) {
1692 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1693 Paint::Color(_) => {}
1694 Paint::LinearGradient(lg) => {
1695 if !self
1696 .linear_gradients
1697 .iter()
1698 .any(|other| Arc::ptr_eq(lg, other))
1699 {
1700 self.linear_gradients.push(lg.clone());
1701 }
1702 }
1703 Paint::RadialGradient(rg) => {
1704 if !self
1705 .radial_gradients
1706 .iter()
1707 .any(|other| Arc::ptr_eq(rg, other))
1708 {
1709 self.radial_gradients.push(rg.clone());
1710 }
1711 }
1712 Paint::Pattern(patt) => {
1713 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1714 self.patterns.push(patt.clone());
1715 }
1716 }
1717 });
1718 }
1719}
1720
1721fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1722 for child in &parent.children {
1723 if child.id() == id {
1724 return Some(child);
1725 }
1726
1727 if let Node::Group(g) = child {
1728 if let Some(n) = node_by_id(g, id) {
1729 return Some(n);
1730 }
1731 }
1732 }
1733
1734 None
1735}
1736
1737fn has_text_nodes(root: &Group) -> bool {
1738 for node in &root.children {
1739 if let Node::Text(_) = node {
1740 return true;
1741 }
1742
1743 let mut has_text = false;
1744
1745 if let Node::Image(image) = node {
1746 if let ImageKind::SVG(tree) = &image.kind {
1747 if has_text_nodes(&tree.root) {
1748 has_text = true;
1749 }
1750 }
1751 }
1752
1753 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1754
1755 if has_text {
1756 return true;
1757 }
1758 }
1759
1760 false
1761}
1762
1763fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1764 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1765 if let Some(paint) = paint {
1766 f(paint);
1767 }
1768 }
1769
1770 for node in &parent.children {
1771 match node {
1772 Node::Group(group) => loop_over_paint_servers(group, f),
1773 Node::Path(path) => {
1774 push(path.fill.as_ref().map(|f| &f.paint), f);
1775 push(path.stroke.as_ref().map(|f| &f.paint), f);
1776 }
1777 Node::Image(_) => {}
1778 Node::Text(_) => {}
1780 }
1781
1782 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1783 }
1784}
1785
1786impl Group {
1787 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1788 for node in self.children() {
1789 if let Node::Group(g) = node {
1790 if let Some(clip) = &g.clip_path {
1791 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1792 clip_paths.push(clip.clone());
1793 }
1794
1795 if let Some(sub_clip) = &clip.clip_path {
1796 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1797 clip_paths.push(sub_clip.clone());
1798 }
1799 }
1800 }
1801 }
1802
1803 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1804
1805 if let Node::Group(g) = node {
1806 g.collect_clip_paths(clip_paths);
1807 }
1808 }
1809 }
1810
1811 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1812 for node in self.children() {
1813 if let Node::Group(g) = node {
1814 if let Some(mask) = &g.mask {
1815 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1816 masks.push(mask.clone());
1817 }
1818
1819 if let Some(sub_mask) = &mask.mask {
1820 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1821 masks.push(sub_mask.clone());
1822 }
1823 }
1824 }
1825 }
1826
1827 node.subroots(|subroot| subroot.collect_masks(masks));
1828
1829 if let Node::Group(g) = node {
1830 g.collect_masks(masks);
1831 }
1832 }
1833 }
1834
1835 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1836 for node in self.children() {
1837 if let Node::Group(g) = node {
1838 for filter in g.filters() {
1839 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1840 filters.push(filter.clone());
1841 }
1842 }
1843 }
1844
1845 node.subroots(|subroot| subroot.collect_filters(filters));
1846
1847 if let Node::Group(g) = node {
1848 g.collect_filters(filters);
1849 }
1850 }
1851 }
1852
1853 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1854 let mut bbox = BBox::default();
1855 for child in &self.children {
1856 let mut c_bbox = child.bounding_box();
1857 if let Node::Group(group) = child {
1858 if let Some(r) = c_bbox.transform(group.transform) {
1859 c_bbox = r;
1860 }
1861 }
1862
1863 bbox = bbox.expand(c_bbox);
1864 }
1865
1866 bbox.to_non_zero_rect()
1867 }
1868
1869 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1870 let mut bbox = BBox::default();
1871 let mut abs_bbox = BBox::default();
1872 let mut stroke_bbox = BBox::default();
1873 let mut abs_stroke_bbox = BBox::default();
1874 let mut layer_bbox = BBox::default();
1875 for child in &self.children {
1876 {
1877 let mut c_bbox = child.bounding_box();
1878 if let Node::Group(group) = child {
1879 if let Some(r) = c_bbox.transform(group.transform) {
1880 c_bbox = r;
1881 }
1882 }
1883
1884 bbox = bbox.expand(c_bbox);
1885 }
1886
1887 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1888
1889 {
1890 let mut c_bbox = child.stroke_bounding_box();
1891 if let Node::Group(group) = child {
1892 if let Some(r) = c_bbox.transform(group.transform) {
1893 c_bbox = r;
1894 }
1895 }
1896
1897 stroke_bbox = stroke_bbox.expand(c_bbox);
1898 }
1899
1900 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1901
1902 if let Node::Group(group) = child {
1903 let r = group.layer_bounding_box;
1904 if let Some(r) = r.transform(group.transform) {
1905 layer_bbox = layer_bbox.expand(r);
1906 }
1907 } else {
1908 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1910 }
1911 }
1912
1913 if let Some(bbox) = bbox.to_rect() {
1916 self.bounding_box = bbox;
1917 self.abs_bounding_box = abs_bbox.to_rect()?;
1918 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1919 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1920 }
1921
1922 if let Some(filter_bbox) = self.filters_bounding_box() {
1924 self.layer_bounding_box = filter_bbox;
1925 } else {
1926 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1927 }
1928
1929 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1930
1931 Some(())
1932 }
1933}