1pub mod filter;
6mod geom;
7mod text;
8
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}
174
175impl Default for ImageRendering {
176 fn default() -> Self {
177 Self::OptimizeQuality
178 }
179}
180
181impl std::str::FromStr for ImageRendering {
182 type Err = &'static str;
183
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
185 match s {
186 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
187 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
188 _ => Err("invalid"),
189 }
190 }
191}
192
193#[allow(missing_docs)]
197#[derive(Clone, Copy, PartialEq, Debug)]
198pub enum BlendMode {
199 Normal,
200 Multiply,
201 Screen,
202 Overlay,
203 Darken,
204 Lighten,
205 ColorDodge,
206 ColorBurn,
207 HardLight,
208 SoftLight,
209 Difference,
210 Exclusion,
211 Hue,
212 Saturation,
213 Color,
214 Luminosity,
215}
216
217impl Default for BlendMode {
218 fn default() -> Self {
219 Self::Normal
220 }
221}
222
223#[allow(missing_docs)]
227#[derive(Clone, Copy, PartialEq, Debug)]
228pub enum SpreadMethod {
229 Pad,
230 Reflect,
231 Repeat,
232}
233
234impl Default for SpreadMethod {
235 fn default() -> Self {
236 Self::Pad
237 }
238}
239
240#[derive(Debug)]
242pub struct BaseGradient {
243 pub(crate) id: NonEmptyString,
244 pub(crate) units: Units, pub(crate) transform: Transform,
246 pub(crate) spread_method: SpreadMethod,
247 pub(crate) stops: Vec<Stop>,
248}
249
250impl BaseGradient {
251 pub fn id(&self) -> &str {
256 self.id.get()
257 }
258
259 pub fn transform(&self) -> Transform {
263 self.transform
264 }
265
266 pub fn spread_method(&self) -> SpreadMethod {
270 self.spread_method
271 }
272
273 pub fn stops(&self) -> &[Stop] {
275 &self.stops
276 }
277}
278
279#[derive(Debug)]
283pub struct LinearGradient {
284 pub(crate) base: BaseGradient,
285 pub(crate) x1: f32,
286 pub(crate) y1: f32,
287 pub(crate) x2: f32,
288 pub(crate) y2: f32,
289}
290
291impl LinearGradient {
292 pub fn x1(&self) -> f32 {
294 self.x1
295 }
296
297 pub fn y1(&self) -> f32 {
299 self.y1
300 }
301
302 pub fn x2(&self) -> f32 {
304 self.x2
305 }
306
307 pub fn y2(&self) -> f32 {
309 self.y2
310 }
311}
312
313impl std::ops::Deref for LinearGradient {
314 type Target = BaseGradient;
315
316 fn deref(&self) -> &Self::Target {
317 &self.base
318 }
319}
320
321#[derive(Debug)]
325pub struct RadialGradient {
326 pub(crate) base: BaseGradient,
327 pub(crate) cx: f32,
328 pub(crate) cy: f32,
329 pub(crate) r: PositiveF32,
330 pub(crate) fx: f32,
331 pub(crate) fy: f32,
332}
333
334impl RadialGradient {
335 pub fn cx(&self) -> f32 {
337 self.cx
338 }
339
340 pub fn cy(&self) -> f32 {
342 self.cy
343 }
344
345 pub fn r(&self) -> PositiveF32 {
347 self.r
348 }
349
350 pub fn fx(&self) -> f32 {
352 self.fx
353 }
354
355 pub fn fy(&self) -> f32 {
357 self.fy
358 }
359}
360
361impl std::ops::Deref for RadialGradient {
362 type Target = BaseGradient;
363
364 fn deref(&self) -> &Self::Target {
365 &self.base
366 }
367}
368
369pub type StopOffset = NormalizedF32;
371
372#[derive(Clone, Copy, Debug)]
376pub struct Stop {
377 pub(crate) offset: StopOffset,
378 pub(crate) color: Color,
379 pub(crate) opacity: Opacity,
380}
381
382impl Stop {
383 pub fn offset(&self) -> StopOffset {
387 self.offset
388 }
389
390 pub fn color(&self) -> Color {
394 self.color
395 }
396
397 pub fn opacity(&self) -> Opacity {
401 self.opacity
402 }
403}
404
405#[derive(Debug)]
409pub struct Pattern {
410 pub(crate) id: NonEmptyString,
411 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
414 pub(crate) rect: NonZeroRect,
415 pub(crate) view_box: Option<ViewBox>,
416 pub(crate) root: Group,
417}
418
419impl Pattern {
420 pub fn id(&self) -> &str {
425 self.id.get()
426 }
427
428 pub fn transform(&self) -> Transform {
432 self.transform
433 }
434
435 pub fn rect(&self) -> NonZeroRect {
439 self.rect
440 }
441
442 pub fn root(&self) -> &Group {
444 &self.root
445 }
446}
447
448pub type StrokeWidth = NonZeroPositiveF32;
450
451#[derive(Clone, Copy, Debug)]
455pub struct StrokeMiterlimit(f32);
456
457impl StrokeMiterlimit {
458 #[inline]
460 pub fn new(n: f32) -> Self {
461 debug_assert!(n.is_finite());
462 debug_assert!(n >= 1.0);
463
464 let n = if !(n >= 1.0) { 1.0 } else { n };
465
466 StrokeMiterlimit(n)
467 }
468
469 #[inline]
471 pub fn get(&self) -> f32 {
472 self.0
473 }
474}
475
476impl Default for StrokeMiterlimit {
477 #[inline]
478 fn default() -> Self {
479 StrokeMiterlimit::new(4.0)
480 }
481}
482
483impl From<f32> for StrokeMiterlimit {
484 #[inline]
485 fn from(n: f32) -> Self {
486 Self::new(n)
487 }
488}
489
490impl PartialEq for StrokeMiterlimit {
491 #[inline]
492 fn eq(&self, other: &Self) -> bool {
493 self.0.approx_eq_ulps(&other.0, 4)
494 }
495}
496
497#[allow(missing_docs)]
501#[derive(Clone, Copy, PartialEq, Debug)]
502pub enum LineCap {
503 Butt,
504 Round,
505 Square,
506}
507
508impl Default for LineCap {
509 fn default() -> Self {
510 Self::Butt
511 }
512}
513
514#[allow(missing_docs)]
518#[derive(Clone, Copy, PartialEq, Debug)]
519pub enum LineJoin {
520 Miter,
521 MiterClip,
522 Round,
523 Bevel,
524}
525
526impl Default for LineJoin {
527 fn default() -> Self {
528 Self::Miter
529 }
530}
531
532#[derive(Clone, Debug)]
534pub struct Stroke {
535 pub(crate) paint: Paint,
536 pub(crate) dasharray: Option<Vec<f32>>,
537 pub(crate) dashoffset: f32,
538 pub(crate) miterlimit: StrokeMiterlimit,
539 pub(crate) opacity: Opacity,
540 pub(crate) width: StrokeWidth,
541 pub(crate) linecap: LineCap,
542 pub(crate) linejoin: LineJoin,
543 pub(crate) context_element: Option<ContextElement>,
546}
547
548impl Stroke {
549 pub fn paint(&self) -> &Paint {
551 &self.paint
552 }
553
554 pub fn dasharray(&self) -> Option<&[f32]> {
556 self.dasharray.as_deref()
557 }
558
559 pub fn dashoffset(&self) -> f32 {
561 self.dashoffset
562 }
563
564 pub fn miterlimit(&self) -> StrokeMiterlimit {
566 self.miterlimit
567 }
568
569 pub fn opacity(&self) -> Opacity {
571 self.opacity
572 }
573
574 pub fn width(&self) -> StrokeWidth {
576 self.width
577 }
578
579 pub fn linecap(&self) -> LineCap {
581 self.linecap
582 }
583
584 pub fn linejoin(&self) -> LineJoin {
586 self.linejoin
587 }
588
589 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
591 let mut stroke = tiny_skia_path::Stroke {
592 width: self.width.get(),
593 miter_limit: self.miterlimit.get(),
594 line_cap: match self.linecap {
595 LineCap::Butt => tiny_skia_path::LineCap::Butt,
596 LineCap::Round => tiny_skia_path::LineCap::Round,
597 LineCap::Square => tiny_skia_path::LineCap::Square,
598 },
599 line_join: match self.linejoin {
600 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
601 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
602 LineJoin::Round => tiny_skia_path::LineJoin::Round,
603 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
604 },
605 dash: None,
608 };
609
610 if let Some(ref list) = self.dasharray {
611 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
612 }
613
614 stroke
615 }
616}
617
618#[allow(missing_docs)]
622#[derive(Clone, Copy, PartialEq, Debug)]
623pub enum FillRule {
624 NonZero,
625 EvenOdd,
626}
627
628impl Default for FillRule {
629 fn default() -> Self {
630 Self::NonZero
631 }
632}
633
634#[derive(Clone, Copy, Debug)]
635pub(crate) enum ContextElement {
636 UseNode,
641 PathNode(Transform, Option<NonZeroRect>),
646}
647
648#[derive(Clone, Debug)]
650pub struct Fill {
651 pub(crate) paint: Paint,
652 pub(crate) opacity: Opacity,
653 pub(crate) rule: FillRule,
654 pub(crate) context_element: Option<ContextElement>,
657}
658
659impl Fill {
660 pub fn paint(&self) -> &Paint {
662 &self.paint
663 }
664
665 pub fn opacity(&self) -> Opacity {
667 self.opacity
668 }
669
670 pub fn rule(&self) -> FillRule {
672 self.rule
673 }
674}
675
676impl Default for Fill {
677 fn default() -> Self {
678 Fill {
679 paint: Paint::Color(Color::black()),
680 opacity: Opacity::ONE,
681 rule: FillRule::default(),
682 context_element: None,
683 }
684 }
685}
686
687#[derive(Clone, Copy, PartialEq, Debug)]
689#[allow(missing_docs)]
690pub struct Color {
691 pub red: u8,
692 pub green: u8,
693 pub blue: u8,
694}
695
696impl Color {
697 #[inline]
699 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
700 Color { red, green, blue }
701 }
702
703 #[inline]
705 pub fn black() -> Color {
706 Color::new_rgb(0, 0, 0)
707 }
708
709 #[inline]
711 pub fn white() -> Color {
712 Color::new_rgb(255, 255, 255)
713 }
714}
715
716#[allow(missing_docs)]
720#[derive(Clone, Debug)]
721pub enum Paint {
722 Color(Color),
723 LinearGradient(Arc<LinearGradient>),
724 RadialGradient(Arc<RadialGradient>),
725 Pattern(Arc<Pattern>),
726}
727
728impl PartialEq for Paint {
729 #[inline]
730 fn eq(&self, other: &Self) -> bool {
731 match (self, other) {
732 (Self::Color(lc), Self::Color(rc)) => lc == rc,
733 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Arc::ptr_eq(lg1, lg2),
734 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Arc::ptr_eq(rg1, rg2),
735 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Arc::ptr_eq(p1, p2),
736 _ => false,
737 }
738 }
739}
740
741#[derive(Debug)]
745pub struct ClipPath {
746 pub(crate) id: NonEmptyString,
747 pub(crate) transform: Transform,
748 pub(crate) clip_path: Option<Arc<ClipPath>>,
749 pub(crate) root: Group,
750}
751
752impl ClipPath {
753 pub(crate) fn empty(id: NonEmptyString) -> Self {
754 ClipPath {
755 id,
756 transform: Transform::default(),
757 clip_path: None,
758 root: Group::empty(),
759 }
760 }
761
762 pub fn id(&self) -> &str {
767 self.id.get()
768 }
769
770 pub fn transform(&self) -> Transform {
774 self.transform
775 }
776
777 pub fn clip_path(&self) -> Option<&ClipPath> {
781 self.clip_path.as_deref()
782 }
783
784 pub fn root(&self) -> &Group {
786 &self.root
787 }
788}
789
790#[derive(Clone, Copy, PartialEq, Debug)]
792pub enum MaskType {
793 Luminance,
795 Alpha,
797}
798
799impl Default for MaskType {
800 fn default() -> Self {
801 Self::Luminance
802 }
803}
804
805#[derive(Debug)]
809pub struct Mask {
810 pub(crate) id: NonEmptyString,
811 pub(crate) rect: NonZeroRect,
812 pub(crate) kind: MaskType,
813 pub(crate) mask: Option<Arc<Mask>>,
814 pub(crate) root: Group,
815}
816
817impl Mask {
818 pub fn id(&self) -> &str {
823 self.id.get()
824 }
825
826 pub fn rect(&self) -> NonZeroRect {
830 self.rect
831 }
832
833 pub fn kind(&self) -> MaskType {
837 self.kind
838 }
839
840 pub fn mask(&self) -> Option<&Mask> {
844 self.mask.as_deref()
845 }
846
847 pub fn root(&self) -> &Group {
851 &self.root
852 }
853}
854
855#[allow(missing_docs)]
857#[derive(Clone, Debug)]
858pub enum Node {
859 Group(Box<Group>),
860 Path(Box<Path>),
861 Image(Box<Image>),
862 Text(Box<Text>),
863}
864
865impl Node {
866 pub fn id(&self) -> &str {
868 match self {
869 Node::Group(ref e) => e.id.as_str(),
870 Node::Path(ref e) => e.id.as_str(),
871 Node::Image(ref e) => e.id.as_str(),
872 Node::Text(ref e) => e.id.as_str(),
873 }
874 }
875
876 pub fn abs_transform(&self) -> Transform {
880 match self {
881 Node::Group(ref group) => group.abs_transform(),
882 Node::Path(ref path) => path.abs_transform(),
883 Node::Image(ref image) => image.abs_transform(),
884 Node::Text(ref text) => text.abs_transform(),
885 }
886 }
887
888 pub fn bounding_box(&self) -> Rect {
890 match self {
891 Node::Group(ref group) => group.bounding_box(),
892 Node::Path(ref path) => path.bounding_box(),
893 Node::Image(ref image) => image.bounding_box(),
894 Node::Text(ref text) => text.bounding_box(),
895 }
896 }
897
898 pub fn abs_bounding_box(&self) -> Rect {
900 match self {
901 Node::Group(ref group) => group.abs_bounding_box(),
902 Node::Path(ref path) => path.abs_bounding_box(),
903 Node::Image(ref image) => image.abs_bounding_box(),
904 Node::Text(ref text) => text.abs_bounding_box(),
905 }
906 }
907
908 pub fn stroke_bounding_box(&self) -> Rect {
910 match self {
911 Node::Group(ref group) => group.stroke_bounding_box(),
912 Node::Path(ref path) => path.stroke_bounding_box(),
913 Node::Image(ref image) => image.bounding_box(),
915 Node::Text(ref text) => text.stroke_bounding_box(),
916 }
917 }
918
919 pub fn abs_stroke_bounding_box(&self) -> Rect {
921 match self {
922 Node::Group(ref group) => group.abs_stroke_bounding_box(),
923 Node::Path(ref path) => path.abs_stroke_bounding_box(),
924 Node::Image(ref image) => image.abs_bounding_box(),
926 Node::Text(ref text) => text.abs_stroke_bounding_box(),
927 }
928 }
929
930 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
937 match self {
938 Node::Group(ref group) => Some(group.abs_layer_bounding_box()),
939 Node::Path(ref path) => path.abs_bounding_box().to_non_zero_rect(),
941 Node::Image(ref image) => image.abs_bounding_box().to_non_zero_rect(),
942 Node::Text(ref text) => text.abs_bounding_box().to_non_zero_rect(),
943 }
944 }
945
946 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
971 match self {
972 Node::Group(ref group) => group.subroots(&mut f),
973 Node::Path(ref path) => path.subroots(&mut f),
974 Node::Image(ref image) => image.subroots(&mut f),
975 Node::Text(ref text) => text.subroots(&mut f),
976 }
977 }
978}
979
980#[derive(Clone, Debug)]
987pub struct Group {
988 pub(crate) id: String,
989 pub(crate) transform: Transform,
990 pub(crate) abs_transform: Transform,
991 pub(crate) opacity: Opacity,
992 pub(crate) blend_mode: BlendMode,
993 pub(crate) isolate: bool,
994 pub(crate) clip_path: Option<Arc<ClipPath>>,
995 pub(crate) is_context_element: bool,
997 pub(crate) mask: Option<Arc<Mask>>,
998 pub(crate) filters: Vec<Arc<filter::Filter>>,
999 pub(crate) bounding_box: Rect,
1000 pub(crate) abs_bounding_box: Rect,
1001 pub(crate) stroke_bounding_box: Rect,
1002 pub(crate) abs_stroke_bounding_box: Rect,
1003 pub(crate) layer_bounding_box: NonZeroRect,
1004 pub(crate) abs_layer_bounding_box: NonZeroRect,
1005 pub(crate) children: Vec<Node>,
1006}
1007
1008impl Group {
1009 pub(crate) fn empty() -> Self {
1010 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1011 Group {
1012 id: String::new(),
1013 transform: Transform::default(),
1014 abs_transform: Transform::default(),
1015 opacity: Opacity::ONE,
1016 blend_mode: BlendMode::Normal,
1017 isolate: false,
1018 clip_path: None,
1019 mask: None,
1020 filters: Vec::new(),
1021 is_context_element: false,
1022 bounding_box: dummy,
1023 abs_bounding_box: dummy,
1024 stroke_bounding_box: dummy,
1025 abs_stroke_bounding_box: dummy,
1026 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1027 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1028 children: Vec::new(),
1029 }
1030 }
1031
1032 pub fn id(&self) -> &str {
1038 &self.id
1039 }
1040
1041 pub fn transform(&self) -> Transform {
1045 self.transform
1046 }
1047
1048 pub fn abs_transform(&self) -> Transform {
1055 self.abs_transform
1056 }
1057
1058 pub fn opacity(&self) -> Opacity {
1063 self.opacity
1064 }
1065
1066 pub fn blend_mode(&self) -> BlendMode {
1070 self.blend_mode
1071 }
1072
1073 pub fn isolate(&self) -> bool {
1077 self.isolate
1078 }
1079
1080 pub fn clip_path(&self) -> Option<&ClipPath> {
1082 self.clip_path.as_deref()
1083 }
1084
1085 pub fn mask(&self) -> Option<&Mask> {
1087 self.mask.as_deref()
1088 }
1089
1090 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1092 &self.filters
1093 }
1094
1095 pub fn bounding_box(&self) -> Rect {
1101 self.bounding_box
1102 }
1103
1104 pub fn abs_bounding_box(&self) -> Rect {
1108 self.abs_bounding_box
1109 }
1110
1111 pub fn stroke_bounding_box(&self) -> Rect {
1115 self.stroke_bounding_box
1116 }
1117
1118 pub fn abs_stroke_bounding_box(&self) -> Rect {
1122 self.abs_stroke_bounding_box
1123 }
1124
1125 pub fn layer_bounding_box(&self) -> NonZeroRect {
1137 self.layer_bounding_box
1138 }
1139
1140 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1142 self.abs_layer_bounding_box
1143 }
1144
1145 pub fn children(&self) -> &[Node] {
1147 &self.children
1148 }
1149
1150 pub fn should_isolate(&self) -> bool {
1152 self.isolate
1153 || self.opacity != Opacity::ONE
1154 || self.clip_path.is_some()
1155 || self.mask.is_some()
1156 || !self.filters.is_empty()
1157 || self.blend_mode != BlendMode::Normal }
1159
1160 pub fn has_children(&self) -> bool {
1162 !self.children.is_empty()
1163 }
1164
1165 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1176 let mut full_region = BBox::default();
1177 for filter in &self.filters {
1178 full_region = full_region.expand(filter.rect);
1179 }
1180
1181 full_region.to_non_zero_rect()
1182 }
1183
1184 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1185 if let Some(ref clip) = self.clip_path {
1186 f(&clip.root);
1187
1188 if let Some(ref sub_clip) = clip.clip_path {
1189 f(&sub_clip.root);
1190 }
1191 }
1192
1193 if let Some(ref mask) = self.mask {
1194 f(&mask.root);
1195
1196 if let Some(ref sub_mask) = mask.mask {
1197 f(&sub_mask.root);
1198 }
1199 }
1200
1201 for filter in &self.filters {
1202 for primitive in &filter.primitives {
1203 if let filter::Kind::Image(ref image) = primitive.kind {
1204 f(image.root());
1205 }
1206 }
1207 }
1208 }
1209}
1210
1211#[derive(Clone, Copy, PartialEq, Debug)]
1218#[allow(missing_docs)]
1219pub enum PaintOrder {
1220 FillAndStroke,
1221 StrokeAndFill,
1222}
1223
1224impl Default for PaintOrder {
1225 fn default() -> Self {
1226 Self::FillAndStroke
1227 }
1228}
1229
1230#[derive(Clone, Debug)]
1232pub struct Path {
1233 pub(crate) id: String,
1234 pub(crate) visible: bool,
1235 pub(crate) fill: Option<Fill>,
1236 pub(crate) stroke: Option<Stroke>,
1237 pub(crate) paint_order: PaintOrder,
1238 pub(crate) rendering_mode: ShapeRendering,
1239 pub(crate) data: Arc<tiny_skia_path::Path>,
1240 pub(crate) abs_transform: Transform,
1241 pub(crate) bounding_box: Rect,
1242 pub(crate) abs_bounding_box: Rect,
1243 pub(crate) stroke_bounding_box: Rect,
1244 pub(crate) abs_stroke_bounding_box: Rect,
1245}
1246
1247impl Path {
1248 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1249 Self::new(
1250 String::new(),
1251 true,
1252 None,
1253 None,
1254 PaintOrder::default(),
1255 ShapeRendering::default(),
1256 data,
1257 Transform::default(),
1258 )
1259 }
1260
1261 pub(crate) fn new(
1262 id: String,
1263 visible: bool,
1264 fill: Option<Fill>,
1265 stroke: Option<Stroke>,
1266 paint_order: PaintOrder,
1267 rendering_mode: ShapeRendering,
1268 data: Arc<tiny_skia_path::Path>,
1269 abs_transform: Transform,
1270 ) -> Option<Self> {
1271 let bounding_box = data.compute_tight_bounds()?;
1272 let stroke_bounding_box =
1273 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1274
1275 let abs_bounding_box: Rect;
1276 let abs_stroke_bounding_box: Rect;
1277 if abs_transform.has_skew() {
1278 let path2 = data.as_ref().clone();
1280 let path2 = path2.transform(abs_transform)?;
1281 abs_bounding_box = path2.compute_tight_bounds()?;
1282 abs_stroke_bounding_box =
1283 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1284 } else {
1285 abs_bounding_box = bounding_box.transform(abs_transform)?;
1287 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1288 }
1289
1290 Some(Path {
1291 id,
1292 visible,
1293 fill,
1294 stroke,
1295 paint_order,
1296 rendering_mode,
1297 data,
1298 abs_transform,
1299 bounding_box,
1300 abs_bounding_box,
1301 stroke_bounding_box,
1302 abs_stroke_bounding_box,
1303 })
1304 }
1305
1306 pub fn id(&self) -> &str {
1312 &self.id
1313 }
1314
1315 pub fn is_visible(&self) -> bool {
1317 self.visible
1318 }
1319
1320 pub fn fill(&self) -> Option<&Fill> {
1322 self.fill.as_ref()
1323 }
1324
1325 pub fn stroke(&self) -> Option<&Stroke> {
1327 self.stroke.as_ref()
1328 }
1329
1330 pub fn paint_order(&self) -> PaintOrder {
1337 self.paint_order
1338 }
1339
1340 pub fn rendering_mode(&self) -> ShapeRendering {
1344 self.rendering_mode
1345 }
1346
1347 pub fn data(&self) -> &tiny_skia_path::Path {
1352 self.data.as_ref()
1353 }
1354
1355 pub fn abs_transform(&self) -> Transform {
1362 self.abs_transform
1363 }
1364
1365 pub fn bounding_box(&self) -> Rect {
1369 self.bounding_box
1370 }
1371
1372 pub fn abs_bounding_box(&self) -> Rect {
1376 self.abs_bounding_box
1377 }
1378
1379 pub fn stroke_bounding_box(&self) -> Rect {
1383 self.stroke_bounding_box
1384 }
1385
1386 pub fn abs_stroke_bounding_box(&self) -> Rect {
1390 self.abs_stroke_bounding_box
1391 }
1392
1393 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1394 let mut stroke = stroke?.to_tiny_skia();
1395 stroke.dash = None;
1397
1398 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1402 return stroked_path.compute_tight_bounds();
1403 }
1404
1405 None
1406 }
1407
1408 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1409 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1410 f(patt.root())
1411 }
1412 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1413 f(patt.root())
1414 }
1415 }
1416}
1417
1418#[derive(Clone)]
1420pub enum ImageKind {
1421 JPEG(Arc<Vec<u8>>),
1423 PNG(Arc<Vec<u8>>),
1425 GIF(Arc<Vec<u8>>),
1427 SVG(Tree),
1429}
1430
1431impl ImageKind {
1432 pub(crate) fn actual_size(&self) -> Option<Size> {
1433 match self {
1434 ImageKind::JPEG(ref data) | ImageKind::PNG(ref data) | ImageKind::GIF(ref data) => {
1435 imagesize::blob_size(data)
1436 .ok()
1437 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1438 .log_none(|| log::warn!("Image has an invalid size. Skipped."))
1439 }
1440 ImageKind::SVG(ref svg) => Some(svg.size),
1441 }
1442 }
1443}
1444
1445impl std::fmt::Debug for ImageKind {
1446 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1447 match self {
1448 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1449 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1450 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1451 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1452 }
1453 }
1454}
1455
1456#[derive(Clone, Debug)]
1460pub struct Image {
1461 pub(crate) id: String,
1462 pub(crate) visible: bool,
1463 pub(crate) size: Size,
1464 pub(crate) rendering_mode: ImageRendering,
1465 pub(crate) kind: ImageKind,
1466 pub(crate) abs_transform: Transform,
1467 pub(crate) abs_bounding_box: NonZeroRect,
1468}
1469
1470impl Image {
1471 pub fn id(&self) -> &str {
1477 &self.id
1478 }
1479
1480 pub fn is_visible(&self) -> bool {
1482 self.visible
1483 }
1484
1485 pub fn size(&self) -> Size {
1490 self.size
1491 }
1492
1493 pub fn rendering_mode(&self) -> ImageRendering {
1497 self.rendering_mode
1498 }
1499
1500 pub fn kind(&self) -> &ImageKind {
1502 &self.kind
1503 }
1504
1505 pub fn abs_transform(&self) -> Transform {
1512 self.abs_transform
1513 }
1514
1515 pub fn bounding_box(&self) -> Rect {
1519 self.size.to_rect(0.0, 0.0).unwrap()
1520 }
1521
1522 pub fn abs_bounding_box(&self) -> Rect {
1526 self.abs_bounding_box.to_rect()
1527 }
1528
1529 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1530 if let ImageKind::SVG(ref tree) = self.kind {
1531 f(&tree.root)
1532 }
1533 }
1534}
1535
1536#[allow(missing_debug_implementations)]
1538#[derive(Clone, Debug)]
1539pub struct Tree {
1540 pub(crate) size: Size,
1541 pub(crate) root: Group,
1542 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1543 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1544 pub(crate) patterns: Vec<Arc<Pattern>>,
1545 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1546 pub(crate) masks: Vec<Arc<Mask>>,
1547 pub(crate) filters: Vec<Arc<filter::Filter>>,
1548 #[cfg(feature = "text")]
1549 pub(crate) fontdb: Arc<fontdb::Database>,
1550}
1551
1552impl Tree {
1553 pub fn size(&self) -> Size {
1559 self.size
1560 }
1561
1562 pub fn root(&self) -> &Group {
1564 &self.root
1565 }
1566
1567 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1571 if id.is_empty() {
1572 return None;
1573 }
1574
1575 node_by_id(&self.root, id)
1576 }
1577
1578 pub fn has_text_nodes(&self) -> bool {
1580 has_text_nodes(&self.root)
1581 }
1582
1583 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1585 &self.linear_gradients
1586 }
1587
1588 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1590 &self.radial_gradients
1591 }
1592
1593 pub fn patterns(&self) -> &[Arc<Pattern>] {
1595 &self.patterns
1596 }
1597
1598 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1600 &self.clip_paths
1601 }
1602
1603 pub fn masks(&self) -> &[Arc<Mask>] {
1605 &self.masks
1606 }
1607
1608 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1610 &self.filters
1611 }
1612
1613 #[cfg(feature = "text")]
1615 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1616 &self.fontdb
1617 }
1618
1619 pub(crate) fn collect_paint_servers(&mut self) {
1620 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1621 Paint::Color(_) => {}
1622 Paint::LinearGradient(lg) => {
1623 if !self
1624 .linear_gradients
1625 .iter()
1626 .any(|other| Arc::ptr_eq(&lg, other))
1627 {
1628 self.linear_gradients.push(lg.clone());
1629 }
1630 }
1631 Paint::RadialGradient(rg) => {
1632 if !self
1633 .radial_gradients
1634 .iter()
1635 .any(|other| Arc::ptr_eq(&rg, other))
1636 {
1637 self.radial_gradients.push(rg.clone());
1638 }
1639 }
1640 Paint::Pattern(patt) => {
1641 if !self.patterns.iter().any(|other| Arc::ptr_eq(&patt, other)) {
1642 self.patterns.push(patt.clone());
1643 }
1644 }
1645 });
1646 }
1647}
1648
1649fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1650 for child in &parent.children {
1651 if child.id() == id {
1652 return Some(child);
1653 }
1654
1655 if let Node::Group(ref g) = child {
1656 if let Some(n) = node_by_id(g, id) {
1657 return Some(n);
1658 }
1659 }
1660 }
1661
1662 None
1663}
1664
1665fn has_text_nodes(root: &Group) -> bool {
1666 for node in &root.children {
1667 if let Node::Text(_) = node {
1668 return true;
1669 }
1670
1671 let mut has_text = false;
1672
1673 if let Node::Image(ref image) = node {
1674 if let ImageKind::SVG(ref tree) = image.kind {
1675 if has_text_nodes(&tree.root) {
1676 has_text = true;
1677 }
1678 }
1679 }
1680
1681 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1682
1683 if has_text {
1684 return true;
1685 }
1686 }
1687
1688 true
1689}
1690
1691fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1692 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1693 if let Some(paint) = paint {
1694 f(paint);
1695 }
1696 }
1697
1698 for node in &parent.children {
1699 match node {
1700 Node::Group(ref group) => loop_over_paint_servers(group, f),
1701 Node::Path(ref path) => {
1702 push(path.fill.as_ref().map(|f| &f.paint), f);
1703 push(path.stroke.as_ref().map(|f| &f.paint), f);
1704 }
1705 Node::Image(_) => {}
1706 Node::Text(_) => {}
1708 }
1709
1710 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1711 }
1712}
1713
1714impl Group {
1715 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1716 for node in self.children() {
1717 if let Node::Group(ref g) = node {
1718 if let Some(ref clip) = g.clip_path {
1719 if !clip_paths.iter().any(|other| Arc::ptr_eq(&clip, other)) {
1720 clip_paths.push(clip.clone());
1721 }
1722
1723 if let Some(ref sub_clip) = clip.clip_path {
1724 if !clip_paths.iter().any(|other| Arc::ptr_eq(&sub_clip, other)) {
1725 clip_paths.push(sub_clip.clone());
1726 }
1727 }
1728 }
1729 }
1730
1731 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1732
1733 if let Node::Group(ref g) = node {
1734 g.collect_clip_paths(clip_paths);
1735 }
1736 }
1737 }
1738
1739 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1740 for node in self.children() {
1741 if let Node::Group(ref g) = node {
1742 if let Some(ref mask) = g.mask {
1743 if !masks.iter().any(|other| Arc::ptr_eq(&mask, other)) {
1744 masks.push(mask.clone());
1745 }
1746
1747 if let Some(ref sub_mask) = mask.mask {
1748 if !masks.iter().any(|other| Arc::ptr_eq(&sub_mask, other)) {
1749 masks.push(sub_mask.clone());
1750 }
1751 }
1752 }
1753 }
1754
1755 node.subroots(|subroot| subroot.collect_masks(masks));
1756
1757 if let Node::Group(ref g) = node {
1758 g.collect_masks(masks);
1759 }
1760 }
1761 }
1762
1763 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1764 for node in self.children() {
1765 if let Node::Group(ref g) = node {
1766 for filter in g.filters() {
1767 if !filters.iter().any(|other| Arc::ptr_eq(&filter, other)) {
1768 filters.push(filter.clone());
1769 }
1770 }
1771 }
1772
1773 node.subroots(|subroot| subroot.collect_filters(filters));
1774
1775 if let Node::Group(ref g) = node {
1776 g.collect_filters(filters);
1777 }
1778 }
1779 }
1780
1781 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1782 let mut bbox = BBox::default();
1783 for child in &self.children {
1784 let mut c_bbox = child.bounding_box();
1785 if let Node::Group(ref group) = child {
1786 if let Some(r) = c_bbox.transform(group.transform) {
1787 c_bbox = r;
1788 }
1789 }
1790
1791 bbox = bbox.expand(c_bbox);
1792 }
1793
1794 bbox.to_non_zero_rect()
1795 }
1796
1797 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1798 let mut bbox = BBox::default();
1799 let mut abs_bbox = BBox::default();
1800 let mut stroke_bbox = BBox::default();
1801 let mut abs_stroke_bbox = BBox::default();
1802 let mut layer_bbox = BBox::default();
1803 for child in &self.children {
1804 {
1805 let mut c_bbox = child.bounding_box();
1806 if let Node::Group(ref group) = child {
1807 if let Some(r) = c_bbox.transform(group.transform) {
1808 c_bbox = r;
1809 }
1810 }
1811
1812 bbox = bbox.expand(c_bbox);
1813 }
1814
1815 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1816
1817 {
1818 let mut c_bbox = child.stroke_bounding_box();
1819 if let Node::Group(ref group) = child {
1820 if let Some(r) = c_bbox.transform(group.transform) {
1821 c_bbox = r;
1822 }
1823 }
1824
1825 stroke_bbox = stroke_bbox.expand(c_bbox);
1826 }
1827
1828 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1829
1830 if let Node::Group(ref group) = child {
1831 let r = group.layer_bounding_box;
1832 if let Some(r) = r.transform(group.transform) {
1833 layer_bbox = layer_bbox.expand(r);
1834 }
1835 } else {
1836 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1838 }
1839 }
1840
1841 if let Some(bbox) = bbox.to_rect() {
1844 self.bounding_box = bbox;
1845 self.abs_bounding_box = abs_bbox.to_rect()?;
1846 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1847 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1848 }
1849
1850 if let Some(filter_bbox) = self.filters_bounding_box() {
1852 self.layer_bounding_box = filter_bbox;
1853 } else {
1854 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1855 }
1856
1857 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1858
1859 Some(())
1860 }
1861}