1#![forbid(unsafe_code)]
13#![warn(missing_docs)]
14#![warn(missing_debug_implementations)]
15#![warn(missing_copy_implementations)]
16#![allow(clippy::collapsible_else_if)]
17#![allow(clippy::neg_cmp_op_on_partial_ord)]
18#![allow(clippy::too_many_arguments)]
19#![allow(clippy::derivable_impls)]
20
21pub mod filter;
22mod geom;
23mod text;
24
25use std::cell::RefCell;
26use std::rc::Rc;
27use std::sync::Arc;
28
29pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
30pub use svgtypes::{Align, AspectRatio};
31
32pub use tiny_skia_path;
33
34pub use crate::geom::*;
35pub use crate::text::*;
36
37pub type Opacity = NormalizedF32;
39
40#[derive(Clone, Copy, Debug)]
44pub struct NonZeroF32(f32);
45
46impl NonZeroF32 {
47 #[inline]
49 pub fn new(n: f32) -> Option<Self> {
50 if n.approx_eq_ulps(&0.0, 4) {
51 None
52 } else {
53 Some(NonZeroF32(n))
54 }
55 }
56
57 #[inline]
59 pub fn get(&self) -> f32 {
60 self.0
61 }
62}
63
64#[allow(missing_docs)]
66#[derive(Clone, Copy, PartialEq, Debug)]
67pub enum Units {
68 UserSpaceOnUse,
69 ObjectBoundingBox,
70}
71
72#[allow(missing_docs)]
78#[derive(Clone, Copy, PartialEq, Debug)]
79pub enum Visibility {
80 Visible,
81 Hidden,
82 Collapse,
83}
84
85impl Default for Visibility {
86 fn default() -> Self {
87 Self::Visible
88 }
89}
90
91#[derive(Clone, Copy, PartialEq, Debug)]
95#[allow(missing_docs)]
96pub enum ShapeRendering {
97 OptimizeSpeed,
98 CrispEdges,
99 GeometricPrecision,
100}
101
102impl ShapeRendering {
103 pub fn use_shape_antialiasing(self) -> bool {
105 match self {
106 ShapeRendering::OptimizeSpeed => false,
107 ShapeRendering::CrispEdges => false,
108 ShapeRendering::GeometricPrecision => true,
109 }
110 }
111}
112
113impl Default for ShapeRendering {
114 fn default() -> Self {
115 Self::GeometricPrecision
116 }
117}
118
119impl std::str::FromStr for ShapeRendering {
121 type Err = &'static str;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 match s {
125 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
126 "crispEdges" => Ok(ShapeRendering::CrispEdges),
127 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
128 _ => Err("invalid"),
129 }
130 }
131}
132
133#[allow(missing_docs)]
137#[derive(Clone, Copy, PartialEq, Debug)]
138pub enum TextRendering {
139 OptimizeSpeed,
140 OptimizeLegibility,
141 GeometricPrecision,
142}
143
144impl Default for TextRendering {
145 fn default() -> Self {
146 Self::OptimizeLegibility
147 }
148}
149
150impl std::str::FromStr for TextRendering {
151 type Err = &'static str;
152
153 fn from_str(s: &str) -> Result<Self, Self::Err> {
154 match s {
155 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
156 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
157 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
158 _ => Err("invalid"),
159 }
160 }
161}
162
163#[allow(missing_docs)]
167#[derive(Clone, Copy, PartialEq, Debug)]
168pub enum ImageRendering {
169 OptimizeQuality,
170 OptimizeSpeed,
171}
172
173impl Default for ImageRendering {
174 fn default() -> Self {
175 Self::OptimizeQuality
176 }
177}
178
179impl std::str::FromStr for ImageRendering {
180 type Err = &'static str;
181
182 fn from_str(s: &str) -> Result<Self, Self::Err> {
183 match s {
184 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
185 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
186 _ => Err("invalid"),
187 }
188 }
189}
190
191#[allow(missing_docs)]
195#[derive(Clone, Copy, PartialEq, Debug)]
196pub enum BlendMode {
197 Normal,
198 Multiply,
199 Screen,
200 Overlay,
201 Darken,
202 Lighten,
203 ColorDodge,
204 ColorBurn,
205 HardLight,
206 SoftLight,
207 Difference,
208 Exclusion,
209 Hue,
210 Saturation,
211 Color,
212 Luminosity,
213}
214
215impl Default for BlendMode {
216 fn default() -> Self {
217 Self::Normal
218 }
219}
220
221#[allow(missing_docs)]
225#[derive(Clone, Copy, PartialEq, Debug)]
226pub enum SpreadMethod {
227 Pad,
228 Reflect,
229 Repeat,
230}
231
232impl Default for SpreadMethod {
233 fn default() -> Self {
234 Self::Pad
235 }
236}
237
238#[derive(Clone, Debug)]
240pub struct BaseGradient {
241 pub id: String,
246
247 pub units: Units,
251
252 pub transform: Transform,
256
257 pub spread_method: SpreadMethod,
261
262 pub stops: Vec<Stop>,
264}
265
266#[allow(missing_docs)]
270#[derive(Clone, Debug)]
271pub struct LinearGradient {
272 pub base: BaseGradient,
274
275 pub x1: f32,
276 pub y1: f32,
277 pub x2: f32,
278 pub y2: f32,
279}
280
281impl std::ops::Deref for LinearGradient {
282 type Target = BaseGradient;
283
284 fn deref(&self) -> &Self::Target {
285 &self.base
286 }
287}
288
289#[allow(missing_docs)]
293#[derive(Clone, Debug)]
294pub struct RadialGradient {
295 pub base: BaseGradient,
297
298 pub cx: f32,
299 pub cy: f32,
300 pub r: PositiveF32,
301 pub fx: f32,
302 pub fy: f32,
303}
304
305impl std::ops::Deref for RadialGradient {
306 type Target = BaseGradient;
307
308 fn deref(&self) -> &Self::Target {
309 &self.base
310 }
311}
312
313pub type StopOffset = NormalizedF32;
315
316#[derive(Clone, Copy, Debug)]
320pub struct Stop {
321 pub offset: StopOffset,
325
326 pub color: Color,
330
331 pub opacity: Opacity,
335}
336
337#[derive(Clone, Debug)]
341pub struct Pattern {
342 pub id: String,
347
348 pub units: Units,
352
353 pub content_units: Units,
358
359 pub transform: Transform,
363
364 pub rect: NonZeroRect,
368
369 pub view_box: Option<ViewBox>,
371
372 pub root: Group,
374}
375
376pub type StrokeWidth = NonZeroPositiveF32;
378
379#[derive(Clone, Copy, Debug)]
383pub struct StrokeMiterlimit(f32);
384
385impl StrokeMiterlimit {
386 #[inline]
388 pub fn new(n: f32) -> Self {
389 debug_assert!(n.is_finite());
390 debug_assert!(n >= 1.0);
391
392 let n = if !(n >= 1.0) { 1.0 } else { n };
393
394 StrokeMiterlimit(n)
395 }
396
397 #[inline]
399 pub fn get(&self) -> f32 {
400 self.0
401 }
402}
403
404impl Default for StrokeMiterlimit {
405 #[inline]
406 fn default() -> Self {
407 StrokeMiterlimit::new(4.0)
408 }
409}
410
411impl From<f32> for StrokeMiterlimit {
412 #[inline]
413 fn from(n: f32) -> Self {
414 Self::new(n)
415 }
416}
417
418impl PartialEq for StrokeMiterlimit {
419 #[inline]
420 fn eq(&self, other: &Self) -> bool {
421 self.0.approx_eq_ulps(&other.0, 4)
422 }
423}
424
425#[allow(missing_docs)]
429#[derive(Clone, Copy, PartialEq, Debug)]
430pub enum LineCap {
431 Butt,
432 Round,
433 Square,
434}
435
436impl Default for LineCap {
437 fn default() -> Self {
438 Self::Butt
439 }
440}
441
442#[allow(missing_docs)]
446#[derive(Clone, Copy, PartialEq, Debug)]
447pub enum LineJoin {
448 Miter,
449 MiterClip,
450 Round,
451 Bevel,
452}
453
454impl Default for LineJoin {
455 fn default() -> Self {
456 Self::Miter
457 }
458}
459
460#[allow(missing_docs)]
462#[derive(Clone, Debug)]
463pub struct Stroke {
464 pub paint: Paint,
465 pub dasharray: Option<Vec<f32>>,
466 pub dashoffset: f32,
467 pub miterlimit: StrokeMiterlimit,
468 pub opacity: Opacity,
469 pub width: StrokeWidth,
470 pub linecap: LineCap,
471 pub linejoin: LineJoin,
472}
473
474impl Default for Stroke {
475 fn default() -> Self {
476 Stroke {
477 paint: Paint::Color(Color::black()),
480 dasharray: None,
481 dashoffset: 0.0,
482 miterlimit: StrokeMiterlimit::default(),
483 opacity: Opacity::ONE,
484 width: StrokeWidth::new(1.0).unwrap(),
485 linecap: LineCap::default(),
486 linejoin: LineJoin::default(),
487 }
488 }
489}
490
491impl Stroke {
492 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
494 let mut stroke = tiny_skia_path::Stroke {
495 width: self.width.get(),
496 miter_limit: self.miterlimit.get(),
497 line_cap: match self.linecap {
498 LineCap::Butt => tiny_skia_path::LineCap::Butt,
499 LineCap::Round => tiny_skia_path::LineCap::Round,
500 LineCap::Square => tiny_skia_path::LineCap::Square,
501 },
502 line_join: match self.linejoin {
503 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
504 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
505 LineJoin::Round => tiny_skia_path::LineJoin::Round,
506 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
507 },
508 dash: None,
511 };
512
513 if let Some(ref list) = self.dasharray {
514 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
515 }
516
517 stroke
518 }
519}
520
521#[allow(missing_docs)]
525#[derive(Clone, Copy, PartialEq, Debug)]
526pub enum FillRule {
527 NonZero,
528 EvenOdd,
529}
530
531impl Default for FillRule {
532 fn default() -> Self {
533 Self::NonZero
534 }
535}
536
537#[allow(missing_docs)]
539#[derive(Clone, Debug)]
540pub struct Fill {
541 pub paint: Paint,
542 pub opacity: Opacity,
543 pub rule: FillRule,
544}
545
546impl Fill {
547 pub fn from_paint(paint: Paint) -> Self {
551 Fill {
552 paint,
553 ..Fill::default()
554 }
555 }
556}
557
558impl Default for Fill {
559 fn default() -> Self {
560 Fill {
561 paint: Paint::Color(Color::black()),
562 opacity: Opacity::ONE,
563 rule: FillRule::default(),
564 }
565 }
566}
567
568#[derive(Clone, Copy, PartialEq, Debug)]
570#[allow(missing_docs)]
571pub struct Color {
572 pub red: u8,
573 pub green: u8,
574 pub blue: u8,
575}
576
577impl Color {
578 #[inline]
580 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
581 Color { red, green, blue }
582 }
583
584 #[inline]
586 pub fn black() -> Color {
587 Color::new_rgb(0, 0, 0)
588 }
589
590 #[inline]
592 pub fn white() -> Color {
593 Color::new_rgb(255, 255, 255)
594 }
595}
596
597#[allow(missing_docs)]
601#[derive(Clone, Debug)]
602pub enum Paint {
603 Color(Color),
604 LinearGradient(Rc<LinearGradient>),
605 RadialGradient(Rc<RadialGradient>),
606 Pattern(Rc<RefCell<Pattern>>),
607}
608
609impl Paint {
610 #[inline]
614 pub fn units(&self) -> Option<Units> {
615 match self {
616 Self::Color(_) => None,
617 Self::LinearGradient(ref lg) => Some(lg.units),
618 Self::RadialGradient(ref rg) => Some(rg.units),
619 Self::Pattern(ref patt) => Some(patt.borrow().units),
620 }
621 }
622}
623
624impl PartialEq for Paint {
625 #[inline]
626 fn eq(&self, other: &Self) -> bool {
627 match (self, other) {
628 (Self::Color(lc), Self::Color(rc)) => lc == rc,
629 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Rc::ptr_eq(lg1, lg2),
630 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Rc::ptr_eq(rg1, rg2),
631 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Rc::ptr_eq(p1, p2),
632 _ => false,
633 }
634 }
635}
636
637#[derive(Clone, Debug)]
641pub struct ClipPath {
642 pub id: String,
647
648 pub units: Units,
652
653 pub transform: Transform,
657
658 pub clip_path: Option<SharedClipPath>,
662
663 pub root: Group,
665}
666
667pub type SharedClipPath = Rc<RefCell<ClipPath>>;
669
670impl Default for ClipPath {
671 fn default() -> Self {
672 ClipPath {
673 id: String::new(),
674 units: Units::UserSpaceOnUse,
675 transform: Transform::default(),
676 clip_path: None,
677 root: Group::default(),
678 }
679 }
680}
681
682#[derive(Clone, Copy, PartialEq, Debug)]
684pub enum MaskType {
685 Luminance,
687 Alpha,
689}
690
691impl Default for MaskType {
692 fn default() -> Self {
693 Self::Luminance
694 }
695}
696
697#[derive(Clone, Debug)]
701pub struct Mask {
702 pub id: String,
707
708 pub units: Units,
712
713 pub content_units: Units,
717
718 pub rect: NonZeroRect,
722
723 pub kind: MaskType,
727
728 pub mask: Option<SharedMask>,
732
733 pub root: Group,
735}
736
737pub type SharedMask = Rc<RefCell<Mask>>;
739
740#[allow(missing_docs)]
742#[derive(Clone, Debug)]
743pub enum Node {
744 Group(Box<Group>),
745 Path(Box<Path>),
746 Image(Box<Image>),
747 Text(Box<Text>),
748}
749
750impl Node {
751 pub fn id(&self) -> &str {
753 match self {
754 Node::Group(ref e) => e.id.as_str(),
755 Node::Path(ref e) => e.id.as_str(),
756 Node::Image(ref e) => e.id.as_str(),
757 Node::Text(ref e) => e.id.as_str(),
758 }
759 }
760
761 pub fn abs_transform(&self) -> Transform {
768 match self {
769 Node::Group(ref group) => group.abs_transform,
770 Node::Path(ref path) => path.abs_transform,
771 Node::Image(ref image) => image.abs_transform,
772 Node::Text(ref text) => text.abs_transform,
773 }
774 }
775
776 pub fn bounding_box(&self) -> Option<Rect> {
780 match self {
781 Node::Group(ref group) => group.bounding_box,
782 Node::Path(ref path) => path.bounding_box,
783 Node::Image(ref image) => image.bounding_box.map(|r| r.to_rect()),
784 Node::Text(ref text) => text.bounding_box.map(|r| r.to_rect()),
785 }
786 }
787
788 pub fn abs_bounding_box(&self) -> Option<Rect> {
792 self.bounding_box()?.transform(self.abs_transform())
793 }
794
795 pub fn stroke_bounding_box(&self) -> Option<NonZeroRect> {
799 match self {
800 Node::Group(ref group) => group.stroke_bounding_box,
801 Node::Path(ref path) => path.stroke_bounding_box,
802 Node::Image(ref image) => image.bounding_box,
804 Node::Text(ref text) => text.stroke_bounding_box,
805 }
806 }
807
808 pub fn abs_stroke_bounding_box(&self) -> Option<NonZeroRect> {
812 self.stroke_bounding_box()?.transform(self.abs_transform())
813 }
814
815 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
840 match self {
841 Node::Group(ref group) => group.subroots(&mut f),
842 Node::Path(ref path) => path.subroots(&mut f),
843 Node::Image(ref image) => image.subroots(&mut f),
844 Node::Text(ref text) => text.subroots(&mut f),
845 }
846 }
847
848 pub fn subroots_mut<F: FnMut(&mut Group)>(&mut self, mut f: F) {
852 match self {
853 Node::Group(ref mut group) => group.subroots_mut(&mut f),
854 Node::Path(ref mut path) => path.subroots_mut(&mut f),
855 Node::Image(ref mut image) => image.subroots_mut(&mut f),
856 Node::Text(ref mut text) => text.subroots_mut(&mut f),
857 }
858 }
859}
860
861#[derive(Clone, Debug)]
868pub struct Group {
869 pub id: String,
875
876 pub transform: Transform,
880
881 pub abs_transform: Transform,
890
891 pub opacity: Opacity,
896
897 pub blend_mode: BlendMode,
901
902 pub isolate: bool,
906
907 pub clip_path: Option<SharedClipPath>,
909
910 pub mask: Option<SharedMask>,
912
913 pub filters: Vec<filter::SharedFilter>,
915
916 pub bounding_box: Option<Rect>,
924
925 pub stroke_bounding_box: Option<NonZeroRect>,
929
930 pub layer_bounding_box: Option<NonZeroRect>,
944
945 pub children: Vec<Node>,
947}
948
949impl Default for Group {
950 fn default() -> Self {
951 Group {
952 id: String::new(),
953 transform: Transform::default(),
954 abs_transform: Transform::default(),
955 opacity: Opacity::ONE,
956 blend_mode: BlendMode::Normal,
957 isolate: false,
958 clip_path: None,
959 mask: None,
960 filters: Vec::new(),
961 bounding_box: None,
962 stroke_bounding_box: None,
963 layer_bounding_box: None,
964 children: Vec::new(),
965 }
966 }
967}
968
969impl Group {
970 pub fn should_isolate(&self) -> bool {
972 self.isolate
973 || self.opacity != Opacity::ONE
974 || self.clip_path.is_some()
975 || self.mask.is_some()
976 || !self.filters.is_empty()
977 || self.blend_mode != BlendMode::Normal }
979
980 pub fn has_children(&self) -> bool {
982 !self.children.is_empty()
983 }
984
985 pub fn abs_bounding_box(&self) -> Option<Rect> {
987 self.bounding_box?.transform(self.abs_transform)
988 }
989
990 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1001 let mut full_region = BBox::default();
1002
1003 for filter in &self.filters {
1004 let mut region = filter.borrow().rect;
1005
1006 if filter.borrow().units == Units::ObjectBoundingBox {
1007 let object_bbox = self.bounding_box.and_then(|bbox| bbox.to_non_zero_rect());
1008 if let Some(object_bbox) = object_bbox {
1009 region = region.bbox_transform(object_bbox);
1010 } else {
1011 continue;
1013 }
1014 }
1015
1016 full_region = full_region.expand(BBox::from(region));
1017 }
1018
1019 full_region.to_non_zero_rect()
1020 }
1021
1022 pub fn abs_filters_bounding_box(&self) -> Option<NonZeroRect> {
1024 self.filters_bounding_box()?.transform(self.abs_transform)
1025 }
1026
1027 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1028 if let Some(ref clip) = self.clip_path {
1029 f(&clip.borrow().root);
1030
1031 if let Some(ref sub_clip) = clip.borrow().clip_path {
1032 f(&sub_clip.borrow().root);
1033 }
1034 }
1035
1036 if let Some(ref mask) = self.mask {
1037 f(&mask.borrow().root);
1038
1039 if let Some(ref sub_mask) = mask.borrow().mask {
1040 f(&sub_mask.borrow().root);
1041 }
1042 }
1043
1044 for filter in &self.filters {
1045 for primitive in &filter.borrow().primitives {
1046 if let filter::Kind::Image(ref image) = primitive.kind {
1047 if let filter::ImageKind::Use(ref use_node) = image.data {
1048 f(use_node);
1049 }
1050 }
1051 }
1052 }
1053 }
1054
1055 fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1056 if let Some(ref clip) = self.clip_path {
1057 f(&mut clip.borrow_mut().root);
1058
1059 if let Some(ref sub_clip) = clip.borrow().clip_path {
1060 f(&mut sub_clip.borrow_mut().root);
1061 }
1062 }
1063
1064 if let Some(ref mask) = self.mask {
1065 f(&mut mask.borrow_mut().root);
1066
1067 if let Some(ref sub_mask) = mask.borrow_mut().mask {
1068 f(&mut sub_mask.borrow_mut().root);
1069 }
1070 }
1071
1072 for filter in &mut self.filters {
1073 for primitive in &mut filter.borrow_mut().primitives {
1074 if let filter::Kind::Image(ref mut image) = primitive.kind {
1075 if let filter::ImageKind::Use(ref mut use_node) = image.data {
1076 f(use_node);
1077 }
1078 }
1079 }
1080 }
1081 }
1082}
1083
1084#[derive(Clone, Copy, PartialEq, Debug)]
1091#[allow(missing_docs)]
1092pub enum PaintOrder {
1093 FillAndStroke,
1094 StrokeAndFill,
1095}
1096
1097impl Default for PaintOrder {
1098 fn default() -> Self {
1099 Self::FillAndStroke
1100 }
1101}
1102
1103#[derive(Clone, Debug)]
1105pub struct Path {
1106 pub id: String,
1112
1113 pub visibility: Visibility,
1115
1116 pub fill: Option<Fill>,
1118
1119 pub stroke: Option<Stroke>,
1121
1122 pub paint_order: PaintOrder,
1129
1130 pub rendering_mode: ShapeRendering,
1134
1135 pub data: Rc<tiny_skia_path::Path>,
1139
1140 pub abs_transform: Transform,
1149
1150 pub bounding_box: Option<Rect>,
1156
1157 pub stroke_bounding_box: Option<NonZeroRect>,
1163}
1164
1165impl Path {
1166 pub fn new(data: Rc<tiny_skia_path::Path>) -> Self {
1168 Path {
1169 id: String::new(),
1170 visibility: Visibility::Visible,
1171 fill: None,
1172 stroke: None,
1173 paint_order: PaintOrder::default(),
1174 rendering_mode: ShapeRendering::default(),
1175 data,
1176 abs_transform: Transform::default(),
1177 bounding_box: None,
1178 stroke_bounding_box: None,
1179 }
1180 }
1181
1182 pub fn calculate_stroke_bounding_box(&self) -> Option<NonZeroRect> {
1186 let stroke = self.stroke.as_ref()?;
1187 let mut stroke = stroke.to_tiny_skia();
1188 stroke.dash = None;
1190
1191 if let Some(stroked_path) = self.data.stroke(&stroke, 1.0) {
1193 return stroked_path
1196 .compute_tight_bounds()
1197 .and_then(|r| r.to_non_zero_rect());
1198 }
1199
1200 None
1201 }
1202
1203 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1204 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1205 f(&patt.borrow().root)
1206 }
1207 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1208 f(&patt.borrow().root)
1209 }
1210 }
1211
1212 fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1213 if let Some(Paint::Pattern(ref mut patt)) = self.fill.as_mut().map(|f| &mut f.paint) {
1214 f(&mut patt.borrow_mut().root)
1215 }
1216 if let Some(Paint::Pattern(ref mut patt)) = self.stroke.as_mut().map(|f| &mut f.paint) {
1217 f(&mut patt.borrow_mut().root)
1218 }
1219 }
1220}
1221
1222#[derive(Clone)]
1224pub enum ImageKind {
1225 JPEG(Arc<Vec<u8>>),
1227 PNG(Arc<Vec<u8>>),
1229 GIF(Arc<Vec<u8>>),
1231 SVG(Tree),
1233}
1234
1235impl std::fmt::Debug for ImageKind {
1236 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1237 match self {
1238 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1239 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1240 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1241 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1242 }
1243 }
1244}
1245
1246#[derive(Clone, Debug)]
1250pub struct Image {
1251 pub id: String,
1257
1258 pub visibility: Visibility,
1260
1261 pub view_box: ViewBox,
1266
1267 pub rendering_mode: ImageRendering,
1271
1272 pub kind: ImageKind,
1274
1275 pub abs_transform: Transform,
1284
1285 pub bounding_box: Option<NonZeroRect>,
1291}
1292
1293impl Image {
1294 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1295 if let ImageKind::SVG(ref tree) = self.kind {
1296 f(&tree.root)
1297 }
1298 }
1299
1300 fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
1301 if let ImageKind::SVG(ref mut tree) = self.kind {
1302 f(&mut tree.root)
1303 }
1304 }
1305}
1306
1307#[allow(missing_debug_implementations)]
1309#[derive(Clone, Debug)]
1310pub struct Tree {
1311 pub size: Size,
1317
1318 pub view_box: ViewBox,
1324
1325 pub root: Group,
1327}
1328
1329impl Tree {
1330 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1334 if id.is_empty() {
1335 return None;
1336 }
1337
1338 node_by_id(&self.root, id)
1339 }
1340
1341 pub fn has_text_nodes(&self) -> bool {
1343 has_text_nodes(&self.root)
1344 }
1345
1346 pub fn paint_servers<F: FnMut(&Paint)>(&self, mut f: F) {
1350 loop_over_paint_servers(&self.root, &mut f)
1351 }
1352
1353 pub fn clip_paths<F: FnMut(SharedClipPath)>(&self, mut f: F) {
1357 loop_over_clip_paths(&self.root, &mut f)
1358 }
1359
1360 pub fn masks<F: FnMut(SharedMask)>(&self, mut f: F) {
1364 loop_over_masks(&self.root, &mut f)
1365 }
1366
1367 pub fn filters<F: FnMut(filter::SharedFilter)>(&self, mut f: F) {
1371 loop_over_filters(&self.root, &mut f)
1372 }
1373
1374 pub fn calculate_abs_transforms(&mut self) {
1378 self.root.calculate_abs_transforms(Transform::identity());
1379 }
1380
1381 pub fn calculate_bounding_boxes(&mut self) {
1385 self.root.calculate_bounding_boxes();
1386 }
1387}
1388
1389fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1390 for child in &parent.children {
1391 if child.id() == id {
1392 return Some(child);
1393 }
1394
1395 if let Node::Group(ref g) = child {
1396 if let Some(n) = node_by_id(g, id) {
1397 return Some(n);
1398 }
1399 }
1400 }
1401
1402 None
1403}
1404
1405fn has_text_nodes(root: &Group) -> bool {
1406 for node in &root.children {
1407 if let Node::Text(_) = node {
1408 return true;
1409 }
1410
1411 let mut has_text = false;
1412
1413 if let Node::Image(ref image) = node {
1414 if let ImageKind::SVG(ref tree) = image.kind {
1415 if has_text_nodes(&tree.root) {
1416 has_text = true;
1417 }
1418 }
1419 }
1420
1421 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1422
1423 if has_text {
1424 return true;
1425 }
1426 }
1427
1428 true
1429}
1430
1431fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1432 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1433 if let Some(paint) = paint {
1434 f(paint);
1435 }
1436 }
1437
1438 for node in &parent.children {
1439 match node {
1440 Node::Group(ref group) => loop_over_paint_servers(group, f),
1441 Node::Path(ref path) => {
1442 push(path.fill.as_ref().map(|f| &f.paint), f);
1443 push(path.stroke.as_ref().map(|f| &f.paint), f);
1444 }
1445 Node::Image(_) => {}
1446 Node::Text(ref text) => {
1447 if text.flattened.is_none() {
1449 for chunk in &text.chunks {
1450 for span in &chunk.spans {
1451 push(span.fill.as_ref().map(|f| &f.paint), f);
1452 push(span.stroke.as_ref().map(|f| &f.paint), f);
1453
1454 if let Some(ref underline) = span.decoration.underline {
1455 push(underline.fill.as_ref().map(|f| &f.paint), f);
1456 push(underline.stroke.as_ref().map(|f| &f.paint), f);
1457 }
1458
1459 if let Some(ref overline) = span.decoration.overline {
1460 push(overline.fill.as_ref().map(|f| &f.paint), f);
1461 push(overline.stroke.as_ref().map(|f| &f.paint), f);
1462 }
1463
1464 if let Some(ref line_through) = span.decoration.line_through {
1465 push(line_through.fill.as_ref().map(|f| &f.paint), f);
1466 push(line_through.stroke.as_ref().map(|f| &f.paint), f);
1467 }
1468 }
1469 }
1470 }
1471 }
1472 }
1473
1474 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1475 }
1476}
1477
1478fn loop_over_clip_paths(parent: &Group, f: &mut dyn FnMut(SharedClipPath)) {
1479 for node in &parent.children {
1480 if let Node::Group(ref g) = node {
1481 if let Some(ref clip) = g.clip_path {
1482 f(clip.clone());
1483
1484 if let Some(ref sub_clip) = clip.borrow().clip_path {
1485 f(sub_clip.clone());
1486 }
1487 }
1488 }
1489
1490 node.subroots(|subroot| loop_over_clip_paths(subroot, f));
1491
1492 if let Node::Group(ref g) = node {
1493 loop_over_clip_paths(g, f);
1494 }
1495 }
1496}
1497
1498fn loop_over_masks(parent: &Group, f: &mut dyn FnMut(SharedMask)) {
1499 for node in &parent.children {
1500 if let Node::Group(ref g) = node {
1501 if let Some(ref mask) = g.mask {
1502 f(mask.clone());
1503
1504 if let Some(ref sub_mask) = mask.borrow().mask {
1505 f(sub_mask.clone());
1506 }
1507 }
1508
1509 loop_over_masks(g, f);
1510 }
1511
1512 node.subroots(|subroot| loop_over_masks(subroot, f));
1513
1514 if let Node::Group(ref g) = node {
1515 loop_over_masks(g, f);
1516 }
1517 }
1518}
1519
1520fn loop_over_filters(parent: &Group, f: &mut dyn FnMut(filter::SharedFilter)) {
1521 for node in &parent.children {
1522 if let Node::Group(ref g) = node {
1523 for filter in &g.filters {
1524 f(filter.clone());
1525 }
1526 }
1527
1528 node.subroots(|subroot| loop_over_filters(subroot, f));
1529
1530 if let Node::Group(ref g) = node {
1531 loop_over_filters(g, f);
1532 }
1533 }
1534}
1535
1536impl Group {
1537 pub fn calculate_abs_transforms(&mut self, transform: Transform) {
1541 for node in &mut self.children {
1542 match node {
1543 Node::Group(ref mut group) => {
1544 let abs_ts = transform.pre_concat(group.transform);
1545 group.abs_transform = abs_ts;
1546 group.calculate_abs_transforms(abs_ts);
1547 }
1548 Node::Path(ref mut path) => path.abs_transform = transform,
1549 Node::Image(ref mut image) => image.abs_transform = transform,
1550 Node::Text(ref mut text) => text.abs_transform = transform,
1551 }
1552
1553 node.subroots_mut(|root| root.calculate_abs_transforms(Transform::identity()));
1555 }
1556 }
1557
1558 pub fn calculate_bounding_boxes(&mut self) {
1562 for node in &mut self.children {
1563 match node {
1564 Node::Path(ref mut path) => {
1565 path.bounding_box = path.data.compute_tight_bounds();
1566 path.stroke_bounding_box = path.calculate_stroke_bounding_box();
1567 if path.stroke_bounding_box.is_none() {
1568 path.stroke_bounding_box =
1569 path.bounding_box.and_then(|r| r.to_non_zero_rect());
1570 }
1571 }
1572 Node::Image(ref mut image) => image.bounding_box = Some(image.view_box.rect),
1574 Node::Group(ref mut group) => {
1576 group.calculate_bounding_boxes();
1577 }
1578 Node::Text(_) => {}
1580 }
1581
1582 node.subroots_mut(|root| root.calculate_bounding_boxes());
1584 }
1585
1586 let mut bbox = BBox::default();
1587 let mut stroke_bbox = BBox::default();
1588 let mut layer_bbox = BBox::default();
1589 for child in &self.children {
1590 if let Some(mut c_bbox) = child.bounding_box() {
1591 if let Node::Group(ref group) = child {
1592 if let Some(r) = c_bbox.transform(group.transform) {
1593 c_bbox = r;
1594 }
1595 }
1596
1597 bbox = bbox.expand(c_bbox);
1598 }
1599
1600 if let Some(mut c_bbox) = child.stroke_bounding_box() {
1601 if let Node::Group(ref group) = child {
1602 if let Some(r) = c_bbox.transform(group.transform) {
1603 c_bbox = r;
1604 }
1605 }
1606
1607 stroke_bbox = stroke_bbox.expand(c_bbox);
1608 }
1609
1610 if let Node::Group(ref group) = child {
1611 if let Some(r) = group.layer_bounding_box {
1612 if let Some(r) = r.transform(group.transform) {
1613 layer_bbox = layer_bbox.expand(r);
1614 }
1615 }
1616 } else if let Some(c_bbox) = child.stroke_bounding_box() {
1617 layer_bbox = layer_bbox.expand(c_bbox);
1619 }
1620 }
1621
1622 self.bounding_box = bbox.to_rect();
1623 self.stroke_bounding_box = stroke_bbox.to_non_zero_rect();
1624
1625 if let Some(filter_bbox) = self.filters_bounding_box() {
1627 self.layer_bounding_box = Some(filter_bbox);
1628 } else {
1629 self.layer_bounding_box = layer_bbox.to_non_zero_rect();
1630 }
1631 }
1632}