use svgdom::{
Color,
PaintFallback,
};
use super::prelude::*;
pub fn remove_invalid_gradients(doc: &mut Document) {
let mut ids = Vec::new();
let mut nodes = Vec::new();
for gradient in doc.root().descendants().filter(|n| n.is_gradient()) {
let count = gradient.children().count();
if count == 0 || count == 1 {
let linked_nodes = gradient.linked_nodes().clone();
for mut linked in linked_nodes {
collect_ids(&linked, &gradient, &mut ids);
for aid in &ids {
if count == 0 {
let av = linked.attributes().get_value(*aid).cloned();
let av = if let Some(AValue::Paint(_, fallback)) = av {
match fallback {
Some(PaintFallback::None) => {
AValue::None
}
Some(PaintFallback::CurrentColor) => {
debug_panic!("'currentColor' must be already resolved.");
AValue::None
}
Some(PaintFallback::Color(c)) => {
AValue::Color(c)
}
None => {
AValue::None
}
}
} else {
AValue::None
};
linked.set_attribute((*aid, av));
} else {
let stop = gradient.first_child().unwrap();
let color = stop.attributes().get_color(AId::StopColor)
.unwrap_or(Color::new(0, 0, 0));
let opacity = stop.attributes().get_number_or(AId::StopOpacity, 1.0);
prepare_link_opacity(&mut linked, *aid, opacity);
linked.set_attribute((*aid, color));
}
}
}
nodes.push(gradient);
} else if gradient.is_tag_name(EId::RadialGradient) {
if process_negative_r(&gradient, &mut ids) {
nodes.push(gradient);
}
}
}
for node in nodes {
doc.remove_node(node);
}
}
fn process_negative_r(
gradient: &Node,
ids: &mut Vec<AId>,
) -> bool {
let r = try_opt_warn!(gradient.attributes().get_number(AId::R), false,
"'r' attribute in 'radialGradient' should be already resolved.");
if !r.is_fuzzy_zero() {
return false;
}
let stop = match gradient.last_child() {
Some(s) => s,
None => return false,
};
let linked_nodes = gradient.linked_nodes().clone();
for mut linked in linked_nodes {
collect_ids(&linked, gradient, ids);
for id in ids.iter() {
let color = stop.attributes().get_color(AId::StopColor)
.unwrap_or(Color::new(0, 0, 0));
let opacity = stop.attributes().get_number_or(AId::StopOpacity, 1.0);
prepare_link_opacity(&mut linked, *id, opacity);
linked.set_attribute((*id, color));
}
}
true
}
fn collect_ids(linked: &Node, gradient: &Node, ids: &mut Vec<AId>) {
ids.clear();
for (aid, attr) in linked.attributes().iter().svg() {
match attr.value {
AValue::Paint(ref link, _) => {
if link == gradient {
ids.push(aid);
}
}
_ => {}
}
}
}
fn prepare_link_opacity(linked: &mut Node, aid: AId, opacity: f64) {
if opacity.fuzzy_ne(&1.0) {
match aid {
AId::Fill => {
update_opacity(linked, AId::FillOpacity, opacity);
}
AId::Stroke => {
update_opacity(linked, AId::StrokeOpacity, opacity);
}
_ => {
}
}
}
}
fn update_opacity(node: &mut Node, aid: AId, new_opacity: f64) {
let old_opacity = node.attributes().get_number_or(aid, 1.0);
node.set_attribute((aid, old_opacity * new_opacity));
}