using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using System.Xml.Serialization; using System.Xml; using System.Diagnostics; using Svg.Pathing; using Svg.Transforms; namespace Svg { /// /// Represents an SVG path element. /// [SvgElement("path")] public class SvgPath : SvgVisualElement { private SvgPathSegmentList _pathData; private GraphicsPath _path; private int _pathLength; /// /// Gets or sets a of path data. /// [SvgAttribute("d")] public SvgPathSegmentList PathData { get { return this._pathData; } set { this._pathData = value; this._pathData._owner = this; this.IsPathDirty = true; } } /// /// Gets or sets the length of the path. /// [SvgAttribute("pathLength")] public int PathLength { get { return this._pathLength; } set { this._pathLength = value; } } /// /// Gets or sets the marker (end cap) of the path. /// [SvgAttribute("marker-end")] public Uri MarkerEnd { get { return this.Attributes.GetAttribute("marker-end"); } set { this.Attributes["marker-end"] = value; } } /// /// Gets or sets the marker (start cap) of the path. /// [SvgAttribute("marker-start")] public Uri MarkerStart { get { return this.Attributes.GetAttribute("marker-start"); } set { this.Attributes["marker-start"] = value; } } /// /// Gets the for this element. /// public override GraphicsPath Path { get { if (this._path == null || this.IsPathDirty) { _path = new GraphicsPath(); foreach (SvgPathSegment segment in this.PathData) { segment.AddToPath(_path); } this.IsPathDirty = false; } return _path; } } internal void OnPathUpdated() { this.IsPathDirty = true; OnAttributeChanged(new AttributeEventArgs{ Attribute = "d", Value = this.PathData }); } /// /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered. /// protected override bool RequiresSmoothRendering { get { return true; } } /// /// Gets the bounds of the element. /// /// The bounds. public override System.Drawing.RectangleF Bounds { get { return this.Path.GetBounds(); } } /// /// Initializes a new instance of the class. /// public SvgPath() { this._pathData = new SvgPathSegmentList(); this._pathData._owner = this; } /// /// Renders the stroke of the to the specified /// /// The object to render to. protected internal override void RenderStroke(SvgRenderer renderer) { if (this.Stroke != null) { float strokeWidth = this.StrokeWidth.ToDeviceValue(this); using (var pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth)) { if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0) { /* divide by stroke width - GDI behaviour that I don't quite understand yet.*/ pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray(); } //hardcoded transformation matrix. I am not sure why this is not in proportion or rotated correctly (something to do with how the endcaps are determined in GDI) var transMatrix = new Matrix(); transMatrix.Rotate(-90f); transMatrix.Scale(.6f, .6f); if (this.MarkerStart != null) { var marker = this.OwnerDocument.GetElementById(this.MarkerStart.ToString()); var markerPath = marker.Path.Clone() as GraphicsPath; markerPath.Transform(transMatrix); pen.CustomStartCap = new CustomLineCap(markerPath, null); } if (this.MarkerEnd != null) { var marker = this.OwnerDocument.GetElementById(this.MarkerEnd.ToString()); var markerPath = marker.Path.Clone() as GraphicsPath; markerPath.Transform(transMatrix); pen.CustomEndCap = new CustomLineCap(markerPath, null); } renderer.DrawPath(pen, this.Path); } } } public override SvgElement DeepCopy() { return DeepCopy(); } public override SvgElement DeepCopy() { var newObj = base.DeepCopy() as SvgPath; foreach (var pathData in this.PathData) newObj.PathData.Add(pathData.Clone()); newObj.PathLength = this.PathLength; newObj.MarkerStart = this.MarkerStart; newObj.MarkerEnd = this.MarkerEnd; return newObj; } } }