diff --git a/Basic Shapes/SvgGraphicsElement.cs b/Basic Shapes/SvgGraphicsElement.cs index 7f09b076d57731a6b2f28bdd298160247b6cfc8e..a9316cf20b2693a92333ea9478500d36d14606a7 100644 --- a/Basic Shapes/SvgGraphicsElement.cs +++ b/Basic Shapes/SvgGraphicsElement.cs @@ -13,10 +13,11 @@ namespace Svg /// /// The class that all SVG elements should derive from when they are to be rendered. /// - public abstract partial class SvgGraphicsElement : SvgElement, ISvgStylable + public abstract partial class SvgGraphicsElement : SvgElement, ISvgStylable, ISvgClipable { private bool _dirty; private bool _requiresSmoothRendering; + private Region _previousClip; /// /// Gets the for this element. @@ -40,6 +41,16 @@ namespace Svg set { this._dirty = value; } } + /// + /// Gets the associated if one has been specified. + /// + [SvgAttribute("clip-path")] + public virtual Uri ClipPath + { + get { return this.Attributes.GetAttribute("clip-path"); } + set { this.Attributes["clip-path"] = value; } + } + /// /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered. /// @@ -66,6 +77,7 @@ namespace Svg if (this.Path != null && this.Visible) { this.PushTransforms(renderer); + this.SetClip(renderer); // If this element needs smoothing enabled turn anti aliasing on if (this.RequiresSmoothRendering) @@ -104,8 +116,38 @@ namespace Svg renderer.SmoothingMode = SmoothingMode.Default; } + this.ResetClip(renderer); this.PopTransforms(renderer); } } + + protected internal virtual void SetClip(SvgRenderer renderer) + { + if (this.ClipPath != null) + { + SvgClipPath clipPath = this.OwnerDocument.GetElementById(this.ClipPath.ToString()); + this._previousClip = renderer.Clip; + renderer.SetClip(clipPath.GetClipRegion()); + } + } + + protected internal virtual void ResetClip(SvgRenderer renderer) + { + if (this.ClipPath != null) + { + renderer.SetClip(this._previousClip); + this._previousClip = null; + } + } + + void ISvgClipable.SetClip(SvgRenderer renderer) + { + this.SetClip(renderer); + } + + void ISvgClipable.ResetClip(SvgRenderer renderer) + { + this.ResetClip(renderer); + } } } \ No newline at end of file diff --git a/Clipping and Masking/ISvgClipable.cs b/Clipping and Masking/ISvgClipable.cs index 2536579554ac62205f81f3e5e26682f2add2be2a..ff3b7dd43ab449e09206ca8186d98a87f537e8db 100644 --- a/Clipping and Masking/ISvgClipable.cs +++ b/Clipping and Masking/ISvgClipable.cs @@ -8,8 +8,11 @@ namespace Svg { public interface ISvgClipable { - SvgClipPath ClipPath { get; set; } - void SetClip(Graphics graphics); - void ResetClip(Graphics graphics); + /// + /// Gets or sets the ID of the associated if one has been specified. + /// + Uri ClipPath { get; set; } + void SetClip(SvgRenderer renderer); + void ResetClip(SvgRenderer renderer); } } \ No newline at end of file diff --git a/Clipping and Masking/SvgClipPath.cs b/Clipping and Masking/SvgClipPath.cs index 09779298f5e99e7ee2b3900b0b3a0e0f69393170..3d856feb18052c56aae0dd5e59f41956438651e4 100644 --- a/Clipping and Masking/SvgClipPath.cs +++ b/Clipping and Masking/SvgClipPath.cs @@ -6,12 +6,18 @@ using System.Drawing.Drawing2D; namespace Svg { + /// + /// + /// public sealed class SvgClipPath : SvgElement { private SvgCoordinateUnits _clipPathUnits; private bool _pathDirty; private Region _region; + /// + /// + /// [SvgAttribute("clipPathUnits")] public SvgCoordinateUnits ClipPathUnits { @@ -27,7 +33,11 @@ namespace Svg this._clipPathUnits = SvgCoordinateUnits.ObjectBoundingBox; } - private Region GetClipRegion() + /// + /// Gets this 's region to be clipped. + /// + /// A new containing the area to be clipped. + protected internal Region GetClipRegion() { if (_region == null || _pathDirty) { @@ -44,18 +54,23 @@ namespace Svg return _region; } + /// + /// + /// + /// + /// private void ComplementRegion(Region region, SvgElement element) { SvgGraphicsElement graphicsElement = element as SvgGraphicsElement; - if (graphicsElement != null) + if (graphicsElement != null && graphicsElement.Path != null) { region.Complement(graphicsElement.Path); } foreach (SvgElement child in element.Children) { - ComplementRegion(region, element); + ComplementRegion(region, child); } } @@ -71,6 +86,10 @@ namespace Svg this._pathDirty = true; } + /// + /// Renders the and contents to the specified object. + /// + /// The object to render to. protected override void Render(SvgRenderer renderer) { // Do nothing diff --git a/DataTypes/SvgUnit.cs b/DataTypes/SvgUnit.cs index 40befbd4b5293e15613b9affe0bd9aa6a04414b6..3346754b7b3c8a96486e699758e68d423883c134 100644 --- a/DataTypes/SvgUnit.cs +++ b/DataTypes/SvgUnit.cs @@ -90,6 +90,10 @@ namespace Svg switch (this.Type) { + case SvgUnitType.Em: + float points = (float)(this.Value * 9); + _deviceValue = (points / 72) * ppi; + break; case SvgUnitType.Centimeter: _deviceValue = (float)((this.Value / cmInInch) * ppi); break; diff --git a/Document Structure/SvgDocument.cs b/Document Structure/SvgDocument.cs index 8e55f346c1527dbdd6aeca336159a82e7c8c16b8..9056f929baa71ac5c6c189eb28bfd376c436a6f3 100644 --- a/Document Structure/SvgDocument.cs +++ b/Document Structure/SvgDocument.cs @@ -93,6 +93,16 @@ namespace Svg return IdManager.GetElementById(id); } + /// + /// Retrieves the with the specified ID. + /// + /// A containing the ID of the element to find. + /// An of one exists with the specified ID; otherwise false. + public virtual TSvgElement GetElementById(string id) where TSvgElement : SvgElement + { + return (this.GetElementById(id) as TSvgElement); + } + /// /// Opens the document at the specified path and loads the contents. /// diff --git a/Document Structure/SvgUse.cs b/Document Structure/SvgUse.cs index b9ed59b6115f376cf875e9a2f96ad6d496ad8b3d..515caf634a11705625b48d93ea3940a4d213953a 100644 --- a/Document Structure/SvgUse.cs +++ b/Document Structure/SvgUse.cs @@ -46,7 +46,11 @@ namespace Svg public override System.Drawing.Drawing2D.GraphicsPath Path { - get { return null; } + get + { + SvgGraphicsElement element = (SvgGraphicsElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement); + return (element != null) ? element.Path : null; + } } public override System.Drawing.RectangleF Bounds @@ -54,6 +58,17 @@ namespace Svg get { return new System.Drawing.RectangleF(); } } + public override SvgElementCollection Children + { + get + { + SvgElement element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement); + SvgElementCollection elements = new SvgElementCollection(this, true); + elements.Add(element); + return elements; + } + } + protected override void Render(SvgRenderer renderer) { this.PushTransforms(renderer); diff --git a/Svg.csproj b/Svg.csproj index 6e5ef311aaafd4f5505df07002ebe190ae174027..b83ba77850d076f5b78ab20344f92fc4c6afb017 100644 --- a/Svg.csproj +++ b/Svg.csproj @@ -135,8 +135,11 @@ + + + diff --git a/SvgElementCollection.cs b/SvgElementCollection.cs index d2bb9d8fdee7a1cbbb8a6ef16e669539711cbd15..dcf61ae7de088529f4657eea23d59f3b467fa4f7 100644 --- a/SvgElementCollection.cs +++ b/SvgElementCollection.cs @@ -11,12 +11,19 @@ namespace Svg { private List _elements; private SvgElement _owner; + private bool _mock; /// /// Initialises a new instance of an class. /// /// The owner of the collection. internal SvgElementCollection(SvgElement owner) + : this(owner, false) + { + + } + + internal SvgElementCollection(SvgElement owner, bool mock) { if (owner == null) { @@ -25,6 +32,7 @@ namespace Svg this._elements = new List(); this._owner = owner; + this._mock = mock; } /// @@ -65,14 +73,19 @@ namespace Svg public void Add(SvgElement item) { - if (this._owner.OwnerDocument != null) + if (!this._mock) { - this._owner.OwnerDocument.IdManager.Add(item); + if (this._owner.OwnerDocument != null) + { + this._owner.OwnerDocument.IdManager.Add(item); + } + + item._parent = this._owner; } - this._elements.Add(item); - item._parent = this._owner; item._parent.OnElementAdded(item, this.Count - 1); + + this._elements.Add(item); } public void Clear() @@ -111,11 +124,15 @@ namespace Svg if (removed) { this._owner.OnElementRemoved(item); - item._parent = null; - if (this._owner.OwnerDocument != null) + if (!this._mock) { - this._owner.OwnerDocument.IdManager.Remove(item); + item._parent = null; + + if (this._owner.OwnerDocument != null) + { + this._owner.OwnerDocument.IdManager.Remove(item); + } } } diff --git a/SvgElementIdManager.cs b/SvgElementIdManager.cs index 2a77531ddd174cf5dfd0f3e00ab4bbe66544c35b..1956501922db0dd3df0d6d795bd8e19ee76ed627 100644 --- a/SvgElementIdManager.cs +++ b/SvgElementIdManager.cs @@ -20,11 +20,20 @@ namespace Svg /// An of one exists with the specified ID; otherwise false. public virtual SvgElement GetElementById(string id) { + if (id.StartsWith("url(")) + { + id = id.Substring(4); + id = id.TrimEnd(')'); + } if (id.StartsWith("#")) { id = id.Substring(1); } - return this._idValueMap[id]; + + SvgElement element = null; + this._idValueMap.TryGetValue(id, out element); + + return element; } public virtual SvgElement GetElementById(Uri uri) diff --git a/SvgRenderer.cs b/SvgRenderer.cs index 155390110bbb436e936d188827f9367235b83719..c216e39fd0a7ec83a0266f76760436bfd6b95240 100644 --- a/SvgRenderer.cs +++ b/SvgRenderer.cs @@ -19,6 +19,12 @@ namespace Svg { } + public Region Clip + { + get { return this._innerGraphics.Clip; } + set { this._innerGraphics.Clip = value; } + } + /// /// Creates a new from the specified . /// @@ -30,6 +36,11 @@ namespace Svg return renderer; } + public void SetClip(Region region) + { + this._innerGraphics.SetClip(region, CombineMode.Union); + } + public void FillPath(Brush brush, GraphicsPath path) { this._innerGraphics.FillPath(brush, path); diff --git a/Transforms/SvgRotate.cs b/Transforms/SvgRotate.cs index 6d2310b5ac69bc833494decb5dfa98fa2b7139ec..4743aae822b6833b27a845bc690f8fa90708d485 100644 --- a/Transforms/SvgRotate.cs +++ b/Transforms/SvgRotate.cs @@ -4,7 +4,7 @@ using System.Text; namespace Svg.Transforms { - public class SvgRotate : SvgTransform + public sealed class SvgRotate : SvgTransform { private float angle; diff --git a/Transforms/SvgScale.cs b/Transforms/SvgScale.cs index 9be303a8b8903a103161f5c6edf0a617c6ac69c2..1961e17e79342132d6d9f3fe598acb3ec2a07a33 100644 --- a/Transforms/SvgScale.cs +++ b/Transforms/SvgScale.cs @@ -5,7 +5,7 @@ using System.Drawing.Drawing2D; namespace Svg.Transforms { - public class SvgScale : SvgTransform + public sealed class SvgScale : SvgTransform { private float scaleFactorX; private float scaleFactorY; diff --git a/Transforms/SvgTransformConverter.cs b/Transforms/SvgTransformConverter.cs index 15ae0b42391c8528560a923ea1aca307180f418f..1bcff7f1c47c1b8bea2537b89fa3c3045baa4189 100644 --- a/Transforms/SvgTransformConverter.cs +++ b/Transforms/SvgTransformConverter.cs @@ -20,7 +20,7 @@ namespace Svg.Transforms if (transforms[i] == ')') { yield return transforms.Substring(transformEnd, i - transformEnd + 1).Trim(); - transformEnd = i+1; + transformEnd = i + 1; } } } @@ -57,7 +57,7 @@ namespace Svg.Transforms switch (transformName) { case "translate": - string[] coords = contents.Split(new char[]{',', ' '}, StringSplitOptions.RemoveEmptyEntries); + string[] coords = contents.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (coords.Length != 2) { @@ -92,6 +92,51 @@ namespace Svg.Transforms transformList.Add(new SvgScale(sx)); } + break; + case "matrix": + string[] points = contents.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (points.Length != 6) + { + throw new FormatException("Matrix transforms must be in the format 'matrix(m11, m12, m21, m22, dx, dy)'"); + } + + List mPoints = new List(); + foreach (string point in points) + { + mPoints.Add(float.Parse(point.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture)); + } + + transformList.Add(new SvgMatrix(mPoints)); + break; + case "shear": + string[] shears = contents.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (shears.Length == 0 || shears.Length > 2) + { + throw new FormatException("Shear transforms must be in the format 'shear(x [,y])'"); + } + + float hx = float.Parse(shears[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture); + + if (shears.Length > 1) + { + float hy = float.Parse(shears[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture); + transformList.Add(new SvgShear(hx, hy)); + } + else + { + transformList.Add(new SvgShear(hx)); + } + + break; + case "skewX": + float ax = float.Parse(contents, NumberStyles.Float, CultureInfo.InvariantCulture); + transformList.Add(new SvgSkew(ax, 0)); + break; + case "skewY": + float ay = float.Parse(contents, NumberStyles.Float, CultureInfo.InvariantCulture); + transformList.Add(new SvgSkew(0, ay)); break; } } diff --git a/Transforms/SvgTranslate.cs b/Transforms/SvgTranslate.cs index d3a54e6bf74cf9e69d6de7c0a88da546ed311e13..5e04a1d8893a520b98c285cf59c2a0ef2307a9f0 100644 --- a/Transforms/SvgTranslate.cs +++ b/Transforms/SvgTranslate.cs @@ -4,7 +4,7 @@ using System.Text; namespace Svg.Transforms { - public class SvgTranslate : SvgTransform + public sealed class SvgTranslate : SvgTransform { private float x; private float y;