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;