// THIS FILE IS PART OF SVG PROJECT
// THE SVG PROJECT IS AN OPENSOURCE LIBRARY LICENSED UNDER THE MS-PL License.
// COPYRIGHT (C) svg-net. ALL RIGHTS RESERVED.
// GITHUB: https://github.com/svg-net/SVG
using AntdUI.Svg.Transforms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
namespace AntdUI.Svg
{
///
/// The base class of which all SVG elements are derived from.
///
public abstract partial class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
{
internal const int StyleSpecificity_PresAttribute = 0;
internal const int StyleSpecificity_InlineStyle = 1 << 16;
internal SvgElement _parent;
private string _elementName;
private SvgAttributeCollection _attributes;
private EventHandlerList _eventHandlers;
private SvgElementCollection _children;
private Region _graphicsClip;
private Matrix _graphicsMatrix;
private SvgCustomAttributeCollection _customAttributes;
private List _nodes = new List();
private Dictionary> _styles = new Dictionary>();
public void AddStyle(string name, string value, int specificity)
{
SortedDictionary rules;
if (!_styles.TryGetValue(name, out rules))
{
rules = new SortedDictionary();
_styles[name] = rules;
}
while (rules.ContainsKey(specificity)) specificity++;
rules[specificity] = value;
}
public void FlushStyles()
{
if (_styles.Any())
{
var styles = new Dictionary>();
foreach (var s in _styles)
{
if (!SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, OwnerDocument, isStyle: true))
{
styles.Add(s.Key, s.Value);
}
}
_styles = styles;
}
}
///
/// Gets the name of the element.
///
protected internal string ElementName
{
get
{
if (string.IsNullOrEmpty(_elementName))
{
_elementName = ClassName;
}
return _elementName;
}
internal set { _elementName = value; }
}
public virtual string ClassName => string.Empty;
///
/// Gets or sets the color of this element which drives the currentColor property.
///
[SvgAttribute("color", true)]
public virtual SvgPaintServer Color
{
get { return (Attributes["color"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)Attributes["color"]; }
set { Attributes["color"] = value; }
}
///
/// Gets or sets the content of the element.
///
private string _content;
public virtual string Content
{
get
{
return _content;
}
set
{
if (_content != null)
{
var oldVal = _content;
_content = value;
if (_content != oldVal)
OnContentChanged(new ContentEventArgs { Content = value });
}
else
{
_content = value;
OnContentChanged(new ContentEventArgs { Content = value });
}
}
}
///
/// Gets an of all events belonging to the element.
///
protected virtual EventHandlerList Events
{
get { return _eventHandlers; }
}
///
/// Gets a collection of all child objects.
///
public virtual SvgElementCollection Children
{
get { return _children; }
}
public IList Nodes
{
get { return _nodes; }
}
public IEnumerable Descendants()
{
return AsEnumerable().Descendants();
}
private IEnumerable AsEnumerable()
{
yield return this;
}
///
/// Gets a value to determine whether the element has children.
///
public virtual bool HasChildren()
{
return (Children.Count > 0);
}
///
/// Gets the parent .
///
/// An if one exists; otherwise null.
public virtual SvgElement Parent
{
get { return _parent; }
}
public IEnumerable Parents
{
get
{
var curr = this;
while (curr.Parent != null)
{
curr = curr.Parent;
yield return curr;
}
}
}
public IEnumerable ParentsAndSelf
{
get
{
var curr = this;
yield return curr;
while (curr.Parent != null)
{
curr = curr.Parent;
yield return curr;
}
}
}
///
/// Gets the owner .
///
public virtual SvgDocument OwnerDocument
{
get
{
if (this is SvgDocument)
{
return this as SvgDocument;
}
else
{
if (Parent != null)
return Parent.OwnerDocument;
else
return null;
}
}
}
///
/// Gets a collection of element attributes.
///
protected internal virtual SvgAttributeCollection Attributes
{
get
{
if (_attributes == null)
{
_attributes = new SvgAttributeCollection(this);
}
return _attributes;
}
}
///
/// Gets a collection of custom attributes
///
public SvgCustomAttributeCollection CustomAttributes
{
get { return _customAttributes; }
}
///
/// Applies the required transforms to .
///
/// The to be transformed.
protected internal virtual bool PushTransforms(ISvgRenderer renderer)
{
_graphicsMatrix = renderer.Transform;
_graphicsClip = renderer.GetClip();
// Return if there are no transforms
if (Transforms == null || Transforms.Count == 0) return true;
Matrix transformMatrix = renderer.Transform.Clone();
var bound = renderer.GetBoundable();
foreach (SvgTransform transformation in Transforms)
{
transformMatrix.Multiply(transformation.Matrix(bound.Bounds.Width, bound.Bounds.Height));
}
renderer.Transform = transformMatrix;
return true;
}
///
/// Removes any previously applied transforms from the specified .
///
/// The that should have transforms removed.
protected internal virtual void PopTransforms(ISvgRenderer renderer)
{
renderer.Transform = _graphicsMatrix;
_graphicsMatrix = null;
renderer.SetClip(_graphicsClip);
_graphicsClip = null;
}
///
/// Applies the required transforms to .
///
/// The to be transformed.
void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
{
PushTransforms(renderer);
}
///
/// Removes any previously applied transforms from the specified .
///
/// The that should have transforms removed.
void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
{
PopTransforms(renderer);
}
///
/// Gets or sets the element transforms.
///
/// The transforms.
[SvgAttribute("transform")]
public SvgTransformCollection Transforms
{
get { return (Attributes.GetAttribute("transform")); }
set
{
var old = Transforms;
if (old != null)
old.TransformChanged -= Attributes_AttributeChanged;
value.TransformChanged += Attributes_AttributeChanged;
Attributes["transform"] = value;
}
}
///
/// Transforms the given rectangle with the set transformation, if any.
/// Can be applied to bounds calculated without considering the element transformation.
///
/// The rectangle to be transformed.
/// The transformed rectangle, or the original rectangle if no transformation exists.
protected RectangleF TransformedBounds(RectangleF bounds)
{
if (Transforms != null && Transforms.Count > 0)
{
var path = new GraphicsPath();
path.AddRectangle(bounds);
path.Transform(Transforms.GetMatrix());
return path.GetBounds();
}
return bounds;
}
///
/// Gets or sets the ID of the element.
///
/// The ID is already used within the .
[SvgAttribute("id")]
public string ID
{
get { return Attributes.GetAttribute("id"); }
set
{
SetAndForceUniqueID(value, false);
}
}
///
/// Gets or sets the space handling.
///
/// The space handling.
[SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
public virtual XmlSpaceHandling SpaceHandling
{
get { return (Attributes["space"] == null) ? XmlSpaceHandling.@default : (XmlSpaceHandling)Attributes["space"]; }
set { Attributes["space"] = value; }
}
public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action logElementOldIDNewID = null)
{
// Don't do anything if it hasn't changed
if (string.Compare(ID, value) == 0)
{
return;
}
if (OwnerDocument != null)
{
OwnerDocument.IdManager.Remove(this);
}
Attributes["id"] = value;
if (OwnerDocument != null)
{
OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
}
}
///
/// Only used by the ID Manager
///
///
internal void ForceUniqueID(string newID)
{
Attributes["id"] = newID;
}
///
/// 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)
{
}
///
/// Fired when an Element was added to the children of this Element
///
public event EventHandler ChildAdded;
///
/// 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)
{
AddElement(child, index);
SvgElement sibling = null;
if (index < (Children.Count - 1))
{
sibling = Children[index + 1];
}
var handler = ChildAdded;
if (handler != null)
{
handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
}
}
///
/// 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)
{
RemoveElement(child);
}
///
/// Initializes a new instance of the class.
///
public SvgElement()
{
_children = new SvgElementCollection(this);
_eventHandlers = new EventHandlerList();
_customAttributes = new SvgCustomAttributeCollection(this);
Transforms = new SvgTransformCollection();
//subscribe to attribute events
Attributes.AttributeChanged += Attributes_AttributeChanged;
CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
}
//dispatch attribute event
void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
{
OnAttributeChanged(e);
}
///
/// Renders this element to the .
///
/// The that the element should use to render itself.
public void RenderElement(ISvgRenderer renderer)
{
Render(renderer);
}
/// Derrived classes may decide that the element should not be written. For example, the text element shouldn't be written if it's empty.
public virtual bool ShouldWriteElement()
{
//Write any element who has a name.
return (ElementName != String.Empty);
}
///
/// Renders the and contents to the specified object.
///
/// The object to render to.
protected virtual void Render(ISvgRenderer renderer)
{
PushTransforms(renderer);
RenderChildren(renderer);
PopTransforms(renderer);
}
///
/// Renders the children of this .
///
/// The to render the child s to.
protected virtual void RenderChildren(ISvgRenderer renderer)
{
foreach (SvgElement element in Children)
{
element.Render(renderer);
}
}
///
/// Renders the and contents to the specified object.
///
/// The object to render to.
void ISvgElement.Render(ISvgRenderer renderer)
{
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)
{
// Skip to avoid double calculate Symbol element
// symbol element is only referenced by use element
// So here we need to skip when it is directly considered
if (child is Svg.Document_Structure.SvgSymbol)
continue;
if (child is SvgVisualElement)
{
if (!(child is SvgGroup))
{
var childPath = ((SvgVisualElement)child).Path(null);
if (childPath != null)
{
childPath = (GraphicsPath)childPath.Clone();
if (child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix());
if (childPath.PointCount > 0) path.AddPath(childPath, false);
}
}
}
if (!(child is SvgPaintServer)) AddPaths(child, path);
}
}
///
/// Recursive method to add up the paths of all children
///
///
///
protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
{
var ret = new GraphicsPath();
foreach (var child in elem.Children)
{
if (child is SvgVisualElement)
{
if (!(child is SvgGroup))
{
var childPath = ((SvgVisualElement)child).Path(renderer);
// Non-group element can have child element which we have to consider. i.e tspan in text element
if (child.Children.Count > 0)
childPath.AddPath(GetPaths(child, renderer), false);
if (childPath != null && childPath.PointCount > 0)
{
childPath = (GraphicsPath)childPath.Clone();
if (child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix());
ret.AddPath(childPath, false);
}
}
else
{
var childPath = GetPaths(child, renderer);
if (childPath != null && childPath.PointCount > 0)
{
if (child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix());
ret.AddPath(childPath, false);
}
}
}
}
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 MemberwiseClone();
}
protected void OnAttributeChanged(AttributeEventArgs args)
{
}
///
/// Fired when an Atrribute of this Element has changed
///
public event EventHandler ContentChanged;
protected void OnContentChanged(ContentEventArgs args)
{
var handler = ContentChanged;
if (handler != null)
{
handler(this, args);
}
}
}
public class SVGArg : EventArgs
{
public string SessionID;
}
///
/// Describes the Attribute which was set
///
public class AttributeEventArgs : SVGArg
{
public string Attribute;
public object Value;
}
///
/// Content of this whas was set
///
public class ContentEventArgs : SVGArg
{
public string Content;
}
///
/// Describes the Attribute which was set
///
public class ChildAddedEventArgs : SVGArg
{
public SvgElement NewChild;
public SvgElement BeforeSibling;
}
///
/// Represents a string argument
///
public class StringArg : SVGArg
{
public string s;
}
public class MouseScrollArg : SVGArg
{
public int Scroll;
///
/// Alt modifier key pressed
///
public bool AltKey;
///
/// Shift modifier key pressed
///
public bool ShiftKey;
///
/// Control modifier key pressed
///
public bool CtrlKey;
}
public interface ISvgNode
{
string Content { get; }
}
/// This interface mostly indicates that a node is not to be drawn when rendering the SVG.
public interface ISvgDescriptiveElement
{
}
internal interface ISvgElement
{
SvgElement Parent { get; }
SvgElementCollection Children { get; }
IList Nodes { get; }
string ClassName { get; }
void Render(ISvgRenderer renderer);
}
}