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#[derive(Debug)]
25pub(crate) struct NonEmptyString(String);
26
27impl NonEmptyString {
28 pub(crate) fn new(string: String) -> Option<Self> {
29 if string.trim().is_empty() {
30 return None;
31 }
32
33 Some(NonEmptyString(string))
34 }
35
36 pub(crate) fn get(&self) -> &str {
37 &self.0
38 }
39
40 pub(crate) fn take(self) -> String {
41 self.0
42 }
43}
44
45#[derive(Clone, Copy, Debug)]
49pub struct NonZeroF32(f32);
50
51impl NonZeroF32 {
52 #[inline]
54 pub fn new(n: f32) -> Option<Self> {
55 if n.approx_eq_ulps(&0.0, 4) {
56 None
57 } else {
58 Some(NonZeroF32(n))
59 }
60 }
61
62 #[inline]
64 pub fn get(&self) -> f32 {
65 self.0
66 }
67}
68
69#[derive(Clone, Copy, PartialEq, Debug)]
70pub(crate) enum Units {
71 UserSpaceOnUse,
72 ObjectBoundingBox,
73}
74
75#[allow(missing_docs)]
81#[derive(Clone, Copy, PartialEq, Debug)]
82pub(crate) enum Visibility {
83 Visible,
84 Hidden,
85 Collapse,
86}
87
88impl Default for Visibility {
89 fn default() -> Self {
90 Self::Visible
91 }
92}
93
94#[derive(Clone, Copy, PartialEq, Debug)]
98#[allow(missing_docs)]
99pub enum ShapeRendering {
100 OptimizeSpeed,
101 CrispEdges,
102 GeometricPrecision,
103}
104
105impl ShapeRendering {
106 pub fn use_shape_antialiasing(self) -> bool {
108 match self {
109 ShapeRendering::OptimizeSpeed => false,
110 ShapeRendering::CrispEdges => false,
111 ShapeRendering::GeometricPrecision => true,
112 }
113 }
114}
115
116impl Default for ShapeRendering {
117 fn default() -> Self {
118 Self::GeometricPrecision
119 }
120}
121
122impl std::str::FromStr for ShapeRendering {
123 type Err = &'static str;
124
125 fn from_str(s: &str) -> Result<Self, Self::Err> {
126 match s {
127 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
128 "crispEdges" => Ok(ShapeRendering::CrispEdges),
129 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
130 _ => Err("invalid"),
131 }
132 }
133}
134
135#[allow(missing_docs)]
139#[derive(Clone, Copy, PartialEq, Debug)]
140pub enum TextRendering {
141 OptimizeSpeed,
142 OptimizeLegibility,
143 GeometricPrecision,
144}
145
146impl Default for TextRendering {
147 fn default() -> Self {
148 Self::OptimizeLegibility
149 }
150}
151
152impl std::str::FromStr for TextRendering {
153 type Err = &'static str;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 match s {
157 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
158 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
159 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
160 _ => Err("invalid"),
161 }
162 }
163}
164
165#[allow(missing_docs)]
169#[derive(Clone, Copy, PartialEq, Debug)]
170pub enum ImageRendering {
171 OptimizeQuality,
172 OptimizeSpeed,
173 Smooth,
175 HighQuality,
176 CrispEdges,
177 Pixelated,
178}
179
180impl Default for ImageRendering {
181 fn default() -> Self {
182 Self::OptimizeQuality
183 }
184}
185
186impl std::str::FromStr for ImageRendering {
187 type Err = &'static str;
188
189 fn from_str(s: &str) -> Result<Self, Self::Err> {
190 match s {
191 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
192 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
193 "smooth" => Ok(ImageRendering::Smooth),
194 "high-quality" => Ok(ImageRendering::HighQuality),
195 "crisp-edges" => Ok(ImageRendering::CrispEdges),
196 "pixelated" => Ok(ImageRendering::Pixelated),
197 _ => Err("invalid"),
198 }
199 }
200}
201
202#[allow(missing_docs)]
206#[derive(Clone, Copy, PartialEq, Debug)]
207pub enum BlendMode {
208 Normal,
209 Multiply,
210 Screen,
211 Overlay,
212 Darken,
213 Lighten,
214 ColorDodge,
215 ColorBurn,
216 HardLight,
217 SoftLight,
218 Difference,
219 Exclusion,
220 Hue,
221 Saturation,
222 Color,
223 Luminosity,
224}
225
226impl Default for BlendMode {
227 fn default() -> Self {
228 Self::Normal
229 }
230}
231
232impl Display for BlendMode {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 let blend_mode = match self {
235 BlendMode::Normal => "normal",
236 BlendMode::Multiply => "multiply",
237 BlendMode::Screen => "screen",
238 BlendMode::Overlay => "overlay",
239 BlendMode::Darken => "darken",
240 BlendMode::Lighten => "lighten",
241 BlendMode::ColorDodge => "color-dodge",
242 BlendMode::ColorBurn => "color-burn",
243 BlendMode::HardLight => "hard-light",
244 BlendMode::SoftLight => "soft-light",
245 BlendMode::Difference => "difference",
246 BlendMode::Exclusion => "exclusion",
247 BlendMode::Hue => "hue",
248 BlendMode::Saturation => "saturation",
249 BlendMode::Color => "color",
250 BlendMode::Luminosity => "luminosity",
251 };
252 write!(f, "{blend_mode}")
253 }
254}
255
256#[allow(missing_docs)]
260#[derive(Clone, Copy, PartialEq, Debug)]
261pub enum SpreadMethod {
262 Pad,
263 Reflect,
264 Repeat,
265}
266
267impl Default for SpreadMethod {
268 fn default() -> Self {
269 Self::Pad
270 }
271}
272
273#[derive(Debug)]
275pub struct BaseGradient {
276 pub(crate) id: NonEmptyString,
277 pub(crate) units: Units, pub(crate) transform: Transform,
279 pub(crate) spread_method: SpreadMethod,
280 pub(crate) stops: Vec<Stop>,
281}
282
283impl BaseGradient {
284 pub fn id(&self) -> &str {
289 self.id.get()
290 }
291
292 pub fn transform(&self) -> Transform {
296 self.transform
297 }
298
299 pub fn spread_method(&self) -> SpreadMethod {
303 self.spread_method
304 }
305
306 pub fn stops(&self) -> &[Stop] {
308 &self.stops
309 }
310}
311
312#[derive(Debug)]
316pub struct LinearGradient {
317 pub(crate) base: BaseGradient,
318 pub(crate) x1: f32,
319 pub(crate) y1: f32,
320 pub(crate) x2: f32,
321 pub(crate) y2: f32,
322}
323
324impl LinearGradient {
325 pub fn x1(&self) -> f32 {
327 self.x1
328 }
329
330 pub fn y1(&self) -> f32 {
332 self.y1
333 }
334
335 pub fn x2(&self) -> f32 {
337 self.x2
338 }
339
340 pub fn y2(&self) -> f32 {
342 self.y2
343 }
344}
345
346impl std::ops::Deref for LinearGradient {
347 type Target = BaseGradient;
348
349 fn deref(&self) -> &Self::Target {
350 &self.base
351 }
352}
353
354#[derive(Debug)]
358pub struct RadialGradient {
359 pub(crate) base: BaseGradient,
360 pub(crate) cx: f32,
361 pub(crate) cy: f32,
362 pub(crate) r: PositiveF32,
363 pub(crate) fx: f32,
364 pub(crate) fy: f32,
365 pub(crate) fr: PositiveF32,
366}
367
368impl RadialGradient {
369 pub fn cx(&self) -> f32 {
371 self.cx
372 }
373
374 pub fn cy(&self) -> f32 {
376 self.cy
377 }
378
379 pub fn r(&self) -> PositiveF32 {
381 self.r
382 }
383
384 pub fn fx(&self) -> f32 {
386 self.fx
387 }
388
389 pub fn fy(&self) -> f32 {
391 self.fy
392 }
393
394 pub fn fr(&self) -> PositiveF32 {
396 self.fr
397 }
398}
399
400impl std::ops::Deref for RadialGradient {
401 type Target = BaseGradient;
402
403 fn deref(&self) -> &Self::Target {
404 &self.base
405 }
406}
407
408pub type StopOffset = NormalizedF32;
410
411#[derive(Clone, Copy, Debug)]
415pub struct Stop {
416 pub(crate) offset: StopOffset,
417 pub(crate) color: Color,
418 pub(crate) opacity: Opacity,
419}
420
421impl Stop {
422 pub fn offset(&self) -> StopOffset {
426 self.offset
427 }
428
429 pub fn color(&self) -> Color {
433 self.color
434 }
435
436 pub fn opacity(&self) -> Opacity {
440 self.opacity
441 }
442}
443
444#[derive(Debug)]
448pub struct Pattern {
449 pub(crate) id: NonEmptyString,
450 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
453 pub(crate) rect: NonZeroRect,
454 pub(crate) view_box: Option<ViewBox>,
455 pub(crate) root: Group,
456}
457
458impl Pattern {
459 pub fn id(&self) -> &str {
464 self.id.get()
465 }
466
467 pub fn transform(&self) -> Transform {
471 self.transform
472 }
473
474 pub fn rect(&self) -> NonZeroRect {
478 self.rect
479 }
480
481 pub fn root(&self) -> &Group {
483 &self.root
484 }
485}
486
487pub type StrokeWidth = NonZeroPositiveF32;
489
490#[derive(Clone, Copy, Debug)]
494pub struct StrokeMiterlimit(f32);
495
496impl StrokeMiterlimit {
497 #[inline]
499 pub fn new(n: f32) -> Self {
500 debug_assert!(n.is_finite());
501 debug_assert!(n >= 1.0);
502
503 let n = if !(n >= 1.0) { 1.0 } else { n };
504
505 StrokeMiterlimit(n)
506 }
507
508 #[inline]
510 pub fn get(&self) -> f32 {
511 self.0
512 }
513}
514
515impl Default for StrokeMiterlimit {
516 #[inline]
517 fn default() -> Self {
518 StrokeMiterlimit::new(4.0)
519 }
520}
521
522impl From<f32> for StrokeMiterlimit {
523 #[inline]
524 fn from(n: f32) -> Self {
525 Self::new(n)
526 }
527}
528
529impl PartialEq for StrokeMiterlimit {
530 #[inline]
531 fn eq(&self, other: &Self) -> bool {
532 self.0.approx_eq_ulps(&other.0, 4)
533 }
534}
535
536#[allow(missing_docs)]
540#[derive(Clone, Copy, PartialEq, Debug)]
541pub enum LineCap {
542 Butt,
543 Round,
544 Square,
545}
546
547impl Default for LineCap {
548 fn default() -> Self {
549 Self::Butt
550 }
551}
552
553#[allow(missing_docs)]
557#[derive(Clone, Copy, PartialEq, Debug)]
558pub enum LineJoin {
559 Miter,
560 MiterClip,
561 Round,
562 Bevel,
563}
564
565impl Default for LineJoin {
566 fn default() -> Self {
567 Self::Miter
568 }
569}
570
571#[derive(Clone, Debug)]
573pub struct Stroke {
574 pub(crate) paint: Paint,
575 pub(crate) dasharray: Option<Vec<f32>>,
576 pub(crate) dashoffset: f32,
577 pub(crate) miterlimit: StrokeMiterlimit,
578 pub(crate) opacity: Opacity,
579 pub(crate) width: StrokeWidth,
580 pub(crate) linecap: LineCap,
581 pub(crate) linejoin: LineJoin,
582 pub(crate) context_element: Option<ContextElement>,
585}
586
587impl Stroke {
588 pub fn paint(&self) -> &Paint {
590 &self.paint
591 }
592
593 pub fn dasharray(&self) -> Option<&[f32]> {
595 self.dasharray.as_deref()
596 }
597
598 pub fn dashoffset(&self) -> f32 {
600 self.dashoffset
601 }
602
603 pub fn miterlimit(&self) -> StrokeMiterlimit {
605 self.miterlimit
606 }
607
608 pub fn opacity(&self) -> Opacity {
610 self.opacity
611 }
612
613 pub fn width(&self) -> StrokeWidth {
615 self.width
616 }
617
618 pub fn linecap(&self) -> LineCap {
620 self.linecap
621 }
622
623 pub fn linejoin(&self) -> LineJoin {
625 self.linejoin
626 }
627
628 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
630 let mut stroke = tiny_skia_path::Stroke {
631 width: self.width.get(),
632 miter_limit: self.miterlimit.get(),
633 line_cap: match self.linecap {
634 LineCap::Butt => tiny_skia_path::LineCap::Butt,
635 LineCap::Round => tiny_skia_path::LineCap::Round,
636 LineCap::Square => tiny_skia_path::LineCap::Square,
637 },
638 line_join: match self.linejoin {
639 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
640 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
641 LineJoin::Round => tiny_skia_path::LineJoin::Round,
642 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
643 },
644 dash: None,
647 };
648
649 if let Some(ref list) = self.dasharray {
650 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
651 }
652
653 stroke
654 }
655}
656
657#[allow(missing_docs)]
661#[derive(Clone, Copy, PartialEq, Debug)]
662pub enum FillRule {
663 NonZero,
664 EvenOdd,
665}
666
667impl Default for FillRule {
668 fn default() -> Self {
669 Self::NonZero
670 }
671}
672
673#[derive(Clone, Copy, Debug)]
674pub(crate) enum ContextElement {
675 UseNode,
680 PathNode(Transform, Option<NonZeroRect>),
685}
686
687#[derive(Clone, Debug)]
689pub struct Fill {
690 pub(crate) paint: Paint,
691 pub(crate) opacity: Opacity,
692 pub(crate) rule: FillRule,
693 pub(crate) context_element: Option<ContextElement>,
696}
697
698impl Fill {
699 pub fn paint(&self) -> &Paint {
701 &self.paint
702 }
703
704 pub fn opacity(&self) -> Opacity {
706 self.opacity
707 }
708
709 pub fn rule(&self) -> FillRule {
711 self.rule
712 }
713}
714
715impl Default for Fill {
716 fn default() -> Self {
717 Fill {
718 paint: Paint::Color(Color::black()),
719 opacity: Opacity::ONE,
720 rule: FillRule::default(),
721 context_element: None,
722 }
723 }
724}
725
726#[derive(Clone, Copy, PartialEq, Debug)]
728#[allow(missing_docs)]
729pub struct Color {
730 pub red: u8,
731 pub green: u8,
732 pub blue: u8,
733}
734
735impl Color {
736 #[inline]
738 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
739 Color { red, green, blue }
740 }
741
742 #[inline]
744 pub fn black() -> Color {
745 Color::new_rgb(0, 0, 0)
746 }
747
748 #[inline]
750 pub fn white() -> Color {
751 Color::new_rgb(255, 255, 255)
752 }
753}
754
755#[allow(missing_docs)]
759#[derive(Clone, Debug)]
760pub enum Paint {
761 Color(Color),
762 LinearGradient(Arc<LinearGradient>),
763 RadialGradient(Arc<RadialGradient>),
764 Pattern(Arc<Pattern>),
765}
766
767impl PartialEq for Paint {
768 #[inline]
769 fn eq(&self, other: &Self) -> bool {
770 match (self, other) {
771 (Self::Color(lc), Self::Color(rc)) => lc == rc,
772 (Self::LinearGradient(lg1), Self::LinearGradient(lg2)) => Arc::ptr_eq(lg1, lg2),
773 (Self::RadialGradient(rg1), Self::RadialGradient(rg2)) => Arc::ptr_eq(rg1, rg2),
774 (Self::Pattern(p1), Self::Pattern(p2)) => Arc::ptr_eq(p1, p2),
775 _ => false,
776 }
777 }
778}
779
780#[derive(Debug)]
784pub struct ClipPath {
785 pub(crate) id: NonEmptyString,
786 pub(crate) transform: Transform,
787 pub(crate) clip_path: Option<Arc<ClipPath>>,
788 pub(crate) root: Group,
789}
790
791impl ClipPath {
792 pub(crate) fn empty(id: NonEmptyString) -> Self {
793 ClipPath {
794 id,
795 transform: Transform::default(),
796 clip_path: None,
797 root: Group::empty(),
798 }
799 }
800
801 pub fn id(&self) -> &str {
806 self.id.get()
807 }
808
809 pub fn transform(&self) -> Transform {
813 self.transform
814 }
815
816 pub fn clip_path(&self) -> Option<&ClipPath> {
820 self.clip_path.as_deref()
821 }
822
823 pub fn root(&self) -> &Group {
825 &self.root
826 }
827}
828
829#[derive(Clone, Copy, PartialEq, Debug)]
831pub enum MaskType {
832 Luminance,
834 Alpha,
836}
837
838impl Default for MaskType {
839 fn default() -> Self {
840 Self::Luminance
841 }
842}
843
844#[derive(Debug)]
848pub struct Mask {
849 pub(crate) id: NonEmptyString,
850 pub(crate) rect: NonZeroRect,
851 pub(crate) kind: MaskType,
852 pub(crate) mask: Option<Arc<Mask>>,
853 pub(crate) root: Group,
854}
855
856impl Mask {
857 pub fn id(&self) -> &str {
862 self.id.get()
863 }
864
865 pub fn rect(&self) -> NonZeroRect {
869 self.rect
870 }
871
872 pub fn kind(&self) -> MaskType {
876 self.kind
877 }
878
879 pub fn mask(&self) -> Option<&Mask> {
883 self.mask.as_deref()
884 }
885
886 pub fn root(&self) -> &Group {
890 &self.root
891 }
892}
893
894#[allow(missing_docs)]
896#[derive(Clone, Debug)]
897pub enum Node {
898 Group(Box<Group>),
899 Path(Box<Path>),
900 Image(Box<Image>),
901 Text(Box<Text>),
902}
903
904impl Node {
905 pub fn id(&self) -> &str {
907 match self {
908 Node::Group(e) => e.id.as_str(),
909 Node::Path(e) => e.id.as_str(),
910 Node::Image(e) => e.id.as_str(),
911 Node::Text(e) => e.id.as_str(),
912 }
913 }
914
915 pub fn abs_transform(&self) -> Transform {
919 match self {
920 Node::Group(group) => group.abs_transform(),
921 Node::Path(path) => path.abs_transform(),
922 Node::Image(image) => image.abs_transform(),
923 Node::Text(text) => text.abs_transform(),
924 }
925 }
926
927 pub fn bounding_box(&self) -> Rect {
929 match self {
930 Node::Group(group) => group.bounding_box(),
931 Node::Path(path) => path.bounding_box(),
932 Node::Image(image) => image.bounding_box(),
933 Node::Text(text) => text.bounding_box(),
934 }
935 }
936
937 pub fn abs_bounding_box(&self) -> Rect {
939 match self {
940 Node::Group(group) => group.abs_bounding_box(),
941 Node::Path(path) => path.abs_bounding_box(),
942 Node::Image(image) => image.abs_bounding_box(),
943 Node::Text(text) => text.abs_bounding_box(),
944 }
945 }
946
947 pub fn stroke_bounding_box(&self) -> Rect {
949 match self {
950 Node::Group(group) => group.stroke_bounding_box(),
951 Node::Path(path) => path.stroke_bounding_box(),
952 Node::Image(image) => image.bounding_box(),
954 Node::Text(text) => text.stroke_bounding_box(),
955 }
956 }
957
958 pub fn abs_stroke_bounding_box(&self) -> Rect {
960 match self {
961 Node::Group(group) => group.abs_stroke_bounding_box(),
962 Node::Path(path) => path.abs_stroke_bounding_box(),
963 Node::Image(image) => image.abs_bounding_box(),
965 Node::Text(text) => text.abs_stroke_bounding_box(),
966 }
967 }
968
969 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
976 match self {
977 Node::Group(group) => Some(group.abs_layer_bounding_box()),
978 Node::Path(path) => path.abs_bounding_box().to_non_zero_rect(),
980 Node::Image(image) => image.abs_bounding_box().to_non_zero_rect(),
981 Node::Text(text) => text.abs_bounding_box().to_non_zero_rect(),
982 }
983 }
984
985 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1010 match self {
1011 Node::Group(group) => group.subroots(&mut f),
1012 Node::Path(path) => path.subroots(&mut f),
1013 Node::Image(image) => image.subroots(&mut f),
1014 Node::Text(text) => text.subroots(&mut f),
1015 }
1016 }
1017}
1018
1019#[derive(Clone, Debug)]
1026pub struct Group {
1027 pub(crate) id: String,
1028 pub(crate) transform: Transform,
1029 pub(crate) abs_transform: Transform,
1030 pub(crate) opacity: Opacity,
1031 pub(crate) blend_mode: BlendMode,
1032 pub(crate) isolate: bool,
1033 pub(crate) clip_path: Option<Arc<ClipPath>>,
1034 pub(crate) is_context_element: bool,
1036 pub(crate) mask: Option<Arc<Mask>>,
1037 pub(crate) filters: Vec<Arc<filter::Filter>>,
1038 pub(crate) bounding_box: Rect,
1039 pub(crate) abs_bounding_box: Rect,
1040 pub(crate) stroke_bounding_box: Rect,
1041 pub(crate) abs_stroke_bounding_box: Rect,
1042 pub(crate) layer_bounding_box: NonZeroRect,
1043 pub(crate) abs_layer_bounding_box: NonZeroRect,
1044 pub(crate) children: Vec<Node>,
1045}
1046
1047impl Group {
1048 pub(crate) fn empty() -> Self {
1049 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1050 Group {
1051 id: String::new(),
1052 transform: Transform::default(),
1053 abs_transform: Transform::default(),
1054 opacity: Opacity::ONE,
1055 blend_mode: BlendMode::Normal,
1056 isolate: false,
1057 clip_path: None,
1058 mask: None,
1059 filters: Vec::new(),
1060 is_context_element: false,
1061 bounding_box: dummy,
1062 abs_bounding_box: dummy,
1063 stroke_bounding_box: dummy,
1064 abs_stroke_bounding_box: dummy,
1065 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1066 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1067 children: Vec::new(),
1068 }
1069 }
1070
1071 pub fn id(&self) -> &str {
1077 &self.id
1078 }
1079
1080 pub fn transform(&self) -> Transform {
1084 self.transform
1085 }
1086
1087 pub fn abs_transform(&self) -> Transform {
1094 self.abs_transform
1095 }
1096
1097 pub fn opacity(&self) -> Opacity {
1102 self.opacity
1103 }
1104
1105 pub fn blend_mode(&self) -> BlendMode {
1109 self.blend_mode
1110 }
1111
1112 pub fn isolate(&self) -> bool {
1116 self.isolate
1117 }
1118
1119 pub fn clip_path(&self) -> Option<&ClipPath> {
1121 self.clip_path.as_deref()
1122 }
1123
1124 pub fn mask(&self) -> Option<&Mask> {
1126 self.mask.as_deref()
1127 }
1128
1129 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1131 &self.filters
1132 }
1133
1134 pub fn bounding_box(&self) -> Rect {
1140 self.bounding_box
1141 }
1142
1143 pub fn abs_bounding_box(&self) -> Rect {
1147 self.abs_bounding_box
1148 }
1149
1150 pub fn stroke_bounding_box(&self) -> Rect {
1154 self.stroke_bounding_box
1155 }
1156
1157 pub fn abs_stroke_bounding_box(&self) -> Rect {
1161 self.abs_stroke_bounding_box
1162 }
1163
1164 pub fn layer_bounding_box(&self) -> NonZeroRect {
1178 self.layer_bounding_box
1179 }
1180
1181 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1183 self.abs_layer_bounding_box
1184 }
1185
1186 pub fn children(&self) -> &[Node] {
1188 &self.children
1189 }
1190
1191 pub fn should_isolate(&self) -> bool {
1193 self.isolate
1194 || self.opacity != Opacity::ONE
1195 || self.clip_path.is_some()
1196 || self.mask.is_some()
1197 || !self.filters.is_empty()
1198 || self.blend_mode != BlendMode::Normal }
1200
1201 pub fn has_children(&self) -> bool {
1203 !self.children.is_empty()
1204 }
1205
1206 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1217 let mut full_region = BBox::default();
1218 for filter in &self.filters {
1219 full_region = full_region.expand(filter.rect);
1220 }
1221
1222 full_region.to_non_zero_rect()
1223 }
1224
1225 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1226 if let Some(ref clip) = self.clip_path {
1227 f(&clip.root);
1228
1229 if let Some(ref sub_clip) = clip.clip_path {
1230 f(&sub_clip.root);
1231 }
1232 }
1233
1234 if let Some(ref mask) = self.mask {
1235 f(&mask.root);
1236
1237 if let Some(ref sub_mask) = mask.mask {
1238 f(&sub_mask.root);
1239 }
1240 }
1241
1242 for filter in &self.filters {
1243 for primitive in &filter.primitives {
1244 if let filter::Kind::Image(ref image) = primitive.kind {
1245 f(image.root());
1246 }
1247 }
1248 }
1249 }
1250}
1251
1252#[derive(Clone, Copy, PartialEq, Debug)]
1259#[allow(missing_docs)]
1260pub enum PaintOrder {
1261 FillAndStroke,
1262 StrokeAndFill,
1263}
1264
1265impl Default for PaintOrder {
1266 fn default() -> Self {
1267 Self::FillAndStroke
1268 }
1269}
1270
1271#[derive(Clone, Debug)]
1273pub struct Path {
1274 pub(crate) id: String,
1275 pub(crate) visible: bool,
1276 pub(crate) fill: Option<Fill>,
1277 pub(crate) stroke: Option<Stroke>,
1278 pub(crate) paint_order: PaintOrder,
1279 pub(crate) rendering_mode: ShapeRendering,
1280 pub(crate) data: Arc<tiny_skia_path::Path>,
1281 pub(crate) abs_transform: Transform,
1282 pub(crate) bounding_box: Rect,
1283 pub(crate) abs_bounding_box: Rect,
1284 pub(crate) stroke_bounding_box: Rect,
1285 pub(crate) abs_stroke_bounding_box: Rect,
1286}
1287
1288impl Path {
1289 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1290 Self::new(
1291 String::new(),
1292 true,
1293 None,
1294 None,
1295 PaintOrder::default(),
1296 ShapeRendering::default(),
1297 data,
1298 Transform::default(),
1299 )
1300 }
1301
1302 pub(crate) fn new(
1303 id: String,
1304 visible: bool,
1305 fill: Option<Fill>,
1306 stroke: Option<Stroke>,
1307 paint_order: PaintOrder,
1308 rendering_mode: ShapeRendering,
1309 data: Arc<tiny_skia_path::Path>,
1310 abs_transform: Transform,
1311 ) -> Option<Self> {
1312 let bounding_box = data.compute_tight_bounds()?;
1313 let stroke_bounding_box =
1314 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1315
1316 let abs_bounding_box: Rect;
1317 let abs_stroke_bounding_box: Rect;
1318 if abs_transform.has_skew() {
1319 let path2 = data.as_ref().clone();
1321 let path2 = path2.transform(abs_transform)?;
1322 abs_bounding_box = path2.compute_tight_bounds()?;
1323 abs_stroke_bounding_box =
1324 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1325 } else {
1326 abs_bounding_box = bounding_box.transform(abs_transform)?;
1328 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1329 }
1330
1331 Some(Path {
1332 id,
1333 visible,
1334 fill,
1335 stroke,
1336 paint_order,
1337 rendering_mode,
1338 data,
1339 abs_transform,
1340 bounding_box,
1341 abs_bounding_box,
1342 stroke_bounding_box,
1343 abs_stroke_bounding_box,
1344 })
1345 }
1346
1347 pub fn id(&self) -> &str {
1353 &self.id
1354 }
1355
1356 pub fn is_visible(&self) -> bool {
1358 self.visible
1359 }
1360
1361 pub fn fill(&self) -> Option<&Fill> {
1363 self.fill.as_ref()
1364 }
1365
1366 pub fn stroke(&self) -> Option<&Stroke> {
1368 self.stroke.as_ref()
1369 }
1370
1371 pub fn paint_order(&self) -> PaintOrder {
1378 self.paint_order
1379 }
1380
1381 pub fn rendering_mode(&self) -> ShapeRendering {
1385 self.rendering_mode
1386 }
1387
1388 pub fn data(&self) -> &tiny_skia_path::Path {
1393 self.data.as_ref()
1394 }
1395
1396 pub fn abs_transform(&self) -> Transform {
1403 self.abs_transform
1404 }
1405
1406 pub fn bounding_box(&self) -> Rect {
1410 self.bounding_box
1411 }
1412
1413 pub fn abs_bounding_box(&self) -> Rect {
1417 self.abs_bounding_box
1418 }
1419
1420 pub fn stroke_bounding_box(&self) -> Rect {
1424 self.stroke_bounding_box
1425 }
1426
1427 pub fn abs_stroke_bounding_box(&self) -> Rect {
1431 self.abs_stroke_bounding_box
1432 }
1433
1434 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1435 let mut stroke = stroke?.to_tiny_skia();
1436 stroke.dash = None;
1438
1439 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1443 return stroked_path.compute_tight_bounds();
1444 }
1445
1446 None
1447 }
1448
1449 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1450 if let Some(Paint::Pattern(patt)) = self.fill.as_ref().map(|f| &f.paint) {
1451 f(patt.root());
1452 }
1453 if let Some(Paint::Pattern(patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1454 f(patt.root());
1455 }
1456 }
1457}
1458
1459#[derive(Clone)]
1461pub enum ImageKind {
1462 JPEG(Arc<Vec<u8>>),
1464 PNG(Arc<Vec<u8>>),
1466 GIF(Arc<Vec<u8>>),
1468 WEBP(Arc<Vec<u8>>),
1470 SVG(Tree),
1472}
1473
1474impl ImageKind {
1475 pub(crate) fn actual_size(&self) -> Option<Size> {
1476 match self {
1477 ImageKind::JPEG(data)
1478 | ImageKind::PNG(data)
1479 | ImageKind::GIF(data)
1480 | ImageKind::WEBP(data) => imagesize::blob_size(data)
1481 .ok()
1482 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1483 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1484 ImageKind::SVG(svg) => Some(svg.size),
1485 }
1486 }
1487}
1488
1489impl std::fmt::Debug for ImageKind {
1490 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1491 match self {
1492 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1493 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1494 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1495 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1496 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1497 }
1498 }
1499}
1500
1501#[derive(Clone, Debug)]
1505pub struct Image {
1506 pub(crate) id: String,
1507 pub(crate) visible: bool,
1508 pub(crate) size: Size,
1509 pub(crate) rendering_mode: ImageRendering,
1510 pub(crate) kind: ImageKind,
1511 pub(crate) abs_transform: Transform,
1512 pub(crate) abs_bounding_box: NonZeroRect,
1513}
1514
1515impl Image {
1516 pub fn id(&self) -> &str {
1522 &self.id
1523 }
1524
1525 pub fn is_visible(&self) -> bool {
1527 self.visible
1528 }
1529
1530 pub fn size(&self) -> Size {
1535 self.size
1536 }
1537
1538 pub fn rendering_mode(&self) -> ImageRendering {
1542 self.rendering_mode
1543 }
1544
1545 pub fn kind(&self) -> &ImageKind {
1547 &self.kind
1548 }
1549
1550 pub fn abs_transform(&self) -> Transform {
1557 self.abs_transform
1558 }
1559
1560 pub fn bounding_box(&self) -> Rect {
1564 self.size.to_rect(0.0, 0.0).unwrap()
1565 }
1566
1567 pub fn abs_bounding_box(&self) -> Rect {
1571 self.abs_bounding_box.to_rect()
1572 }
1573
1574 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1575 if let ImageKind::SVG(ref tree) = self.kind {
1576 f(&tree.root);
1577 }
1578 }
1579}
1580
1581#[allow(missing_debug_implementations)]
1583#[derive(Clone, Debug)]
1584pub struct Tree {
1585 pub(crate) size: Size,
1586 pub(crate) root: Group,
1587 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1588 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1589 pub(crate) patterns: Vec<Arc<Pattern>>,
1590 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1591 pub(crate) masks: Vec<Arc<Mask>>,
1592 pub(crate) filters: Vec<Arc<filter::Filter>>,
1593 #[cfg(feature = "text")]
1594 pub(crate) fontdb: Arc<fontdb::Database>,
1595}
1596
1597impl Tree {
1598 pub fn size(&self) -> Size {
1604 self.size
1605 }
1606
1607 pub fn root(&self) -> &Group {
1609 &self.root
1610 }
1611
1612 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1616 if id.is_empty() {
1617 return None;
1618 }
1619
1620 node_by_id(&self.root, id)
1621 }
1622
1623 pub fn has_text_nodes(&self) -> bool {
1625 has_text_nodes(&self.root)
1626 }
1627
1628 pub fn has_defs_nodes(&self) -> bool {
1630 !self.linear_gradients().is_empty()
1631 || !self.radial_gradients().is_empty()
1632 || !self.patterns().is_empty()
1633 || !self.filters().is_empty()
1634 || !self.clip_paths().is_empty()
1635 || !self.masks().is_empty()
1636 }
1637
1638 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1640 &self.linear_gradients
1641 }
1642
1643 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1645 &self.radial_gradients
1646 }
1647
1648 pub fn patterns(&self) -> &[Arc<Pattern>] {
1650 &self.patterns
1651 }
1652
1653 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1655 &self.clip_paths
1656 }
1657
1658 pub fn masks(&self) -> &[Arc<Mask>] {
1660 &self.masks
1661 }
1662
1663 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1665 &self.filters
1666 }
1667
1668 #[cfg(feature = "text")]
1670 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1671 &self.fontdb
1672 }
1673
1674 pub(crate) fn collect_paint_servers(&mut self) {
1675 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1676 Paint::Color(_) => {}
1677 Paint::LinearGradient(lg) => {
1678 if !self
1679 .linear_gradients
1680 .iter()
1681 .any(|other| Arc::ptr_eq(lg, other))
1682 {
1683 self.linear_gradients.push(lg.clone());
1684 }
1685 }
1686 Paint::RadialGradient(rg) => {
1687 if !self
1688 .radial_gradients
1689 .iter()
1690 .any(|other| Arc::ptr_eq(rg, other))
1691 {
1692 self.radial_gradients.push(rg.clone());
1693 }
1694 }
1695 Paint::Pattern(patt) => {
1696 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1697 self.patterns.push(patt.clone());
1698 }
1699 }
1700 });
1701 }
1702}
1703
1704fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1705 for child in &parent.children {
1706 if child.id() == id {
1707 return Some(child);
1708 }
1709
1710 if let Node::Group(g) = child {
1711 if let Some(n) = node_by_id(g, id) {
1712 return Some(n);
1713 }
1714 }
1715 }
1716
1717 None
1718}
1719
1720fn has_text_nodes(root: &Group) -> bool {
1721 for node in &root.children {
1722 if let Node::Text(_) = node {
1723 return true;
1724 }
1725
1726 let mut has_text = false;
1727
1728 if let Node::Image(image) = node {
1729 if let ImageKind::SVG(tree) = &image.kind {
1730 if has_text_nodes(&tree.root) {
1731 has_text = true;
1732 }
1733 }
1734 }
1735
1736 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1737
1738 if has_text {
1739 return true;
1740 }
1741 }
1742
1743 false
1744}
1745
1746fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1747 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1748 if let Some(paint) = paint {
1749 f(paint);
1750 }
1751 }
1752
1753 for node in &parent.children {
1754 match node {
1755 Node::Group(group) => loop_over_paint_servers(group, f),
1756 Node::Path(path) => {
1757 push(path.fill.as_ref().map(|f| &f.paint), f);
1758 push(path.stroke.as_ref().map(|f| &f.paint), f);
1759 }
1760 Node::Image(_) => {}
1761 Node::Text(_) => {}
1763 }
1764
1765 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1766 }
1767}
1768
1769impl Group {
1770 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1771 for node in self.children() {
1772 if let Node::Group(g) = node {
1773 if let Some(clip) = &g.clip_path {
1774 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1775 clip_paths.push(clip.clone());
1776 }
1777
1778 if let Some(sub_clip) = &clip.clip_path {
1779 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1780 clip_paths.push(sub_clip.clone());
1781 }
1782 }
1783 }
1784 }
1785
1786 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1787
1788 if let Node::Group(g) = node {
1789 g.collect_clip_paths(clip_paths);
1790 }
1791 }
1792 }
1793
1794 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1795 for node in self.children() {
1796 if let Node::Group(g) = node {
1797 if let Some(mask) = &g.mask {
1798 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1799 masks.push(mask.clone());
1800 }
1801
1802 if let Some(sub_mask) = &mask.mask {
1803 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1804 masks.push(sub_mask.clone());
1805 }
1806 }
1807 }
1808 }
1809
1810 node.subroots(|subroot| subroot.collect_masks(masks));
1811
1812 if let Node::Group(g) = node {
1813 g.collect_masks(masks);
1814 }
1815 }
1816 }
1817
1818 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1819 for node in self.children() {
1820 if let Node::Group(g) = node {
1821 for filter in g.filters() {
1822 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1823 filters.push(filter.clone());
1824 }
1825 }
1826 }
1827
1828 node.subroots(|subroot| subroot.collect_filters(filters));
1829
1830 if let Node::Group(g) = node {
1831 g.collect_filters(filters);
1832 }
1833 }
1834 }
1835
1836 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1837 let mut bbox = BBox::default();
1838 for child in &self.children {
1839 let mut c_bbox = child.bounding_box();
1840 if let Node::Group(group) = child {
1841 if let Some(r) = c_bbox.transform(group.transform) {
1842 c_bbox = r;
1843 }
1844 }
1845
1846 bbox = bbox.expand(c_bbox);
1847 }
1848
1849 bbox.to_non_zero_rect()
1850 }
1851
1852 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1853 let mut bbox = BBox::default();
1854 let mut abs_bbox = BBox::default();
1855 let mut stroke_bbox = BBox::default();
1856 let mut abs_stroke_bbox = BBox::default();
1857 let mut layer_bbox = BBox::default();
1858 for child in &self.children {
1859 {
1860 let mut c_bbox = child.bounding_box();
1861 if let Node::Group(group) = child {
1862 if let Some(r) = c_bbox.transform(group.transform) {
1863 c_bbox = r;
1864 }
1865 }
1866
1867 bbox = bbox.expand(c_bbox);
1868 }
1869
1870 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1871
1872 {
1873 let mut c_bbox = child.stroke_bounding_box();
1874 if let Node::Group(group) = child {
1875 if let Some(r) = c_bbox.transform(group.transform) {
1876 c_bbox = r;
1877 }
1878 }
1879
1880 stroke_bbox = stroke_bbox.expand(c_bbox);
1881 }
1882
1883 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1884
1885 if let Node::Group(group) = child {
1886 let r = group.layer_bounding_box;
1887 if let Some(r) = r.transform(group.transform) {
1888 layer_bbox = layer_bbox.expand(r);
1889 }
1890 } else {
1891 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1893 }
1894 }
1895
1896 if let Some(bbox) = bbox.to_rect() {
1899 self.bounding_box = bbox;
1900 self.abs_bounding_box = abs_bbox.to_rect()?;
1901 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1902 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1903 }
1904
1905 if let Some(filter_bbox) = self.filters_bounding_box() {
1907 self.layer_bounding_box = filter_bbox;
1908 } else {
1909 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1910 }
1911
1912 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1913
1914 Some(())
1915 }
1916}