use svgdom::{
AspectRatio,
};
use super::prelude::*;
pub fn resolve_linear_gradient_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::LinearGradient) {
resolve_attr(node, AId::GradientUnits,
Some(AValue::from("objectBoundingBox")));
resolve_attr(node, AId::SpreadMethod, Some(AValue::from("pad")));
resolve_attr(node, AId::X1, Some(AValue::from(0.0)));
resolve_attr(node, AId::Y1, Some(AValue::from(0.0)));
resolve_attr(node, AId::X2, Some(AValue::from(1.0)));
resolve_attr(node, AId::Y2, Some(AValue::from(0.0)));
resolve_attr(node, AId::GradientTransform, None);
}
}
pub fn resolve_radial_gradient_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::RadialGradient) {
resolve_attr(node, AId::GradientUnits, Some(AValue::from("objectBoundingBox")));
resolve_attr(node, AId::SpreadMethod, Some(AValue::from("pad")));
resolve_attr(node, AId::Cx, Some(AValue::from(0.5)));
resolve_attr(node, AId::Cy, Some(AValue::from(0.5)));
resolve_attr(node, AId::R, Some(AValue::from(0.5)));
resolve_attr(node, AId::GradientTransform, None);
let r = node.attributes().get_number(AId::R).unwrap();
if r < 0.0 {
node.set_attribute((AId::R, 0.0));
}
let cx = node.attributes().get_value(AId::Cx).cloned();
resolve_attr(node, AId::Fx, cx);
let cy = node.attributes().get_value(AId::Cy).cloned();
resolve_attr(node, AId::Fy, cy);
prepare_focal(node);
}
}
pub fn resolve_pattern_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::Pattern) {
resolve_attr(node, AId::PatternUnits, Some(AValue::from("objectBoundingBox")));
resolve_attr(node, AId::PatternContentUnits, Some(AValue::from("userSpaceOnUse")));
resolve_attr(node, AId::PatternTransform, None);
resolve_attr(node, AId::X, Some(AValue::from(0.0)));
resolve_attr(node, AId::Y, Some(AValue::from(0.0)));
resolve_attr(node, AId::Width, Some(AValue::from(0.0)));
resolve_attr(node, AId::Height, Some(AValue::from(0.0)));
resolve_attr(node, AId::PreserveAspectRatio, Some(AValue::from(AspectRatio::default())));
resolve_attr(node, AId::ViewBox, None);
}
}
pub fn resolve_filter_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::Filter) {
resolve_attr(node, AId::FilterUnits, Some(AValue::from("objectBoundingBox")));
resolve_attr(node, AId::PrimitiveUnits, Some(AValue::from("userSpaceOnUse")));
resolve_attr(node, AId::X, Some(AValue::from(-0.1)));
resolve_attr(node, AId::Y, Some(AValue::from(-0.1)));
resolve_attr(node, AId::Width, Some(AValue::from(1.2)));
resolve_attr(node, AId::Height, Some(AValue::from(1.2)));
let is_valid_filter_units = {
let attrs = node.attributes();
let filter_units = attrs.get_str_or(AId::FilterUnits, "");
filter_units == "objectBoundingBox" || filter_units == "userSpaceOnUse"
};
if !is_valid_filter_units {
node.set_attribute((AId::FilterUnits, "objectBoundingBox"));
}
}
}
fn gen_order(doc: &Document, eid: EId) -> Vec<Node> {
let nodes = doc.root().descendants().filter(|n| n.is_tag_name(eid))
.collect::<Vec<Node>>();
let mut order = Vec::with_capacity(nodes.len());
while order.len() != nodes.len() {
for node in &nodes {
if order.iter().any(|n| n == node) {
continue;
}
let c = node.linked_nodes().iter().filter(|n| {
n.is_tag_name(eid) && !order.iter().any(|on| on == *n)
}).count();
if c == 0 {
order.push(node.clone());
}
}
}
order
}
fn resolve_attr(node: &mut Node, id: AId, def_value: Option<AValue>) {
if node.has_attribute(id) {
return;
}
let v = match node.tag_id().unwrap() {
EId::LinearGradient => resolve_lg_attr(node, id, def_value),
EId::RadialGradient => resolve_rg_attr(node, id, def_value),
EId::Pattern => resolve_patt_attr(node, id, def_value),
EId::Filter => resolve_filter_attr(node, id, def_value),
_ => None,
};
if let Some(v) = v {
node.set_attribute((id, v));
}
}
fn resolve_lg_attr(
node: &Node,
aid: AId,
def_value: Option<AValue>,
) -> Option<AValue> {
if node.has_attribute(aid) {
return node.attributes().get_value(aid).cloned();
}
let link = match node.attributes().get_value(AId::Href) {
Some(&AValue::Link(ref link)) => link.clone(),
_ => {
return match node.attributes().get_value(aid) {
Some(v) => Some(v.clone()),
None => def_value,
};
}
};
let eid = match link.tag_id() {
Some(eid) => eid,
None => return def_value,
};
match (aid, eid) {
(AId::X1, EId::LinearGradient)
| (AId::Y1, EId::LinearGradient)
| (AId::X2, EId::LinearGradient)
| (AId::Y2, EId::LinearGradient)
| (AId::GradientUnits, EId::LinearGradient)
| (AId::GradientUnits, EId::RadialGradient)
| (AId::SpreadMethod, EId::LinearGradient)
| (AId::SpreadMethod, EId::RadialGradient)
| (AId::GradientTransform, EId::LinearGradient)
| (AId::GradientTransform, EId::RadialGradient) => {
resolve_lg_attr(&link, aid, def_value)
}
_ => def_value
}
}
fn resolve_rg_attr(
node: &Node,
aid: AId,
def_value: Option<AValue>,
) -> Option<AValue> {
if node.has_attribute(aid) {
return node.attributes().get_value(aid).cloned();
}
let link = match node.attributes().get_value(AId::Href) {
Some(&AValue::Link(ref link)) => link.clone(),
_ => {
return match node.attributes().get_value(aid) {
Some(v) => Some(v.clone()),
None => def_value,
};
}
};
let eid = match link.tag_id() {
Some(eid) => eid,
None => return def_value,
};
match (aid, eid) {
(AId::Cx, EId::RadialGradient)
| (AId::Cy, EId::RadialGradient)
| (AId::R, EId::RadialGradient)
| (AId::Fx, EId::RadialGradient)
| (AId::Fy, EId::RadialGradient)
| (AId::GradientUnits, EId::LinearGradient)
| (AId::GradientUnits, EId::RadialGradient)
| (AId::SpreadMethod, EId::LinearGradient)
| (AId::SpreadMethod, EId::RadialGradient)
| (AId::GradientTransform, EId::LinearGradient)
| (AId::GradientTransform, EId::RadialGradient) => {
resolve_rg_attr(&link, aid, def_value)
}
_ => def_value
}
}
fn resolve_patt_attr(
node: &Node,
aid: AId,
def_value: Option<AValue>,
) -> Option<AValue> {
if node.has_attribute(aid) {
return node.attributes().get_value(aid).cloned();
}
let link = match node.attributes().get_value(AId::Href) {
Some(&AValue::Link(ref link)) => link.clone(),
_ => {
return match node.attributes().get_value(aid) {
Some(v) => Some(v.clone()),
None => def_value,
};
}
};
match link.tag_id() {
Some(EId::Pattern) => resolve_patt_attr(&link, aid, def_value),
_ => def_value,
}
}
fn resolve_filter_attr(
node: &Node,
aid: AId,
def_value: Option<AValue>,
) -> Option<AValue> {
if node.has_attribute(aid) {
return node.attributes().get_value(aid).cloned();
}
let link = match node.attributes().get_value(AId::Href) {
Some(&AValue::Link(ref link)) => link.clone(),
_ => {
return match node.attributes().get_value(aid) {
Some(v) => Some(v.clone()),
None => def_value,
};
}
};
match link.tag_id() {
Some(EId::Filter) => resolve_filter_attr(&link, aid, def_value),
_ => def_value,
}
}
fn prepare_focal(node: &mut Node) {
let (new_fx, new_fy) = {
let attrs = node.attributes();
let cx = attrs.get_number(AId::Cx).unwrap();
let cy = attrs.get_number(AId::Cy).unwrap();
let r = attrs.get_number(AId::R).unwrap();
let fx = attrs.get_number(AId::Fx).unwrap();
let fy = attrs.get_number(AId::Fy).unwrap();
let max_r = r - r * 0.001;
let mut line = Line::new(cx, cy, fx, fy);
if line.length() > max_r {
line.set_length(max_r);
}
(line.x2, line.y2)
};
node.set_attribute((AId::Fx, new_fx));
node.set_attribute((AId::Fy, new_fy));
}