use {
AttributeId,
AttributeValue,
ElementId,
ElementType,
Node,
};
use types::{
Length,
LengthUnit,
};
pub fn fix_rect_attributes(node: &mut Node) {
debug_assert!(node.is_tag_name(ElementId::Rect));
fix_len(node, AttributeId::Width, Length::zero());
fix_len(node, AttributeId::Height, Length::zero());
rm_negative_len(node, AttributeId::Rx);
rm_negative_len(node, AttributeId::Ry);
}
#[cfg(test)]
mod test_rect {
use super::*;
use {Document, ElementId, WriteToString};
macro_rules! test {
($name:ident, $in_text:expr, $out_text:expr) => (
#[test]
fn $name() {
let doc = Document::from_str($in_text).unwrap();
for mut node in doc.descendants().svg().filter(|n| n.is_tag_name(ElementId::Rect)) {
fix_rect_attributes(&mut node);
}
assert_eq_text!(doc.to_string_with_opt(&write_opt_for_tests!()), $out_text);
}
)
}
test!(fix_rect_1,
"<svg>
<rect/>
<rect width='-1' height='-1'/>
<rect width='30'/>
<rect height='40'/>
<rect width='-30'/>
<rect height='-40'/>
<rect width='0'/>
<rect height='0'/>
</svg>",
"<svg>
<rect height='0' width='0'/>
<rect height='0' width='0'/>
<rect height='0' width='30'/>
<rect height='40' width='0'/>
<rect height='0' width='0'/>
<rect height='0' width='0'/>
<rect height='0' width='0'/>
<rect height='0' width='0'/>
</svg>
");
test!(fix_rect_2,
"<svg>
<rect height='50' width='40'/>
<rect height='50' rx='-5' width='40'/>
<rect height='50' ry='-5' width='40'/>
<rect height='50' rx='-5' ry='-5' width='40'/>
</svg>",
"<svg>
<rect height='50' width='40'/>
<rect height='50' width='40'/>
<rect height='50' width='40'/>
<rect height='50' width='40'/>
</svg>
");
}
pub fn fix_poly_attributes(node: &mut Node) {
debug_assert!(node.is_tag_name(ElementId::Polyline) || node.is_tag_name(ElementId::Polygon));
let mut attrs_data = node.attributes_mut();
let mut is_empty = false;
if let Some(points_value) = attrs_data.get_value_mut(AttributeId::Points) {
if let AttributeValue::NumberList(ref mut p) = *points_value {
if p.is_empty() {
is_empty = true;
} else if p.len() % 2 != 0 {
p.pop();
if p.is_empty() {
is_empty = true;
}
}
}
}
if is_empty {
attrs_data.remove(AttributeId::Points);
}
}
#[cfg(test)]
mod test_poly {
use super::*;
use {Document, ElementId, WriteToString};
macro_rules! test {
($name:ident, $in_text:expr, $out_text:expr) => (
#[test]
fn $name() {
let doc = Document::from_str($in_text).unwrap();
for mut node in doc.descendants().svg()
.filter(|n| n.is_tag_name(ElementId::Polygon) || n.is_tag_name(ElementId::Polyline)) {
fix_poly_attributes(&mut node);
}
assert_eq_text!(doc.to_string_with_opt(&write_opt_for_tests!()), $out_text);
}
)
}
test!(fix_polyline_1,
"<svg>
<polyline points='5 6 7'/>
<polyline points='5'/>
<polyline points=''/>
<polyline/>
</svg>",
"<svg>
<polyline points='5 6'/>
<polyline/>
<polyline/>
<polyline/>
</svg>
");
}
pub fn fix_stop_attributes(node: &Node) {
debug_assert!(node.is_gradient());
let mut prev_offset = 0.0;
for mut child in node.children() {
let av = child.attributes().get_value(AttributeId::Offset).cloned();
let mut offset = match av {
Some(AttributeValue::Length(n)) => {
if n.unit == LengthUnit::None {
n.num
} else {
unreachable!("'offset' must be resolved")
}
}
_ => unreachable!("'offset' must be resolved"),
};
if offset < 0.0 {
offset = 0.0;
} else if offset > 1.0 {
offset = 1.0;
}
if offset < prev_offset {
offset = prev_offset;
}
child.set_attribute((AttributeId::Offset, Length::new_number(offset)));
prev_offset = offset;
}
}
#[cfg(test)]
mod test_stop {
use super::*;
use {Document, WriteToString, ElementType};
use postproc::resolve_stop_attributes;
macro_rules! test {
($name:ident, $in_text:expr, $out_text:expr) => (
#[test]
fn $name() {
let doc = Document::from_str($in_text).unwrap();
resolve_stop_attributes(&doc).unwrap();
for node in doc.descendants().svg().filter(|n| n.is_gradient()) {
fix_stop_attributes(&node);
}
assert_eq_text!(doc.to_string_with_opt(&write_opt_for_tests!()), $out_text);
}
)
}
test!(fix_stop_1,
"<svg>
<linearGradient>
<stop offset='-1'/>
<stop offset='0.4'/>
<stop offset='0.3'/>
<stop offset='10'/>
<stop offset='0.5'/>
</linearGradient>
</svg>",
"<svg>
<linearGradient>
<stop offset='0'/>
<stop offset='0.4'/>
<stop offset='0.4'/>
<stop offset='1'/>
<stop offset='1'/>
</linearGradient>
</svg>
");
}
fn fix_len(node: &mut Node, id: AttributeId, new_len: Length) {
if node.has_attribute(id) {
fix_negative_len(node, id, new_len);
} else {
node.set_attribute((id, new_len));
}
}
fn fix_negative_len(node: &mut Node, id: AttributeId, new_len: Length) {
let av = node.attributes().get_value(id).cloned();
if let Some(AttributeValue::Length(l)) = av {
if l.num.is_sign_negative() {
node.set_attribute((id, new_len));
}
}
}
fn rm_negative_len(node: &mut Node, id: AttributeId) {
let av = node.attributes().get_value(id).cloned();
if let Some(AttributeValue::Length(l)) = av {
if l.num.is_sign_negative() {
node.remove_attribute(id);
}
}
}