use svgdom;
use tree;
use super::prelude::*;
pub fn convert(
node: &svgdom::Node,
opt: &Options,
tree: &mut tree::Tree,
) {
let ref attrs = node.attributes();
let rect = super::convert_rect(attrs);
if !(rect.width > 0.0 && rect.height > 0.0) {
warn!("Filter '{}' has an invalid region. Skipped.", node.id());
return;
}
let children = try_opt!(collect_children(node, opt), ());
tree.append_to_defs(
tree::NodeKind::Filter(tree::Filter {
id: node.id().clone(),
units: super::convert_element_units(&attrs, AId::FilterUnits),
primitive_units: super::convert_element_units(&attrs, AId::PrimitiveUnits),
rect,
children,
})
);
}
fn collect_children(
filter: &svgdom::Node,
opt: &Options,
) -> Option<Vec<tree::FilterPrimitive>> {
let mut children = Vec::new();
for child in filter.children() {
let kind = match child.tag_id() {
Some(EId::FeGaussianBlur) => {
convert_fe_gaussian_blur(&child)
}
Some(EId::FeOffset) => {
convert_fe_offset(&child)
}
Some(EId::FeBlend) => {
convert_fe_blend(&child)
}
Some(EId::FeFlood) => {
convert_fe_flood(&child)
}
Some(EId::FeComposite) => {
convert_fe_composite(&child)
}
Some(EId::FeMerge) => {
convert_fe_merge(&child)
}
Some(EId::FeTile) => {
tree::FilterKind::FeTile
}
Some(EId::FeImage) => {
convert_fe_image(&child, opt)
}
Some(_) => {
warn!("Filter with '{}' child is not supported.", child.tag_name());
continue;
}
None => continue,
};
if let Some(fe) = convert_primitive(&child, kind) {
children.push(fe);
}
}
if !children.is_empty() {
Some(children)
} else {
None
}
}
fn convert_primitive(
fe: &svgdom::Node,
kind: tree::FilterKind,
) -> Option<tree::FilterPrimitive> {
let attrs = fe.attributes();
let filter_input = if let Some(s) = attrs.get_str(AId::In) {
Some(parse_in(s))
} else {
None
};
let color_interpolation = super::convert_color_interpolation(
&attrs, AId::ColorInterpolationFilters, tree::ColorInterpolation::LinearRGB
);
Some(tree::FilterPrimitive {
x: attrs.get_number(AId::X),
y: attrs.get_number(AId::Y),
width: attrs.get_number(AId::Width),
height: attrs.get_number(AId::Height),
color_interpolation,
filter_input,
filter_result: attrs.get_str(AId::Result).map(|s| s.to_string()),
kind,
})
}
fn convert_fe_gaussian_blur(fe: &svgdom::Node) -> tree::FilterKind {
let attrs = fe.attributes();
let std_dev_list = attrs.get_number_list(AId::StdDeviation).cloned();
let (mut std_dev_x, mut std_dev_y) = match std_dev_list {
Some(list) => {
if list.len() == 1 {
(list[0], list[0])
} else if list.len() == 2 {
(list[0], list[1])
} else {
(0.0, 0.0)
}
}
None => {
(0.0, 0.0)
}
};
if std_dev_x.is_sign_negative() { std_dev_x = 0.0; }
if std_dev_y.is_sign_negative() { std_dev_y = 0.0; }
tree::FilterKind::FeGaussianBlur(tree::FeGaussianBlur {
std_dev_x: std_dev_x.into(),
std_dev_y: std_dev_y.into(),
})
}
fn convert_fe_offset(fe: &svgdom::Node) -> tree::FilterKind {
let attrs = fe.attributes();
tree::FilterKind::FeOffset(tree::FeOffset {
dx: attrs.get_number_or(AId::Dx, 0.0),
dy: attrs.get_number_or(AId::Dy, 0.0),
})
}
fn convert_fe_blend(fe: &svgdom::Node) -> tree::FilterKind {
let attrs = fe.attributes();
let mode = match attrs.get_str_or(AId::Mode, "normal") {
"multiply" => tree::FeBlendMode::Multiply,
"screen" => tree::FeBlendMode::Screen,
"darken" => tree::FeBlendMode::Darken,
"lighten" => tree::FeBlendMode::Lighten,
_ => tree::FeBlendMode::Normal,
};
let filter_input2 = Some(parse_in(attrs.get_str_or(AId::In2, "SourceGraphic")));
tree::FilterKind::FeBlend(tree::FeBlend {
mode,
filter_input2,
})
}
fn convert_fe_flood(fe: &svgdom::Node) -> tree::FilterKind {
let attrs = fe.attributes();
let color = attrs.get_color(AId::FloodColor).unwrap_or(tree::Color::black());
let opacity = f64_bound(0.0, attrs.get_number_or(AId::FloodOpacity, 1.0), 1.0);
tree::FilterKind::FeFlood(tree::FeFlood {
color,
opacity: tree::Opacity::new(opacity),
})
}
fn convert_fe_composite(fe: &svgdom::Node) -> tree::FilterKind {
let attrs = fe.attributes();
let operator = match attrs.get_str_or(AId::Operator, "over") {
"in" => tree::FeCompositeOperator::In,
"out" => tree::FeCompositeOperator::Out,
"atop" => tree::FeCompositeOperator::Atop,
"xor" => tree::FeCompositeOperator::Xor,
"arithmetic" => tree::FeCompositeOperator::Arithmetic,
_ => tree::FeCompositeOperator::Over,
};
let filter_input2 = Some(parse_in(attrs.get_str_or(AId::In2, "SourceGraphic")));
tree::FilterKind::FeComposite(tree::FeComposite {
operator,
filter_input2,
})
}
fn convert_fe_merge(fe: &svgdom::Node) -> tree::FilterKind {
let mut inputs = Vec::new();
for child in fe.children() {
let attrs = child.attributes();
let filter_input = if let Some(s) = attrs.get_str(AId::In) {
Some(parse_in(s))
} else {
None
};
inputs.push(filter_input);
}
tree::FilterKind::FeMerge(tree::FeMerge {
inputs,
})
}
fn convert_fe_image(
fe: &svgdom::Node,
opt: &Options,
) -> tree::FilterKind {
let ref attrs = fe.attributes();
let aspect = super::convert_aspect(attrs);
let href = match attrs.get_value(AId::Href) {
Some(&AValue::String(ref s)) => s,
_ => {
warn!("The 'feImage' element lacks the 'xlink:href' attribute. Skipped.");
return tree::FilterKind::FeImage(tree::FeImage {
aspect,
data: tree::FeImageKind::None,
});
}
};
let (img_data, format) = match super::image::get_href_data(href, opt.path.as_ref()) {
Some((data, format)) => (data, format),
None => {
return tree::FilterKind::FeImage(tree::FeImage {
aspect,
data: tree::FeImageKind::None,
});
}
};
tree::FilterKind::FeImage(tree::FeImage {
aspect: super::convert_aspect(attrs),
data: tree::FeImageKind::Image(img_data, format),
})
}
fn parse_in(s: &str) -> tree::FilterInput {
match s {
"SourceGraphic" => tree::FilterInput::SourceGraphic,
"SourceAlpha" => tree::FilterInput::SourceAlpha,
"BackgroundImage" => tree::FilterInput::BackgroundImage,
"BackgroundAlpha" => tree::FilterInput::BackgroundAlpha,
"FillPaint" => tree::FilterInput::FillPaint,
"StrokePaint" => tree::FilterInput::StrokePaint,
_ => tree::FilterInput::Reference(s.to_string())
}
}