From f10b0336705d53a70a02cc322ffc8349e482f3c1 Mon Sep 17 00:00:00 2001 From: Eric Domke Date: Thu, 24 Jul 2014 12:53:34 -0400 Subject: [PATCH] Various Parsing Bug Fixes - Implemented .Net 3.5 support mostly by removing ISvgEventCaller functionality in .Net 3.5 build - Added ability to parse colors such as rgb(#%, #%, #%) - Fix issues with calculating the next coordinate after a close figure and a relative move - Allow the std. deviation in a gaussian blur to be a float. - Allow "none" for a unit collection - Allow gradients to reference other gradients defined later in the SVG - Fix href attribute bug on the SVG use element - Adding a descendants convenience methods (similar to the Xml to Linq API) - Removed non-functional property from the SvgDescription class to encourage use of the Content property instead --- Samples/SVGViewer/SvgViewer.Designer.cs | 141 +++++++------- Samples/SvgExamples/Workflow.svg | 181 ++++++++++++++++++ Source/DataTypes/SvgFontWeight.cs | 13 +- Source/DataTypes/SvgUnitCollection.cs | 1 + Source/Document Structure/SvgDescription.cs | 11 +- Source/Document Structure/SvgTitle.cs | 7 +- Source/Document Structure/SvgUse.cs | 2 +- Source/Extensions.cs | 64 +++++++ .../feGaussianBlur/SvgGaussianBlur.cs | 27 +-- Source/Painting/SvgColourConverter.cs | 13 +- Source/Painting/SvgDeferredPaintServer.cs | 88 +++++++++ Source/Painting/SvgGradientServer.cs | 15 +- Source/Painting/SvgLinearGradientServer.cs | 1 + Source/Painting/SvgPaintServerFactory.cs | 15 +- Source/Painting/SvgRadialGradientServer.cs | 1 + Source/Paths/SvgClosePathSegment.cs | 8 + Source/Paths/SvgPathBuilder.cs | 5 +- Source/Svg.csproj | 2 + Source/SvgDocument.cs | 8 +- Source/SvgElement.cs | 17 +- Source/Text/SvgText.cs | 4 +- 21 files changed, 505 insertions(+), 119 deletions(-) create mode 100644 Samples/SvgExamples/Workflow.svg create mode 100644 Source/Extensions.cs create mode 100644 Source/Painting/SvgDeferredPaintServer.cs diff --git a/Samples/SVGViewer/SvgViewer.Designer.cs b/Samples/SVGViewer/SvgViewer.Designer.cs index c0b42f9..4b78ae3 100644 --- a/Samples/SVGViewer/SvgViewer.Designer.cs +++ b/Samples/SVGViewer/SvgViewer.Designer.cs @@ -28,74 +28,79 @@ /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SVGViewer)); - this.toolStrip1 = new System.Windows.Forms.ToolStrip(); - this.open = new System.Windows.Forms.ToolStripButton(); - this.svgImage = new System.Windows.Forms.PictureBox(); - this.openSvgFile = new System.Windows.Forms.OpenFileDialog(); - this.textBox1 = new System.Windows.Forms.TextBox(); - this.toolStrip1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.svgImage)).BeginInit(); - this.SuspendLayout(); - // - // toolStrip1 - // - this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.open}); - this.toolStrip1.Location = new System.Drawing.Point(0, 0); - this.toolStrip1.Name = "toolStrip1"; - this.toolStrip1.Size = new System.Drawing.Size(1060, 25); - this.toolStrip1.TabIndex = 0; - this.toolStrip1.Text = "toolStrip1"; - // - // open - // - this.open.Image = ((System.Drawing.Image)(resources.GetObject("open.Image"))); - this.open.ImageTransparentColor = System.Drawing.Color.Magenta; - this.open.Name = "open"; - this.open.Size = new System.Drawing.Size(53, 22); - this.open.Text = "Open"; - this.open.Click += new System.EventHandler(this.open_Click); - // - // svgImage - // - this.svgImage.Dock = System.Windows.Forms.DockStyle.Left; - this.svgImage.Location = new System.Drawing.Point(0, 25); - this.svgImage.Name = "svgImage"; - this.svgImage.Size = new System.Drawing.Size(565, 449); - this.svgImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; - this.svgImage.TabIndex = 1; - this.svgImage.TabStop = false; - // - // openSvgFile - // - this.openSvgFile.Filter = "Vector Graphics (*.svg)|*.svg"; - // - // textBox1 - // - this.textBox1.Location = new System.Drawing.Point(571, 25); - this.textBox1.MaxLength = 327670; - this.textBox1.Multiline = true; - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(477, 446); - this.textBox1.TabIndex = 2; - this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); - // - // SVGViewer - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1060, 474); - this.Controls.Add(this.textBox1); - this.Controls.Add(this.svgImage); - this.Controls.Add(this.toolStrip1); - this.Name = "SVGViewer"; - this.Text = "SVG Viewer"; - this.toolStrip1.ResumeLayout(false); - this.toolStrip1.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.svgImage)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SVGViewer)); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.open = new System.Windows.Forms.ToolStripButton(); + this.svgImage = new System.Windows.Forms.PictureBox(); + this.openSvgFile = new System.Windows.Forms.OpenFileDialog(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.toolStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.svgImage)).BeginInit(); + this.SuspendLayout(); + // + // toolStrip1 + // + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.open}); + this.toolStrip1.Location = new System.Drawing.Point(0, 0); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(1060, 25); + this.toolStrip1.TabIndex = 0; + this.toolStrip1.Text = "toolStrip1"; + // + // open + // + this.open.Image = ((System.Drawing.Image)(resources.GetObject("open.Image"))); + this.open.ImageTransparentColor = System.Drawing.Color.Magenta; + this.open.Name = "open"; + this.open.Size = new System.Drawing.Size(56, 22); + this.open.Text = "Open"; + this.open.Click += new System.EventHandler(this.open_Click); + // + // svgImage + // + this.svgImage.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.svgImage.Location = new System.Drawing.Point(0, 25); + this.svgImage.Name = "svgImage"; + this.svgImage.Size = new System.Drawing.Size(735, 449); + this.svgImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.svgImage.TabIndex = 1; + this.svgImage.TabStop = false; + // + // openSvgFile + // + this.openSvgFile.Filter = "Vector Graphics (*.svg)|*.svg"; + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(741, 25); + this.textBox1.MaxLength = 327670; + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(307, 446); + this.textBox1.TabIndex = 2; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // SVGViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1060, 474); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.svgImage); + this.Controls.Add(this.toolStrip1); + this.Name = "SVGViewer"; + this.Text = "SVG Viewer"; + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.svgImage)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + } #endregion diff --git a/Samples/SvgExamples/Workflow.svg b/Samples/SvgExamples/Workflow.svg new file mode 100644 index 0000000..0ecd70c --- /dev/null +++ b/Samples/SvgExamples/Workflow.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ClosedStart + + + +ClosedSubmit ECO + +ClosedMDM Review + + + +ClosedNeed Info + + + +ActiveECO Plan Approval + + + +PendingMDM Review + + + +PendingPrint/Drawing Update + + + +PendingECO Pre-Approval + + + +PendingInterim Workflow Resolution + + + +PendingDraft Changes + + + +PendingApproval Resolution + + + +PendingChange Review + + + +PendingChange Review + + + +PendingPre-Prod Approval Resolution + + + +PendingImpl. Plan Review + + + +PendingPre-Production Approval + + + +PendingEffectivity Wait + + + +PendingImpl. Approval + + + +PendingImpl. Approved + + + +PendingCancel + + + +PendingClose Change +Go +Cancel ECO +Submit +Unsubmit +iLink - Pre-Approval +Cancel ECO +Legacy Workflow +iLink - Pre-Approved +Pre-Production Workflow +Interim Workflow (Approval) +Interim Workflow (Prints) +Added Info +Re-Review +Approve ECO +Need more info +Reject +Prints Updated +Unsubmit +Approve +Reject +Unsubmit +Redo Approval +Submit for Review +Repeat Need Info +Repeat Approval +Reject +Approve +Rework +Approve +Unsubmit +Redo Approval +Approve Plan +Approve & Implement +Reject ECO +Approve & Wait +Req. Impl. Approval +Ready +Not Ready +Fully Impl/Close +Continue Wait + diff --git a/Source/DataTypes/SvgFontWeight.cs b/Source/DataTypes/SvgFontWeight.cs index 0fe1b0d..a5b4bf8 100644 --- a/Source/DataTypes/SvgFontWeight.cs +++ b/Source/DataTypes/SvgFontWeight.cs @@ -5,11 +5,10 @@ using System.Text; namespace Svg.DataTypes { - public enum SvgFontWeight - { - normal, - bold - } - - + public enum SvgFontWeight + { + inherit, + normal, + bold + } } diff --git a/Source/DataTypes/SvgUnitCollection.cs b/Source/DataTypes/SvgUnitCollection.cs index e3fa829..284dfe0 100644 --- a/Source/DataTypes/SvgUnitCollection.cs +++ b/Source/DataTypes/SvgUnitCollection.cs @@ -45,6 +45,7 @@ namespace Svg { if (value is string) { + if (string.Compare(((string)value).Trim(), "none", StringComparison.InvariantCultureIgnoreCase) == 0) return null; string[] points = ((string)value).Trim().Split(new char[] { ',', ' ', '\r', '\n', '\t' }, StringSplitOptions.RemoveEmptyEntries); SvgUnitCollection units = new SvgUnitCollection(); diff --git a/Source/Document Structure/SvgDescription.cs b/Source/Document Structure/SvgDescription.cs index 34006e9..d8bd349 100644 --- a/Source/Document Structure/SvgDescription.cs +++ b/Source/Document Structure/SvgDescription.cs @@ -9,17 +9,9 @@ namespace Svg [SvgElement("desc")] public class SvgDescription : SvgElement { - private string _text; - - public string Text - { - get { return this._text; } - set { this._text = value; } - } - public override string ToString() { - return this.Text; + return this.Content; } @@ -31,7 +23,6 @@ namespace Svg public override SvgElement DeepCopy() { var newObj = base.DeepCopy() as SvgDescription; - newObj.Text = this.Text; return newObj; } diff --git a/Source/Document Structure/SvgTitle.cs b/Source/Document Structure/SvgTitle.cs index 77d85ff..be755d3 100644 --- a/Source/Document Structure/SvgTitle.cs +++ b/Source/Document Structure/SvgTitle.cs @@ -7,7 +7,12 @@ namespace Svg { [SvgElement("title")] public class SvgTitle : SvgElement - { + { + public override string ToString() + { + return this.Content; + } + public override SvgElement DeepCopy() { return DeepCopy(); diff --git a/Source/Document Structure/SvgUse.cs b/Source/Document Structure/SvgUse.cs index bd9152a..8897f2f 100644 --- a/Source/Document Structure/SvgUse.cs +++ b/Source/Document Structure/SvgUse.cs @@ -13,7 +13,7 @@ namespace Svg { private Uri _referencedElement; - [SvgAttribute("xlink:href")] + [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)] public virtual Uri ReferencedElement { get { return this._referencedElement; } diff --git a/Source/Extensions.cs b/Source/Extensions.cs new file mode 100644 index 0000000..1a22889 --- /dev/null +++ b/Source/Extensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; + +namespace Svg +{ + public static class Extensions + { + public static IEnumerable Descendants(this IEnumerable source) where T : SvgElement + { + if (source == null) throw new ArgumentNullException("source"); + return GetDescendants(source, false); + } + private static IEnumerable GetAncestors(IEnumerable source, bool self) where T : SvgElement + { + foreach (var start in source) + { + if (start != null) + { + for (var elem = (self ? start : start.Parent) as SvgElement; elem != null; elem = (elem.Parent as SvgElement)) + { + yield return elem; + } + } + } + yield break; + } + private static IEnumerable GetDescendants(IEnumerable source, bool self) where T : SvgElement + { + var positons = new Stack(); + int currPos; + SvgElement currParent; + foreach (var start in source) + { + if (start != null) + { + if (self) yield return start; + + positons.Push(0); + currParent = start; + + while (positons.Count > 0) + { + currPos = positons.Pop(); + if (currPos < currParent.Children.Count) + { + yield return currParent.Children[currPos]; + currParent = currParent.Children[currPos]; + positons.Push(currPos + 1); + positons.Push(0); + } + else + { + currParent = currParent.Parent; + } + } + } + } + yield break; + } + } +} \ No newline at end of file diff --git a/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs b/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs index c5183b6..6690222 100644 --- a/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs +++ b/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs @@ -14,7 +14,7 @@ namespace Svg.FilterEffects [SvgElement("feGaussianBlur")] public class SvgGaussianBlur : SvgFilterPrimitive { - private int _stdDeviation; + private float _stdDeviation; private BlurType _blurType; private int[] _kernel; @@ -26,12 +26,13 @@ namespace Svg.FilterEffects { } - public SvgGaussianBlur(int stdDeviation) + public SvgGaussianBlur(float stdDeviation) : this(stdDeviation, BlurType.Both) { } - public SvgGaussianBlur(int stdDeviation, BlurType blurType) : base() + public SvgGaussianBlur(float stdDeviation, BlurType blurType) + : base() { _stdDeviation = stdDeviation; _blurType = blurType; @@ -42,13 +43,13 @@ namespace Svg.FilterEffects private void PreCalculate() { - int sz = _stdDeviation * 2 + 1; + int sz = (int)(_stdDeviation * 2 + 1); _kernel = new int[sz]; _multable = new int[sz, 256]; for (int i = 1; i <= _stdDeviation; i++) { - int szi = _stdDeviation - i; - int szj = _stdDeviation + i; + int szi = (int)(_stdDeviation - i); + int szj = (int)(_stdDeviation + i); _kernel[szj] = _kernel[szi] = (szi + 1) * (szi + 1); _kernelSum += (_kernel[szj] + _kernel[szi]); for (int j = 0; j < 256; j++) @@ -56,11 +57,11 @@ namespace Svg.FilterEffects _multable[szj, j] = _multable[szi, j] = _kernel[szj] * j; } } - _kernel[_stdDeviation] = (_stdDeviation + 1) * (_stdDeviation + 1); - _kernelSum += _kernel[_stdDeviation]; + _kernel[(int)_stdDeviation] = (int)((_stdDeviation + 1) * (_stdDeviation + 1)); + _kernelSum += _kernel[(int)_stdDeviation]; for (int j = 0; j < 256; j++) { - _multable[_stdDeviation, j] = _kernel[_stdDeviation] * j; + _multable[(int)_stdDeviation, j] = _kernel[(int)_stdDeviation] * j; } } @@ -104,7 +105,7 @@ namespace Svg.FilterEffects for (int i = 0; i < pixelCount; i++) { bsum = gsum = rsum = asum = 0; - read = i - _stdDeviation; + read = (int)(i - _stdDeviation); for (int z = 0; z < _kernel.Length; z++) { if (read < start) @@ -156,7 +157,7 @@ namespace Svg.FilterEffects index = 0; for (int i = 0; i < src.Height; i++) { - int y = i - _stdDeviation; + int y = (int)(i - _stdDeviation); start = y * src.Width; for (int j = 0; j < src.Width; j++) { @@ -223,12 +224,12 @@ namespace Svg.FilterEffects /// Gets or sets the radius of the blur (only allows for one value - not the two specified in the SVG Spec) /// [SvgAttribute("stdDeviation")] - public int StdDeviation + public float StdDeviation { get { return _stdDeviation; } set { - if (value < 1) + if (value <= 0) { throw new InvalidOperationException("Radius must be greater then 0"); } diff --git a/Source/Painting/SvgColourConverter.cs b/Source/Painting/SvgColourConverter.cs index e36d2cf..4c6d1a4 100644 --- a/Source/Painting/SvgColourConverter.cs +++ b/Source/Painting/SvgColourConverter.cs @@ -69,7 +69,18 @@ namespace Svg alphaValue = (int)alphaDecimal; } } - Color colorpart = System.Drawing.Color.FromArgb(alphaValue, int.Parse(values[0]), int.Parse(values[1]), int.Parse(values[2])); + + Color colorpart; + if (values[0].Trim().EndsWith("%")) + { + colorpart = System.Drawing.Color.FromArgb(alphaValue, (int)(255 * float.Parse(values[0].Trim().TrimEnd('%')) / 100f), + (int)(255 * float.Parse(values[1].Trim().TrimEnd('%')) / 100f), + (int)(255 * float.Parse(values[2].Trim().TrimEnd('%')) / 100f)); + } + else + { + colorpart = System.Drawing.Color.FromArgb(alphaValue, int.Parse(values[0]), int.Parse(values[1]), int.Parse(values[2])); + } return colorpart; } diff --git a/Source/Painting/SvgDeferredPaintServer.cs b/Source/Painting/SvgDeferredPaintServer.cs new file mode 100644 index 0000000..88172a3 --- /dev/null +++ b/Source/Painting/SvgDeferredPaintServer.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Svg +{ + /// + /// A wrapper for a paint server which isn't defined currently in the parse process, but + /// should be defined by the time the image needs to render. + /// + public class SvgDeferredPaintServer : SvgPaintServer + { + private bool _serverLoaded = false; + private SvgPaintServer _concreteServer; + + public SvgDocument Document { get; set; } + public string DeferredId { get; set; } + + public SvgDeferredPaintServer() { } + public SvgDeferredPaintServer(SvgDocument document, string id) + { + this.Document = document; + this.DeferredId = id; + } + + private void EnsureServer() + { + if (!_serverLoaded) + { + _concreteServer = this.Document.IdManager.GetElementById(this.DeferredId) as SvgPaintServer; + _serverLoaded = true; + } + } + + public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, float opacity) + { + EnsureServer(); + return _concreteServer.GetBrush(styleOwner, opacity); + } + + public override SvgElement DeepCopy() + { + return DeepCopy(); + } + + public override SvgElement DeepCopy() + { + var newObj = base.DeepCopy() as SvgDeferredPaintServer; + newObj.Document = this.Document; + newObj.DeferredId = this.DeferredId; + return newObj; + } + + public override bool Equals(object obj) + { + var other = obj as SvgDeferredPaintServer; + if (other == null) + return false; + + return this.Document == other.Document && this.DeferredId == other.DeferredId; + } + + public override int GetHashCode() + { + if (this.Document == null || this.DeferredId == null) return 0; + return this.Document.GetHashCode() ^ this.DeferredId.GetHashCode(); + } + + public override string ToString() + { + return (_serverLoaded ? _serverLoaded.ToString() : string.Format("deferred: {0}", this.DeferredId)); + } + + public static T TryGet(SvgPaintServer server) where T : SvgPaintServer + { + var deferred = server as SvgDeferredPaintServer; + if (deferred == null) + { + return server as T; + } + else + { + return deferred._concreteServer as T; + } + } + } +} \ No newline at end of file diff --git a/Source/Painting/SvgGradientServer.cs b/Source/Painting/SvgGradientServer.cs index 27d16b6..e904a17 100644 --- a/Source/Painting/SvgGradientServer.cs +++ b/Source/Painting/SvgGradientServer.cs @@ -13,7 +13,7 @@ namespace Svg { private SvgCoordinateUnits _gradientUnits; private SvgGradientSpreadMethod _spreadMethod; - private SvgGradientServer _inheritGradient; + private SvgPaintServer _inheritGradient; private List _stops; /// @@ -89,13 +89,12 @@ namespace Svg /// Gets or sets another gradient fill from which to inherit the stops from. /// [SvgAttribute("href")] - public SvgGradientServer InheritGradient + public SvgPaintServer InheritGradient { get { return this._inheritGradient; } set { this._inheritGradient = value; - this.InheritStops(); } } @@ -220,14 +219,12 @@ namespace Svg return blend; } - /// - // If this gradient contains no stops then it will search any inherited gradients for stops. - /// - protected virtual void InheritStops() + protected void LoadStops() { - if (this.Stops.Count == 0 && this.InheritGradient != null) + var core = SvgDeferredPaintServer.TryGet(_inheritGradient); + if (this.Stops.Count == 0 && core != null) { - _stops.AddRange(this.InheritGradient.Stops); + _stops.AddRange(core.Stops); } } diff --git a/Source/Painting/SvgLinearGradientServer.cs b/Source/Painting/SvgLinearGradientServer.cs index 517aa5a..9400c8a 100644 --- a/Source/Painting/SvgLinearGradientServer.cs +++ b/Source/Painting/SvgLinearGradientServer.cs @@ -81,6 +81,7 @@ namespace Svg public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) { + LoadStops(); if (IsInvalid) { return null; diff --git a/Source/Painting/SvgPaintServerFactory.cs b/Source/Painting/SvgPaintServerFactory.cs index 4c9bbee..a45a451 100644 --- a/Source/Painting/SvgPaintServerFactory.cs +++ b/Source/Painting/SvgPaintServerFactory.cs @@ -38,7 +38,18 @@ namespace Svg { return (SvgPaintServer)document.IdManager.GetElementById(value); } - else // Otherwise try and parse as colour + else if (value.StartsWith("#")) // Otherwise try and parse as colour + { + try + { + return new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Trim())); + } + catch + { + return new SvgDeferredPaintServer(document, value); + } + } + else { return new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Trim())); } @@ -49,7 +60,7 @@ namespace Svg if (value is string) { var s = (string) value; - if(String.Equals( s.Trim(), "none", StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(s)) + if (String.Equals(s.Trim(), "none", StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(s) || s.Trim().Length < 1) return SvgPaintServer.None; else return SvgPaintServerFactory.Create(s, (SvgDocument)context); diff --git a/Source/Painting/SvgRadialGradientServer.cs b/Source/Painting/SvgRadialGradientServer.cs index 61654fc..fafb951 100644 --- a/Source/Painting/SvgRadialGradientServer.cs +++ b/Source/Painting/SvgRadialGradientServer.cs @@ -97,6 +97,7 @@ namespace Svg public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) { + LoadStops(); var origin = CalculateOrigin(renderingElement); var centerPoint = CalculateCenterPoint(renderingElement, origin); diff --git a/Source/Paths/SvgClosePathSegment.cs b/Source/Paths/SvgClosePathSegment.cs index 3f028d0..d3909d7 100644 --- a/Source/Paths/SvgClosePathSegment.cs +++ b/Source/Paths/SvgClosePathSegment.cs @@ -8,6 +8,14 @@ namespace Svg.Pathing { public override void AddToPath(System.Drawing.Drawing2D.GraphicsPath graphicsPath) { + // Important for custom line caps. Force the path the close with an explicit line, not just an implicit close of the figure. + if (graphicsPath.PathPoints.Length > 1 && !graphicsPath.PathPoints[0].Equals(graphicsPath.PathPoints[graphicsPath.PathPoints.Length - 1])) + { + int i = graphicsPath.PathTypes.Length - 1; + while (i >= 0 && graphicsPath.PathTypes[i] > 0) i--; + if (i < 0) i = 0; + graphicsPath.AddLine(graphicsPath.PathPoints[graphicsPath.PathPoints.Length - 1], graphicsPath.PathPoints[i]); + } graphicsPath.CloseFigure(); } diff --git a/Source/Paths/SvgPathBuilder.cs b/Source/Paths/SvgPathBuilder.cs index 7595c91..f4bb240 100644 --- a/Source/Paths/SvgPathBuilder.cs +++ b/Source/Paths/SvgPathBuilder.cs @@ -222,9 +222,9 @@ namespace Svg { var lastSegment = segments.Last; - // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 + // if the last element is a SvgClosePathSegment the position of the previous move to should be used because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment) - lastSegment = segments[segments.Count - 2]; + lastSegment = segments.OfType().Last(); if (isRelativeX) { @@ -290,6 +290,7 @@ namespace Svg { if (value is string) { + if (string.IsNullOrEmpty((string)value)) return new SvgPathSegmentList(); return Parse((string)value); } diff --git a/Source/Svg.csproj b/Source/Svg.csproj index b5bd45c..9b7a51b 100644 --- a/Source/Svg.csproj +++ b/Source/Svg.csproj @@ -116,7 +116,9 @@ + + diff --git a/Source/SvgDocument.cs b/Source/SvgDocument.cs index 8952be1..4aa5188 100644 --- a/Source/SvgDocument.cs +++ b/Source/SvgDocument.cs @@ -241,7 +241,7 @@ namespace Svg element.Content = value.ToString(); // Reset content value for new element - value.Clear(); + value.Length = 0; } break; case XmlNodeType.CDATA: @@ -277,8 +277,10 @@ namespace Svg throw new ArgumentNullException("document"); } - Stream stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml)); - return Open(stream, null); + using (var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml))) + { + return Open(stream, null); + } } public static Bitmap OpenAsBitmap(string path) diff --git a/Source/SvgElement.cs b/Source/SvgElement.cs index f160771..4a5df1e 100644 --- a/Source/SvgElement.cs +++ b/Source/SvgElement.cs @@ -117,6 +117,15 @@ namespace Svg get { return this._children; } } + public IEnumerable Descendants() + { + return this.AsEnumerable().Descendants(); + } + private IEnumerable AsEnumerable() + { + yield return this; + } + /// /// Gets a value to determine whether the element has children. /// @@ -763,6 +772,7 @@ namespace Svg onmouseout = "" */ +#if Net4 /// /// Use this method to provide your implementation ISvgEventCaller which can register Actions /// and call them if one of the events occurs. Make sure, that your SvgElement has a unique ID. @@ -804,6 +814,7 @@ namespace Svg caller.UnregisterAction(rpcID + "onmouseout"); } } +#endif [SvgAttribute("onclick")] public event EventHandler Click; @@ -826,12 +837,14 @@ namespace Svg [SvgAttribute("onmouseout")] public event EventHandler MouseOut; +#if Net4 protected Action CreateMouseEventAction(Action eventRaiser) { return (x, y, button, clickCount, altKey, shiftKey, ctrlKey, sessionID) => eventRaiser(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID }); } - +#endif + //click protected void RaiseMouseClick(object sender, MouseArg e) { @@ -943,6 +956,7 @@ namespace Svg public SvgElement BeforeSibling; } +#if Net4 //deriving class registers event actions and calls the actions if the event occurs public interface ISvgEventCaller { @@ -957,6 +971,7 @@ namespace Svg void RegisterAction(string rpcID, Action action); void UnregisterAction(string rpcID); } +#endif /// /// Represents the state of the mouse at the moment the event occured. diff --git a/Source/Text/SvgText.cs b/Source/Text/SvgText.cs index ea27fdf..8f3cc33 100644 --- a/Source/Text/SvgText.cs +++ b/Source/Text/SvgText.cs @@ -313,7 +313,7 @@ namespace Svg { // Make sure the path is always null if there is no text //if there is a TSpan inside of this text element then path should not be null (even if this text is empty!) - if (string.IsNullOrWhiteSpace(this.Text) && this.Children.Where(x => x is SvgTextSpan).Select(x => x as SvgTextSpan).Count() == 0) + if ((string.IsNullOrEmpty(this.Text) || this.Text.Trim().Length < 1) && this.Children.Where(x => x is SvgTextSpan).Select(x => x as SvgTextSpan).Count() == 0) return _path = null; //NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF? // When an empty string is passed to GraphicsPath, it rises an InvalidArgumentException... @@ -462,6 +462,7 @@ namespace Svg } } +#if Net4 public override void RegisterEvents(ISvgEventCaller caller) { //register basic events @@ -480,6 +481,7 @@ namespace Svg caller.UnregisterAction(this.ID + "/onchange"); } +#endif public override SvgElement DeepCopy() { -- GitLab