use std::fmt;
use super::{
AttributeId,
Node,
ValueId,
WriteBuffer,
WriteOptions,
WriteToString,
};
use types::{
Color,
Length,
LengthUnit,
Transform
};
use types::path;
static PRESENTATION_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::AlignmentBaseline,
AttributeId::BaselineShift,
AttributeId::Clip,
AttributeId::ClipPath,
AttributeId::ClipRule,
AttributeId::Color,
AttributeId::ColorInterpolation,
AttributeId::ColorInterpolationFilters,
AttributeId::ColorProfile,
AttributeId::ColorRendering,
AttributeId::Cursor,
AttributeId::Direction,
AttributeId::Display,
AttributeId::DominantBaseline,
AttributeId::EnableBackground,
AttributeId::Fill,
AttributeId::FillOpacity,
AttributeId::FillRule,
AttributeId::Filter,
AttributeId::FloodColor,
AttributeId::FloodOpacity,
AttributeId::Font,
AttributeId::FontFamily,
AttributeId::FontSize,
AttributeId::FontSizeAdjust,
AttributeId::FontStretch,
AttributeId::FontStyle,
AttributeId::FontVariant,
AttributeId::FontWeight,
AttributeId::GlyphOrientationHorizontal,
AttributeId::GlyphOrientationVertical,
AttributeId::ImageRendering,
AttributeId::Kerning,
AttributeId::LetterSpacing,
AttributeId::LightingColor,
AttributeId::Marker,
AttributeId::MarkerEnd,
AttributeId::MarkerMid,
AttributeId::MarkerStart,
AttributeId::Mask,
AttributeId::Opacity,
AttributeId::Overflow,
AttributeId::PointerEvents,
AttributeId::ShapeRendering,
AttributeId::StopColor,
AttributeId::StopOpacity,
AttributeId::Stroke,
AttributeId::StrokeDasharray,
AttributeId::StrokeDashoffset,
AttributeId::StrokeLinecap,
AttributeId::StrokeLinejoin,
AttributeId::StrokeMiterlimit,
AttributeId::StrokeOpacity,
AttributeId::StrokeWidth,
AttributeId::TextAnchor,
AttributeId::TextDecoration,
AttributeId::TextRendering,
AttributeId::UnicodeBidi,
AttributeId::Visibility,
AttributeId::WordSpacing,
AttributeId::WritingMode,
];
static NON_INHERITABLE_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::AlignmentBaseline,
AttributeId::BaselineShift,
AttributeId::Clip,
AttributeId::ClipPath,
AttributeId::Display,
AttributeId::DominantBaseline,
AttributeId::EnableBackground,
AttributeId::Filter,
AttributeId::FloodColor,
AttributeId::FloodOpacity,
AttributeId::LightingColor,
AttributeId::Mask,
AttributeId::Opacity,
AttributeId::Overflow,
AttributeId::StopColor,
AttributeId::StopOpacity,
AttributeId::DominantBaseline,
AttributeId::TextDecoration,
AttributeId::UnicodeBidi,
AttributeId::Visibility,
];
static ANIMATION_EVENT_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::Onbegin,
AttributeId::Onend,
AttributeId::Onload,
AttributeId::Onrepeat,
];
static GRAPHICAL_EVENT_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::Onactivate,
AttributeId::Onclick,
AttributeId::Onfocusin,
AttributeId::Onfocusout,
AttributeId::Onload,
AttributeId::Onmousedown,
AttributeId::Onmousemove,
AttributeId::Onmouseout,
AttributeId::Onmouseover,
AttributeId::Onmouseup,
];
static DOCUMENT_EVENT_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::Onabort,
AttributeId::Onerror,
AttributeId::Onresize,
AttributeId::Onscroll,
AttributeId::Onunload,
AttributeId::Onzoom,
];
static CONDITIONAL_PROCESSING_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::RequiredExtensions,
AttributeId::RequiredFeatures,
AttributeId::SystemLanguage,
];
static CORE_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::XmlBase,
AttributeId::XmlLang,
AttributeId::XmlSpace,
];
static FILL_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::Fill,
AttributeId::FillOpacity,
AttributeId::FillRule,
];
static STROKE_ATTRIBUTES: &'static [AttributeId] = &[
AttributeId::Stroke,
AttributeId::StrokeDasharray,
AttributeId::StrokeDashoffset,
AttributeId::StrokeLinecap,
AttributeId::StrokeLinejoin,
AttributeId::StrokeMiterlimit,
AttributeId::StrokeOpacity,
AttributeId::StrokeWidth,
];
pub type NumberList = Vec<f64>;
pub type LengthList = Vec<Length>;
#[derive(Clone,PartialEq,Debug)]
#[allow(missing_docs)]
pub enum AttributeValue {
Color(Color),
Length(Length),
LengthList(LengthList),
Link(Node),
FuncLink(Node),
Number(f64),
NumberList(NumberList),
Path(path::Path),
PredefValue(ValueId),
String(String),
Transform(Transform),
}
impl<'a> From<&'a str> for AttributeValue {
fn from(value: &str) -> AttributeValue {
AttributeValue::String(value.to_owned())
}
}
impl From<String> for AttributeValue {
fn from(value: String) -> AttributeValue {
AttributeValue::String(value)
}
}
impl From<i32> for AttributeValue {
fn from(value: i32) -> AttributeValue {
AttributeValue::Number(value as f64)
}
}
impl From<f64> for AttributeValue {
fn from(value: f64) -> AttributeValue {
AttributeValue::Number(value)
}
}
impl From<NumberList> for AttributeValue {
fn from(value: NumberList) -> AttributeValue {
AttributeValue::NumberList(value)
}
}
impl From<Length> for AttributeValue {
fn from(value: Length) -> AttributeValue {
AttributeValue::Length(value)
}
}
impl From<(i32, LengthUnit)> for AttributeValue {
fn from(value: (i32, LengthUnit)) -> AttributeValue {
AttributeValue::Length(Length::new(value.0 as f64, value.1))
}
}
impl From<(f64, LengthUnit)> for AttributeValue {
fn from(value: (f64, LengthUnit)) -> AttributeValue {
AttributeValue::Length(Length::new(value.0, value.1))
}
}
impl From<LengthList> for AttributeValue {
fn from(value: LengthList) -> AttributeValue {
AttributeValue::LengthList(value)
}
}
impl From<Transform> for AttributeValue {
fn from(value: Transform) -> AttributeValue {
AttributeValue::Transform(value)
}
}
impl From<path::Path> for AttributeValue {
fn from(value: path::Path) -> AttributeValue {
AttributeValue::Path(value)
}
}
impl From<Color> for AttributeValue {
fn from(value: Color) -> AttributeValue {
AttributeValue::Color(value)
}
}
impl From<ValueId> for AttributeValue {
fn from(value: ValueId) -> AttributeValue {
AttributeValue::PredefValue(value)
}
}
macro_rules! impl_as_type {
($name:ident, $t:ident, $out:ty) => (
#[allow(missing_docs)]
pub fn $name(&self) -> Option<&$out> {
match *self {
AttributeValue::$t(ref v) => Some(v),
_ => None,
}
}
)
}
impl AttributeValue {
impl_as_type!(as_color, Color, Color);
impl_as_type!(as_length, Length, Length);
impl_as_type!(as_length_list, LengthList, LengthList);
impl_as_type!(as_link, Link, Node);
impl_as_type!(as_func_link, FuncLink, Node);
impl_as_type!(as_number, Number, f64);
impl_as_type!(as_number_list, NumberList, NumberList);
impl_as_type!(as_path, Path, path::Path);
impl_as_type!(as_predef_value, PredefValue, ValueId);
impl_as_type!(as_string, String, String);
impl_as_type!(as_transform, Transform, Transform);
pub fn default_value(id: AttributeId) -> Option<AttributeValue> {
macro_rules! some {
($expr:expr) => (Some(AttributeValue::from($expr)))
}
match id {
AttributeId::AlignmentBaseline
| AttributeId::ColorProfile
| AttributeId::ColorRendering
| AttributeId::Cursor
| AttributeId::DominantBaseline
| AttributeId::GlyphOrientationVertical
| AttributeId::ImageRendering
| AttributeId::Kerning
| AttributeId::ShapeRendering
| AttributeId::TextRendering => some!(ValueId::Auto),
AttributeId::ClipPath
| AttributeId::Filter
| AttributeId::FontSizeAdjust
| AttributeId::Marker
| AttributeId::MarkerEnd
| AttributeId::MarkerMid
| AttributeId::MarkerStart
| AttributeId::Mask
| AttributeId::Stroke
| AttributeId::StrokeDasharray
| AttributeId::TextDecoration => some!(ValueId::None),
AttributeId::FontStretch
| AttributeId::FontStyle
| AttributeId::FontVariant
| AttributeId::FontWeight
| AttributeId::LetterSpacing
| AttributeId::UnicodeBidi
| AttributeId::WordSpacing => some!(ValueId::Normal),
AttributeId::Fill
| AttributeId::FloodColor
| AttributeId::StopColor => some!(Color::new(0, 0, 0)),
AttributeId::FillOpacity
| AttributeId::FloodOpacity
| AttributeId::Opacity
| AttributeId::StopOpacity
| AttributeId::StrokeOpacity => some!(1.0),
AttributeId::ClipRule
| AttributeId::FillRule => some!(ValueId::Nonzero),
AttributeId::BaselineShift => some!(ValueId::Baseline),
AttributeId::ColorInterpolation => some!(ValueId::SRGB),
AttributeId::ColorInterpolationFilters => some!(ValueId::LinearRGB),
AttributeId::Direction => some!(ValueId::Ltr),
AttributeId::Display => some!(ValueId::Inline),
AttributeId::EnableBackground => some!(ValueId::Accumulate),
AttributeId::FontSize => some!(ValueId::Medium),
AttributeId::GlyphOrientationHorizontal => some!("0deg"),
AttributeId::LightingColor => some!(Color::new(255, 255, 255)),
AttributeId::StrokeDashoffset => some!((0.0, LengthUnit::None)),
AttributeId::StrokeLinecap => some!(ValueId::Butt),
AttributeId::StrokeLinejoin => some!(ValueId::Miter),
AttributeId::StrokeMiterlimit => some!((4.0, LengthUnit::None)),
AttributeId::StrokeWidth => some!((1.0, LengthUnit::None)),
AttributeId::TextAnchor => some!(ValueId::Start),
AttributeId::Visibility => some!(ValueId::Visible),
AttributeId::WritingMode => some!(ValueId::LrTb),
_ => None,
}
}
}
impl WriteBuffer for AttributeValue {
fn write_buf_opt(&self, opt: &WriteOptions, buf: &mut Vec<u8>) {
match *self {
AttributeValue::String(ref s) => {
buf.extend_from_slice(s.as_bytes());
},
AttributeValue::Number(ref n) => {
n.write_buf_opt(opt, buf);
},
AttributeValue::NumberList(ref list) => {
for (i, num) in list.iter().enumerate() {
num.write_buf_opt(opt, buf);
if i < list.len() - 1 {
buf.push(b' ');
}
}
},
AttributeValue::Length(ref l) => {
l.write_buf_opt(opt, buf);
},
AttributeValue::LengthList(ref list) => {
for (n, l) in list.iter().enumerate() {
l.write_buf_opt(opt, buf);
if n < list.len() - 1 {
buf.push(b' ');
}
}
},
AttributeValue::Transform(ref t) => {
t.write_buf_opt(opt, buf);
}
AttributeValue::Path(ref p) => {
p.write_buf_opt(opt, buf);
}
AttributeValue::Link(ref n) => {
buf.push(b'#');
buf.extend_from_slice(n.id().as_bytes());
},
AttributeValue::FuncLink(ref n) => {
buf.extend_from_slice(b"url(#");
buf.extend_from_slice(n.id().as_bytes());
buf.push(b')');
},
AttributeValue::Color(ref c) => {
c.write_buf_opt(opt, buf);
},
AttributeValue::PredefValue(ref v) => {
buf.extend_from_slice(v.name().as_bytes())
},
}
}
}
impl_display!(AttributeValue);
#[derive(PartialEq,Clone,Debug)]
pub struct Attribute {
pub id: AttributeId,
pub value: AttributeValue,
pub visible: bool,
}
macro_rules! impl_is_type {
($name:ident, $t:ident) => (
#[allow(missing_docs)]
pub fn $name(&self) -> bool {
match self.value {
AttributeValue::$t(_) => true,
_ => false,
}
}
)
}
impl Attribute {
pub fn new<T>(id: AttributeId, value: T) -> Attribute
where AttributeValue: From<T>
{
Attribute {
id: id,
value: AttributeValue::from(value),
visible: true,
}
}
pub fn default(id: AttributeId) -> Option<Attribute> {
match AttributeValue::default_value(id) {
Some(v) => Some(Attribute::new(id, v)),
None => None,
}
}
pub fn check_is_default(&self) -> bool {
match AttributeValue::default_value(self.id) {
Some(v) => self.value == v,
None => false,
}
}
pub fn is_presentation(&self) -> bool {
PRESENTATION_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_inheritable(&self) -> bool {
self.is_presentation() && NON_INHERITABLE_ATTRIBUTES.binary_search(&self.id).is_err()
}
pub fn is_animation_event(&self) -> bool {
ANIMATION_EVENT_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_graphical_event(&self) -> bool {
GRAPHICAL_EVENT_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_document_event(&self) -> bool {
DOCUMENT_EVENT_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_conditional_processing(&self) -> bool {
CONDITIONAL_PROCESSING_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_core(&self) -> bool {
CORE_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_fill(&self) -> bool {
FILL_ATTRIBUTES.binary_search(&self.id).is_ok()
}
pub fn is_stroke(&self) -> bool {
STROKE_ATTRIBUTES.binary_search(&self.id).is_ok()
}
impl_is_type!(is_color, Color);
impl_is_type!(is_length, Length);
impl_is_type!(is_length_list, LengthList);
impl_is_type!(is_link, Link);
impl_is_type!(is_func_link, FuncLink);
impl_is_type!(is_number, Number);
impl_is_type!(is_number_list, NumberList);
impl_is_type!(is_path, Path);
impl_is_type!(is_predef_value, PredefValue);
impl_is_type!(is_string, String);
impl_is_type!(is_transform, Transform);
}
fn write_quote(opt: &WriteOptions, out: &mut Vec<u8>) {
if opt.use_single_quote {
out.push(b'\'');
} else {
out.push(b'"');
}
}
impl WriteBuffer for Attribute {
fn write_buf_opt(&self, opt: &WriteOptions, buf: &mut Vec<u8>) {
let name = self.id.name();
buf.extend_from_slice(name.as_bytes());
buf.push(b'=');
write_quote(opt, buf);
self.value.write_buf_opt(opt, buf);
write_quote(opt, buf);
}
}
impl_display!(Attribute);