use svgdom::{
AspectRatio,
};
use super::prelude::*;
pub fn resolve_linear_gradient_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::LinearGradient) {
check_attr(node, AId::GradientUnits,
Some(AValue::from("objectBoundingBox")));
check_attr(node, AId::SpreadMethod, Some(AValue::from("pad")));
check_attr(node, AId::X1, Some(AValue::from(0.0)));
check_attr(node, AId::Y1, Some(AValue::from(0.0)));
check_attr(node, AId::X2, Some(AValue::from(1.0)));
check_attr(node, AId::Y2, Some(AValue::from(0.0)));
check_attr(node, AId::GradientTransform, None);
}
}
pub fn resolve_radial_gradient_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::RadialGradient) {
check_attr(node, AId::GradientUnits,
Some(AValue::from("objectBoundingBox")));
check_attr(node, AId::SpreadMethod, Some(AValue::from("pad")));
check_attr(node, AId::Cx, Some(AValue::from(0.5)));
check_attr(node, AId::Cy, Some(AValue::from(0.5)));
check_attr(node, AId::R, Some(AValue::from(0.5)));
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();
let cy = node.attributes().get_value(AId::Cy).cloned();
check_attr(node, AId::Fx, cx);
check_attr(node, AId::Fy, cy);
prepare_focal(node);
check_attr(node, AId::GradientTransform, None);
}
}
pub fn resolve_pattern_attributes(doc: &Document) {
for node in &mut gen_order(doc, EId::Pattern) {
check_attr(node, AId::PatternUnits, Some(AValue::from("objectBoundingBox")));
check_attr(node, AId::PatternContentUnits, Some(AValue::from("userSpaceOnUse")));
check_attr(node, AId::PatternTransform, None);
check_attr(node, AId::X, Some(AValue::from(0.0)));
check_attr(node, AId::Y, Some(AValue::from(0.0)));
check_attr(node, AId::Width, Some(AValue::from(0.0)));
check_attr(node, AId::Height, Some(AValue::from(0.0)));
check_attr(node, AId::PreserveAspectRatio, Some(AValue::from(AspectRatio::default())));
check_attr(node, AId::ViewBox, None);
}
}
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 check_attr(node: &mut Node, id: AId, def_value: Option<AValue>) {
if !node.has_attribute(id) {
let eid = node.tag_id().unwrap();
let v = match eid {
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),
_ => 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(("xlink", 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(("xlink", 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(("xlink", 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 prepare_focal(node: &mut Node) {
let mut attrs = node.attributes_mut();
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 (new_fx, new_fy) = _prepare_focal(cx, cy, r, fx, fy);
attrs.insert_from(AId::Fx, new_fx);
attrs.insert_from(AId::Fy, new_fy);
}
fn _prepare_focal(cx: f64, cy: f64, r: f64, fx: f64, fy: f64) -> (f64, f64) {
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)
}