use svgdom;
use tree;
use tree::prelude::*;
use super::prelude::*;
use super::{
fill,
stroke,
};
pub fn convert(
text_elem: &svgdom::Node,
mut parent: tree::Node,
rtree: &mut tree::Tree,
) {
let attrs = text_elem.attributes();
let text_node = parent.append_kind(tree::NodeKind::Text(tree::Text {
id: text_elem.id().clone(),
transform: attrs.get_transform(AId::Transform).unwrap_or_default(),
rotate: attrs.get_number_list(AId::Rotate).cloned(),
}));
convert_chunks(text_elem, text_node, rtree);
}
fn convert_chunks(
text_elem: &svgdom::Node,
mut parent: tree::Node,
rtree: &mut tree::Tree,
) {
let ref root_attrs = text_elem.attributes();
let mut chunk_node = parent.append_kind(tree::NodeKind::TextChunk(tree::TextChunk {
x: root_attrs.get_number_list(AId::X).cloned(),
y: root_attrs.get_number_list(AId::Y).cloned(),
dx: root_attrs.get_number_list(AId::Dx).cloned(),
dy: root_attrs.get_number_list(AId::Dy).cloned(),
anchor: conv_text_anchor(root_attrs),
}));
for tspan in text_elem.children() {
debug_assert!(tspan.is_tag_name(EId::Tspan));
let text = if let Some(node) = tspan.first_child() {
node.text().clone()
} else {
continue;
};
let ref attrs = tspan.attributes();
let x = attrs.get_number_list(AId::X).cloned();
let y = attrs.get_number_list(AId::Y).cloned();
let dx = attrs.get_number_list(AId::Dx).cloned();
let dy = attrs.get_number_list(AId::Dy).cloned();
if x.is_some() || y.is_some() || dx.is_some() || dy.is_some() {
if chunk_node.children().count() > 0 {
chunk_node = parent.append_kind(tree::NodeKind::TextChunk(tree::TextChunk {
x,
y,
dx,
dy,
anchor: conv_text_anchor(attrs),
}));
} else {
if let tree::NodeKind::TextChunk(ref mut d) = *chunk_node.borrow_mut() {
if x.is_some() { d.x = x; }
if y.is_some() { d.y = y; }
if dx.is_some() { d.dx = dx; }
if dy.is_some() { d.dy = dy; }
d.anchor = conv_text_anchor(attrs);
}
}
}
let fill = fill::convert(rtree, attrs, true);
let stroke = stroke::convert(rtree, attrs, true);
let decoration = conv_tspan_decoration2(rtree, text_elem, &tspan);
chunk_node.append_kind(tree::NodeKind::TSpan(Box::new(tree::TSpan {
fill,
stroke,
font: convert_font(attrs),
decoration,
text,
})));
}
debug_assert!(chunk_node.children().count() > 0);
}
struct TextDecoTypes {
has_underline: bool,
has_overline: bool,
has_line_through: bool,
}
fn conv_text_decoration(node: &svgdom::Node) -> TextDecoTypes {
debug_assert!(node.is_tag_name(EId::Text));
let attrs = node.attributes();
let text = attrs.get_str_or(AId::TextDecoration, "");
TextDecoTypes {
has_underline: text.contains("underline"),
has_overline: text.contains("overline"),
has_line_through: text.contains("linethrough"),
}
}
fn conv_tspan_decoration(tspan: &svgdom::Node) -> TextDecoTypes {
debug_assert!(tspan.is_tag_name(EId::Tspan));
let attrs = tspan.attributes();
let has_attr = |decoration_id: &str| {
if let Some(id) = attrs.get_str(AId::TextDecoration) {
if id == decoration_id {
return true;
}
}
false
};
TextDecoTypes {
has_underline: has_attr("underline"),
has_overline: has_attr("overline"),
has_line_through: has_attr("line-through"),
}
}
fn conv_tspan_decoration2(
rtree: &tree::Tree,
node: &svgdom::Node,
tspan: &svgdom::Node
) -> tree::TextDecoration {
let text_dec = conv_text_decoration(node);
let tspan_dec = conv_tspan_decoration(tspan);
let gen_style = |in_tspan: bool, in_text: bool| {
let n = if in_tspan {
tspan.clone()
} else if in_text {
node.clone()
} else {
return None;
};
let ref attrs = n.attributes();
let fill = fill::convert(rtree, attrs, true);
let stroke = stroke::convert(rtree, attrs, true);
Some(tree::TextDecorationStyle {
fill,
stroke,
})
};
tree::TextDecoration {
underline: gen_style(tspan_dec.has_underline, text_dec.has_underline),
overline: gen_style(tspan_dec.has_overline, text_dec.has_overline),
line_through: gen_style(tspan_dec.has_line_through, text_dec.has_line_through),
}
}
fn conv_text_anchor(attrs: &svgdom::Attributes) -> tree::TextAnchor {
let av = attrs.get_str_or(AId::TextAnchor, "start");
match av {
"start" => tree::TextAnchor::Start,
"middle" => tree::TextAnchor::Middle,
"end" => tree::TextAnchor::End,
_ => tree::TextAnchor::Start,
}
}
fn convert_font(attrs: &svgdom::Attributes) -> tree::Font {
let style = attrs.get_str_or(AId::FontStyle, "normal");
let style = match style {
"normal" => tree::FontStyle::Normal,
"italic" => tree::FontStyle::Italic,
"oblique" => tree::FontStyle::Oblique,
_ => tree::FontStyle::Normal,
};
let variant = attrs.get_str_or(AId::FontVariant, "normal");
let variant = match variant {
"normal" => tree::FontVariant::Normal,
"small-caps" => tree::FontVariant::SmallCaps,
_ => tree::FontVariant::Normal,
};
let weight = attrs.get_str_or(AId::FontWeight, "normal");
let weight = match weight {
"normal" => tree::FontWeight::W400,
"bold" => tree::FontWeight::W700,
"100" => tree::FontWeight::W100,
"200" => tree::FontWeight::W200,
"300" => tree::FontWeight::W300,
"400" => tree::FontWeight::W400,
"500" => tree::FontWeight::W500,
"600" => tree::FontWeight::W600,
"700" => tree::FontWeight::W700,
"800" => tree::FontWeight::W800,
"900" => tree::FontWeight::W900,
"bolder" | "lighter" => {
warn!("'bolder' and 'lighter' font-weight must be already resolved.");
tree::FontWeight::W400
}
_ => tree::FontWeight::W400,
};
let stretch = attrs.get_str_or(AId::FontStretch, "normal");
let stretch = match stretch {
"normal" => tree::FontStretch::Normal,
"wider" => tree::FontStretch::Wider,
"narrower" => tree::FontStretch::Narrower,
"ultra-condensed" => tree::FontStretch::UltraCondensed,
"extra-condensed" => tree::FontStretch::ExtraCondensed,
"condensed" => tree::FontStretch::Condensed,
"semi-condensed" => tree::FontStretch::SemiCondensed,
"semi-expanded" => tree::FontStretch::SemiExpanded,
"expanded" => tree::FontStretch::Expanded,
"extra-expanded" => tree::FontStretch::ExtraExpanded,
"ultra-expanded" => tree::FontStretch::UltraExpanded,
_ => tree::FontStretch::Normal,
};
let size = attrs.get_number_or(AId::FontSize, ::DEFAULT_FONT_SIZE);
debug_assert!(size > 0.0);
let family = attrs.get_str_or(AId::FontFamily, ::DEFAULT_FONT_FAMILY).to_owned();
tree::Font {
family,
size,
style,
variant,
weight,
stretch,
}
}