Commit 9e269139 authored by James Welle's avatar James Welle Committed by Eric Domke
Browse files

Gradient Improvements

- Added support for the "gradientTransform" attribute on both linear and
  radial gradients. The matrix in this attribute needs to be applied to
  attributes with coordinate values on the gradient element in order to
  transform them into the correct coordinate space.

- Added support for a value of "pad" for the "spreadMode" attribute on
  both linear and radial gradients. This is the default value but was not
  implemented correctly. In order to implement, we examine the properties
  of the gradient along with the element to which the gradient is being
  applied to determine if we need to expand the bounds of the gradient to
  fill the element. If so, we do so and adjust the color stops and
  positions so they are correct for the new gradient bounds.

- Divided ISvgStylable into ISvgBoundable and ISvgStylable. The
  SvgUnit.ToDeviceValue method just needs bounds so it can take
  ISvgBoundable. Moved SvgDocument.GetDimensions() to SvgFragment and made
  SvgFragment ISvgBoundable.

- Fixed a bug in SvgFragment.PushTransforms where it was calling the
  SvgUnit.ToDeviceValue overload that takes no parameters. This overload
  doesn't work if the value being converted is a percentage. (The overload
  should probably be removed entirely, but we didn't take that on in this
  commit.)

- Fixed an issue in SvgGroup.Bounds where a child with empty bounds would
  cause the group's bounds to be reported as empty.

- Fixed broken build by adding missing SvgMarker.MarkerUnits property.

- Converted files that we touched with mixed tabs and spaces to spaces.
  Also removed unused usings from files we touched.

- Converted SvgLinearGradientServer to use properties without backing
  fields for X1, Y1, etc. in order to match SvgRadialGradientServer.

- Moved default value assignments into constructors for consistency.
parent 4f3df864
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Diagnostics;
namespace Svg namespace Svg
{ {
/// <summary> /// <summary>
/// The class that all SVG elements should derive from when they are to be rendered. /// The class that all SVG elements should derive from when they are to be rendered.
/// </summary> /// </summary>
public abstract partial class SvgVisualElement : SvgElement, ISvgStylable, ISvgClipable public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvgStylable, ISvgClipable
{ {
private bool _dirty; private bool _dirty;
private bool _requiresSmoothRendering; private bool _requiresSmoothRendering;
...@@ -23,6 +17,23 @@ namespace Svg ...@@ -23,6 +17,23 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
public abstract GraphicsPath Path { get; protected set; } public abstract GraphicsPath Path { get; protected set; }
PointF ISvgBoundable.Location
{
get
{
return Bounds.Location;
}
}
SizeF ISvgBoundable.Size
{
get
{
return Bounds.Size;
}
}
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
...@@ -61,15 +72,15 @@ namespace Svg ...@@ -61,15 +72,15 @@ namespace Svg
set { this.Attributes["clip-rule"] = value; } set { this.Attributes["clip-rule"] = value; }
} }
/// <summary> /// <summary>
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified. /// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
/// </summary> /// </summary>
[SvgAttribute("filter")] [SvgAttribute("filter")]
public virtual Uri Filter public virtual Uri Filter
{ {
get { return this.Attributes.GetAttribute<Uri>("filter"); } get { return this.Attributes.GetAttribute<Uri>("filter"); }
set { this.Attributes["filter"] = value; } set { this.Attributes["filter"] = value; }
} }
/// <summary> /// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered. /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
...@@ -131,7 +142,7 @@ namespace Svg ...@@ -131,7 +142,7 @@ namespace Svg
{ {
if (brush != null) if (brush != null)
{ {
this.Path.FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate; this.Path.FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate;
renderer.FillPath(brush, this.Path); renderer.FillPath(brush, this.Path);
} }
} }
...@@ -209,31 +220,31 @@ namespace Svg ...@@ -209,31 +220,31 @@ namespace Svg
this.ResetClip(renderer); this.ResetClip(renderer);
} }
public override SvgElement DeepCopy<T>() public override SvgElement DeepCopy<T>()
{ {
var newObj = base.DeepCopy<T>() as SvgVisualElement; var newObj = base.DeepCopy<T>() as SvgVisualElement;
newObj.ClipPath = this.ClipPath; newObj.ClipPath = this.ClipPath;
newObj.ClipRule = this.ClipRule; newObj.ClipRule = this.ClipRule;
newObj.Filter = this.Filter; newObj.Filter = this.Filter;
newObj.Visible = this.Visible; newObj.Visible = this.Visible;
if (this.Fill != null) if (this.Fill != null)
newObj.Fill = this.Fill; newObj.Fill = this.Fill;
if (this.Stroke != null) if (this.Stroke != null)
newObj.Stroke = this.Stroke; newObj.Stroke = this.Stroke;
newObj.FillRule = this.FillRule; newObj.FillRule = this.FillRule;
newObj.FillOpacity = this.FillOpacity; newObj.FillOpacity = this.FillOpacity;
newObj.StrokeWidth = this.StrokeWidth; newObj.StrokeWidth = this.StrokeWidth;
newObj.StrokeLineCap = this.StrokeLineCap; newObj.StrokeLineCap = this.StrokeLineCap;
newObj.StrokeLineJoin = this.StrokeLineJoin; newObj.StrokeLineJoin = this.StrokeLineJoin;
newObj.StrokeMiterLimit = this.StrokeMiterLimit; newObj.StrokeMiterLimit = this.StrokeMiterLimit;
newObj.StrokeDashArray = this.StrokeDashArray; newObj.StrokeDashArray = this.StrokeDashArray;
newObj.StrokeDashOffset = this.StrokeDashOffset; newObj.StrokeDashOffset = this.StrokeDashOffset;
newObj.StrokeOpacity = this.StrokeOpacity; newObj.StrokeOpacity = this.StrokeOpacity;
newObj.Opacity = this.Opacity; newObj.Opacity = this.Opacity;
return newObj; return newObj;
} }
} }
} }
\ No newline at end of file
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Globalization; using System.Globalization;
namespace Svg namespace Svg
...@@ -73,16 +70,16 @@ namespace Svg ...@@ -73,16 +70,16 @@ namespace Svg
/// Converts the current unit to one that can be used at render time. /// Converts the current unit to one that can be used at render time.
/// </summary> /// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns> /// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(ISvgStylable styleOwner) public float ToDeviceValue(ISvgBoundable boundable)
{ {
return this.ToDeviceValue(styleOwner, false); return this.ToDeviceValue(boundable, false);
} }
/// <summary> /// <summary>
/// Converts the current unit to one that can be used at render time. /// Converts the current unit to one that can be used at render time.
/// </summary> /// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns> /// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(ISvgStylable styleOwner, bool vertical) public float ToDeviceValue(ISvgBoundable boundable, bool vertical)
{ {
// If it's already been calculated // If it's already been calculated
if (this._deviceValue.HasValue) if (this._deviceValue.HasValue)
...@@ -131,14 +128,14 @@ namespace Svg ...@@ -131,14 +128,14 @@ namespace Svg
break; break;
case SvgUnitType.Percentage: case SvgUnitType.Percentage:
// Can't calculate if there is no style owner // Can't calculate if there is no style owner
if (styleOwner == null) if (boundable == null)
{ {
_deviceValue = this.Value; _deviceValue = this.Value;
break; break;
} }
// TODO : Support height percentages // TODO : Support height percentages
System.Drawing.RectangleF size = styleOwner.Bounds; System.Drawing.SizeF size = boundable.Bounds.Size;
_deviceValue = (((vertical) ? size.Height : size.Width) / 100) * this.Value; _deviceValue = (((vertical) ? size.Height : size.Width) / 100) * this.Value;
break; break;
default: default:
...@@ -165,43 +162,43 @@ namespace Svg ...@@ -165,43 +162,43 @@ namespace Svg
} }
} }
#region Equals and GetHashCode implementation #region Equals and GetHashCode implementation
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj == null) return false; if (obj == null) return false;
if (!(obj.GetType() == typeof (SvgUnit))) return false; if (!(obj.GetType() == typeof (SvgUnit))) return false;
var unit = (SvgUnit)obj; var unit = (SvgUnit)obj;
return (unit.Value == this.Value && unit.Type == this.Type); return (unit.Value == this.Value && unit.Type == this.Type);
} }
public bool Equals(SvgUnit other) public bool Equals(SvgUnit other)
{ {
return this._type == other._type && (this._value == other._value); return this._type == other._type && (this._value == other._value);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
int hashCode = 0; int hashCode = 0;
unchecked { unchecked {
hashCode += 1000000007 * _type.GetHashCode(); hashCode += 1000000007 * _type.GetHashCode();
hashCode += 1000000009 * _value.GetHashCode(); hashCode += 1000000009 * _value.GetHashCode();
hashCode += 1000000021 * _isEmpty.GetHashCode(); hashCode += 1000000021 * _isEmpty.GetHashCode();
hashCode += 1000000033 * _deviceValue.GetHashCode(); hashCode += 1000000033 * _deviceValue.GetHashCode();
} }
return hashCode; return hashCode;
} }
public static bool operator ==(SvgUnit lhs, SvgUnit rhs) public static bool operator ==(SvgUnit lhs, SvgUnit rhs)
{ {
return lhs.Equals(rhs); return lhs.Equals(rhs);
} }
public static bool operator !=(SvgUnit lhs, SvgUnit rhs) public static bool operator !=(SvgUnit lhs, SvgUnit rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
#endregion #endregion
public override string ToString() public override string ToString()
{ {
......
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Drawing.Drawing2D;
using System.Drawing; using System.Drawing;
using System.ComponentModel; using System.Drawing.Drawing2D;
namespace Svg namespace Svg
{ {
...@@ -12,13 +8,37 @@ namespace Svg ...@@ -12,13 +8,37 @@ namespace Svg
/// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document. /// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
/// </summary> /// </summary>
[SvgElement("svg")] [SvgElement("svg")]
public class SvgFragment : SvgElement, ISvgViewPort public class SvgFragment : SvgElement, ISvgViewPort, ISvgBoundable
{ {
/// <summary> /// <summary>
/// Gets the SVG namespace string. /// Gets the SVG namespace string.
/// </summary> /// </summary>
public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg"); public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg");
PointF ISvgBoundable.Location
{
get
{
return PointF.Empty;
}
}
SizeF ISvgBoundable.Size
{
get
{
return GetDimensions();
}
}
RectangleF ISvgBoundable.Bounds
{
get
{
return new RectangleF(((ISvgBoundable)this).Location, ((ISvgBoundable)this).Size);
}
}
private SvgUnit _x; private SvgUnit _x;
private SvgUnit _y; private SvgUnit _y;
...@@ -28,15 +48,15 @@ namespace Svg ...@@ -28,15 +48,15 @@ namespace Svg
[SvgAttribute("x")] [SvgAttribute("x")]
public SvgUnit X public SvgUnit X
{ {
get { return _x; } get { return _x; }
set set
{ {
if(_x != value) if(_x != value)
{ {
_x = value; _x = value;
OnAttributeChanged(new AttributeEventArgs{ Attribute = "x", Value = value }); OnAttributeChanged(new AttributeEventArgs{ Attribute = "x", Value = value });
} }
} }
} }
/// <summary> /// <summary>
...@@ -45,15 +65,15 @@ namespace Svg ...@@ -45,15 +65,15 @@ namespace Svg
[SvgAttribute("y")] [SvgAttribute("y")]
public SvgUnit Y public SvgUnit Y
{ {
get { return _y; } get { return _y; }
set set
{ {
if(_y != value) if(_y != value)
{ {
_y = value; _y = value;
OnAttributeChanged(new AttributeEventArgs{ Attribute = "y", Value = value }); OnAttributeChanged(new AttributeEventArgs{ Attribute = "y", Value = value });
} }
} }
} }
/// <summary> /// <summary>
...@@ -64,7 +84,7 @@ namespace Svg ...@@ -64,7 +84,7 @@ namespace Svg
public SvgUnit Width public SvgUnit Width
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("width"); } get { return this.Attributes.GetAttribute<SvgUnit>("width"); }
set { this.Attributes["width"] = value; } set { this.Attributes["width"] = value; }
} }
/// <summary> /// <summary>
...@@ -75,15 +95,15 @@ namespace Svg ...@@ -75,15 +95,15 @@ namespace Svg
public SvgUnit Height public SvgUnit Height
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("height"); } get { return this.Attributes.GetAttribute<SvgUnit>("height"); }
set { this.Attributes["height"] = value; } set { this.Attributes["height"] = value; }
} }
[SvgAttribute("overflow")] [SvgAttribute("overflow")]
public virtual SvgOverflow Overflow public virtual SvgOverflow Overflow
{ {
get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); } get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); }
set { this.Attributes["overflow"] = value; } set { this.Attributes["overflow"] = value; }
} }
/// <summary> /// <summary>
/// Gets or sets the viewport of the element. /// Gets or sets the viewport of the element.
...@@ -100,11 +120,11 @@ namespace Svg ...@@ -100,11 +120,11 @@ namespace Svg
/// Gets or sets the aspect of the viewport. /// Gets or sets the aspect of the viewport.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
[SvgAttribute("preserveAspectRatio")] [SvgAttribute("preserveAspectRatio")]
public SvgAspectRatio AspectRatio public SvgAspectRatio AspectRatio
{ {
get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); } get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
set { this.Attributes["preserveAspectRatio"] = value; } set { this.Attributes["preserveAspectRatio"] = value; }
} }
/// <summary> /// <summary>
...@@ -117,68 +137,68 @@ namespace Svg ...@@ -117,68 +137,68 @@ namespace Svg
if (!this.ViewBox.Equals(SvgViewBox.Empty)) if (!this.ViewBox.Equals(SvgViewBox.Empty))
{ {
float fScaleX = this.Width.ToDeviceValue() / this.ViewBox.Width; float fScaleX = this.Width.ToDeviceValue(this, false) / this.ViewBox.Width;
float fScaleY = this.Height.ToDeviceValue() / this.ViewBox.Height; float fScaleY = this.Height.ToDeviceValue(this, true) / this.ViewBox.Height;
float fMinX = -this.ViewBox.MinX; float fMinX = -this.ViewBox.MinX;
float fMinY = -this.ViewBox.MinY; float fMinY = -this.ViewBox.MinY;
if (AspectRatio.Align != SvgPreserveAspectRatio.none) if (AspectRatio.Align != SvgPreserveAspectRatio.none)
{ {
if (AspectRatio.Slice) if (AspectRatio.Slice)
{ {
fScaleX = Math.Max(fScaleX, fScaleY); fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY); fScaleY = Math.Max(fScaleX, fScaleY);
} }
else else
{ {
fScaleX = Math.Min(fScaleX, fScaleY); fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY); fScaleY = Math.Min(fScaleX, fScaleY);
} }
float fViewMidX = (this.ViewBox.Width / 2) * fScaleX; float fViewMidX = (this.ViewBox.Width / 2) * fScaleX;
float fViewMidY = (this.ViewBox.Height / 2) * fScaleY; float fViewMidY = (this.ViewBox.Height / 2) * fScaleY;
float fMidX = this.Width.ToDeviceValue() / 2; float fMidX = this.Width.ToDeviceValue(this, false) / 2;
float fMidY = this.Height.ToDeviceValue() / 2; float fMidY = this.Height.ToDeviceValue(this, true) / 2;
switch (AspectRatio.Align) switch (AspectRatio.Align)
{ {
case SvgPreserveAspectRatio.xMinYMin: case SvgPreserveAspectRatio.xMinYMin:
break; break;
case SvgPreserveAspectRatio.xMidYMin: case SvgPreserveAspectRatio.xMidYMin:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += (fMidX - fViewMidX) / fScaleX;
break; break;
case SvgPreserveAspectRatio.xMaxYMin: case SvgPreserveAspectRatio.xMaxYMin:
fMinX += (this.Width.ToDeviceValue() / fScaleX) - this.ViewBox.Width; fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width;
break; break;
case SvgPreserveAspectRatio.xMinYMid: case SvgPreserveAspectRatio.xMinYMid:
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += (fMidY - fViewMidY) / fScaleY;
break; break;
case SvgPreserveAspectRatio.xMidYMid: case SvgPreserveAspectRatio.xMidYMid:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += (fMidX - fViewMidX) / fScaleX;
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += (fMidY - fViewMidY) / fScaleY;
break; break;
case SvgPreserveAspectRatio.xMaxYMid: case SvgPreserveAspectRatio.xMaxYMid:
fMinX += (this.Width.ToDeviceValue() / fScaleX) - this.ViewBox.Width; fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width;
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += (fMidY - fViewMidY) / fScaleY;
break; break;
case SvgPreserveAspectRatio.xMinYMax: case SvgPreserveAspectRatio.xMinYMax:
fMinY += (this.Height.ToDeviceValue() / fScaleY) - this.ViewBox.Height; fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height;
break; break;
case SvgPreserveAspectRatio.xMidYMax: case SvgPreserveAspectRatio.xMidYMax:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += (fMidX - fViewMidX) / fScaleX;
fMinY += (this.Height.ToDeviceValue() / fScaleY) - this.ViewBox.Height; fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height;
break; break;
case SvgPreserveAspectRatio.xMaxYMax: case SvgPreserveAspectRatio.xMaxYMax:
fMinX += (this.Width.ToDeviceValue() / fScaleX) - this.ViewBox.Width; fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width;
fMinY += (this.Height.ToDeviceValue() / fScaleY) - this.ViewBox.Height; fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height;
break; break;
default: default:
break; break;
} }
} }
renderer.TranslateTransform(_x, _y); renderer.TranslateTransform(_x, _y);
renderer.TranslateTransform(fMinX, fMinY); renderer.TranslateTransform(fMinX, fMinY);
renderer.ScaleTransform(fScaleX, fScaleY); renderer.ScaleTransform(fScaleX, fScaleY);
} }
} }
...@@ -190,11 +210,11 @@ namespace Svg ...@@ -190,11 +210,11 @@ namespace Svg
{ {
get get
{ {
var path = new GraphicsPath(); var path = new GraphicsPath();
AddPaths(this, path); AddPaths(this, path);
return path; return path;
} }
} }
...@@ -204,10 +224,10 @@ namespace Svg ...@@ -204,10 +224,10 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public RectangleF Bounds public RectangleF Bounds
{ {
get get
{ {
return this.Path.GetBounds(); return this.Path.GetBounds();
} }
} }
/// <summary> /// <summary>
...@@ -215,7 +235,7 @@ namespace Svg ...@@ -215,7 +235,7 @@ namespace Svg
/// </summary> /// </summary>
public SvgFragment() public SvgFragment()
{ {
_x = 0.0f; _x = 0.0f;
_y = 0.0f; _y = 0.0f;
this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f); this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f);
this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f); this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f);
...@@ -223,21 +243,41 @@ namespace Svg ...@@ -223,21 +243,41 @@ namespace Svg
this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid); this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid);
} }
public SizeF GetDimensions()
{
var w = Width.ToDeviceValue();
var h = Height.ToDeviceValue();
RectangleF bounds = new RectangleF();
var isWidthperc = Width.Type == SvgUnitType.Percentage;
var isHeightperc = Height.Type == SvgUnitType.Percentage;
if (isWidthperc || isHeightperc)
{
bounds = this.Bounds; //do just one call to the recursive bounds property
if (isWidthperc) w = (bounds.Width + bounds.X) * (w * 0.01f);
if (isHeightperc) h = (bounds.Height + bounds.Y) * (h * 0.01f);
}
return new SizeF(w, h);
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgFragment>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgFragment;
newObj.Height = this.Height;
newObj.Width = this.Width;
newObj.Overflow = this.Overflow;
newObj.ViewBox = this.ViewBox;
newObj.AspectRatio = this.AspectRatio;
return newObj;
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgFragment>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgFragment;
newObj.Height = this.Height;
newObj.Width = this.Width;
newObj.Overflow = this.Overflow;
newObj.ViewBox = this.ViewBox;
newObj.AspectRatio = this.AspectRatio;
return newObj;
}
} }
} }
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Linq;
using Svg.Transforms;
namespace Svg namespace Svg
{ {
...@@ -27,10 +21,10 @@ namespace Svg ...@@ -27,10 +21,10 @@ namespace Svg
{ {
get get
{ {
//var path = new GraphicsPath(); //var path = new GraphicsPath();
//AddPaths(this, path); //AddPaths(this, path);
return GetPaths(this); return GetPaths(this);
} }
protected set protected set
{ } { }
...@@ -44,21 +38,29 @@ namespace Svg ...@@ -44,21 +38,29 @@ namespace Svg
{ {
get get
{ {
var r = new RectangleF(); var r = new RectangleF();
foreach(var c in this.Children) foreach(var c in this.Children)
{ {
if (c is SvgVisualElement) if (c is SvgVisualElement)
{ {
// First it should check if rectangle is empty or it will return the wrong Bounds. // First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0 // This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty) if (r.IsEmpty)
{
r = ((SvgVisualElement)c).Bounds; r = ((SvgVisualElement)c).Bounds;
}
else else
r = RectangleF.Union(r, ((SvgVisualElement)c).Bounds); {
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
}
} }
} }
return r; return r;
} }
} }
...@@ -78,18 +80,18 @@ namespace Svg ...@@ -78,18 +80,18 @@ namespace Svg
this.PopTransforms(renderer); this.PopTransforms(renderer);
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
{ {
return DeepCopy<SvgGroup>(); return DeepCopy<SvgGroup>();
} }
public override SvgElement DeepCopy<T>() public override SvgElement DeepCopy<T>()
{ {
var newObj = base.DeepCopy<T>() as SvgGroup; var newObj = base.DeepCopy<T>() as SvgGroup;
if (this.Fill != null) if (this.Fill != null)
newObj.Fill = this.Fill.DeepCopy() as SvgPaintServer; newObj.Fill = this.Fill.DeepCopy() as SvgPaintServer;
return newObj; return newObj;
} }
} }
} }
using System.Drawing;
namespace Svg
{
public interface ISvgBoundable
{
PointF Location
{
get;
}
SizeF Size
{
get;
}
RectangleF Bounds
{
get;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
namespace Svg namespace Svg
...@@ -24,6 +20,5 @@ namespace Svg ...@@ -24,6 +20,5 @@ namespace Svg
SvgUnitCollection StrokeDashArray { get; set; } SvgUnitCollection StrokeDashArray { get; set; }
SvgUnit StrokeDashOffset { get; set; } SvgUnit StrokeDashOffset { get; set; }
GraphicsPath Path { get; } GraphicsPath Path { get; }
RectangleF Bounds { get; }
} }
} }
\ No newline at end of file
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using Svg.Transforms;
namespace Svg namespace Svg
{ {
...@@ -10,7 +12,7 @@ namespace Svg ...@@ -10,7 +12,7 @@ namespace Svg
public abstract class SvgGradientServer : SvgPaintServer public abstract class SvgGradientServer : SvgPaintServer
{ {
private SvgCoordinateUnits _gradientUnits; private SvgCoordinateUnits _gradientUnits;
private SvgGradientSpreadMethod _spreadMethod = SvgGradientSpreadMethod.Pad; private SvgGradientSpreadMethod _spreadMethod;
private SvgGradientServer _inheritGradient; private SvgGradientServer _inheritGradient;
private List<SvgGradientStop> _stops; private List<SvgGradientStop> _stops;
...@@ -20,6 +22,7 @@ namespace Svg ...@@ -20,6 +22,7 @@ namespace Svg
internal SvgGradientServer() internal SvgGradientServer()
{ {
this.GradientUnits = SvgCoordinateUnits.ObjectBoundingBox; this.GradientUnits = SvgCoordinateUnits.ObjectBoundingBox;
this.SpreadMethod = SvgGradientSpreadMethod.Pad;
this._stops = new List<SvgGradientStop>(); this._stops = new List<SvgGradientStop>();
} }
...@@ -96,12 +99,39 @@ namespace Svg ...@@ -96,12 +99,39 @@ namespace Svg
} }
} }
[SvgAttribute("gradientTransform")]
public SvgTransformCollection GradientTransform
{
get
{
return (this.Attributes.GetAttribute<SvgTransformCollection>("gradientTransform"));
}
set
{
this.Attributes["gradientTransform"] = value;
}
}
private Matrix EffectiveGradientTransform
{
get
{
var transform = new Matrix();
if (GradientTransform != null)
{
transform.Multiply(GradientTransform.GetMatrix());
}
return transform;
}
}
/// <summary> /// <summary>
/// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops. /// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
/// </summary> /// </summary>
/// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param> /// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the colour blend.</param> /// <param name="opacity">The opacity of the colour blend.</param>
protected ColorBlend GetColourBlend(SvgVisualElement owner, float opacity, bool radial) protected ColorBlend GetColorBlend(SvgVisualElement owner, float opacity, bool radial)
{ {
int colourBlends = this.Stops.Count; int colourBlends = this.Stops.Count;
bool insertStart = false; bool insertStart = false;
...@@ -201,16 +231,49 @@ namespace Svg ...@@ -201,16 +231,49 @@ namespace Svg
} }
} }
protected ISvgBoundable CalculateBoundable(SvgVisualElement renderingElement)
{
return (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? (ISvgBoundable)renderingElement : renderingElement.OwnerDocument;
}
public override SvgElement DeepCopy<T>() protected PointF TransformPoint(PointF originalPoint)
{ {
var newObj = base.DeepCopy<T>() as SvgGradientServer; 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 static double CalculateDistance(PointF first, PointF second)
{
return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
}
protected static float CalculateLength(PointF vector)
{
return (float)Math.Sqrt(Math.Pow(vector.X, 2) + Math.Pow(vector.Y, 2));
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgGradientServer;
newObj.SpreadMethod = this.SpreadMethod; newObj.SpreadMethod = this.SpreadMethod;
newObj.GradientUnits = this.GradientUnits; newObj.GradientUnits = this.GradientUnits;
newObj.InheritGradient = this.InheritGradient; newObj.InheritGradient = this.InheritGradient;
newObj.GradientTransform = this.GradientTransform;
return newObj; return newObj;
} }
} }
} }
\ No newline at end of file
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Drawing; using System.Drawing;
using System.ComponentModel; using System.Drawing.Drawing2D;
using System.Linq;
namespace Svg namespace Svg
{ {
[SvgElement("linearGradient")] [SvgElement("linearGradient")]
public sealed class SvgLinearGradientServer : SvgGradientServer public sealed class SvgLinearGradientServer : SvgGradientServer
{ {
private SvgUnit _x1; [SvgAttribute("x1")]
private SvgUnit _y1;
private SvgUnit _x2;
private SvgUnit _y2;
[DefaultValue(typeof(SvgUnit), "0"), SvgAttribute("x1")]
public SvgUnit X1 public SvgUnit X1
{ {
get { return this._x1; } get
{
return this.Attributes.GetAttribute<SvgUnit>("x1");
}
set set
{ {
this._x1 = value; Attributes["x1"] = value;
} }
} }
[DefaultValue(typeof(SvgUnit), "0"), SvgAttribute("y1")] [SvgAttribute("y1")]
public SvgUnit Y1 public SvgUnit Y1
{ {
get { return this._y1; } get
{
return this.Attributes.GetAttribute<SvgUnit>("y1");
}
set set
{ {
this._y1 = value; this.Attributes["y1"] = value;
} }
} }
[DefaultValue(typeof(SvgUnit), "0"), SvgAttribute("x2")] [SvgAttribute("x2")]
public SvgUnit X2 public SvgUnit X2
{ {
get { return this._x2; } get
{
return this.Attributes.GetAttribute<SvgUnit>("x2");
}
set set
{ {
this._x2 = value; Attributes["x2"] = value;
} }
} }
[DefaultValue(typeof(SvgUnit), "0"), SvgAttribute("y2")] [SvgAttribute("y2")]
public SvgUnit Y2 public SvgUnit Y2
{ {
get { return this._y2; } get
{
return this.Attributes.GetAttribute<SvgUnit>("y2");
}
set set
{ {
this._y2 = value; this.Attributes["y2"] = value;
}
}
private bool IsInvalid
{
get
{
// Need at least 2 colours to do the gradient fill
return this.Stops.Count < 2;
} }
} }
public SvgLinearGradientServer() public SvgLinearGradientServer()
{ {
this._x1 = new SvgUnit(0.0f); X1 = new SvgUnit(SvgUnitType.Percentage, 0F);
this._y1 = new SvgUnit(0.0f); Y1 = new SvgUnit(SvgUnitType.Percentage, 0F);
this._x2 = new SvgUnit(0.0f); X2 = new SvgUnit(SvgUnitType.Percentage, 100F);
this._y2 = new SvgUnit(0.0f); Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
}
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
{
if (IsInvalid)
{
return null;
}
var boundable = CalculateBoundable(renderingElement);
var specifiedStart = CalculateStart(boundable);
var specifiedEnd = CalculateEnd(boundable);
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd))
{
var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd);
effectiveStart = expansion.Item1;
effectiveEnd = expansion.Item2;
}
return new LinearGradientBrush(effectiveStart, effectiveEnd, Color.Transparent, Color.Transparent)
{
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
}
private PointF CalculateStart(ISvgBoundable boundable)
{
return TransformPoint(new PointF(this.X1.ToDeviceValue(boundable), this.Y1.ToDeviceValue(boundable, true)));
} }
public SvgPoint Start private PointF CalculateEnd(ISvgBoundable boundable)
{ {
get { return new SvgPoint(this.X1, this.Y1); } return TransformPoint(new PointF(this.X2.ToDeviceValue(boundable), this.Y2.ToDeviceValue(boundable, true)));
} }
public SvgPoint End private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{ {
get { return new SvgPoint(this.X2, this.Y2); } return SpreadMethod == SvgGradientSpreadMethod.Pad && (boundable.Bounds.Contains(specifiedStart) || boundable.Bounds.Contains(specifiedEnd));
} }
public override Brush GetBrush(SvgVisualElement owner, float opacity) private Tuple<PointF, PointF> ExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{ {
// Need at least 2 colours to do the gradient fill if (!NeedToExpandGradient(boundable, specifiedStart, specifiedEnd))
if (this.Stops.Count < 2)
{ {
return null; Debug.Fail("Unexpectedly expanding gradient when not needed!");
return new Tuple<PointF, PointF>(specifiedStart, specifiedEnd);
} }
PointF start; var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
PointF end; var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
RectangleF bounds = (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? owner.Bounds : owner.OwnerDocument.GetDimensions();
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
var elementDiagonal = (float)CalculateDistance(new PointF(boundable.Bounds.Left, boundable.Bounds.Top), new PointF(boundable.Bounds.Right, boundable.Bounds.Bottom));
var expandedStart = MovePointAlongVector(effectiveStart, specifiedUnitVector, -elementDiagonal);
var expandedEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, elementDiagonal);
var intersectionPoints = new LineF(expandedStart.X, expandedStart.Y, expandedEnd.X, expandedEnd.Y).Intersection(boundable.Bounds);
// Have start/end points been set? If not the gradient is horizontal if (boundable.Bounds.Contains(specifiedStart))
if (!this.End.IsEmpty())
{ {
// Get the points to work out an angle effectiveStart = CalculateClosestIntersectionPoint(expandedStart, intersectionPoints);
if (this.Start.IsEmpty())
{ effectiveStart = MovePointAlongVector(effectiveStart, specifiedUnitVector, -1);
start = bounds.Location; }
}
else if (boundable.Bounds.Contains(specifiedEnd))
{ {
start = new PointF(this.Start.X.ToDeviceValue(owner), this.Start.Y.ToDeviceValue(owner, true)); effectiveEnd = CalculateClosestIntersectionPoint(effectiveEnd, intersectionPoints);
}
effectiveEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, 1);
}
return new Tuple<PointF, PointF>(effectiveStart, effectiveEnd);
}
private ColorBlend CalculateColorBlend(SvgVisualElement owner, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{
var colorBlend = GetColorBlend(owner, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart);
var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
if (!(startDelta > 0) && !(endDelta > 0))
{
return colorBlend;
}
var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
var effectiveLength = CalculateDistance(effectiveStart, effectiveEnd);
for (var i = 0; i < colorBlend.Positions.Length; i++)
{
var originalPoint = MovePointAlongVector(specifiedStart, specifiedUnitVector, (float) specifiedLength * colorBlend.Positions[i]);
float x = (this.End.X.IsEmpty) ? start.X : this.End.X.ToDeviceValue(owner); var distanceFromEffectiveStart = CalculateDistance(effectiveStart, originalPoint);
end = new PointF(x, this.End.Y.ToDeviceValue(owner, true));
colorBlend.Positions[i] = (float) Math.Max(0F, Math.Min((distanceFromEffectiveStart / effectiveLength), 1.0F));
}
if (startDelta > 0)
{
colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
} }
else
if (endDelta > 0)
{ {
// Default: horizontal colorBlend.Positions = colorBlend.Positions.Concat(new[] { 1F }).ToArray();
start = bounds.Location; colorBlend.Colors = colorBlend.Colors.Concat(new[] { colorBlend.Colors.Last() }).ToArray();
end = new PointF(bounds.Right, bounds.Top);
} }
LinearGradientBrush gradient = new LinearGradientBrush(start, end, Color.Transparent, Color.Transparent); return colorBlend;
gradient.InterpolationColors = base.GetColourBlend(owner, opacity, false); }
private static PointF CalculateClosestIntersectionPoint(PointF sourcePoint, IList<PointF> targetPoints)
{
Debug.Assert(targetPoints.Count == 2, "Unexpected number of intersection points!");
return CalculateDistance(sourcePoint, targetPoints[0]) < CalculateDistance(sourcePoint, targetPoints[1]) ? targetPoints[0] : targetPoints[1];
}
private static PointF MovePointAlongVector(PointF start, PointF unitVector, float distance)
{
return start + new SizeF(unitVector.X * distance, unitVector.Y * distance);
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgLinearGradientServer>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgLinearGradientServer;
newObj.X1 = this.X1;
newObj.Y1 = this.Y1;
newObj.X2 = this.X2;
newObj.Y2 = this.Y2;
return newObj;
// Needed to fix an issue where the gradient was being wrapped when though it had the correct bounds
gradient.WrapMode = WrapMode.TileFlipX;
return gradient;
} }
private sealed class LineF
{
private float X1
{
get;
set;
}
private float Y1
{
get;
set;
}
private float X2
{
get;
set;
}
private float Y2
{
get;
set;
}
public LineF(float x1, float y1, float x2, float y2)
{
X1 = x1;
Y1 = y1;
X2 = x2;
Y2 = y2;
}
public List<PointF> Intersection(RectangleF rectangle)
{
var result = new List<PointF>();
AddIfIntersect(this, new LineF(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Y), result);
AddIfIntersect(this, new LineF(rectangle.Right, rectangle.Y, rectangle.Right, rectangle.Bottom), result);
AddIfIntersect(this, new LineF(rectangle.Right, rectangle.Bottom, rectangle.X, rectangle.Bottom), result);
AddIfIntersect(this, new LineF(rectangle.X, rectangle.Bottom, rectangle.X, rectangle.Y), result);
return result;
}
private PointF? Intersection(LineF other)
{
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;
public override SvgElement DeepCopy() if (r1 != 0 && r2 != 0 && Math.Sign(r1) == Math.Sign(r2))
{ {
return DeepCopy<SvgLinearGradientServer>(); return (null);
} }
var denom = a1 * b2 - a2 * b1;
if (denom == 0)
{
return null;
}
var offset = denom < 0 ? -denom / 2 : denom / 2;
public override SvgElement DeepCopy<T>() var num = b1 * c2 - b2 * c1;
{ var x = (num < 0 ? num - offset : num + offset) / denom;
var newObj = base.DeepCopy<T>() as SvgLinearGradientServer;
newObj.X1 = this.X1;
newObj.Y1 = this.Y1;
newObj.X2 = this.X2;
newObj.Y2 = this.Y2;
return newObj;
} 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<PointF> result)
{
var intersection = first.Intersection(second);
if (intersection != null)
{
result.Add(intersection.Value);
}
}
}
} }
} }
\ No newline at end of file
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
using System.Drawing.Drawing2D;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using Svg.DataTypes; using Svg.DataTypes;
namespace Svg namespace Svg
...@@ -14,7 +9,7 @@ namespace Svg ...@@ -14,7 +9,7 @@ namespace Svg
[SvgElement("marker")] [SvgElement("marker")]
public class SvgMarker : SvgVisualElement, ISvgViewPort public class SvgMarker : SvgVisualElement, ISvgViewPort
{ {
private SvgOrient _svgOrient = new SvgOrient(); private SvgOrient _svgOrient = new SvgOrient();
[SvgAttribute("refX")] [SvgAttribute("refX")]
public virtual SvgUnit RefX public virtual SvgUnit RefX
...@@ -31,68 +26,75 @@ namespace Svg ...@@ -31,68 +26,75 @@ namespace Svg
} }
[SvgAttribute("orient")] [SvgAttribute("orient")]
public virtual SvgOrient Orient public virtual SvgOrient Orient
{ {
get { return _svgOrient; } get { return _svgOrient; }
set { _svgOrient = value; } set { _svgOrient = value; }
} }
[SvgAttribute("overflow")] [SvgAttribute("overflow")]
public virtual SvgOverflow Overflow public virtual SvgOverflow Overflow
{ {
get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); } get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); }
set { this.Attributes["overflow"] = value; } set { this.Attributes["overflow"] = value; }
} }
[SvgAttribute("viewBox")] [SvgAttribute("viewBox")]
public virtual SvgViewBox ViewBox public virtual SvgViewBox ViewBox
{ {
get { return this.Attributes.GetAttribute<SvgViewBox>("viewBox"); } get { return this.Attributes.GetAttribute<SvgViewBox>("viewBox"); }
set { this.Attributes["viewBox"] = value; } set { this.Attributes["viewBox"] = value; }
} }
[SvgAttribute("preserveAspectRatio")] [SvgAttribute("preserveAspectRatio")]
public virtual SvgAspectRatio AspectRatio public virtual SvgAspectRatio AspectRatio
{ {
get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); } get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
set { this.Attributes["preserveAspectRatio"] = value; } set { this.Attributes["preserveAspectRatio"] = value; }
} }
[SvgAttribute("markerWidth")] [SvgAttribute("markerWidth")]
public virtual SvgUnit MarkerWidth public virtual SvgUnit MarkerWidth
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("markerWidth"); } get { return this.Attributes.GetAttribute<SvgUnit>("markerWidth"); }
set { this.Attributes["markerWidth"] = value; } set { this.Attributes["markerWidth"] = value; }
} }
[SvgAttribute("markerHeight")] [SvgAttribute("markerHeight")]
public virtual SvgUnit MarkerHeight public virtual SvgUnit MarkerHeight
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("markerHeight"); } get { return this.Attributes.GetAttribute<SvgUnit>("markerHeight"); }
set { this.Attributes["markerHeight"] = value; } set { this.Attributes["markerHeight"] = value; }
} }
public SvgMarker() [SvgAttribute("markerUnits")]
{ public virtual SvgMarkerUnits MarkerUnits
MarkerUnits = SvgMarkerUnits.strokeWidth; {
MarkerHeight = 3; get { return this.Attributes.GetAttribute<SvgMarkerUnits>("markerUnits"); }
MarkerWidth = 3; set { this.Attributes["markerUnits"] = value; }
Overflow = SvgOverflow.hidden; }
}
public SvgMarker()
public override System.Drawing.Drawing2D.GraphicsPath Path {
MarkerUnits = SvgMarkerUnits.strokeWidth;
MarkerHeight = 3;
MarkerWidth = 3;
Overflow = SvgOverflow.hidden;
}
public override System.Drawing.Drawing2D.GraphicsPath Path
{ {
get get
{ {
var path = this.Children.FirstOrDefault(x => x is SvgPath); var path = this.Children.FirstOrDefault(x => x is SvgPath);
if (path != null) if (path != null)
return (path as SvgPath).Path; return (path as SvgPath).Path;
return null; return null;
} }
protected set protected set
{ {
...@@ -104,172 +106,172 @@ namespace Svg ...@@ -104,172 +106,172 @@ namespace Svg
{ {
get get
{ {
var path = this.Path; var path = this.Path;
if (path != null) if (path != null)
return path.GetBounds(); return path.GetBounds();
return new System.Drawing.RectangleF(); return new System.Drawing.RectangleF();
} }
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
{ {
return DeepCopy<SvgMarker>(); return DeepCopy<SvgMarker>();
} }
public override SvgElement DeepCopy<T>() public override SvgElement DeepCopy<T>()
{ {
var newObj = base.DeepCopy<T>() as SvgMarker; var newObj = base.DeepCopy<T>() as SvgMarker;
newObj.RefX = this.RefX; newObj.RefX = this.RefX;
newObj.RefY = this.RefY; newObj.RefY = this.RefY;
newObj.Orient = this.Orient; newObj.Orient = this.Orient;
newObj.ViewBox = this.ViewBox; newObj.ViewBox = this.ViewBox;
newObj.Overflow = this.Overflow; newObj.Overflow = this.Overflow;
newObj.AspectRatio = this.AspectRatio; newObj.AspectRatio = this.AspectRatio;
return newObj; return newObj;
} }
/// <summary> /// <summary>
/// Render this marker using the slope of the given line segment /// Render this marker using the slope of the given line segment
/// </summary> /// </summary>
/// <param name="pRenderer"></param> /// <param name="pRenderer"></param>
/// <param name="pOwner"></param> /// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param> /// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param> /// <param name="pMarkerPoint2"></param>
public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2) public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
{ {
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X; float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y; float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
RenderPart2(fAngle1, pRenderer, pOwner, pRefPoint); RenderPart2(fAngle1, pRenderer, pOwner, pRefPoint);
} }
/// <summary> /// <summary>
/// Render this marker using the average of the slopes of the two given line segments /// Render this marker using the average of the slopes of the two given line segments
/// </summary> /// </summary>
/// <param name="pRenderer"></param> /// <param name="pRenderer"></param>
/// <param name="pOwner"></param> /// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param> /// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param> /// <param name="pMarkerPoint2"></param>
/// <param name="pMarkerPoint3"></param> /// <param name="pMarkerPoint3"></param>
public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3) public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
{ {
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X; float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y; float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
xDiff = pMarkerPoint3.X - pMarkerPoint2.X; xDiff = pMarkerPoint3.X - pMarkerPoint2.X;
yDiff = pMarkerPoint3.Y - pMarkerPoint2.Y; yDiff = pMarkerPoint3.Y - pMarkerPoint2.Y;
float fAngle2 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); float fAngle2 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
RenderPart2((fAngle1 + fAngle2) / 2, pRenderer, pOwner, pRefPoint); RenderPart2((fAngle1 + fAngle2) / 2, pRenderer, pOwner, pRefPoint);
} }
/// <summary> /// <summary>
/// Common code for rendering a marker once the orientation angle has been calculated /// Common code for rendering a marker once the orientation angle has been calculated
/// </summary> /// </summary>
/// <param name="fAngle"></param> /// <param name="fAngle"></param>
/// <param name="pRenderer"></param> /// <param name="pRenderer"></param>
/// <param name="pOwner"></param> /// <param name="pOwner"></param>
/// <param name="pMarkerPoint"></param> /// <param name="pMarkerPoint"></param>
private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint) private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{ {
Pen pRenderPen = CreatePen(pOwner); Pen pRenderPen = CreatePen(pOwner);
GraphicsPath markerPath = GetClone(pOwner); GraphicsPath markerPath = GetClone(pOwner);
Matrix transMatrix = new Matrix(); Matrix transMatrix = new Matrix();
transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y); transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y);
if (Orient.IsAuto) if (Orient.IsAuto)
transMatrix.Rotate(fAngle); transMatrix.Rotate(fAngle);
else else
transMatrix.Rotate(Orient.Angle); transMatrix.Rotate(Orient.Angle);
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth)); transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth));
break; break;
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY); transMatrix.Translate(-RefX, -RefY);
break; break;
} }
markerPath.Transform(transMatrix); markerPath.Transform(transMatrix);
pRenderer.DrawPath(pRenderPen, markerPath); pRenderer.DrawPath(pRenderPen, markerPath);
SvgPaintServer pFill = Fill; SvgPaintServer pFill = Fill;
SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for? SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for?
float fOpacity = FillOpacity; float fOpacity = FillOpacity;
if (pFill != null) if (pFill != null)
{ {
Brush pBrush = pFill.GetBrush(this, fOpacity); Brush pBrush = pFill.GetBrush(this, fOpacity);
pRenderer.FillPath(pBrush, markerPath); pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose(); pBrush.Dispose();
} }
pRenderPen.Dispose(); pRenderPen.Dispose();
markerPath.Dispose(); markerPath.Dispose();
transMatrix.Dispose(); transMatrix.Dispose();
} }
/// <summary> /// <summary>
/// Create a pen that can be used to render this marker /// Create a pen that can be used to render this marker
/// </summary> /// </summary>
/// <param name="pStroke"></param> /// <param name="pStroke"></param>
/// <returns></returns> /// <returns></returns>
private Pen CreatePen(SvgPath pPath) private Pen CreatePen(SvgPath pPath)
{ {
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity); Brush pBrush = pPath.Stroke.GetBrush(this, Opacity);
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth)); return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth));
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth));
} }
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth));
} }
/// <summary> /// <summary>
/// Get a clone of the current path, scaled for the stroke with /// Get a clone of the current path, scaled for the stroke with
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath) private GraphicsPath GetClone(SvgPath pPath)
{ {
GraphicsPath pRet = Path.Clone() as GraphicsPath; GraphicsPath pRet = Path.Clone() as GraphicsPath;
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
Matrix transMatrix = new Matrix(); Matrix transMatrix = new Matrix();
transMatrix.Scale(AdjustForViewBoxWidth(pPath.StrokeWidth), AdjustForViewBoxHeight(pPath.StrokeWidth)); transMatrix.Scale(AdjustForViewBoxWidth(pPath.StrokeWidth), AdjustForViewBoxHeight(pPath.StrokeWidth));
pRet.Transform(transMatrix); pRet.Transform(transMatrix);
break; break;
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
break; break;
} }
return (pRet); return (pRet);
} }
/// <summary> /// <summary>
/// Adjust the given value to account for the width of the viewbox in the viewport /// Adjust the given value to account for the width of the viewbox in the viewport
/// </summary> /// </summary>
/// <param name="fWidth"></param> /// <param name="fWidth"></param>
/// <returns></returns> /// <returns></returns>
private float AdjustForViewBoxWidth(float fWidth) private float AdjustForViewBoxWidth(float fWidth)
{ {
// TODO: We know this isn't correct // TODO: We know this isn't correct
return (fWidth / ViewBox.Width); return (fWidth / ViewBox.Width);
} }
/// <summary> /// <summary>
/// Adjust the given value to account for the height of the viewbox in the viewport /// Adjust the given value to account for the height of the viewbox in the viewport
/// </summary> /// </summary>
/// <param name="fWidth"></param> /// <param name="fWidth"></param>
/// <returns></returns> /// <returns></returns>
private float AdjustForViewBoxHeight(float fHeight) private float AdjustForViewBoxHeight(float fHeight)
{ {
// TODO: We know this isn't correct // TODO: We know this isn't correct
return (fHeight / ViewBox.Height); return (fHeight / ViewBox.Height);
} }
} }
} }
\ No newline at end of file
using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Linq;
namespace Svg namespace Svg
{ {
...@@ -9,22 +12,40 @@ namespace Svg ...@@ -9,22 +12,40 @@ namespace Svg
[SvgAttribute("cx")] [SvgAttribute("cx")]
public SvgUnit CenterX public SvgUnit CenterX
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("cx"); } get
set { this.Attributes["cx"] = value; } {
return this.Attributes.GetAttribute<SvgUnit>("cx");
}
set
{
this.Attributes["cx"] = value;
}
} }
[SvgAttribute("cy")] [SvgAttribute("cy")]
public SvgUnit CenterY public SvgUnit CenterY
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("cy"); } get
set { this.Attributes["cy"] = value; } {
return this.Attributes.GetAttribute<SvgUnit>("cy");
}
set
{
this.Attributes["cy"] = value;
}
} }
[SvgAttribute("r")] [SvgAttribute("r")]
public SvgUnit Radius public SvgUnit Radius
{ {
get { return this.Attributes.GetAttribute<SvgUnit>("r"); } get
set { this.Attributes["r"] = value; } {
return this.Attributes.GetAttribute<SvgUnit>("r");
}
set
{
this.Attributes["r"] = value;
}
} }
[SvgAttribute("fx")] [SvgAttribute("fx")]
...@@ -41,8 +62,10 @@ namespace Svg ...@@ -41,8 +62,10 @@ namespace Svg
return value; return value;
} }
set
set { this.Attributes["fx"] = value; } {
this.Attributes["fx"] = value;
}
} }
[SvgAttribute("fy")] [SvgAttribute("fy")]
...@@ -59,72 +82,150 @@ namespace Svg ...@@ -59,72 +82,150 @@ namespace Svg
return value; return value;
} }
set
set { this.Attributes["fy"] = value; } {
this.Attributes["fy"] = value;
}
} }
/// <summary>
/// Initializes a new instance of the <see cref="SvgRadialGradientServer"/> class.
/// </summary>
public SvgRadialGradientServer() public SvgRadialGradientServer()
{ {
//Apply default values of 50% to cX,cY and r CenterX = new SvgUnit(SvgUnitType.Percentage, 50F);
CenterX = new SvgUnit(SvgUnitType.Percentage, 50); CenterY = new SvgUnit(SvgUnitType.Percentage, 50F);
CenterY = new SvgUnit(SvgUnitType.Percentage, 50); Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
Radius = new SvgUnit(SvgUnitType.Percentage, 50);
} }
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
{ {
float radius = this.Radius.ToDeviceValue(renderingElement); var origin = CalculateOrigin(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin);
var focalPoint = CalculateFocalPoint(renderingElement, origin);
var specifiedRadius = CalculateRadius(renderingElement);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
if (radius <= 0) var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
{ {
return null; InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedRadius, effectiveRadius),
CenterPoint = focalPoint
};
Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!");
return brush;
}
private PointF CalculateOrigin(SvgVisualElement renderingElement)
{
return CalculateBoundable(renderingElement).Location;
}
private PointF CalculateCenterPoint(ISvgBoundable boundable, PointF origin)
{
var deviceCenterX = origin.X + CenterX.ToDeviceValue(boundable);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(boundable, true);
var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY));
return transformedCenterPoint;
}
private PointF CalculateFocalPoint(ISvgBoundable boundable, PointF origin)
{
var deviceFocalX = origin.X + FocalX.ToDeviceValue(boundable);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(boundable, true);
var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
return transformedFocalPoint;
}
private float CalculateRadius(ISvgBoundable boundable)
{
var radius = Radius.ToDeviceValue(boundable);
var transformRadiusVector = TransformVector(new PointF(radius, 0));
var transformedRadius = CalculateLength(transformRadiusVector);
return transformedRadius;
}
private float CalculateEffectiveRadius(ISvgBoundable boundable, PointF centerPoint, float specifiedRadius)
{
if (SpreadMethod != SvgGradientSpreadMethod.Pad)
{
return specifiedRadius;
} }
GraphicsPath path = new GraphicsPath();
float left = this.CenterX.ToDeviceValue(renderingElement);
float top = this.CenterY.ToDeviceValue(renderingElement, true);
RectangleF boundingBox = (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? renderingElement.Bounds : renderingElement.OwnerDocument.GetDimensions();
path.AddEllipse( var topLeft = new PointF(boundable.Bounds.Left, boundable.Bounds.Top);
boundingBox.Left + left - radius, var topRight = new PointF(boundable.Bounds.Right, boundable.Bounds.Top);
boundingBox.Top + top - radius, var bottomRight = new PointF(boundable.Bounds.Right, boundable.Bounds.Bottom);
radius * 2, var bottomLeft = new PointF(boundable.Bounds.Left, boundable.Bounds.Bottom);
radius * 2);
var effectiveRadius = (float)Math.Ceiling(
Math.Max(
Math.Max(
CalculateDistance(centerPoint, topLeft),
CalculateDistance(centerPoint, topRight)
),
Math.Max(
CalculateDistance(centerPoint, bottomRight),
CalculateDistance(centerPoint, bottomLeft)
)
)
);
effectiveRadius = Math.Max(effectiveRadius, specifiedRadius);
return effectiveRadius;
}
PathGradientBrush brush = new PathGradientBrush(path); private static GraphicsPath CreateGraphicsPath(PointF origin, PointF centerPoint, float effectiveRadius)
ColorBlend blend = base.GetColourBlend(renderingElement, opacity, true); {
var path = new GraphicsPath();
brush.InterpolationColors = blend; path.AddEllipse(
brush.CenterPoint = origin.X + centerPoint.X - effectiveRadius,
new PointF( origin.Y + centerPoint.Y - effectiveRadius,
boundingBox.Left + this.FocalX.ToDeviceValue(renderingElement), effectiveRadius * 2,
boundingBox.Top + this.FocalY.ToDeviceValue(renderingElement, true)); effectiveRadius * 2
);
return brush; return path;
} }
private ColorBlend CalculateColorBlend(SvgVisualElement renderingElement, float opacity, float specifiedRadius, float effectiveRadius)
{
var colorBlend = GetColorBlend(renderingElement, opacity, true);
public override SvgElement DeepCopy() if (specifiedRadius >= effectiveRadius)
{ {
return DeepCopy<SvgRadialGradientServer>(); return colorBlend;
} }
for (var i = 0; i < colorBlend.Positions.Length - 1; i++)
{
colorBlend.Positions[i] = 1 - (specifiedRadius / effectiveRadius) * (1 - colorBlend.Positions[i]);
}
public override SvgElement DeepCopy<T>() colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
{ colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
var newObj = base.DeepCopy<T>() as SvgRadialGradientServer;
newObj.CenterX = this.CenterX; return colorBlend;
newObj.CenterY = this.CenterY; }
newObj.Radius = this.Radius;
newObj.FocalX = this.FocalX;
newObj.FocalY = this.FocalY;
return newObj; public override SvgElement DeepCopy()
} {
return DeepCopy<SvgRadialGradientServer>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgRadialGradientServer;
newObj.CenterX = this.CenterX;
newObj.CenterY = this.CenterY;
newObj.Radius = this.Radius;
newObj.FocalX = this.FocalX;
newObj.FocalY = this.FocalY;
return newObj;
}
} }
} }
\ No newline at end of file
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\..\vvvv\public\common\src\thirdparty\</OutputPath> <OutputPath>..\..\vvvv\public\common\src\thirdparty\</OutputPath>
<DefineConstants>TRACE;DEBUG;REFLECTION</DefineConstants> <DefineConstants>TRACE;DEBUG;REFLECTION</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
...@@ -114,6 +114,7 @@ ...@@ -114,6 +114,7 @@
<Compile Include="DataTypes\SvgViewBox.cs" /> <Compile Include="DataTypes\SvgViewBox.cs" />
<Compile Include="Document Structure\SvgTitle.cs" /> <Compile Include="Document Structure\SvgTitle.cs" />
<Compile Include="Document Structure\SvgDocumentMetadata.cs" /> <Compile Include="Document Structure\SvgDocumentMetadata.cs" />
<Compile Include="Painting\ISvgBoundable.cs" />
<Compile Include="Painting\SvgMarker.cs" /> <Compile Include="Painting\SvgMarker.cs" />
<Compile Include="Document Structure\SvgDefinitionList.cs" /> <Compile Include="Document Structure\SvgDefinitionList.cs" />
<Compile Include="Document Structure\SvgDescription.cs" /> <Compile Include="Document Structure\SvgDescription.cs" />
......
...@@ -19,14 +19,12 @@ namespace Svg ...@@ -19,14 +19,12 @@ namespace Svg
public static readonly int PointsPerInch = 96; public static readonly int PointsPerInch = 96;
private SvgElementIdManager _idManager; private SvgElementIdManager _idManager;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SvgDocument"/> class. /// Initializes a new instance of the <see cref="SvgDocument"/> class.
/// </summary> /// </summary>
public SvgDocument() public SvgDocument()
{ {
Ppi = PointsPerInch; Ppi = PointsPerInch;
} }
/// <summary> /// <summary>
...@@ -53,7 +51,7 @@ namespace Svg ...@@ -53,7 +51,7 @@ namespace Svg
/// <param name="manager"></param> /// <param name="manager"></param>
public void OverwriteIdManager(SvgElementIdManager manager) public void OverwriteIdManager(SvgElementIdManager manager)
{ {
_idManager = manager; _idManager = manager;
} }
/// <summary> /// <summary>
...@@ -293,25 +291,6 @@ namespace Svg ...@@ -293,25 +291,6 @@ namespace Svg
return null; return null;
} }
public RectangleF GetDimensions()
{
var w = Width.ToDeviceValue();
var h = Height.ToDeviceValue();
RectangleF bounds = new RectangleF();
var isWidthperc = Width.Type == SvgUnitType.Percentage;
var isHeightperc = Height.Type == SvgUnitType.Percentage;
if(isWidthperc || isHeightperc)
{
bounds = this.Bounds; //do just one call to the recursive bounds property
if(isWidthperc) w = (bounds.Width + bounds.X) * (w * 0.01f);
if(isHeightperc) h = (bounds.Height + bounds.Y) * (h * 0.01f);
}
return new RectangleF(0, 0, w, h);
}
/// <summary> /// <summary>
/// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>. /// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>.
/// </summary> /// </summary>
...@@ -413,6 +392,5 @@ namespace Svg ...@@ -413,6 +392,5 @@ namespace Svg
this.Write(fs); this.Write(fs);
} }
} }
} }
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment