diff --git a/Samples/SVGViewer/SvgViewer.cs b/Samples/SVGViewer/SvgViewer.cs
index ab5d75f733110d0d7decfa848ef46635f1c15bc9..4f44d80d5e6af885864e757f2692ff6e93b9a3f1 100644
--- a/Samples/SVGViewer/SvgViewer.cs
+++ b/Samples/SVGViewer/SvgViewer.cs
@@ -43,9 +43,10 @@ namespace SVGViewer
private void RenderSvg(SvgDocument svgDoc)
{
- var render = new DebugRenderer();
- svgDoc.Draw(render);
+ //var render = new DebugRenderer();
+ //svgDoc.Draw(render);
svgImage.Image = svgDoc.Draw();
+ svgImage.Image.Save(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(svgDoc.BaseUri.LocalPath), "output.png"));
}
}
}
diff --git a/Source/Basic Shapes/SvgPolygon.cs b/Source/Basic Shapes/SvgPolygon.cs
index b1c3e22dcd52cbf8b5e3a72f2b4724941885069b..4c46a1a00ec222d68221cf50e49463c03977d6f2 100644
--- a/Source/Basic Shapes/SvgPolygon.cs
+++ b/Source/Basic Shapes/SvgPolygon.cs
@@ -15,13 +15,13 @@ namespace Svg
public class SvgPolygon : SvgVisualElement
{
private GraphicsPath _path;
- private SvgUnitCollection _points;
+ private SvgPointCollection _points;
///
/// The points that make up the SvgPolygon
///
[SvgAttribute("points")]
- public SvgUnitCollection Points
+ public SvgPointCollection Points
{
get { return this._points; }
set { this._points = value; this.IsPathDirty = true; }
@@ -41,7 +41,7 @@ namespace Svg
try
{
- for (int i = 2; i < this._points.Count; i+=2)
+ for (int i = 2; (i + 1) < this._points.Count; i += 2)
{
var endPoint = SvgUnit.GetDevicePoint(this._points[i], this._points[i+1], renderer, this);
@@ -81,7 +81,7 @@ namespace Svg
public override SvgElement DeepCopy()
{
var newObj = base.DeepCopy() as SvgPolygon;
- newObj.Points = new SvgUnitCollection();
+ newObj.Points = new SvgPointCollection();
foreach (var pt in this.Points)
newObj.Points.Add(pt);
return newObj;
diff --git a/Source/Basic Shapes/SvgPolyline.cs b/Source/Basic Shapes/SvgPolyline.cs
index 2b262f7135298f6a8303f23d8e34b5886310564a..e6912fe62e08ca556e494150b4553dc95df08599 100644
--- a/Source/Basic Shapes/SvgPolyline.cs
+++ b/Source/Basic Shapes/SvgPolyline.cs
@@ -22,7 +22,7 @@ namespace Svg
try
{
- for (int i = 0; i < Points.Count; i += 2)
+ for (int i = 0; (i + 1) < Points.Count; i += 2)
{
PointF endPoint = new PointF(Points[i].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Points[i + 1].ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
diff --git a/Source/Basic Shapes/SvgRectangle.cs b/Source/Basic Shapes/SvgRectangle.cs
index c9d79c3ea698e54bb13aca5c7ea3fb2b9ad6cf86..c46855634d354eb5f4c403e865e6a6119db0b9d7 100644
--- a/Source/Basic Shapes/SvgRectangle.cs
+++ b/Source/Basic Shapes/SvgRectangle.cs
@@ -197,8 +197,8 @@ namespace Svg
var lineEnd = new PointF();
var width = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var height = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- var rx = CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2;
- var ry = CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2;
+ var rx = Math.Min(CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2, width);
+ var ry = Math.Min(CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2, height);
var location = Location.ToDeviceValue(renderer, this);
// Start
diff --git a/Source/Basic Shapes/SvgVisualElement.cs b/Source/Basic Shapes/SvgVisualElement.cs
index 99b14f31a118a144a0360f604996f90cc39879da..e30876b8a330e32c9e9152018d6ce2afc4bb950c 100644
--- a/Source/Basic Shapes/SvgVisualElement.cs
+++ b/Source/Basic Shapes/SvgVisualElement.cs
@@ -2,6 +2,7 @@ using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
+using System.Linq;
namespace Svg
{
@@ -53,13 +54,23 @@ namespace Svg
set { this._dirty = value; }
}
+ ///
+ /// Gets the associated if one has been specified.
+ ///
+ [SvgAttribute("clip")]
+ public virtual string Clip
+ {
+ get { return this.Attributes.GetInheritedAttribute("clip"); }
+ set { this.Attributes["clip"] = value; }
+ }
+
///
/// Gets the associated if one has been specified.
///
[SvgAttribute("clip-path")]
public virtual Uri ClipPath
{
- get { return this.Attributes.GetAttribute("clip-path"); }
+ get { return this.Attributes.GetInheritedAttribute("clip-path"); }
set { this.Attributes["clip-path"] = value; }
}
@@ -79,7 +90,7 @@ namespace Svg
[SvgAttribute("filter")]
public virtual Uri Filter
{
- get { return this.Attributes.GetAttribute("filter"); }
+ get { return this.Attributes.GetInheritedAttribute("filter"); }
set { this.Attributes["filter"] = value; }
}
@@ -180,7 +191,7 @@ namespace Svg
{
if (this.Fill != null)
{
- using (Brush brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
+ using (var brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
{
if (brush != null)
{
@@ -200,15 +211,22 @@ namespace Svg
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
- using (var pen = new Pen(this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1)), strokeWidth))
+ using (var brush = this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1), true))
{
- if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
+ if (brush != null)
{
- /* divide by stroke width - GDI behaviour that I don't quite understand yet.*/
- pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.Value <= 0) ? 1 : u.Value) / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
+ using (var pen = new Pen(brush, 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.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0) ? 1 : u.ToDeviceValue(renderer, UnitRenderingType.Other, this)) /
+ ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
+ }
+
+ renderer.DrawPath(pen, this.Path(renderer));
+ }
}
-
- renderer.DrawPath(pen, this.Path(renderer));
}
}
}
@@ -219,14 +237,27 @@ namespace Svg
/// The to have its clipping region set.
protected internal virtual void SetClip(ISvgRenderer renderer)
{
- if (this.ClipPath != null)
+ if (this.ClipPath != null || !string.IsNullOrEmpty(this.Clip))
{
- SvgClipPath clipPath = this.OwnerDocument.GetElementById(this.ClipPath.ToString());
this._previousClip = renderer.GetClip();
- if (clipPath != null)
+ if (this.ClipPath != null)
+ {
+ SvgClipPath clipPath = this.OwnerDocument.GetElementById(this.ClipPath.ToString());
+ if (clipPath != null) renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
+ }
+
+ var clip = this.Clip;
+ if (!string.IsNullOrEmpty(clip) && clip.StartsWith("rect("))
{
- renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
+ clip = clip.Trim();
+ var offsets = (from o in clip.Substring(5, clip.Length - 6).Split(',')
+ select float.Parse(o.Trim())).ToList();
+ var bounds = this.Bounds;
+ var clipRect = new RectangleF(bounds.Left + offsets[3], bounds.Top + offsets[0],
+ bounds.Width - (offsets[3] + offsets[1]),
+ bounds.Height - (offsets[2] + offsets[0]));
+ renderer.SetClip(new Region(clipRect), CombineMode.Intersect);
}
}
}
diff --git a/Source/Basic Shapes/SvgVisualElementStyle.cs b/Source/Basic Shapes/SvgVisualElementStyle.cs
index f2e08df7d2f01f022b3f0ba0e9f07215b7cfb231..9d6ba6083f70cd169b2b7eb98331ed147b14691c 100644
--- a/Source/Basic Shapes/SvgVisualElementStyle.cs
+++ b/Source/Basic Shapes/SvgVisualElementStyle.cs
@@ -156,6 +156,18 @@ namespace Svg
set { this.Attributes["stroke-opacity"] = FixOpacityValue(value); }
}
+ ///
+ /// Gets or sets the colour of the gradient stop.
+ ///
+ /// Apparently this can be set on non-sensical elements. Don't ask; just check the tests.
+ [SvgAttribute("stop-color")]
+ [TypeConverter(typeof(SvgPaintServerFactory))]
+ public SvgPaintServer StopColor
+ {
+ get { return this.Attributes["stop-color"] as SvgPaintServer; }
+ set { this.Attributes["stop-color"] = value; }
+ }
+
///
/// Gets or sets the opacity of the element. 1.0 is fully opaque; 0.0 is transparent.
///
diff --git a/Source/Clipping and Masking/SvgClipPath.cs b/Source/Clipping and Masking/SvgClipPath.cs
index feed3d38a78252d6a61e9eb25cb72ce3f6bfdd0e..fdc805d8a4792b067380c93e391dafad0b93a474 100644
--- a/Source/Clipping and Masking/SvgClipPath.cs
+++ b/Source/Clipping and Masking/SvgClipPath.cs
@@ -26,7 +26,7 @@ namespace Svg
///
public SvgClipPath()
{
- this.ClipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
+ this.ClipPathUnits = SvgCoordinateUnits.Inherit;
}
private GraphicsPath cachedClipPath = null;
@@ -49,7 +49,20 @@ namespace Svg
this._pathDirty = false;
}
- return new Region(cachedClipPath);
+ var result = cachedClipPath;
+ if (ClipPathUnits == SvgCoordinateUnits.ObjectBoundingBox)
+ {
+ result = (GraphicsPath)cachedClipPath.Clone();
+ using (var transform = new Matrix())
+ {
+ var bounds = owner.Bounds;
+ transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Append);
+ transform.Translate(bounds.Left, bounds.Top, MatrixOrder.Append);
+ result.Transform(transform);
+ }
+ }
+
+ return new Region(result);
}
///
diff --git a/Source/DataTypes/SvgCoordinateUnits.cs b/Source/DataTypes/SvgCoordinateUnits.cs
index 9fdafdbc05b85e0d80a29179f923a4a3ee623317..a17c83ee9c9d5aaaa8319335fd10c3e81979f200 100644
--- a/Source/DataTypes/SvgCoordinateUnits.cs
+++ b/Source/DataTypes/SvgCoordinateUnits.cs
@@ -2,14 +2,17 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.ComponentModel;
namespace Svg
{
///
/// Defines the various coordinate units certain SVG elements may use.
///
+ [TypeConverter(typeof(SvgCoordinateUnitsConverter))]
public enum SvgCoordinateUnits
{
+ Inherit,
///
/// Indicates that the coordinate system of the owner element is to be used.
///
diff --git a/Source/DataTypes/SvgOverflow.cs b/Source/DataTypes/SvgOverflow.cs
index 97bddcc17bae0f6b41c5f849773ddc2e46aed102..1d7ae83185c3f0e84f335eb9b6a27006f3b93ad7 100644
--- a/Source/DataTypes/SvgOverflow.cs
+++ b/Source/DataTypes/SvgOverflow.cs
@@ -7,10 +7,10 @@ namespace Svg
{
public enum SvgOverflow
{
+ inherit,
auto,
visible,
hidden,
- scroll,
- inherit
+ scroll
}
}
diff --git a/Source/DataTypes/SvgPointCollection.cs b/Source/DataTypes/SvgPointCollection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4459e46d99f8996ccf368c8a19e0e0cfc51ee1b1
--- /dev/null
+++ b/Source/DataTypes/SvgPointCollection.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace Svg
+{
+ ///
+ /// Represents a list of used with the and .
+ ///
+ [TypeConverter(typeof(SvgPointCollectionConverter))]
+ public class SvgPointCollection : List
+ {
+ public override string ToString()
+ {
+ string ret = "";
+ foreach (var unit in this)
+ {
+ ret += unit.ToString() + " ";
+ }
+
+ return ret;
+ }
+ }
+
+ ///
+ /// A class to convert string into instances.
+ ///
+ internal class SvgPointCollectionConverter : TypeConverter
+ {
+ //private static readonly SvgUnitConverter _unitConverter = new SvgUnitConverter();
+
+
+ ///
+ /// Converts the given object to the type of this converter, using the specified context and culture information.
+ ///
+ /// An that provides a format context.
+ /// The to use as the current culture.
+ /// The to convert.
+ ///
+ /// An that represents the converted value.
+ ///
+ /// The conversion cannot be performed.
+ public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
+ {
+ if (value is string)
+ {
+ var strValue = ((string)value).Trim();
+ if (string.Compare(strValue, "none", StringComparison.InvariantCultureIgnoreCase) == 0) return null;
+
+ var parser = new CoordinateParser(strValue);
+ var pointValue = 0.0f;
+ var result = new SvgPointCollection();
+ while (parser.TryGetFloat(out pointValue))
+ {
+ result.Add(new SvgUnit(SvgUnitType.User, pointValue));
+ }
+
+ return result;
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return true;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return ((SvgPointCollection)value).ToString();
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+}
diff --git a/Source/DataTypes/SvgUnit.cs b/Source/DataTypes/SvgUnit.cs
index 524b1df1a64a20ce6de44c2382f7b04e4afa32e5..f5a8f12ee4e9d6119be756e2dd415a65ef430357 100644
--- a/Source/DataTypes/SvgUnit.cs
+++ b/Source/DataTypes/SvgUnit.cs
@@ -20,7 +20,7 @@ namespace Svg
///
/// Gets and empty .
///
- public static readonly SvgUnit Empty = new SvgUnit(SvgUnitType.User, 0);
+ public static readonly SvgUnit Empty = new SvgUnit(SvgUnitType.User, 0) { _isEmpty = true };
///
/// Gets an with a value of none.
@@ -86,26 +86,7 @@ namespace Svg
var type = this.Type;
var value = this.Value;
-
- // Deal with fractional pattern units
- var coordElem = owner as ISvgSupportsCoordinateUnits;
- if (coordElem != null && coordElem.GetUnits() == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
- {
- type = SvgUnitType.Percentage;
- value *= 100;
- }
-
- var element = owner as SvgElement;
- if (element != null)
- {
- var pattern = element.Parents.OfType().FirstOrDefault();
- if (pattern != null && pattern.PatternContentUnits == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
- {
- type = SvgUnitType.Percentage;
- value *= 100;
- }
- }
-
+
float points;
switch (type)
@@ -320,9 +301,9 @@ namespace Svg
/// The value.
public SvgUnit(SvgUnitType type, float value)
{
+ this._isEmpty = false;
this._type = type;
this._value = value;
- this._isEmpty = (this._value == 0.0f);
this._deviceValue = null;
}
@@ -332,9 +313,9 @@ namespace Svg
/// The value.
public SvgUnit(float value)
{
+ this._isEmpty = false;
this._value = value;
this._type = SvgUnitType.User;
- this._isEmpty = (this._value == 0.0f);
this._deviceValue = null;
}
diff --git a/Source/DataTypes/SvgUnitConverter.cs b/Source/DataTypes/SvgUnitConverter.cs
index 4787fab54a65516e906d186c64239ce13f4a9899..63e403da82ba67f4715c7544278efbb7449f7640 100644
--- a/Source/DataTypes/SvgUnitConverter.cs
+++ b/Source/DataTypes/SvgUnitConverter.cs
@@ -35,7 +35,7 @@ namespace Svg
for (int i = 0; i < unit.Length; i++)
{
// If the character is a percent sign or a letter which is not an exponent 'e'
- if (unit[i] == '%' || (char.IsLetter(unit[i]) && !(unit[i] == 'e' && i < unit.Length - 1 && !char.IsLetter(unit[i + 1]))))
+ if (unit[i] == '%' || (char.IsLetter(unit[i]) && !((unit[i] == 'e' || unit[i] == 'E') && i < unit.Length - 1 && !char.IsLetter(unit[i + 1]))))
{
identifierIndex = i;
break;
diff --git a/Source/DataTypes/SvgViewBox.cs b/Source/DataTypes/SvgViewBox.cs
index 7795fd95e4beea7528d2296e1bd13b7a610333e3..e545e4b4995796ee2cb5d5fce8f760fd5e66c4f3 100644
--- a/Source/DataTypes/SvgViewBox.cs
+++ b/Source/DataTypes/SvgViewBox.cs
@@ -126,7 +126,14 @@ namespace Svg
public void AddViewBoxTransform(SvgAspectRatio aspectRatio, ISvgRenderer renderer, SvgFragment frag)
{
- if (this.Equals(SvgViewBox.Empty)) return;
+ var x = (frag == null ? 0 : frag.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
+ var y = (frag == null ? 0 : frag.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
+
+ if (this.Equals(SvgViewBox.Empty))
+ {
+ renderer.TranslateTransform(x, y);
+ return;
+ }
var width = (frag == null ? this.Width : frag.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
var height = (frag == null ? this.Height : frag.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
@@ -191,9 +198,6 @@ namespace Svg
}
}
- var x = (frag == null ? 0 : frag.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
- var y = (frag == null ? 0 : frag.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
-
renderer.SetClip(new Region(new RectangleF(x, y, width, height)), CombineMode.Intersect);
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
renderer.TranslateTransform(x, y);
diff --git a/Source/Document Structure/SvgFragment.cs b/Source/Document Structure/SvgFragment.cs
index 36f151570dbcd4e4947a237d189eebe04148fd5c..6233669f1a7bb36380946ff58be2a839de40da9b 100644
--- a/Source/Document Structure/SvgFragment.cs
+++ b/Source/Document Structure/SvgFragment.cs
@@ -157,6 +157,34 @@ namespace Svg
this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, this);
return true;
}
+
+ protected override void Render(ISvgRenderer renderer)
+ {
+ switch (this.Overflow)
+ {
+ case SvgOverflow.auto:
+ case SvgOverflow.visible:
+ case SvgOverflow.scroll:
+ base.Render(renderer);
+ break;
+ default:
+ var prevClip = renderer.GetClip();
+ try
+ {
+ var size = (this.Parent == null ? renderer.GetBoundable().Bounds.Size : GetDimensions());
+ var clip = new RectangleF(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
+ this.Y.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
+ size.Width, size.Height);
+ renderer.SetClip(new Region(clip), CombineMode.Intersect);
+ base.Render(renderer);
+ }
+ finally
+ {
+ renderer.SetClip(prevClip, CombineMode.Replace);
+ }
+ break;
+ }
+ }
///
/// Gets the for this element.
diff --git a/Source/Document Structure/SvgUse.cs b/Source/Document Structure/SvgUse.cs
index 59874874fcb7bf6764da70a4bf4b6a757d902de5..3ed957d63b81f2ae7b4a315bdd3f9aee646e1408 100644
--- a/Source/Document Structure/SvgUse.cs
+++ b/Source/Document Structure/SvgUse.cs
@@ -74,7 +74,7 @@ namespace Svg
{
this.SetClip(renderer);
- var element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
+ var element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement) as SvgVisualElement;
if (element != null)
{
var origParent = element.Parent;
diff --git a/Source/Painting/EnumConverters.cs b/Source/Painting/EnumConverters.cs
index b0f66362ff2560ac384c0b13a492b74265bc97d7..d048d142697ac7f74c8b5ba21066f6418609ab70 100644
--- a/Source/Painting/EnumConverters.cs
+++ b/Source/Painting/EnumConverters.cs
@@ -137,6 +137,23 @@ namespace Svg
}
}
+ public sealed class SvgCoordinateUnitsConverter : EnumBaseConverter
+ {
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value == null || value.ToString() == "") return SvgCoordinateUnits.Inherit;
+ return base.ConvertFrom(context, culture, value);
+ }
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(string) && value is SvgCoordinateUnits && (SvgCoordinateUnits)value == SvgCoordinateUnits.Inherit)
+ {
+ return null;
+ }
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
public sealed class SvgTextDecorationConverter : EnumBaseConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
diff --git a/Source/Painting/SvgColourServer.cs b/Source/Painting/SvgColourServer.cs
index 67fa17d0886f77aeddd817d6771530445c3ada78..44d07cd995f150581fed134466c35d574534444a 100644
--- a/Source/Painting/SvgColourServer.cs
+++ b/Source/Painting/SvgColourServer.cs
@@ -35,11 +35,11 @@ namespace Svg
set { this._colour = value; }
}
- public override Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity, bool forStroke = false)
{
//is none?
if (this == SvgPaintServer.None) return new SolidBrush(System.Drawing.Color.Transparent);
-
+
int alpha = (int)((opacity * (this.Colour.A/255.0f) ) * 255);
Color colour = System.Drawing.Color.FromArgb(alpha, this.Colour);
diff --git a/Source/Painting/SvgDeferredPaintServer.cs b/Source/Painting/SvgDeferredPaintServer.cs
index cb675180d10e413e3bd68842f3ada5e02bc743c0..46e93cf7f5b1af3fb53a73532e4de46906ac2eb5 100644
--- a/Source/Painting/SvgDeferredPaintServer.cs
+++ b/Source/Painting/SvgDeferredPaintServer.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Drawing;
namespace Svg
{
@@ -44,10 +45,10 @@ namespace Svg
}
}
- public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity, bool forStroke = false)
{
EnsureServer(styleOwner);
- return _concreteServer.GetBrush(styleOwner, renderer, opacity);
+ return _concreteServer.GetBrush(styleOwner, renderer, opacity, forStroke);
}
public override SvgElement DeepCopy()
diff --git a/Source/Painting/SvgFallbackPaintServer .cs b/Source/Painting/SvgFallbackPaintServer .cs
new file mode 100644
index 0000000000000000000000000000000000000000..c47136c70a926908eead94d5f4c6b25870fbde24
--- /dev/null
+++ b/Source/Painting/SvgFallbackPaintServer .cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+
+namespace Svg
+{
+ ///
+ /// A wrapper for a paint server has a fallback if the primary server doesn't work.
+ ///
+ public class SvgFallbackPaintServer : SvgPaintServer
+ {
+ private IEnumerable _fallbacks;
+ private SvgPaintServer _primary;
+
+ public SvgFallbackPaintServer() : base() { }
+ public SvgFallbackPaintServer(SvgPaintServer primary, IEnumerable fallbacks) : this()
+ {
+ _fallbacks = fallbacks;
+ _primary = primary;
+ }
+
+ public override Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity, bool forStroke = false)
+ {
+ try
+ {
+ _primary.GetCallback = () => _fallbacks.FirstOrDefault();
+ return _primary.GetBrush(styleOwner, renderer, opacity, forStroke);
+ }
+ finally
+ {
+ _primary.GetCallback = null;
+ }
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ public override SvgElement DeepCopy()
+ {
+ var newObj = base.DeepCopy() as SvgFallbackPaintServer;
+ newObj._fallbacks = this._fallbacks;
+ newObj._primary = this._primary;
+ return newObj;
+ }
+ }
+}
diff --git a/Source/Painting/SvgGradientServer.cs b/Source/Painting/SvgGradientServer.cs
index aea48926dcbb618f54560e3b5c163db910289e61..37a1179d5126896bb0166fa8eda6bb47d074ab96 100644
--- a/Source/Painting/SvgGradientServer.cs
+++ b/Source/Painting/SvgGradientServer.cs
@@ -88,7 +88,7 @@ namespace Svg
///
/// Gets or sets another gradient fill from which to inherit the stops from.
///
- [SvgAttribute("href")]
+ [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public SvgPaintServer InheritGradient
{
get { return this._inheritGradient; }
@@ -101,14 +101,8 @@ namespace Svg
[SvgAttribute("gradientTransform")]
public SvgTransformCollection GradientTransform
{
- get
- {
- return (this.Attributes.GetAttribute("gradientTransform"));
- }
- set
- {
- this.Attributes["gradientTransform"] = value;
- }
+ get { return (this.Attributes.GetAttribute("gradientTransform")); }
+ set { this.Attributes["gradientTransform"] = value; }
}
protected Matrix EffectiveGradientTransform
@@ -186,7 +180,7 @@ namespace Svg
var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
var boundWidth = renderer.GetBoundable().Bounds.Width;
- mergedOpacity = opacity * currentStop.Opacity;
+ mergedOpacity = opacity * currentStop.GetOpacity();
position =
radial
? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth)
@@ -229,33 +223,6 @@ namespace Svg
}
}
- protected PointF TransformPoint(PointF originalPoint)
- {
- var newPoint = new[] { originalPoint };
-
- EffectiveGradientTransform.TransformPoints(newPoint);
-
- return newPoint[0];
- }
-
- protected PointF TransformVector(PointF originalVector)
- {
- var newVector = new[] { originalVector };
-
- EffectiveGradientTransform.TransformVectors(newVector);
-
- return newVector[0];
- }
-
- protected float TransformDistance(float dist)
- {
- var newVector = new[] { new PointF(dist, 0) };
-
- EffectiveGradientTransform.TransformVectors(newVector);
-
- return (float)Math.Sqrt(Math.Pow(newVector[0].X, 2) + Math.Pow(newVector[0].Y, 2));
- }
-
protected static double CalculateDistance(PointF first, PointF second)
{
return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
diff --git a/Source/Painting/SvgGradientStop.cs b/Source/Painting/SvgGradientStop.cs
index 9980b345c9a37cd148288673305412ba809f91e8..aa75ab883ad4d6ec812ccb9132bab9edd6925f14 100644
--- a/Source/Painting/SvgGradientStop.cs
+++ b/Source/Painting/SvgGradientStop.cs
@@ -13,9 +13,7 @@ namespace Svg
public class SvgGradientStop : SvgElement
{
private SvgUnit _offset;
- private SvgPaintServer _colour;
- private float _opacity;
-
+
///
/// Gets or sets the offset, i.e. where the stop begins from the beginning, of the gradient stop.
///
@@ -59,20 +57,31 @@ namespace Svg
///
[SvgAttribute("stop-color")]
[TypeConverter(typeof(SvgPaintServerFactory))]
- public SvgPaintServer Colour
+ public SvgPaintServer StopColor
{
- get { return this._colour; }
- set { this._colour = value; }
+ get
+ {
+ var direct = this.Attributes.GetAttribute("stop-color", SvgColourServer.NotSet);
+ if (direct == SvgColourServer.Inherit) return this.Attributes["stop-color"] as SvgPaintServer ?? SvgColourServer.NotSet;
+ return direct;
+ }
+ set { this.Attributes["stop-color"] = value; }
}
///
/// Gets or sets the opacity of the gradient stop (0-1).
///
[SvgAttribute("stop-opacity")]
- public float Opacity
+ public string Opacity
{
- get { return this._opacity; }
- set { this._opacity = value; }
+ get { return this.Attributes["stop-opacity"] as string; }
+ set { this.Attributes["stop-opacity"] = value; }
+ }
+
+ public float GetOpacity()
+ {
+ var opacity = this.Opacity;
+ return string.IsNullOrEmpty(opacity) ? 1.0f : float.Parse(opacity);
}
///
@@ -81,8 +90,6 @@ namespace Svg
public SvgGradientStop()
{
this._offset = new SvgUnit(0.0f);
- this._colour = SvgColourServer.NotSet;
- this._opacity = 1.0f;
}
///
@@ -93,13 +100,11 @@ namespace Svg
public SvgGradientStop(SvgUnit offset, Color colour)
{
this._offset = offset;
- this._colour = new SvgColourServer(colour);
- this._opacity = 1.0f;
}
public Color GetColor(SvgElement parent)
{
- var core = SvgDeferredPaintServer.TryGet(_colour, parent);
+ var core = SvgDeferredPaintServer.TryGet(this.StopColor, parent);
if (core == null) throw new InvalidOperationException("Invalid paint server for gradient stop detected.");
return core.Colour;
}
@@ -113,9 +118,6 @@ namespace Svg
{
var newObj = base.DeepCopy() as SvgGradientStop;
newObj.Offset = this.Offset;
- newObj.Colour = this.Colour;
- newObj.Opacity = this.Opacity;
-
return newObj;
}
}
diff --git a/Source/Painting/SvgLinearGradientServer.cs b/Source/Painting/SvgLinearGradientServer.cs
index f419e769697a77585d5833f511d1a5654f74e596..e373aaeeb43e50de6d5c2233bf18e146a52dfdad 100644
--- a/Source/Painting/SvgLinearGradientServer.cs
+++ b/Source/Painting/SvgLinearGradientServer.cs
@@ -79,36 +79,94 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
}
- public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
{
LoadStops(renderingElement);
- if (IsInvalid)
+
+ if (this.Stops.Count < 1) return null;
+ if (this.Stops.Count == 1)
{
- return null;
+ var stopColor = this.Stops[0].GetColor(renderingElement);
+ int alpha = (int)((opacity * (stopColor.A/255.0f) ) * 255);
+ Color colour = System.Drawing.Color.FromArgb(alpha, stopColor);
+ return new SolidBrush(colour);
}
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
- var specifiedStart = CalculateStart(renderer);
- var specifiedEnd = CalculateEnd(renderer);
+ var points = new PointF[] {
+ SvgUnit.GetDevicePoint(NormalizeUnit(this.X1), NormalizeUnit(this.Y1), renderer, this),
+ SvgUnit.GetDevicePoint(NormalizeUnit(this.X2), NormalizeUnit(this.Y2), renderer, this)
+ };
+
+ var bounds = renderer.GetBoundable().Bounds;
+ if (bounds.Width <= 0 || bounds.Height <= 0)
+ {
+ if (this.GetCallback != null) return GetCallback().GetBrush(renderingElement, renderer, opacity, forStroke);
+ return null;
+ }
+
+ using (var transform = EffectiveGradientTransform)
+ {
+ var midPoint = new PointF((points[0].X + points[1].X) / 2, (points[0].Y + points[1].Y) / 2);
+ transform.Translate(bounds.X, bounds.Y, MatrixOrder.Prepend);
+ if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
+ {
+ // Transform a normal (i.e. perpendicular line) according to the transform
+ transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Prepend);
+ transform.RotateAt(-90.0f, midPoint, MatrixOrder.Prepend);
+ }
+ transform.TransformPoints(points);
+ }
+
+ if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
+ {
+ // Transform the normal line back to a line such that the gradient still starts in the correct corners, but
+ // has the proper normal vector based on the transforms. If you work out the geometry, these formulas should work.
+ var midPoint = new PointF((points[0].X + points[1].X) / 2, (points[0].Y + points[1].Y) / 2);
+ var dy = (points[1].Y - points[0].Y);
+ var dx = (points[1].X - points[0].X);
+ var x2 = points[0].X;
+ var y2 = points[1].Y;
+
+ if (Math.Round(dx, 4) == 0)
+ {
+ points[0] = new PointF(midPoint.X + dy / 2 * bounds.Width / bounds.Height, midPoint.Y);
+ points[1] = new PointF(midPoint.X - dy / 2 * bounds.Width / bounds.Height, midPoint.Y);
+ }
+ else if (Math.Round(dy, 4) == 0)
+ {
+ points[0] = new PointF(midPoint.X, midPoint.Y - dx / 2 * bounds.Height / bounds.Width);
+ points[1] = new PointF(midPoint.X, midPoint.Y + dx / 2 * bounds.Height / bounds.Width); ;
+ }
+ else
+ {
+ var startX = (float)((dy * dx * (midPoint.Y - y2) + Math.Pow(dx, 2) * midPoint.X + Math.Pow(dy, 2) * x2) /
+ (Math.Pow(dx, 2) + Math.Pow(dy, 2)));
+ var startY = dy * (startX - x2) / dx + y2;
+ points[0] = new PointF(startX, startY);
+ points[1] = new PointF(midPoint.X + (midPoint.X - startX), midPoint.Y + (midPoint.Y - startY));
+ }
+ }
- var effectiveStart = specifiedStart;
- var effectiveEnd = specifiedEnd;
+ var effectiveStart = points[0];
+ var effectiveEnd = points[1];
- if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd))
+ if (PointsToMove(renderingElement, points[0], points[1]) > LinePoints.None)
{
- var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd);
+ var expansion = ExpandGradient(renderingElement, points[0], points[1]);
effectiveStart = expansion.StartPoint;
effectiveEnd = expansion.EndPoint;
}
- return new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
+ var result = new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
{
- InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
+ InterpolationColors = CalculateColorBlend(renderer, opacity, points[0], effectiveStart, points[1], effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
+ return result;
}
finally
{
@@ -116,19 +174,36 @@ namespace Svg
}
}
- private PointF CalculateStart(ISvgRenderer renderer)
+ private SvgUnit NormalizeUnit(SvgUnit orig)
{
- return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
+ return (orig.Type == SvgUnitType.Percentage && this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox ?
+ new SvgUnit(SvgUnitType.User, orig.Value / 100) :
+ orig);
}
- private PointF CalculateEnd(ISvgRenderer renderer)
+ [Flags]
+ private enum LinePoints
{
- return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
+ None = 0,
+ Start = 1,
+ End = 2
}
- private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
+ private LinePoints PointsToMove(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{
- return SpreadMethod == SvgGradientSpreadMethod.Pad && (boundable.Bounds.Contains(specifiedStart) || boundable.Bounds.Contains(specifiedEnd));
+ var bounds = boundable.Bounds;
+ if (specifiedStart.X == specifiedEnd.X)
+ {
+ return (bounds.Top < specifiedStart.Y && specifiedStart.Y < bounds.Bottom ? LinePoints.Start : LinePoints.None) |
+ (bounds.Top < specifiedEnd.Y && specifiedEnd.Y < bounds.Bottom ? LinePoints.End : LinePoints.None);
+ }
+ else if (specifiedStart.Y == specifiedEnd.Y)
+ {
+ return (bounds.Left < specifiedStart.X && specifiedStart.X < bounds.Right ? LinePoints.Start : LinePoints.None) |
+ (bounds.Left < specifiedEnd.X && specifiedEnd.X < bounds.Right ? LinePoints.End : LinePoints.None);
+ }
+ return (boundable.Bounds.Contains(specifiedStart) ? LinePoints.Start : LinePoints.None) |
+ (boundable.Bounds.Contains(specifiedEnd) ? LinePoints.End : LinePoints.None);
}
public struct GradientPoints
@@ -145,44 +220,98 @@ namespace Svg
private GradientPoints ExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{
- if (!NeedToExpandGradient(boundable, specifiedStart, specifiedEnd))
+ var pointsToMove = PointsToMove(boundable, specifiedStart, specifiedEnd);
+ if (pointsToMove == LinePoints.None)
{
Debug.Fail("Unexpectedly expanding gradient when not needed!");
return new GradientPoints(specifiedStart, specifiedEnd);
}
- var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
- var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
-
+ var bounds = boundable.Bounds;
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
+ var intersectionPoints = CandidateIntersections(bounds, specifiedStart, specifiedEnd);
- var elementDiagonal = (float)CalculateDistance(new PointF(boundable.Bounds.Left, boundable.Bounds.Top), new PointF(boundable.Bounds.Right, boundable.Bounds.Bottom));
+ Debug.Assert(intersectionPoints.Count == 2, "Unanticipated number of intersection points");
- var expandedStart = MovePointAlongVector(effectiveStart, specifiedUnitVector, -elementDiagonal);
- var expandedEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, elementDiagonal);
+ if (!(Math.Sign(intersectionPoints[1].X - intersectionPoints[0].X) == Math.Sign(specifiedEnd.X - specifiedStart.X) &&
+ Math.Sign(intersectionPoints[1].Y - intersectionPoints[0].Y) == Math.Sign(specifiedEnd.Y - specifiedStart.Y)))
+ {
+ intersectionPoints = intersectionPoints.Reverse().ToList();
+ }
- var intersectionPoints = new LineF(expandedStart.X, expandedStart.Y, expandedEnd.X, expandedEnd.Y).Intersection(boundable.Bounds);
+ if ((pointsToMove & LinePoints.Start) > 0) effectiveStart = intersectionPoints[0];
+ if ((pointsToMove & LinePoints.End) > 0) effectiveEnd = intersectionPoints[1];
- if (boundable.Bounds.Contains(specifiedStart))
+ switch (SpreadMethod)
{
- effectiveStart = CalculateClosestIntersectionPoint(expandedStart, intersectionPoints);
-
- effectiveStart = MovePointAlongVector(effectiveStart, specifiedUnitVector, -1);
+ case SvgGradientSpreadMethod.Reflect:
+ case SvgGradientSpreadMethod.Repeat:
+ var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
+ var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
+ var oppUnitVector = new PointF(-specifiedUnitVector.X, -specifiedUnitVector.Y);
+
+ var startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength) * specifiedLength);
+ effectiveStart = MovePointAlongVector(specifiedStart, oppUnitVector, startExtend);
+ var endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength) * specifiedLength);
+ effectiveEnd = MovePointAlongVector(specifiedEnd, specifiedUnitVector, endExtend);
+ break;
}
- if (boundable.Bounds.Contains(specifiedEnd))
- {
- effectiveEnd = CalculateClosestIntersectionPoint(expandedEnd, intersectionPoints);
+ return new GradientPoints(effectiveStart, effectiveEnd);
+ }
- effectiveEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, 1);
+ private IList CandidateIntersections(RectangleF bounds, PointF p1, PointF p2)
+ {
+ var results = new List();
+ if (Math.Round(Math.Abs(p1.Y - p2.Y), 4) == 0)
+ {
+ results.Add(new PointF(bounds.Left, p1.Y));
+ results.Add(new PointF(bounds.Right, p1.Y));
+ }
+ else if (Math.Round(Math.Abs(p1.X - p2.X), 4) == 0)
+ {
+ results.Add(new PointF(p1.X, bounds.Top));
+ results.Add(new PointF(p1.X, bounds.Bottom));
+ }
+ else
+ {
+ PointF candidate;
+ // Save some effort and duplication in the trivial case
+ if ((p1.X == bounds.Left || p1.X == bounds.Right) && (p1.Y == bounds.Top || p1.Y == bounds.Bottom))
+ {
+ results.Add(p1);
+ }
+ else
+ {
+ candidate = new PointF(bounds.Left, (p2.Y - p1.Y) / (p2.X - p1.X) * (bounds.Left - p1.X) + p1.Y);
+ if (bounds.Top <= candidate.Y && candidate.Y <= bounds.Bottom) results.Add(candidate);
+ candidate = new PointF(bounds.Right, (p2.Y - p1.Y) / (p2.X - p1.X) * (bounds.Right - p1.X) + p1.Y);
+ if (bounds.Top <= candidate.Y && candidate.Y <= bounds.Bottom) results.Add(candidate);
+ }
+ if ((p2.X == bounds.Left || p2.X == bounds.Right) && (p2.Y == bounds.Top || p2.Y == bounds.Bottom))
+ {
+ results.Add(p2);
+ }
+ else
+ {
+ candidate = new PointF((bounds.Top - p1.Y) / (p2.Y - p1.Y) * (p2.X - p1.X) + p1.X, bounds.Top);
+ if (bounds.Left <= candidate.X && candidate.X <= bounds.Right) results.Add(candidate);
+ candidate = new PointF((bounds.Bottom - p1.Y) / (p2.Y - p1.Y) * (p2.X - p1.X) + p1.X, bounds.Bottom);
+ if (bounds.Left <= candidate.X && candidate.X <= bounds.Right) results.Add(candidate);
+ }
}
- return new GradientPoints(effectiveStart, effectiveEnd);
+ return results;
}
private ColorBlend CalculateColorBlend(ISvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{
+ float startExtend;
+ float endExtend;
+ List colors;
+ List positions;
+
var colorBlend = GetColorBlend(renderer, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart);
@@ -198,25 +327,101 @@ namespace Svg
var effectiveLength = CalculateDistance(effectiveStart, effectiveEnd);
- for (var i = 0; i < colorBlend.Positions.Length; i++)
+ switch (SpreadMethod)
{
- var originalPoint = MovePointAlongVector(specifiedStart, specifiedUnitVector, (float)specifiedLength * colorBlend.Positions[i]);
+ case SvgGradientSpreadMethod.Reflect:
+ startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength));
+ endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength));
+ colors = colorBlend.Colors.ToList();
+ positions = (from p in colorBlend.Positions select p + startExtend).ToList();
- var distanceFromEffectiveStart = CalculateDistance(effectiveStart, originalPoint);
+ for (var i = 0; i < startExtend; i++)
+ {
+ if (i % 2 == 0)
+ {
+ for (var j = 1; j < colorBlend.Positions.Length; j++)
+ {
+ positions.Insert(0, (float)((startExtend - 1 - i) + 1 - colorBlend.Positions[j]));
+ colors.Insert(0, colorBlend.Colors[j]);
+ }
+ }
+ else
+ {
+ for (var j = 0; j < colorBlend.Positions.Length - 1; j++)
+ {
+ positions.Insert(j, (float)((startExtend - 1 - i) + colorBlend.Positions[j]));
+ colors.Insert(j, colorBlend.Colors[j]);
+ }
+ }
+ }
- colorBlend.Positions[i] = (float)Math.Round(Math.Max(0F, Math.Min((distanceFromEffectiveStart / effectiveLength), 1.0F)), 5);
- }
+ int insertPos;
+ for (var i = 0; i < endExtend; i++)
+ {
+ if (i % 2 == 0)
+ {
+ insertPos = positions.Count;
+ for (var j = 0; j < colorBlend.Positions.Length - 1; j++)
+ {
+ positions.Insert(insertPos, (float)((startExtend + 1 + i) + 1 - colorBlend.Positions[j]));
+ colors.Insert(insertPos, colorBlend.Colors[j]);
+ }
+ }
+ else
+ {
+ for (var j = 1; j < colorBlend.Positions.Length; j++)
+ {
+ positions.Add((float)((startExtend + 1 + i) + colorBlend.Positions[j]));
+ colors.Add(colorBlend.Colors[j]);
+ }
+ }
+ }
- if (startDelta > 0)
- {
- colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
- colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
- }
+ colorBlend.Colors = colors.ToArray();
+ colorBlend.Positions = (from p in positions select p / (startExtend + 1 + endExtend)).ToArray();
+ break;
+ case SvgGradientSpreadMethod.Repeat:
+ startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength));
+ endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength));
+ colors = new List();
+ positions = new List();
- if (endDelta > 0)
- {
- colorBlend.Positions = colorBlend.Positions.Concat(new[] { 1F }).ToArray();
- colorBlend.Colors = colorBlend.Colors.Concat(new[] { colorBlend.Colors.Last() }).ToArray();
+ for (int i = 0; i < startExtend + endExtend + 1; i++)
+ {
+ for (int j = 0; j < colorBlend.Positions.Length; j++)
+ {
+ positions.Add((i + colorBlend.Positions[j] * 0.9999f) / (startExtend + endExtend + 1));
+ colors.Add(colorBlend.Colors[j]);
+ }
+ }
+ positions[positions.Count - 1] = 1.0f;
+
+ colorBlend.Colors = colors.ToArray();
+ colorBlend.Positions = positions.ToArray();
+
+ break;
+ default:
+ for (var i = 0; i < colorBlend.Positions.Length; i++)
+ {
+ var originalPoint = MovePointAlongVector(specifiedStart, specifiedUnitVector, (float)specifiedLength * colorBlend.Positions[i]);
+
+ var distanceFromEffectiveStart = CalculateDistance(effectiveStart, originalPoint);
+
+ colorBlend.Positions[i] = (float)Math.Round(Math.Max(0F, Math.Min((distanceFromEffectiveStart / effectiveLength), 1.0F)), 5);
+ }
+
+ if (startDelta > 0)
+ {
+ colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
+ colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
+ }
+
+ if (endDelta > 0)
+ {
+ colorBlend.Positions = colorBlend.Positions.Concat(new[] { 1F }).ToArray();
+ colorBlend.Colors = colorBlend.Colors.Concat(new[] { colorBlend.Colors.Last() }).ToArray();
+ }
+ break;
}
return colorBlend;
@@ -335,48 +540,6 @@ namespace Svg
return null;
}
}
-
-
- //var a1 = Y2 - Y1;
- //var b1 = X1 - X2;
- //var c1 = X2 * Y1 - X1 * Y2;
-
- //var r3 = a1 * other.X1 + b1 * other.Y1 + c1;
- //var r4 = a1 * other.X2 + b1 * other.Y2 + c1;
-
- //if (r3 != 0 && r4 != 0 && Math.Sign(r3) == Math.Sign(r4))
- //{
- // return null;
- //}
-
- //var a2 = other.Y2 - other.Y1;
- //var b2 = other.X1 - other.X2;
- //var c2 = other.X2 * other.Y1 - other.X1 * other.Y2;
-
- //var r1 = a2 * X1 + b2 * Y1 + c2;
- //var r2 = a2 * X2 + b2 * Y2 + c2;
-
- //if (r1 != 0 && r2 != 0 && Math.Sign(r1) == Math.Sign(r2))
- //{
- // return (null);
- //}
-
- //var denom = a1 * b2 - a2 * b1;
-
- //if (denom == 0)
- //{
- // return null;
- //}
-
- //var offset = denom < 0 ? -denom / 2 : denom / 2;
-
- //var num = b1 * c2 - b2 * c1;
- //var x = (num < 0 ? num - offset : num + offset) / denom;
-
- //num = a2 * c1 - a1 * c2;
- //var y = (num < 0 ? num - offset : num + offset) / denom;
-
- //return new PointF(x, y);
}
private static void AddIfIntersect(LineF first, LineF second, ICollection result)
diff --git a/Source/Painting/SvgPaintServer.cs b/Source/Painting/SvgPaintServer.cs
index 417024ce7acff4a6113fc873b4a00046a1ee67d0..0d53403698e1c1f8d4fbf639e170d626e4744180 100644
--- a/Source/Painting/SvgPaintServer.cs
+++ b/Source/Painting/SvgPaintServer.cs
@@ -13,6 +13,8 @@ namespace Svg
[TypeConverter(typeof(SvgPaintServerFactory))]
public abstract class SvgPaintServer : SvgElement
{
+ public Func GetCallback { get; set; }
+
///
/// An unspecified .
///
@@ -39,7 +41,7 @@ namespace Svg
///
/// The owner .
/// The opacity of the brush.
- public abstract Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity);
+ public abstract Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity, bool forStroke = false);
///
/// Returns a that represents the current .
diff --git a/Source/Painting/SvgPaintServerFactory.cs b/Source/Painting/SvgPaintServerFactory.cs
index 54a4ea53881483b1f649c6dccbfe343fc5c7e8c3..0514e60675a7f69a24ba0d2c73c18d5eb1a511c7 100644
--- a/Source/Painting/SvgPaintServerFactory.cs
+++ b/Source/Painting/SvgPaintServerFactory.cs
@@ -1,23 +1,19 @@
using System;
-using System.ComponentModel;
using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml;
+using System.ComponentModel;
using System.Drawing;
using System.Globalization;
+using System.Linq;
namespace Svg
{
internal class SvgPaintServerFactory : TypeConverter
{
private static readonly SvgColourConverter _colourConverter;
- private static readonly Regex _urlRefPattern;
static SvgPaintServerFactory()
{
_colourConverter = new SvgColourConverter();
- _urlRefPattern = new Regex(@"url\((#[^)]+)\)");
}
public static SvgPaintServer Create(string value, SvgDocument document)
@@ -35,34 +31,71 @@ namespace Svg
{
return new SvgDeferredPaintServer(document, value);
}
- else if (value.IndexOf("url(#") > -1)
- {
- Match match = _urlRefPattern.Match(value);
- Uri id = new Uri(match.Groups[1].Value, UriKind.Relative);
- return (SvgPaintServer)document.IdManager.GetElementById(id);
- }
- // If referenced to to a different (linear or radial) gradient
- else if (document.IdManager.GetElementById(value) != null && document.IdManager.GetElementById(value).GetType().BaseType == typeof(SvgGradientServer))
- {
- return (SvgPaintServer)document.IdManager.GetElementById(value);
- }
- else if (value.StartsWith("#")) // Otherwise try and parse as colour
+ else
{
- try
+ var servers = new List();
+
+ while (!string.IsNullOrEmpty(value))
{
- return new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Trim()));
+ if (value.StartsWith("url(#"))
+ {
+ var leftParen = value.IndexOf(')', 5);
+ Uri id = new Uri(value.Substring(5, leftParen - 5), UriKind.Relative);
+ value = value.Substring(leftParen + 1).Trim();
+ servers.Add((SvgPaintServer)document.IdManager.GetElementById(id));
+ }
+ // If referenced to to a different (linear or radial) gradient
+ else if (document.IdManager.GetElementById(value) != null && document.IdManager.GetElementById(value).GetType().BaseType == typeof(SvgGradientServer))
+ {
+ return (SvgPaintServer)document.IdManager.GetElementById(value);
+ }
+ else if (value.StartsWith("#")) // Otherwise try and parse as colour
+ {
+ switch(CountHexDigits(value, 1))
+ {
+ case 3:
+ servers.Add(new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Substring(0, 4))));
+ value = value.Substring(4).Trim();
+ break;
+ case 6:
+ servers.Add(new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Substring(0, 7))));
+ value = value.Substring(7).Trim();
+ break;
+ default:
+ return new SvgDeferredPaintServer(document, value);
+ }
+ }
+ else
+ {
+ return new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Trim()));
+ }
}
- catch
+
+ if (servers.Count > 1)
{
- return new SvgDeferredPaintServer(document, value);
+ return new SvgFallbackPaintServer(servers[0], servers.Skip(1));
}
- }
- else
+ return servers[0];
+ }
+
+
+ }
+
+ private static int CountHexDigits(string value, int start)
+ {
+ int i = Math.Max(start, 0);
+ int count = 0;
+ while (i < value.Length &&
+ ((value[i] >= '0' && value[i] <= '9') ||
+ (value[i] >= 'a' && value[i] <= 'f') ||
+ (value[i] >= 'A' && value[i] <= 'F')))
{
- return new SvgColourServer((Color)_colourConverter.ConvertFrom(value.Trim()));
+ count++;
+ i++;
}
+ return count;
}
-
+
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
diff --git a/Source/Painting/SvgPatternServer.cs b/Source/Painting/SvgPatternServer.cs
index 48adb39d3cd2a8f84d892da21f12ed9d245ef4e0..dba7af9d36d6b18b1ab28b21bd10741e6483e718 100644
--- a/Source/Painting/SvgPatternServer.cs
+++ b/Source/Painting/SvgPatternServer.cs
@@ -6,6 +6,7 @@ using System.Drawing;
using System.ComponentModel;
using Svg.Transforms;
+using System.Linq;
namespace Svg
{
@@ -19,9 +20,10 @@ namespace Svg
private SvgUnit _height;
private SvgUnit _x;
private SvgUnit _y;
+ private SvgPaintServer _inheritGradient;
private SvgViewBox _viewBox;
- private SvgCoordinateUnits _patternUnits = SvgCoordinateUnits.ObjectBoundingBox;
- private SvgCoordinateUnits _patternContentUnits = SvgCoordinateUnits.UserSpaceOnUse;
+ private SvgCoordinateUnits _patternUnits = SvgCoordinateUnits.Inherit;
+ private SvgCoordinateUnits _patternContentUnits = SvgCoordinateUnits.Inherit;
[SvgAttribute("overflow")]
public SvgOverflow Overflow
@@ -76,7 +78,7 @@ namespace Svg
///
/// Gets or sets the width of the pattern.
///
- [SvgAttribute("patternUnits")]
+ [SvgAttribute("patternContentUnits")]
public SvgCoordinateUnits PatternContentUnits
{
get { return this._patternContentUnits; }
@@ -113,15 +115,56 @@ namespace Svg
set { this._y = value; }
}
+ ///
+ /// Gets or sets another gradient fill from which to inherit the stops from.
+ ///
+ [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
+ public SvgPaintServer InheritGradient
+ {
+ get { return this._inheritGradient; }
+ set
+ {
+ this._inheritGradient = value;
+ }
+ }
+
+ [SvgAttribute("patternTransform")]
+ public SvgTransformCollection PatternTransform
+ {
+ get { return (this.Attributes.GetAttribute("gradientTransform")); }
+ set { this.Attributes["gradientTransform"] = value; }
+ }
+
+ protected Matrix EffectivePatternTransform
+ {
+ get
+ {
+ var transform = new Matrix();
+
+ if (PatternTransform != null)
+ {
+ transform.Multiply(PatternTransform.GetMatrix());
+ }
+ return transform;
+ }
+ }
+
///
/// Initializes a new instance of the class.
///
public SvgPatternServer()
{
- this._x = new SvgUnit(0.0f);
- this._y = new SvgUnit(0.0f);
- this._width = new SvgUnit(0.0f);
- this._height = new SvgUnit(0.0f);
+ this._x = SvgUnit.None;
+ this._y = SvgUnit.None;
+ this._width = SvgUnit.None;
+ this._height = SvgUnit.None;
+ }
+
+ private SvgUnit NormalizeUnit(SvgUnit orig)
+ {
+ return (orig.Type == SvgUnitType.Percentage && this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox ?
+ new SvgUnit(SvgUnitType.User, orig.Value / 100) :
+ orig);
}
///
@@ -129,57 +172,78 @@ namespace Svg
///
/// The owner .
/// The opacity of the brush.
- public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
{
- // If there aren't any children, return null
- if (this.Children.Count == 0)
- return null;
+ var chain = new List();
+ var curr = this;
+ while (curr != null)
+ {
+ chain.Add(curr);
+ curr = SvgDeferredPaintServer.TryGet(curr._inheritGradient, renderingElement);
+ }
+
+ var childElem = chain.Where((p) => p.Children != null && p.Children.Count > 0).FirstOrDefault();
+ if (childElem == null) return null;
+ var widthElem = chain.Where((p) => p.Width != null && p.Width != SvgUnit.None).FirstOrDefault();
+ var heightElem = chain.Where((p) => p.Height != null && p.Height != SvgUnit.None).FirstOrDefault();
+ if (widthElem == null && heightElem == null) return null;
- // Can't render if there are no dimensions
- if (this._width.Value == 0.0f || this._height.Value == 0.0f)
- return null;
+ var viewBoxElem = chain.Where((p) => p.ViewBox != null && p.ViewBox != SvgViewBox.Empty).FirstOrDefault();
+ var viewBox = viewBoxElem == null ? SvgViewBox.Empty : viewBoxElem.ViewBox;
+ var xElem = chain.Where((p) => p.X != null && p.X != SvgUnit.None).FirstOrDefault();
+ var yElem = chain.Where((p) => p.Y != null && p.Y != SvgUnit.None).FirstOrDefault();
+ var xUnit = xElem == null ? SvgUnit.Empty : xElem.X;
+ var yUnit = yElem == null ? SvgUnit.Empty : yElem.Y;
+
+ var patternUnitElem = chain.Where((p) => p.PatternUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
+ var patternUnits = (patternUnitElem == null ? SvgCoordinateUnits.ObjectBoundingBox : patternUnitElem.PatternUnits);
+ var patternContentUnitElem = chain.Where((p) => p.PatternContentUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
+ var patternContentUnits = (patternContentUnitElem == null ? SvgCoordinateUnits.UserSpaceOnUse : patternContentUnitElem.PatternContentUnits);
try
{
- if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
-
- float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
- float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
+ if (patternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
- Matrix patternMatrix = new Matrix();
- // Apply a translate if needed
- if (this._x.Value > 0.0f || this._y.Value > 0.0f)
+ using (var patternMatrix = new Matrix())
{
- float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
- float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
+ var bounds = renderer.GetBoundable().Bounds;
+ var xScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1);
+ var yScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1);
- patternMatrix.Translate(x, y);
- }
+ float x = xScale * NormalizeUnit(xUnit).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
+ float y = yScale * NormalizeUnit(yUnit).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
- {
- patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
- this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
- }
+ float width = xScale * NormalizeUnit(widthElem.Width).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
+ float height = yScale * NormalizeUnit(heightElem.Height).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- Bitmap image = new Bitmap((int)width, (int)height);
- using (var iRenderer = SvgRenderer.FromImage(image))
- {
- iRenderer.SetBoundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.GetBoundable());
- iRenderer.Transform = patternMatrix;
- iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
+ // Apply a scale if needed
+ patternMatrix.Scale((patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1) *
+ (viewBox.Width > 0 ? width / viewBox.Width : 1),
+ (patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1) *
+ (viewBox.Height > 0 ? height / viewBox.Height : 1), MatrixOrder.Prepend);
- foreach (SvgElement child in this.Children)
+ Bitmap image = new Bitmap((int)width, (int)height);
+ using (var iRenderer = SvgRenderer.FromImage(image))
{
- child.RenderElement(iRenderer);
- }
- }
+ iRenderer.SetBoundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.GetBoundable());
+ iRenderer.Transform = patternMatrix;
+ iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
+ iRenderer.SetClip(new Region(new RectangleF(0, 0,
+ viewBox.Width > 0 ? viewBox.Width : width,
+ viewBox.Height > 0 ? viewBox.Height : height)));
- image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
-
- TextureBrush textureBrush = new TextureBrush(image);
+ foreach (SvgElement child in childElem.Children)
+ {
+ child.RenderElement(iRenderer);
+ }
+ }
- return textureBrush;
+ TextureBrush textureBrush = new TextureBrush(image);
+ var brushTransform = EffectivePatternTransform.Clone();
+ brushTransform.Translate(x, y, MatrixOrder.Append);
+ textureBrush.Transform = brushTransform;
+ return textureBrush;
+ }
}
finally
{
@@ -187,10 +251,6 @@ namespace Svg
}
}
- private static int imgNumber = 0;
-
-
-
public override SvgElement DeepCopy()
{
return DeepCopy();
diff --git a/Source/Painting/SvgRadialGradientServer.cs b/Source/Painting/SvgRadialGradientServer.cs
index f6a98bcc785e054f246776646aacdd5fab909f7d..981a51fb7d5cb53854b7fc550e2806c19d1860dc 100644
--- a/Source/Painting/SvgRadialGradientServer.cs
+++ b/Source/Painting/SvgRadialGradientServer.cs
@@ -98,29 +98,85 @@ namespace Svg
private object _lockObj = new Object();
- public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
+ private SvgUnit NormalizeUnit(SvgUnit orig)
+ {
+ return (orig.Type == SvgUnitType.Percentage && this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox ?
+ new SvgUnit(SvgUnitType.User, orig.Value / 100) :
+ orig);
+ }
+
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
{
LoadStops(renderingElement);
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
-
+
// Calculate the path and transform it appropriately
- var origin = renderer.GetBoundable().Location;
- var center = new PointF(origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this),
- origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this));
- var specifiedRadius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
+ var center = new PointF(NormalizeUnit(CenterX).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
+ NormalizeUnit(CenterY).ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
+ var focals = new PointF[] {new PointF(NormalizeUnit(FocalX).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
+ NormalizeUnit(FocalY).ToDeviceValue(renderer, UnitRenderingType.Vertical, this)) };
+ var specifiedRadius = NormalizeUnit(Radius).ToDeviceValue(renderer, UnitRenderingType.Other, this);
var path = new GraphicsPath();
path.AddEllipse(
- origin.X + center.X - specifiedRadius, origin.Y + center.Y - specifiedRadius,
+ center.X - specifiedRadius, center.Y - specifiedRadius,
specifiedRadius * 2, specifiedRadius * 2
);
- path.Transform(EffectiveGradientTransform);
+
+ using (var transform = EffectiveGradientTransform)
+ {
+ var bounds = renderer.GetBoundable().Bounds;
+ transform.Translate(bounds.X, bounds.Y, MatrixOrder.Prepend);
+ if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
+ {
+ transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Prepend);
+ }
+ path.Transform(transform);
+ transform.TransformPoints(focals);
+ }
// Calculate any required scaling
- var scale = CalcScale(renderingElement.Bounds, path);
+ var scaleBounds = RectangleF.Inflate(renderingElement.Bounds, renderingElement.StrokeWidth, renderingElement.StrokeWidth);
+ var scale = CalcScale(scaleBounds, path);
+
+ // Not ideal, but this makes sure that the rest of the shape gets properly filled or drawn
+ if (scale > 1.0f && SpreadMethod == SvgGradientSpreadMethod.Pad)
+ {
+ var stop = Stops.Last();
+ var origColor = stop.GetColor(renderingElement);
+ var renderColor = System.Drawing.Color.FromArgb((int)(opacity * stop.GetOpacity() * 255), origColor);
+
+ var origClip = renderer.GetClip();
+ try
+ {
+ using (var solidBrush = new SolidBrush(renderColor))
+ {
+ var newClip = origClip.Clone();
+ newClip.Exclude(path);
+ renderer.SetClip(newClip);
+
+ var renderPath = (GraphicsPath)renderingElement.Path(renderer);
+ if (forStroke)
+ {
+ using (var pen = new Pen(solidBrush, renderingElement.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, renderingElement)))
+ {
+ renderer.DrawPath(pen, renderPath);
+ }
+ }
+ else
+ {
+ renderer.FillPath(solidBrush, renderPath);
+ }
+ }
+ }
+ finally
+ {
+ renderer.SetClip(origClip);
+ }
+ }
// Get the color blend and any tweak to the scaling
var blend = CalculateColorBlend(renderer, opacity, scale, out scale);
@@ -138,7 +194,7 @@ namespace Svg
// calculate the brush
var brush = new PathGradientBrush(path);
- brush.CenterPoint = CalculateFocalPoint(renderer, origin);
+ brush.CenterPoint = focals[0];
brush.InterpolationColors = blend;
return brush;
@@ -185,12 +241,62 @@ namespace Svg
return bounds.Height / (points[2].Y - points[1].Y);
}
- private PointF CalculateFocalPoint(ISvgRenderer renderer, PointF origin)
+ //New plan:
+ // scale the outer rectangle to always encompass ellipse
+ // cut the ellipse in half (either vertical or horizontal)
+ // determine the region on each side of the ellipse
+ private static IEnumerable GetDifference(RectangleF subject, GraphicsPath clip)
{
- var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
- var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
- var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
- return transformedFocalPoint;
+ var clipFlat = (GraphicsPath)clip.Clone();
+ clipFlat.Flatten();
+ var clipBounds = clipFlat.GetBounds();
+ var bounds = RectangleF.Union(subject, clipBounds);
+ bounds.Inflate(bounds.Width * .3f, bounds.Height * 0.3f);
+
+ var clipMidPoint = new PointF((clipBounds.Left + clipBounds.Right) / 2, (clipBounds.Top + clipBounds.Bottom) / 2);
+ var leftPoints = new List();
+ var rightPoints = new List();
+ foreach (var pt in clipFlat.PathPoints)
+ {
+ if (pt.X <= clipMidPoint.X)
+ {
+ leftPoints.Add(pt);
+ }
+ else
+ {
+ rightPoints.Add(pt);
+ }
+ }
+ leftPoints.Sort((p, q) => p.Y.CompareTo(q.Y));
+ rightPoints.Sort((p, q) => p.Y.CompareTo(q.Y));
+
+ var point = new PointF((leftPoints.Last().X + rightPoints.Last().X) / 2,
+ (leftPoints.Last().Y + rightPoints.Last().Y) / 2);
+ leftPoints.Add(point);
+ rightPoints.Add(point);
+ point = new PointF(point.X, bounds.Bottom);
+ leftPoints.Add(point);
+ rightPoints.Add(point);
+
+ leftPoints.Add(new PointF(bounds.Left, bounds.Bottom));
+ leftPoints.Add(new PointF(bounds.Left, bounds.Top));
+ rightPoints.Add(new PointF(bounds.Right, bounds.Bottom));
+ rightPoints.Add(new PointF(bounds.Right, bounds.Top));
+
+ point = new PointF((leftPoints.First().X + rightPoints.First().X) / 2, bounds.Top);
+ leftPoints.Add(point);
+ rightPoints.Add(point);
+ point = new PointF(point.X, (leftPoints.First().Y + rightPoints.First().Y) / 2);
+ leftPoints.Add(point);
+ rightPoints.Add(point);
+
+ var path = new GraphicsPath(FillMode.Winding);
+ path.AddPolygon(leftPoints.ToArray());
+ yield return path;
+
+ path.Reset();
+ path.AddPolygon(rightPoints.ToArray());
+ yield return path;
}
private static GraphicsPath CreateGraphicsPath(PointF origin, PointF centerPoint, float effectiveRadius)
@@ -221,20 +327,26 @@ namespace Svg
{
case SvgGradientSpreadMethod.Reflect:
newScale = (float)Math.Ceiling(scale);
- pos = (from p in colorBlend.Positions select p / newScale).ToList();
+ pos = (from p in colorBlend.Positions select 1 + (p - 1) / newScale).ToList();
colors = colorBlend.Colors.ToList();
for (var i = 1; i < newScale; i++)
{
if (i % 2 == 1)
{
- pos.AddRange(from p in colorBlend.Positions.Reverse().Skip(1) select (1 - p + i) / newScale);
- colors.AddRange(colorBlend.Colors.Reverse().Skip(1));
+ for (int j = 1; j < colorBlend.Positions.Length; j++)
+ {
+ pos.Insert(0, (newScale - i - 1) / newScale + 1 - colorBlend.Positions[j]);
+ colors.Insert(0, colorBlend.Colors[j]);
+ }
}
else
{
- pos.AddRange(from p in colorBlend.Positions.Skip(1) select (p + i) / newScale);
- colors.AddRange(colorBlend.Colors.Skip(1));
+ for (int j = 0; j < colorBlend.Positions.Length - 1; j++)
+ {
+ pos.Insert(j, (newScale - i - 1) / newScale + colorBlend.Positions[j]);
+ colors.Insert(j, colorBlend.Colors[j]);
+ }
}
}
@@ -249,19 +361,23 @@ namespace Svg
for (var i = 1; i < newScale; i++)
{
- pos.AddRange(from p in colorBlend.Positions select (p <= 0 ? 0.001f : p) / newScale);
+ pos.AddRange(from p in colorBlend.Positions select (i + (p <= 0 ? 0.001f : p)) / newScale);
colors.AddRange(colorBlend.Colors);
}
+ colorBlend.Positions = pos.ToArray();
+ colorBlend.Colors = colors.ToArray();
+ outScale = newScale;
break;
default:
- for (var i = 0; i < colorBlend.Positions.Length - 1; i++)
- {
- colorBlend.Positions[i] = 1 - (1 - colorBlend.Positions[i]) / scale;
- }
+ outScale = 1.0f;
+ //for (var i = 0; i < colorBlend.Positions.Length - 1; i++)
+ //{
+ // colorBlend.Positions[i] = 1 - (1 - colorBlend.Positions[i]) / scale;
+ //}
- colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
- colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
+ //colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
+ //colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
break;
}
@@ -288,4 +404,4 @@ namespace Svg
return newObj;
}
}
-}
\ No newline at end of file
+}
diff --git a/Source/Paths/CoordinateParser.cs b/Source/Paths/CoordinateParser.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5216d23319c062d65aac98c5bf6ebfadc8881124
--- /dev/null
+++ b/Source/Paths/CoordinateParser.cs
@@ -0,0 +1,332 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Globalization;
+
+namespace Svg
+{
+ internal class CoordinateParser
+ {
+ private enum NumState
+ {
+ invalid,
+ separator,
+ prefix,
+ integer,
+ decPlace,
+ fraction,
+ exponent,
+ expPrefix,
+ expValue
+ }
+
+ private string _coords;
+ private int _pos = 0;
+ private NumState _currState = NumState.separator;
+ private NumState _newState = NumState.separator;
+ private int i = 0;
+ private bool _parseWorked = true;
+
+ public int Position { get { return _pos; } }
+
+ public CoordinateParser(string coords)
+ {
+ _coords = coords;
+ if (string.IsNullOrEmpty(_coords)) _parseWorked = false;
+ if (char.IsLetter(coords[0])) i++;
+ }
+
+ public bool HasMore { get { return _parseWorked; } }
+
+ private bool MarkState(bool state)
+ {
+ _parseWorked = state;
+ i++;
+ return state;
+ }
+
+ public bool TryGetBool(out bool result)
+ {
+ while (i < _coords.Length && _parseWorked)
+ {
+ switch (_currState)
+ {
+ case NumState.separator:
+ if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else if (_coords[i] == '0')
+ {
+ result = false;
+ _newState = NumState.separator;
+ _pos = i + 1;
+ return MarkState(true);
+ }
+ else if (_coords[i] == '1')
+ {
+ result = true;
+ _newState = NumState.separator;
+ _pos = i + 1;
+ return MarkState(true);
+ }
+ else
+ {
+ result = false;
+ return MarkState(false);
+ }
+ break;
+ default:
+ result = false;
+ return MarkState(false);
+ }
+ i++;
+ }
+ result = false;
+ return MarkState(false);
+ }
+
+ public bool TryGetFloat(out float result)
+ {
+ while (i < _coords.Length && _parseWorked)
+ {
+ switch (_currState)
+ {
+ case NumState.separator:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.integer;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case '.':
+ _newState = NumState.decPlace;
+ break;
+ case '+':
+ case '-':
+ _newState = NumState.prefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ case NumState.prefix:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.integer;
+ }
+ else if (_coords[i] == '.')
+ {
+ _newState = NumState.decPlace;
+ }
+ else
+ {
+ _newState = NumState.invalid;
+ }
+ break;
+ case NumState.integer:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.integer;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case '.':
+ _newState = NumState.decPlace;
+ break;
+ case 'E':
+ case 'e':
+ _newState = NumState.exponent;
+ break;
+ case '+':
+ case '-':
+ _newState = NumState.prefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ case NumState.decPlace:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.fraction;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case 'E':
+ case 'e':
+ _newState = NumState.exponent;
+ break;
+ case '+':
+ case '-':
+ _newState = NumState.prefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ case NumState.fraction:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.fraction;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case '.':
+ _newState = NumState.decPlace;
+ break;
+ case 'E':
+ case 'e':
+ _newState = NumState.exponent;
+ break;
+ case '+':
+ case '-':
+ _newState = NumState.prefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ case NumState.exponent:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.expValue;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.invalid;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case '+':
+ case '-':
+ _newState = NumState.expPrefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ case NumState.expPrefix:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.expValue;
+ }
+ else
+ {
+ _newState = NumState.invalid;
+ }
+ break;
+ case NumState.expValue:
+ if (char.IsNumber(_coords[i]))
+ {
+ _newState = NumState.expValue;
+ }
+ else if (IsCoordSeparator(_coords[i]))
+ {
+ _newState = NumState.separator;
+ }
+ else
+ {
+ switch (_coords[i])
+ {
+ case '.':
+ _newState = NumState.decPlace;
+ break;
+ case '+':
+ case '-':
+ _newState = NumState.prefix;
+ break;
+ default:
+ _newState = NumState.invalid;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (_newState < _currState)
+ {
+ result = float.Parse(_coords.Substring(_pos, i - _pos), NumberStyles.Float, CultureInfo.InvariantCulture);
+ _pos = i;
+ _currState = _newState;
+ return MarkState(true);
+ }
+ else if (_newState != _currState && _currState == NumState.separator)
+ {
+ _pos = i;
+ }
+
+ if (_newState == NumState.invalid)
+ {
+ result = float.MinValue;
+ return MarkState(false);
+ }
+ _currState = _newState;
+ i++;
+ }
+
+ if (_currState == NumState.separator || !_parseWorked || _pos >= _coords.Length)
+ {
+ result = float.MinValue;
+ return MarkState(false);
+ }
+ else
+ {
+ result = float.Parse(_coords.Substring(_pos, _coords.Length - _pos), NumberStyles.Float, CultureInfo.InvariantCulture);
+ _pos = _coords.Length;
+ return MarkState(true);
+ }
+ }
+
+ private static bool IsCoordSeparator(char value)
+ {
+ switch (value)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case ',':
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Source/Paths/SvgPath.cs b/Source/Paths/SvgPath.cs
index 25de7c228d817bb5d3dda3d1f75f8b78ff19f3ee..66f0b2ef38e9f915aa09eef507d8c19c00af7c7c 100644
--- a/Source/Paths/SvgPath.cs
+++ b/Source/Paths/SvgPath.cs
@@ -139,36 +139,42 @@ namespace Svg
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
- using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity * this.Opacity), 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();
- }
-
- var path = this.Path(renderer);
- renderer.DrawPath(pen, path);
-
- if (this.MarkerStart != null)
- {
- SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerStart.ToString());
- marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
- }
-
- if (this.MarkerMid != null)
- {
- SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerMid.ToString());
- for (int i = 1; i <= path.PathPoints.Length - 2; i++)
- marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
- }
-
- if (this.MarkerEnd != null)
- {
- SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerEnd.ToString());
- marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
- }
- }
+ using (var brush = this.Stroke.GetBrush(this, renderer, this.StrokeOpacity * this.Opacity))
+ {
+ if (brush != null)
+ {
+ using (Pen pen = new Pen(brush, 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();
+ }
+
+ var path = this.Path(renderer);
+ renderer.DrawPath(pen, path);
+
+ if (this.MarkerStart != null)
+ {
+ SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerStart.ToString());
+ marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
+ }
+
+ if (this.MarkerMid != null)
+ {
+ SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerMid.ToString());
+ for (int i = 1; i <= path.PathPoints.Length - 2; i++)
+ marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
+ }
+
+ if (this.MarkerEnd != null)
+ {
+ SvgMarker marker = this.OwnerDocument.GetElementById(this.MarkerEnd.ToString());
+ marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
+ }
+ }
+ }
+ }
}
}
diff --git a/Source/Paths/SvgPathBuilder.cs b/Source/Paths/SvgPathBuilder.cs
index 2bd64fe787f7857d1003e244bad1a545a9e7a62e..91abb2f9f2ea9e7c018c99812dade0bf01e68e38 100644
--- a/Source/Paths/SvgPathBuilder.cs
+++ b/Source/Paths/SvgPathBuilder.cs
@@ -284,322 +284,7 @@ namespace Svg
}
}
- private enum NumState
- {
- invalid,
- separator,
- prefix,
- integer,
- decPlace,
- fraction,
- exponent,
- expPrefix,
- expValue
- }
-
- private class CoordinateParser
- {
- private string _coords;
- private int _pos = 0;
- private NumState _currState = NumState.separator;
- private NumState _newState = NumState.separator;
- private int i = 1;
- private bool _parseWorked = true;
-
- public CoordinateParser(string coords)
- {
- _coords = coords;
- }
-
- public bool HasMore { get { return _parseWorked; } }
-
- private bool MarkState(bool state)
- {
- _parseWorked = state;
- i++;
- return state;
- }
-
- public bool TryGetBool(out bool result)
- {
- while (i < _coords.Length && _parseWorked)
- {
- switch (_currState)
- {
- case NumState.separator:
- if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else if (_coords[i] == '0')
- {
- result = false;
- _newState = NumState.separator;
- _pos = i + 1;
- return MarkState(true);
- }
- else if (_coords[i] == '1')
- {
- result = true;
- _newState = NumState.separator;
- _pos = i + 1;
- return MarkState(true);
- }
- else
- {
- result = false;
- return MarkState(false);
- }
- break;
- default:
- result = false;
- return MarkState(false);
- }
- i++;
- }
- result = false;
- return MarkState(false);
- }
-
- public bool TryGetFloat(out float result)
- {
- while (i < _coords.Length && _parseWorked)
- {
- switch (_currState)
- {
- case NumState.separator:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.integer;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else
- {
- switch (_coords[i])
- {
- case '.':
- _newState = NumState.decPlace;
- break;
- case '+':
- case '-':
- _newState = NumState.prefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- case NumState.prefix:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.integer;
- }
- else if (_coords[i] == '.')
- {
- _newState = NumState.decPlace;
- }
- else
- {
- _newState = NumState.invalid;
- }
- break;
- case NumState.integer:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.integer;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else
- {
- switch (_coords[i])
- {
- case '.':
- _newState = NumState.decPlace;
- break;
- case 'e':
- _newState = NumState.exponent;
- break;
- case '+':
- case '-':
- _newState = NumState.prefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- case NumState.decPlace:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.fraction;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else
- {
- switch (_coords[i])
- {
- case 'e':
- _newState = NumState.exponent;
- break;
- case '+':
- case '-':
- _newState = NumState.prefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- case NumState.fraction:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.fraction;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else
- {
- switch (_coords[i])
- {
- case '.':
- _newState = NumState.decPlace;
- break;
- case 'e':
- _newState = NumState.exponent;
- break;
- case '+':
- case '-':
- _newState = NumState.prefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- case NumState.exponent:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.expValue;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.invalid;
- }
- else
- {
- switch (_coords[i])
- {
- case '+':
- case '-':
- _newState = NumState.expPrefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- case NumState.expPrefix:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.expValue;
- }
- else
- {
- _newState = NumState.invalid;
- }
- break;
- case NumState.expValue:
- if (char.IsNumber(_coords[i]))
- {
- _newState = NumState.expValue;
- }
- else if (IsCoordSeparator(_coords[i]))
- {
- _newState = NumState.separator;
- }
- else
- {
- switch (_coords[i])
- {
- case '.':
- _newState = NumState.decPlace;
- break;
- case '+':
- case '-':
- _newState = NumState.prefix;
- break;
- default:
- _newState = NumState.invalid;
- break;
- }
- }
- break;
- }
-
- if (_newState < _currState)
- {
- result = float.Parse(_coords.Substring(_pos, i - _pos), NumberStyles.Float, CultureInfo.InvariantCulture);
- _pos = i;
- _currState = _newState;
- return MarkState(true);
- }
- else if (_newState != _currState && _currState == NumState.separator)
- {
- _pos = i;
- }
-
- if (_newState == NumState.invalid)
- {
- result = float.MinValue;
- return MarkState(false);
- }
- _currState = _newState;
- i++;
- }
-
- if (_currState == NumState.separator || !_parseWorked || _pos >= _coords.Length)
- {
- result = float.MinValue;
- return MarkState(false);
- }
- else
- {
- result = float.Parse(_coords.Substring(_pos, _coords.Length - _pos), NumberStyles.Float, CultureInfo.InvariantCulture);
- _pos = _coords.Length;
- return MarkState(true);
- }
- }
-
- private static bool IsCoordSeparator(char value)
- {
- switch (value)
- {
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- case ',':
- return true;
- }
- return false;
- }
- }
+
//private static IEnumerable ParseCoordinates(string coords)
//{
diff --git a/Source/Svg.csproj b/Source/Svg.csproj
index 56a1bb4c18dbc68660539c8a15ee37c5861de0a2..946db3587bfb0aa8517363e4803280ad97fe8676 100644
--- a/Source/Svg.csproj
+++ b/Source/Svg.csproj
@@ -100,6 +100,7 @@
+
@@ -108,6 +109,8 @@
+
+
diff --git a/Source/SvgAttributeCollection.cs b/Source/SvgAttributeCollection.cs
index 39e85f64de85069dc18f27dc01bebcd6a88f39d2..ac09091079282828eabc9ceaff7fd6237b2d8cff 100644
--- a/Source/SvgAttributeCollection.cs
+++ b/Source/SvgAttributeCollection.cs
@@ -90,7 +90,9 @@ namespace Svg
(value is SvgTextAnchor && (SvgTextAnchor)value == SvgTextAnchor.inherit) ||
(value is SvgFontVariant && (SvgFontVariant)value == SvgFontVariant.inherit) ||
(value is SvgTextDecoration && (SvgTextDecoration)value == SvgTextDecoration.inherit) ||
- (value is XmlSpaceHandling && (XmlSpaceHandling)value == XmlSpaceHandling.inherit) ||
+ (value is XmlSpaceHandling && (XmlSpaceHandling)value == XmlSpaceHandling.inherit) ||
+ (value is SvgOverflow && (SvgOverflow)value == SvgOverflow.inherit) ||
+ (value == SvgColourServer.Inherit) ||
(value is string && (string)value == "inherit")
);
}
diff --git a/Source/SvgDocument.cs b/Source/SvgDocument.cs
index 1d3d0b22ffd0043f48ca3af35f97b214ae7a84c6..67ae458d6eba9342e4b9cd7e4b98e940f1885aa8 100644
--- a/Source/SvgDocument.cs
+++ b/Source/SvgDocument.cs
@@ -435,7 +435,7 @@ namespace Svg
//Trace.TraceInformation("Begin Render");
var size = GetDimensions();
- var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
+ var bitmap = new Bitmap((int)Math.Round(size.Width), (int)Math.Round(size.Height));
// bitmap.SetResolution(300, 300);
try
{
diff --git a/Source/SvgElementIdManager.cs b/Source/SvgElementIdManager.cs
index a4d10b787517ef89391d04849efeab19b4082bcb..2fab1c2e1eb1e65990d7f3a08ba23deeb45e357c 100644
--- a/Source/SvgElementIdManager.cs
+++ b/Source/SvgElementIdManager.cs
@@ -41,6 +41,7 @@ namespace Svg
public virtual SvgElement GetElementById(Uri uri)
{
+ if (uri.ToString().StartsWith("url(")) uri = new Uri(uri.ToString().Substring(4).TrimEnd(')'), UriKind.Relative);
if (!uri.IsAbsoluteUri && this._document.BaseUri != null && !uri.ToString().StartsWith("#"))
{
var fullUri = new Uri(this._document.BaseUri, uri);
diff --git a/Tests/SvgW3CTestRunner/View.cs b/Tests/SvgW3CTestRunner/View.cs
index f34a3a9369e9e20c3d771a2c2396f615c5bea432..59c0233122571af670a98aeea9dff63c8724a9b3 100644
--- a/Tests/SvgW3CTestRunner/View.cs
+++ b/Tests/SvgW3CTestRunner/View.cs
@@ -18,18 +18,24 @@ namespace SvgW3CTestRunner
{
InitializeComponent();
// ignore tests pertaining to javascript or xml reading
- var files = (from f in (from g in Directory.GetFiles(_svgBasePath)
+ var passes = File.ReadAllLines(_svgBasePath + @"..\PassingTests.txt").ToDictionary((f) => f, (f) => true);
+ var files = (from f in
+ (from g in Directory.GetFiles(_svgBasePath)
select Path.GetFileName(g))
where !f.StartsWith("animate-") && !f.StartsWith("conform-viewer") &&
- !f.Contains("-dom-") && !f.StartsWith("linking-") && !f.StartsWith("interact-")
+ !f.Contains("-dom-") && !f.StartsWith("linking-") && !f.StartsWith("interact-") &&
+ !f.StartsWith("script-")
orderby f
select (object)f);
+ files = files.Where((f) => !passes.ContainsKey((string)f)).Union(Enumerable.Repeat((object)"## PASSING ##", 1)).Union(files.Where((f) => passes.ContainsKey((string)f)));
+
lstFiles.Items.AddRange(files.ToArray());
}
private void lstFiles_SelectedIndexChanged(object sender, EventArgs e)
{
var fileName = lstFiles.SelectedItem.ToString();
+ if (fileName.StartsWith("#")) return;
try
{
Debug.Print(fileName);
diff --git a/Tests/W3CTestSuite/PassingTests.txt b/Tests/W3CTestSuite/PassingTests.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fac06cea1fecc79e9189781537aca46b7de6741c
--- /dev/null
+++ b/Tests/W3CTestSuite/PassingTests.txt
@@ -0,0 +1,159 @@
+color-prof-01-f.svg
+color-prop-01-b.svg
+color-prop-02-f.svg
+color-prop-03-t.svg
+color-prop-04-t.svg
+color-prop-05-t.svg
+coords-coord-01-t.svg
+coords-coord-02-t.svg
+coords-trans-01-b.svg
+coords-trans-02-t.svg
+coords-trans-03-t.svg
+coords-trans-04-t.svg
+coords-trans-05-t.svg
+coords-trans-06-t.svg
+coords-trans-07-t.svg
+coords-trans-08-t.svg
+coords-trans-09-t.svg
+coords-trans-10-f.svg
+coords-trans-11-f.svg
+coords-trans-12-f.svg
+coords-trans-13-f.svg
+coords-trans-14-f.svg
+coords-transformattr-01-f.svg
+coords-transformattr-02-f.svg
+coords-transformattr-03-f.svg
+coords-transformattr-04-f.svg
+coords-transformattr-05-f.svg
+coords-units-01-b.svg
+coords-units-02-b.svg
+coords-units-03-b.svg
+coords-viewattr-01-b.svg
+coords-viewattr-02-b.svg
+coords-viewattr-03-b.svg
+masking-path-01-b.svg
+masking-path-02-b.svg
+masking-path-04-b.svg
+masking-path-05-f.svg
+masking-path-06-b.svg
+masking-path-03-b.svg
+pservers-grad-01-b.svg
+pservers-grad-02-b.svg
+pservers-grad-03-b.svg
+pservers-grad-04-b.svg
+pservers-grad-06-b.svg
+pservers-grad-07-b.svg
+pservers-grad-08-b.svg
+pservers-grad-09-b.svg
+pservers-grad-10-b.svg
+pservers-grad-11-b.svg
+pservers-grad-12-b.svg
+pservers-grad-14-b.svg
+pservers-grad-15-b.svg
+pservers-grad-16-b.svg
+pservers-grad-17-b.svg
+pservers-grad-18-b.svg
+pservers-grad-20-b.svg
+pservers-grad-22-b.svg
+pservers-grad-23-f.svg
+pservers-grad-24-f.svg
+pservers-grad-stops-01-f.svg
+pservers-pattern-01-b.svg
+pservers-pattern-02-f.svg
+pservers-pattern-03-f.svg
+pservers-pattern-04-f.svg
+pservers-pattern-05-f.svg
+pservers-pattern-06-f.svg
+pservers-pattern-07-f.svg
+pservers-pattern-08-f.svg
+pservers-pattern-09-f.svg
+render-elems-01-t.svg
+render-elems-02-t.svg
+render-elems-03-t.svg
+render-elems-06-t.svg
+render-elems-07-t.svg
+render-elems-08-t.svg
+render-groups-03-t.svg
+shapes-circle-01-t.svg
+shapes-circle-02-t.svg
+shapes-ellipse-01-t.svg
+shapes-ellipse-02-t.svg
+shapes-ellipse-03-f.svg
+shapes-grammar-01-f.svg
+shapes-intro-01-t.svg
+shapes-intro-02-f.svg
+shapes-line-01-t.svg
+shapes-line-02-f.svg
+shapes-polygon-01-t.svg
+shapes-polygon-02-t.svg
+shapes-polygon-03-t.svg
+shapes-polyline-01-t.svg
+shapes-polyline-02-t.svg
+shapes-rect-01-t.svg
+shapes-rect-02-t.svg
+shapes-rect-03-t.svg
+shapes-rect-04-f.svg
+shapes-rect-06-f.svg
+shapes-rect-07-f.svg
+struct-frag-06-t.svg
+struct-group-01-t.svg
+struct-group-02-b.svg
+paths-data-01-t.svg
+paths-data-02-t.svg
+paths-data-03-f.svg
+paths-data-04-t.svg
+paths-data-05-t.svg
+paths-data-06-t.svg
+paths-data-07-t.svg
+paths-data-08-t.svg
+paths-data-09-t.svg
+paths-data-12-t.svg
+paths-data-13-t.svg
+paths-data-14-t.svg
+paths-data-15-t.svg
+paths-data-17-f.svg
+paths-data-18-f.svg
+paths-data-19-f.svg
+paths-data-20-f.svg
+struct-image-01-t.svg
+struct-image-04-t.svg
+struct-image-06-t.svg
+masking-path-13-f.svg
+masking-path-14-f.svg
+painting-control-01-f.svg
+painting-control-03-f.svg
+painting-fill-01-t.svg
+painting-fill-02-t.svg
+painting-fill-03-t.svg
+painting-fill-04-t.svg
+painting-fill-05-b.svg
+painting-stroke-01-t.svg
+struct-use-05-b.svg
+styling-class-01-f.svg
+styling-css-01-b.svg
+styling-css-03-b.svg
+styling-css-07-f.svg
+text-align-01-b.svg
+text-align-02-b.svg
+text-align-03-b.svg
+text-fonts-01-t.svg
+text-fonts-02-t.svg
+text-fonts-03-t.svg
+text-fonts-05-f.svg
+text-intro-04-t.svg
+text-intro-06-t.svg
+text-path-01-b.svg
+text-path-02-b.svg
+text-spacing-01-b.svg
+text-text-01-b.svg
+text-text-04-t.svg
+text-text-05-t.svg
+text-text-07-t.svg
+text-text-09-t.svg
+text-text-10-t.svg
+text-text-11-t.svg
+text-tref-01-b.svg
+text-tspan-01-b.svg
+text-ws-01-t.svg
+text-ws-02-t.svg
+text-ws-03-t.svg
\ No newline at end of file