use std::sync::Arc;
use svgtypes::{Length, LengthUnit as Unit};
use super::svgtree::{AId, EId, SvgNode};
use super::{converter, OptionLog};
use crate::{Group, Mask, MaskType, Node, NonEmptyString, NonZeroRect, Transform, Units};
pub(crate) fn convert(
node: SvgNode,
state: &converter::State,
object_bbox: Option<NonZeroRect>,
cache: &mut converter::Cache,
) -> Option<Arc<Mask>> {
if node.tag_name() != Some(EId::Mask) {
return None;
}
let units = node
.attribute(AId::MaskUnits)
.unwrap_or(Units::ObjectBoundingBox);
let content_units = node
.attribute(AId::MaskContentUnits)
.unwrap_or(Units::UserSpaceOnUse);
let cacheable = units == Units::UserSpaceOnUse && content_units == Units::UserSpaceOnUse;
if cacheable {
if let Some(mask) = cache.masks.get(node.element_id()) {
return Some(mask.clone());
}
}
let rect = NonZeroRect::from_xywh(
node.convert_length(AId::X, units, state, Length::new(-10.0, Unit::Percent)),
node.convert_length(AId::Y, units, state, Length::new(-10.0, Unit::Percent)),
node.convert_length(AId::Width, units, state, Length::new(120.0, Unit::Percent)),
node.convert_length(AId::Height, units, state, Length::new(120.0, Unit::Percent)),
);
let mut rect =
rect.log_none(|| log::warn!("Mask '{}' has an invalid size. Skipped.", node.element_id()))?;
let mut mask_all = false;
if units == Units::ObjectBoundingBox {
if let Some(bbox) = object_bbox {
rect = rect.bbox_transform(bbox)
} else {
mask_all = true;
}
}
let mut id = NonEmptyString::new(node.element_id().to_string())?;
if !cacheable && cache.masks.contains_key(id.get()) {
id = cache.gen_mask_id();
}
let id_copy = id.get().to_string();
if mask_all {
let mask = Arc::new(Mask {
id,
rect,
kind: MaskType::Luminance,
mask: None,
root: Group::empty(),
});
cache.masks.insert(id_copy, mask.clone());
return Some(mask);
}
let mut mask = None;
if let Some(link) = node.attribute::<SvgNode>(AId::Mask) {
mask = convert(link, state, object_bbox, cache);
if mask.is_none() {
return None;
}
}
let kind = if node.attribute(AId::MaskType) == Some("alpha") {
MaskType::Alpha
} else {
MaskType::Luminance
};
let mut mask = Mask {
id,
rect,
kind,
mask,
root: Group::empty(),
};
let mut subroot = None;
if content_units == Units::ObjectBoundingBox {
let object_bbox = match object_bbox {
Some(v) => v,
None => {
log::warn!("Masking of zero-sized shapes is not allowed.");
return None;
}
};
let mut g = Group::empty();
g.transform = Transform::from_bbox(object_bbox);
g.abs_transform = g.transform;
subroot = Some(g);
}
{
let real_root = subroot.as_mut().unwrap_or(&mut mask.root);
converter::convert_children(node, state, cache, real_root);
if !real_root.has_children() {
return None;
}
}
if let Some(mut subroot) = subroot {
subroot.calculate_bounding_boxes();
mask.root.children.push(Node::Group(Box::new(subroot)));
}
mask.root.calculate_bounding_boxes();
let mask = Arc::new(mask);
cache.masks.insert(id_copy, mask.clone());
Some(mask)
}