using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Xml; using System.Linq; using Svg.Transforms; using System.Reflection; namespace Svg { /// /// The base class of which all SVG elements are derived from. /// public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable { //optimization protected class AttributeTuple { public MemberDescriptor Property; public SvgAttributeAttribute Attribute; } protected IEnumerable _svgAttributes; internal SvgElement _parent; private string _elementName; private SvgAttributeCollection _attributes; private EventHandlerList _eventHandlers; private SvgElementCollection _children; private static readonly object _loadEventKey = new object(); private Matrix _graphicsMatrix; private Dictionary _customAttributes; /// /// Gets the name of the element. /// protected internal string ElementName { get { if (string.IsNullOrEmpty(this._elementName)) { var attr = TypeDescriptor.GetAttributes(this).OfType().SingleOrDefault(); if (attr != null) { this._elementName = attr.ElementName; } } return this._elementName; } internal set { this._elementName = value; } } /// /// Gets or sets the content of the element. /// public virtual string Content { get; set; } /// /// Gets an of all events belonging to the element. /// protected virtual EventHandlerList Events { get { return this._eventHandlers; } } /// /// Occurs when the element is loaded. /// public event EventHandler Load { add { this.Events.AddHandler(_loadEventKey, value); } remove { this.Events.RemoveHandler(_loadEventKey, value); } } /// /// Gets a collection of all child . /// public virtual SvgElementCollection Children { get { return this._children; } } /// /// Gets a value to determine whether the element has children. /// public virtual bool HasChildren() { return (this.Children.Count > 0); } /// /// Gets the parent . /// /// An if one exists; otherwise null. public virtual SvgElement Parent { get { return this._parent; } } /// /// Gets the owner . /// public virtual SvgDocument OwnerDocument { get { if (Parent == null) { if (this is SvgDocument) { return (SvgDocument)this; } else { return null; } } else { return Parent.OwnerDocument; } } } /// /// Gets a collection of element attributes. /// protected internal virtual SvgAttributeCollection Attributes { get { if (this._attributes == null) { this._attributes = new SvgAttributeCollection(this); } return this._attributes; } } public Dictionary CustomAttributes { get { return this._customAttributes; } } /// /// Applies the required transforms to . /// /// The to be transformed. protected internal virtual void PushTransforms(SvgRenderer renderer) { _graphicsMatrix = renderer.Transform; // Return if there are no transforms if (this.Transforms == null || this.Transforms.Count == 0) { return; } Matrix transformMatrix = renderer.Transform; foreach (SvgTransform transformation in this.Transforms) { transformMatrix.Multiply(transformation.Matrix, MatrixOrder.Append); } renderer.Transform = transformMatrix; } /// /// Removes any previously applied transforms from the specified . /// /// The that should have transforms removed. protected internal virtual void PopTransforms(SvgRenderer renderer) { renderer.Transform = _graphicsMatrix; _graphicsMatrix = null; } /// /// Applies the required transforms to . /// /// The to be transformed. void ISvgTransformable.PushTransforms(SvgRenderer renderer) { this.PushTransforms(renderer); } /// /// Removes any previously applied transforms from the specified . /// /// The that should have transforms removed. void ISvgTransformable.PopTransforms(SvgRenderer renderer) { this.PopTransforms(renderer); } /// /// Gets or sets the element transforms. /// /// The transforms. [SvgAttribute("transform")] public SvgTransformCollection Transforms { get { return (this.Attributes.GetAttribute("Transforms") ?? new SvgTransformCollection()); } set { this.Attributes["Transforms"] = value; } } /// /// Gets or sets the ID of the element. /// /// The ID is already used within the . [SvgAttribute("id")] public string ID { get { return this.Attributes.GetAttribute("ID"); } set { // Don't do anything if it hasn't changed if (string.Compare(this.ID, value) == 0) { return; } if (this.OwnerDocument != null) { this.OwnerDocument.IdManager.Remove(this); } this.Attributes["ID"] = value; if (this.OwnerDocument != null) { this.OwnerDocument.IdManager.Add(this); } } } /// /// Called by the underlying when an element has been added to the /// collection. /// /// The that has been added. /// An representing the index where the element was added to the collection. protected virtual void AddElement(SvgElement child, int index) { } /// /// Calls the method with the specified parameters. /// /// The that has been added. /// An representing the index where the element was added to the collection. internal void OnElementAdded(SvgElement child, int index) { this.AddElement(child, index); } /// /// Called by the underlying when an element has been removed from the /// collection. /// /// The that has been removed. protected virtual void RemoveElement(SvgElement child) { } /// /// Calls the method with the specified as the parameter. /// /// The that has been removed. internal void OnElementRemoved(SvgElement child) { this.RemoveElement(child); } /// /// Initializes a new instance of the class. /// public SvgElement() { this._children = new SvgElementCollection(this); this._eventHandlers = new EventHandlerList(); this._elementName = string.Empty; this._customAttributes = new Dictionary(); //fill svg attribute description var search = TypeDescriptor.GetProperties(this).Cast() .Concat(TypeDescriptor.GetEvents(this).Cast()); _svgAttributes = from MemberDescriptor c in search let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute where attribute != null select new AttributeTuple { Property = a, Attribute = attribute }; } public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document) { throw new NotImplementedException(); } /// /// Renders this element to the . /// /// The that the element should use to render itself. public void RenderElement(SvgRenderer renderer) { this.Render(renderer); } public void WriteElement(XmlTextWriter writer) { this.Write(writer); } protected virtual void WriteStartElement(XmlTextWriter writer) { if (this.ElementName != String.Empty) { writer.WriteStartElement(this.ElementName); if (this.ElementName == "svg") { foreach (var ns in SvgAttributeAttribute.Namespaces) { if (string.IsNullOrEmpty(ns.Key)) writer.WriteAttributeString("xmlns", ns.Value); else writer.WriteAttributeString("xmlns:" + ns.Key, ns.Value); } writer.WriteAttributeString("version", "1.1"); } } this.WriteAttributes(writer); } protected virtual void WriteEndElement(XmlTextWriter writer) { if (this.ElementName != String.Empty) { writer.WriteEndElement(); } } protected virtual void WriteAttributes(XmlTextWriter writer) { foreach (var attr in _svgAttributes) { if (attr.Property.Converter.CanConvertTo(typeof(string))) { object propertyValue = attr.Property.GetValue(this); var forceWrite = false; if ((attr.Attribute.Name == "fill") && (Parent != null)) { object parentValue; if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue)) { if ((parentValue == propertyValue) || ((parentValue != null) && parentValue.Equals(propertyValue))) continue; forceWrite = true; } } if (propertyValue != null) { var type = propertyValue.GetType(); string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string)); if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite) { writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value); } } else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none' { string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string)); writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value); } } } //add the custom attributes foreach (var item in this._customAttributes) { writer.WriteAttributeString(item.Key, item.Value); } } private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue) { parentAttributeValue = null; attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1); var currentParent = Parent; var resolved = false; while (currentParent != null) { if (currentParent.Attributes.ContainsKey(attributeKey)) { resolved = true; parentAttributeValue = currentParent.Attributes[attributeKey]; if (parentAttributeValue != null) break; } currentParent = currentParent.Parent; } return resolved; } protected virtual void Write(XmlTextWriter writer) { if (this.ElementName != String.Empty) { this.WriteStartElement(writer); this.WriteChildren(writer); this.WriteEndElement(writer); } } protected virtual void WriteChildren(XmlTextWriter writer) { //write the content if(!String.IsNullOrEmpty(this.Content)) writer.WriteString(this.Content); //write all children foreach (SvgElement child in this.Children) { child.Write(writer); } } /// /// Renders the and contents to the specified object. /// /// The object to render to. protected virtual void Render(SvgRenderer renderer) { this.PushTransforms(renderer); this.RenderChildren(renderer); this.PopTransforms(renderer); } /// /// Renders the children of this . /// /// The to render the child s to. protected virtual void RenderChildren(SvgRenderer renderer) { foreach (SvgElement element in this.Children) { element.Render(renderer); } } /// /// Renders the and contents to the specified object. /// /// The object to render to. void ISvgElement.Render(SvgRenderer renderer) { this.Render(renderer); } /// /// Recursive method to add up the paths of all children /// /// /// protected void AddPaths(SvgElement elem, GraphicsPath path) { foreach(var child in elem.Children) { if (child is SvgVisualElement) { if(!(child is SvgGroup)) { var childPath = ((SvgVisualElement)child).Path; if (childPath != null) { childPath = (GraphicsPath)childPath.Clone(); if(child.Transforms != null) childPath.Transform(child.Transforms.GetMatrix()); path.AddPath(childPath, false); } } } AddPaths(child, path); } } /// /// Recursive method to add up the paths of all children /// /// /// protected GraphicsPath GetPaths(SvgElement elem) { var ret = new GraphicsPath(); foreach(var child in elem.Children) { if (child is SvgVisualElement) { if(!(child is SvgGroup)) { var childPath = ((SvgVisualElement)child).Path; if (childPath != null) { childPath = (GraphicsPath)childPath.Clone(); if(child.Transforms != null) childPath.Transform(child.Transforms.GetMatrix()); ret.AddPath(childPath, false); } } else { var childPath = GetPaths(child); if(child.Transforms != null) childPath.Transform(child.Transforms.GetMatrix()); } } } return ret; } /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// public virtual object Clone() { return this.MemberwiseClone(); } public abstract SvgElement DeepCopy(); public virtual SvgElement DeepCopy() where T : SvgElement, new() { var newObj = new T(); newObj.Content = this.Content; newObj.ElementName = this.ElementName; // if (this.Parent != null) // this.Parent.Children.Add(newObj); if (this.Transforms != null) { newObj.Transforms = new SvgTransformCollection(); foreach (var transform in this.Transforms) newObj.Transforms.Add(transform.Clone() as SvgTransform); } foreach (var child in this.Children) { newObj.Children.Add(child.DeepCopy()); } return newObj; } #region graphical EVENTS /* onfocusin = "" onfocusout = "" onactivate = "" onclick = "" onmousedown = "" onmouseup = "" onmouseover = "" onmousemove = "" onmouseout = "" */ [SvgAttribute("onclick")] public event EventHandler Click; protected void OnClick(float x, float y, int button) { var handler = Click; if(handler != null) { handler(this, new MouseArg { x=x, y=y, button=button}); } } #endregion graphical EVENTS } /// /// Represents the state of the mouse at the moment the event occured. /// public class MouseArg : EventArgs { public float x; public float y; /// /// 0 = left, 1 = middle, 2 = right /// public int button; } internal interface ISvgElement { SvgElement Parent {get;} SvgElementCollection Children { get; } void Render(SvgRenderer renderer); } }