1use std::sync::Arc;
5
6use svgtypes::{Length, LengthUnit as Unit};
7
8use super::svgtree::{AId, EId, SvgNode};
9use super::{converter, OptionLog};
10use crate::{Group, Mask, MaskType, Node, NonEmptyString, NonZeroRect, Transform, Units};
11
12pub(crate) fn convert(
13 node: SvgNode,
14 state: &converter::State,
15 object_bbox: Option<NonZeroRect>,
16 cache: &mut converter::Cache,
17) -> Option<Arc<Mask>> {
18 if node.tag_name() != Some(EId::Mask) {
20 return None;
21 }
22
23 let units = node
24 .attribute(AId::MaskUnits)
25 .unwrap_or(Units::ObjectBoundingBox);
26
27 let content_units = node
28 .attribute(AId::MaskContentUnits)
29 .unwrap_or(Units::UserSpaceOnUse);
30
31 let cacheable = units == Units::UserSpaceOnUse && content_units == Units::UserSpaceOnUse;
37 if cacheable {
38 if let Some(mask) = cache.masks.get(node.element_id()) {
39 return Some(mask.clone());
40 }
41 }
42
43 let rect = NonZeroRect::from_xywh(
44 node.convert_length(AId::X, units, state, Length::new(-10.0, Unit::Percent)),
45 node.convert_length(AId::Y, units, state, Length::new(-10.0, Unit::Percent)),
46 node.convert_length(AId::Width, units, state, Length::new(120.0, Unit::Percent)),
47 node.convert_length(AId::Height, units, state, Length::new(120.0, Unit::Percent)),
48 );
49 let mut rect =
50 rect.log_none(|| log::warn!("Mask '{}' has an invalid size. Skipped.", node.element_id()))?;
51
52 let mut mask_all = false;
53 if units == Units::ObjectBoundingBox {
54 if let Some(bbox) = object_bbox {
55 rect = rect.bbox_transform(bbox)
56 } else {
57 mask_all = true;
61 }
62 }
63
64 let mut id = NonEmptyString::new(node.element_id().to_string())?;
65 if !cacheable && cache.masks.contains_key(id.get()) {
67 id = cache.gen_mask_id();
68 }
69 let id_copy = id.get().to_string();
70
71 if mask_all {
72 let mask = Arc::new(Mask {
73 id,
74 rect,
75 kind: MaskType::Luminance,
76 mask: None,
77 root: Group::empty(),
78 });
79 cache.masks.insert(id_copy, mask.clone());
80 return Some(mask);
81 }
82
83 let mut mask = None;
85 if let Some(link) = node.attribute::<SvgNode>(AId::Mask) {
86 mask = convert(link, state, object_bbox, cache);
87
88 if mask.is_none() {
90 return None;
91 }
92 }
93
94 let kind = if node.attribute(AId::MaskType) == Some("alpha") {
95 MaskType::Alpha
96 } else {
97 MaskType::Luminance
98 };
99
100 let mut mask = Mask {
101 id,
102 rect,
103 kind,
104 mask,
105 root: Group::empty(),
106 };
107
108 let mut subroot = None;
111 if content_units == Units::ObjectBoundingBox {
112 let object_bbox = match object_bbox {
113 Some(v) => v,
114 None => {
115 log::warn!("Masking of zero-sized shapes is not allowed.");
116 return None;
117 }
118 };
119
120 let mut g = Group::empty();
121 g.transform = Transform::from_bbox(object_bbox);
122 g.abs_transform = g.transform;
124
125 subroot = Some(g);
126 }
127
128 {
129 let real_root = subroot.as_mut().unwrap_or(&mut mask.root);
131 converter::convert_children(node, state, cache, real_root);
132
133 if !real_root.has_children() {
136 return None;
137 }
138 }
139
140 if let Some(mut subroot) = subroot {
141 subroot.calculate_bounding_boxes();
142 mask.root.children.push(Node::Group(Box::new(subroot)));
143 }
144
145 mask.root.calculate_bounding_boxes();
146
147 let mask = Arc::new(mask);
148 cache.masks.insert(id_copy, mask.clone());
149 Some(mask)
150}