Commit d5c659a5 authored by Eric Domke's avatar Eric Domke
Browse files

Refactoring while working through W3C tests

- Adding W3C test cases and a test fixture
- Fixed support for CSS stylesheets (particularly when class names are
referenced)
- Refactoring unit calculations so that percentages and fractions
calculate more accurately
- SvgImage:
- Support PreserveAspectRatio attribute
- Support for referencing svg images
- Refactored text rendering to use the AttributeCollection inheritance
scheme
- Initial attempt at 'ex' unit support
- Added support for system color names
- Changed parsing of entities to support XML entities
- Supporting loading of a svg document directly from a XmlDocument with
requiring serializing the document as a string first.
- ...
parent 3aedd8e8
...@@ -17,17 +17,9 @@ namespace Svg ...@@ -17,17 +17,9 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get return GetPaths(this, renderer);
{
//var path = new GraphicsPath();
//AddPaths(this, path);
return GetPaths(this);
}
protected set
{ }
} }
/// <summary> /// <summary>
......
...@@ -38,10 +38,12 @@ namespace Svg ...@@ -38,10 +38,12 @@ namespace Svg
/// Applies the required transforms to <see cref="SvgRenderer"/>. /// Applies the required transforms to <see cref="SvgRenderer"/>.
/// </summary> /// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal override void PushTransforms(SvgRenderer renderer) protected internal override bool PushTransforms(SvgRenderer renderer)
{ {
base.PushTransforms(renderer); if (!base.PushTransforms(renderer)) return false;
renderer.TranslateTransform(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true)); renderer.TranslateTransform(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
this.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
return true;
} }
/// <summary> /// <summary>
...@@ -53,15 +55,10 @@ namespace Svg ...@@ -53,15 +55,10 @@ namespace Svg
this.Y = 0; this.Y = 0;
} }
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
{ return (element != null) ? element.Path(renderer) : null;
SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
return (element != null) ? element.Path : null;
}
protected set
{ }
} }
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
......
...@@ -17,17 +17,9 @@ namespace Svg ...@@ -17,17 +17,9 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get return GetPaths(this, renderer);
{
//var path = new GraphicsPath();
//AddPaths(this, path);
return GetPaths(this);
}
protected set
{ }
} }
/// <summary> /// <summary>
......
...@@ -151,7 +151,7 @@ namespace Svg.FilterEffects ...@@ -151,7 +151,7 @@ namespace Svg.FilterEffects
{ {
if (this.sourceGraphic == null) if (this.sourceGraphic == null)
{ {
RectangleF bounds = element.Path.GetBounds(); RectangleF bounds = element.Path(renderer).GetBounds();
this.sourceGraphic = new Bitmap((int)bounds.Width, (int)bounds.Height); this.sourceGraphic = new Bitmap((int)bounds.Width, (int)bounds.Height);
using (var graphics = Graphics.FromImage(this.sourceGraphic)) using (var graphics = Graphics.FromImage(this.sourceGraphic))
......
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Local" id="616d39c5-86e4-40f8-97e9-b8a423ce7322" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are default test settings for a local test run.</Description>
<Deployment enabled="false" />
<Execution>
<TestTypeSpecific />
<AgentRule name="Execution Agents">
</AgentRule>
</Execution>
</TestSettings>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Svg
{
internal class GenericBoundable : ISvgBoundable
{
private RectangleF _rect;
public GenericBoundable(RectangleF rect)
{
_rect = rect;
}
public GenericBoundable(float x, float y, float width, float height)
{
_rect = new RectangleF(x, y, width, height);
}
public System.Drawing.PointF Location
{
get { return _rect.Location; }
}
public System.Drawing.SizeF Size
{
get { return _rect.Size; }
}
public System.Drawing.RectangleF Bounds
{
get { return _rect; }
}
}
}
...@@ -19,6 +19,6 @@ namespace Svg ...@@ -19,6 +19,6 @@ namespace Svg
float StrokeMiterLimit { get; set; } float StrokeMiterLimit { get; set; }
SvgUnitCollection StrokeDashArray { get; set; } SvgUnitCollection StrokeDashArray { get; set; }
SvgUnit StrokeDashOffset { get; set; } SvgUnit StrokeDashOffset { get; set; }
GraphicsPath Path { get; } GraphicsPath Path(SvgRenderer renderer);
} }
} }
\ No newline at end of file
...@@ -95,6 +95,37 @@ namespace Svg ...@@ -95,6 +95,37 @@ namespace Svg
return base.ConvertFrom(context, culture, colour); return base.ConvertFrom(context, culture, colour);
} }
switch (colour.ToLowerInvariant())
{
case "activeborder": return SystemColors.ActiveBorder;
case "activecaption": return SystemColors.ActiveCaption;
case "appworkspace": return SystemColors.AppWorkspace;
case "background": return SystemColors.Desktop;
case "buttonface": return SystemColors.Control;
case "buttonhighlight": return SystemColors.ControlLightLight;
case "buttonshadow": return SystemColors.ControlDark;
case "buttontext": return SystemColors.ControlText;
case "captiontext": return SystemColors.ActiveCaptionText;
case "graytext": return SystemColors.GrayText;
case "highlight": return SystemColors.Highlight;
case "highlighttext": return SystemColors.HighlightText;
case "inactiveborder": return SystemColors.InactiveBorder;
case "inactivecaption": return SystemColors.InactiveCaption;
case "inactivecaptiontext": return SystemColors.InactiveCaptionText;
case "infobackground": return SystemColors.Info;
case "infotext": return SystemColors.InfoText;
case "menu": return SystemColors.Menu;
case "menutext": return SystemColors.MenuText;
case "scrollbar": return SystemColors.ScrollBar;
case "threeddarkshadow": return SystemColors.ControlDarkDark;
case "threedface": return SystemColors.Control;
case "threedhighlight": return SystemColors.ControlLight;
case "threedlightshadow": return SystemColors.ControlLightLight;
case "window": return SystemColors.Window;
case "windowframe": return SystemColors.WindowFrame;
case "windowtext": return SystemColors.WindowText;
}
Thread.CurrentThread.CurrentCulture = oldCulture; Thread.CurrentThread.CurrentCulture = oldCulture;
} }
......
...@@ -11,9 +11,14 @@ namespace Svg ...@@ -11,9 +11,14 @@ namespace Svg
/// <summary> /// <summary>
/// An unspecified <see cref="SvgPaintServer"/>. /// An unspecified <see cref="SvgPaintServer"/>.
/// </summary> /// </summary>
public static readonly SvgPaintServer NotSet = new SvgColourServer(); public static readonly SvgPaintServer NotSet = new SvgColourServer(System.Drawing.Color.Black);
/// <summary>
public SvgColourServer() : this(Color.Black) /// A <see cref="SvgPaintServer"/> that should inherit from its parent.
/// </summary>
public static readonly SvgPaintServer Inherit = new SvgColourServer(System.Drawing.Color.Black);
public SvgColourServer()
: this(System.Drawing.Color.Black)
{ {
} }
...@@ -30,13 +35,13 @@ namespace Svg ...@@ -30,13 +35,13 @@ namespace Svg
set { this._colour = value; } set { this._colour = value; }
} }
public override Brush GetBrush(SvgVisualElement styleOwner, float opacity) public override Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
{ {
//is none? //is none?
if (this == SvgPaintServer.None) return new SolidBrush(Color.Transparent); if (this == SvgPaintServer.None) return new SolidBrush(System.Drawing.Color.Transparent);
int alpha = (int)((opacity * (this.Colour.A/255.0f) ) * 255); int alpha = (int)((opacity * (this.Colour.A/255.0f) ) * 255);
Color colour = Color.FromArgb(alpha, this.Colour); Color colour = System.Drawing.Color.FromArgb(alpha, this.Colour);
return new SolidBrush(colour); return new SolidBrush(colour);
} }
......
...@@ -24,19 +24,30 @@ namespace Svg ...@@ -24,19 +24,30 @@ namespace Svg
this.DeferredId = id; this.DeferredId = id;
} }
private void EnsureServer() public void EnsureServer(SvgElement styleOwner)
{ {
if (!_serverLoaded) if (!_serverLoaded)
{ {
_concreteServer = this.Document.IdManager.GetElementById(this.DeferredId) as SvgPaintServer; if (this.DeferredId == "currentColor" && styleOwner != null)
{
var colorElement = (from e in styleOwner.ParentsAndSelf.OfType<SvgElement>()
where e.Color != SvgPaintServer.None && e.Color != SvgColourServer.NotSet &&
e.Color != SvgColourServer.Inherit && e.Color != SvgColourServer.None
select e).FirstOrDefault();
_concreteServer = (colorElement == null ? SvgPaintServer.None : colorElement.Color);
}
else
{
_concreteServer = this.Document.IdManager.GetElementById(this.DeferredId) as SvgPaintServer;
}
_serverLoaded = true; _serverLoaded = true;
} }
} }
public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, float opacity) public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
{ {
EnsureServer(); EnsureServer(styleOwner);
return _concreteServer.GetBrush(styleOwner, opacity); return _concreteServer.GetBrush(styleOwner, renderer, opacity);
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
...@@ -72,7 +83,7 @@ namespace Svg ...@@ -72,7 +83,7 @@ namespace Svg
return (_serverLoaded ? _serverLoaded.ToString() : string.Format("deferred: {0}", this.DeferredId)); return (_serverLoaded ? _serverLoaded.ToString() : string.Format("deferred: {0}", this.DeferredId));
} }
public static T TryGet<T>(SvgPaintServer server) where T : SvgPaintServer public static T TryGet<T>(SvgPaintServer server, SvgElement parent) where T : SvgPaintServer
{ {
var deferred = server as SvgDeferredPaintServer; var deferred = server as SvgDeferredPaintServer;
if (deferred == null) if (deferred == null)
...@@ -81,7 +92,7 @@ namespace Svg ...@@ -81,7 +92,7 @@ namespace Svg
} }
else else
{ {
deferred.EnsureServer(); deferred.EnsureServer(parent);
return deferred._concreteServer as T; return deferred._concreteServer as T;
} }
} }
......
...@@ -9,7 +9,7 @@ namespace Svg ...@@ -9,7 +9,7 @@ namespace Svg
/// <summary> /// <summary>
/// Provides the base class for all paint servers that wish to render a gradient. /// Provides the base class for all paint servers that wish to render a gradient.
/// </summary> /// </summary>
public abstract class SvgGradientServer : SvgPaintServer public abstract class SvgGradientServer : SvgPaintServer, ISvgSupportsCoordinateUnits
{ {
private SvgCoordinateUnits _gradientUnits; private SvgCoordinateUnits _gradientUnits;
private SvgGradientSpreadMethod _spreadMethod; private SvgGradientSpreadMethod _spreadMethod;
...@@ -130,7 +130,7 @@ namespace Svg ...@@ -130,7 +130,7 @@ namespace Svg
/// </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 GetColorBlend(SvgVisualElement owner, float opacity, bool radial) protected ColorBlend GetColorBlend(SvgRenderer renderer, float opacity, bool radial)
{ {
int colourBlends = this.Stops.Count; int colourBlends = this.Stops.Count;
bool insertStart = false; bool insertStart = false;
...@@ -179,18 +179,19 @@ namespace Svg ...@@ -179,18 +179,19 @@ namespace Svg
int actualStops = 0; int actualStops = 0;
float mergedOpacity = 0.0f; float mergedOpacity = 0.0f;
float position = 0.0f; float position = 0.0f;
Color colour = Color.Black; Color colour = System.Drawing.Color.Black;
for (int i = 0; i < colourBlends; i++) for (int i = 0; i < colourBlends; i++)
{ {
var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops]; var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
var boundWidth = renderer.Boundable().Bounds.Width;
mergedOpacity = opacity * currentStop.Opacity; mergedOpacity = opacity * currentStop.Opacity;
position = position =
radial radial
? 1 - (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width) ? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth)
: (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width); : (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth);
colour = Color.FromArgb((int)(mergedOpacity * 255), currentStop.Colour); colour = System.Drawing.Color.FromArgb((int)(mergedOpacity * 255), currentStop.GetColor(this));
actualStops++; actualStops++;
...@@ -219,20 +220,15 @@ namespace Svg ...@@ -219,20 +220,15 @@ namespace Svg
return blend; return blend;
} }
protected void LoadStops() protected void LoadStops(SvgVisualElement parent)
{ {
var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient); var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient, parent);
if (this.Stops.Count == 0 && core != null) if (this.Stops.Count == 0 && core != null)
{ {
_stops.AddRange(core.Stops); _stops.AddRange(core.Stops);
} }
} }
protected ISvgBoundable CalculateBoundable(SvgVisualElement renderingElement)
{
return (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? (ISvgBoundable)renderingElement : renderingElement.OwnerDocument;
}
protected PointF TransformPoint(PointF originalPoint) protected PointF TransformPoint(PointF originalPoint)
{ {
var newPoint = new[] { originalPoint }; var newPoint = new[] { originalPoint };
...@@ -272,5 +268,10 @@ namespace Svg ...@@ -272,5 +268,10 @@ namespace Svg
return newObj; return newObj;
} }
public SvgCoordinateUnits GetUnits()
{
return _gradientUnits;
}
} }
} }
\ No newline at end of file
...@@ -13,7 +13,7 @@ namespace Svg ...@@ -13,7 +13,7 @@ namespace Svg
public class SvgGradientStop : SvgElement public class SvgGradientStop : SvgElement
{ {
private SvgUnit _offset; private SvgUnit _offset;
private Color _colour; private SvgPaintServer _colour;
private float _opacity; private float _opacity;
/// <summary> /// <summary>
...@@ -58,8 +58,8 @@ namespace Svg ...@@ -58,8 +58,8 @@ namespace Svg
/// Gets or sets the colour of the gradient stop. /// Gets or sets the colour of the gradient stop.
/// </summary> /// </summary>
[SvgAttribute("stop-color")] [SvgAttribute("stop-color")]
[TypeConverter(typeof(SvgColourConverter))] [TypeConverter(typeof(SvgPaintServerFactory))]
public Color Colour public SvgPaintServer Colour
{ {
get { return this._colour; } get { return this._colour; }
set { this._colour = value; } set { this._colour = value; }
...@@ -81,7 +81,7 @@ namespace Svg ...@@ -81,7 +81,7 @@ namespace Svg
public SvgGradientStop() public SvgGradientStop()
{ {
this._offset = new SvgUnit(0.0f); this._offset = new SvgUnit(0.0f);
this._colour = Color.Transparent; this._colour = SvgColourServer.NotSet;
this._opacity = 1.0f; this._opacity = 1.0f;
} }
...@@ -93,10 +93,16 @@ namespace Svg ...@@ -93,10 +93,16 @@ namespace Svg
public SvgGradientStop(SvgUnit offset, Color colour) public SvgGradientStop(SvgUnit offset, Color colour)
{ {
this._offset = offset; this._offset = offset;
this._colour = colour; this._colour = new SvgColourServer(colour);
this._opacity = 1.0f; this._opacity = 1.0f;
} }
public Color GetColor(SvgElement parent)
{
var core = SvgDeferredPaintServer.TryGet<SvgColourServer>(_colour, parent);
if (core == null) throw new InvalidOperationException("Invalid paint server for gradient stop detected.");
return core.Colour;
}
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
{ {
......
...@@ -79,44 +79,51 @@ namespace Svg ...@@ -79,44 +79,51 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F); Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
} }
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
LoadStops(); LoadStops(renderingElement);
if (IsInvalid) if (IsInvalid)
{ {
return null; return null;
} }
var boundable = CalculateBoundable(renderingElement); try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var specifiedStart = CalculateStart(boundable); var specifiedStart = CalculateStart(renderer);
var specifiedEnd = CalculateEnd(boundable); var specifiedEnd = CalculateEnd(renderer);
var effectiveStart = specifiedStart; var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd; var effectiveEnd = specifiedEnd;
if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd)) if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd))
{ {
var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd); var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd);
effectiveStart = expansion.StartPoint; effectiveStart = expansion.StartPoint;
effectiveEnd = expansion.EndPoint; effectiveEnd = expansion.EndPoint;
} }
return new LinearGradientBrush(effectiveStart, effectiveEnd, Color.Transparent, Color.Transparent) return new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
{
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
}
finally
{ {
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd), if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
WrapMode = WrapMode.TileFlipX }
};
} }
private PointF CalculateStart(ISvgBoundable boundable) private PointF CalculateStart(SvgRenderer renderer)
{ {
return TransformPoint(new PointF(this.X1.ToDeviceValue(boundable), this.Y1.ToDeviceValue(boundable, true))); return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
} }
private PointF CalculateEnd(ISvgBoundable boundable) private PointF CalculateEnd(SvgRenderer renderer)
{ {
return TransformPoint(new PointF(this.X2.ToDeviceValue(boundable), this.Y2.ToDeviceValue(boundable, true))); return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
} }
private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd) private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
...@@ -174,9 +181,9 @@ namespace Svg ...@@ -174,9 +181,9 @@ namespace Svg
return new GradientPoints(effectiveStart, effectiveEnd); return new GradientPoints(effectiveStart, effectiveEnd);
} }
private ColorBlend CalculateColorBlend(SvgVisualElement owner, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd) private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{ {
var colorBlend = GetColorBlend(owner, opacity, false); var colorBlend = GetColorBlend(renderer, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart); var startDelta = CalculateDistance(specifiedStart, effectiveStart);
var endDelta = CalculateDistance(specifiedEnd, effectiveEnd); var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
......
...@@ -87,26 +87,19 @@ namespace Svg ...@@ -87,26 +87,19 @@ namespace Svg
Overflow = SvgOverflow.hidden; Overflow = SvgOverflow.hidden;
} }
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get var path = this.Children.FirstOrDefault(x => x is SvgPath);
{ if (path != null)
var path = this.Children.FirstOrDefault(x => x is SvgPath); return (path as SvgPath).Path(renderer);
if (path != null) return null;
return (path as SvgPath).Path;
return null;
}
protected set
{
// No-op
}
} }
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
{ {
get get
{ {
var path = this.Path; var path = this.Path(null);
if (path != null) if (path != null)
return path.GetBounds(); return path.GetBounds();
return new System.Drawing.RectangleF(); return new System.Drawing.RectangleF();
...@@ -177,7 +170,7 @@ namespace Svg ...@@ -177,7 +170,7 @@ namespace Svg
/// <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, pRenderer);
GraphicsPath markerPath = GetClone(pOwner); GraphicsPath markerPath = GetClone(pOwner);
...@@ -190,10 +183,14 @@ namespace Svg ...@@ -190,10 +183,14 @@ namespace Svg
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth)); transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)),
AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)));
break; break;
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY); transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
break; break;
} }
markerPath.Transform(transMatrix); markerPath.Transform(transMatrix);
...@@ -205,7 +202,7 @@ namespace Svg ...@@ -205,7 +202,7 @@ namespace Svg
if (pFill != null) if (pFill != null)
{ {
Brush pBrush = pFill.GetBrush(this, fOpacity); Brush pBrush = pFill.GetBrush(this, pRenderer, fOpacity);
pRenderer.FillPath(pBrush, markerPath); pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose(); pBrush.Dispose();
} }
...@@ -219,17 +216,18 @@ namespace Svg ...@@ -219,17 +216,18 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="pStroke"></param> /// <param name="pStroke"></param>
/// <returns></returns> /// <returns></returns>
private Pen CreatePen(SvgPath pPath) private Pen CreatePen(SvgPath pPath, SvgRenderer renderer)
{ {
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity); Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this) *
pPath.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
} }
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
} }
/// <summary> /// <summary>
...@@ -238,7 +236,7 @@ namespace Svg ...@@ -238,7 +236,7 @@ namespace Svg
/// <returns></returns> /// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath) private GraphicsPath GetClone(SvgPath pPath)
{ {
GraphicsPath pRet = Path.Clone() as GraphicsPath; GraphicsPath pRet = Path(null).Clone() as GraphicsPath;
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
......
...@@ -39,7 +39,7 @@ namespace Svg ...@@ -39,7 +39,7 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param> /// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param> /// <param name="opacity">The opacity of the brush.</param>
public abstract Brush GetBrush(SvgVisualElement styleOwner, float opacity); public abstract Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity);
/// <summary> /// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
......
...@@ -27,6 +27,14 @@ namespace Svg ...@@ -27,6 +27,14 @@ namespace Svg
{ {
return SvgColourServer.NotSet; return SvgColourServer.NotSet;
} }
else if (value == "inherit")
{
return SvgColourServer.Inherit;
}
else if (value == "currentColor")
{
return new SvgDeferredPaintServer(document, value);
}
else if (value.IndexOf("url(#") > -1) else if (value.IndexOf("url(#") > -1)
{ {
Match match = _urlRefPattern.Match(value); Match match = _urlRefPattern.Match(value);
......
...@@ -13,13 +13,15 @@ namespace Svg ...@@ -13,13 +13,15 @@ namespace Svg
/// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted. /// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
/// </summary> /// </summary>
[SvgElement("pattern")] [SvgElement("pattern")]
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort, ISvgSupportsCoordinateUnits
{ {
private SvgUnit _width; private SvgUnit _width;
private SvgUnit _height; private SvgUnit _height;
private SvgUnit _x; private SvgUnit _x;
private SvgUnit _y; private SvgUnit _y;
private SvgViewBox _viewBox; private SvgViewBox _viewBox;
private SvgCoordinateUnits _patternUnits;
private SvgCoordinateUnits _patternContentUnits;
[SvgAttribute("overflow")] [SvgAttribute("overflow")]
public SvgOverflow Overflow public SvgOverflow Overflow
...@@ -61,6 +63,26 @@ namespace Svg ...@@ -61,6 +63,26 @@ namespace Svg
set { this._width = value; } set { this._width = value; }
} }
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternUnits
{
get { return this._patternUnits; }
set { this._patternUnits = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternContentUnits
{
get { return this._patternContentUnits; }
set { this._patternContentUnits = value; }
}
/// <summary> /// <summary>
/// Gets or sets the height of the pattern. /// Gets or sets the height of the pattern.
/// </summary> /// </summary>
...@@ -107,7 +129,7 @@ namespace Svg ...@@ -107,7 +129,7 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param> /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param> /// <param name="opacity">The opacity of the brush.</param>
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
// If there aren't any children, return null // If there aren't any children, return null
if (this.Children.Count == 0) if (this.Children.Count == 0)
...@@ -117,18 +139,21 @@ namespace Svg ...@@ -117,18 +139,21 @@ namespace Svg
if (this._width.Value == 0.0f || this._height.Value == 0.0f) if (this._width.Value == 0.0f || this._height.Value == 0.0f)
return null; return null;
float width = this._width.ToDeviceValue(renderingElement); try
float height = this._height.ToDeviceValue(renderingElement, true);
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer renderer = SvgRenderer.FromImage(image))
{ {
Matrix patternMatrix = new Matrix(); if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
Matrix patternMatrix = new Matrix();
// Apply a translate if needed // Apply a translate if needed
if (this._x.Value > 0.0f || this._y.Value > 0.0f) if (this._x.Value > 0.0f || this._y.Value > 0.0f)
{ {
patternMatrix.Translate(this._x.ToDeviceValue(renderingElement) + -1.0f, this._y.ToDeviceValue(renderingElement, true) + -1.0f); float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
patternMatrix.Translate(x + -1.0f, y + -1.0f);
} }
else else
{ {
...@@ -137,27 +162,40 @@ namespace Svg ...@@ -137,27 +162,40 @@ namespace Svg
if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0) if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
{ {
patternMatrix.Scale(this.Width.ToDeviceValue() / this.ViewBox.Width, this.Height.ToDeviceValue() / this.ViewBox.Height); patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
} }
renderer.Transform = patternMatrix; Bitmap image = new Bitmap((int)width, (int)height);
renderer.CompositingQuality = CompositingQuality.HighQuality; using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
renderer.SmoothingMode = SmoothingMode.AntiAlias;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{ {
child.RenderElement(renderer); iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
iRenderer.Transform = patternMatrix;
iRenderer.CompositingQuality = CompositingQuality.HighQuality;
iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
iRenderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{
child.RenderElement(iRenderer);
}
iRenderer.Save();
} }
renderer.Save(); image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
}
TextureBrush textureBrush = new TextureBrush(image); TextureBrush textureBrush = new TextureBrush(image);
return textureBrush; return textureBrush;
}
finally
{
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
} }
private static int imgNumber = 0;
...@@ -180,5 +218,10 @@ namespace Svg ...@@ -180,5 +218,10 @@ namespace Svg
return newObj; return newObj;
} }
public SvgCoordinateUnits GetUnits()
{
return _patternUnits;
}
} }
} }
\ No newline at end of file
...@@ -95,53 +95,55 @@ namespace Svg ...@@ -95,53 +95,55 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F); Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
} }
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
LoadStops(); LoadStops(renderingElement);
var origin = CalculateOrigin(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin); try
var focalPoint = CalculateFocalPoint(renderingElement, origin);
var specifiedRadius = CalculateRadius(renderingElement);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
{ {
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedRadius, effectiveRadius), if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
CenterPoint = focalPoint var origin = renderer.Boundable().Location;
}; var centerPoint = CalculateCenterPoint(renderer, origin);
var focalPoint = CalculateFocalPoint(renderer, origin);
Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!"); var specifiedRadius = CalculateRadius(renderer);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
return brush; var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
} {
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius),
CenterPoint = focalPoint
};
private PointF CalculateOrigin(SvgVisualElement renderingElement) Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!");
{
return CalculateBoundable(renderingElement).Location; return brush;
}
finally
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
} }
private PointF CalculateCenterPoint(ISvgBoundable boundable, PointF origin) private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin)
{ {
var deviceCenterX = origin.X + CenterX.ToDeviceValue(boundable); var deviceCenterX = origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(boundable, true); var deviceCenterY = origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY)); var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY));
return transformedCenterPoint; return transformedCenterPoint;
} }
private PointF CalculateFocalPoint(ISvgBoundable boundable, PointF origin) private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
{ {
var deviceFocalX = origin.X + FocalX.ToDeviceValue(boundable); var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(boundable, true); var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY)); var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
return transformedFocalPoint; return transformedFocalPoint;
} }
private float CalculateRadius(ISvgBoundable boundable) private float CalculateRadius(SvgRenderer renderer)
{ {
var radius = Radius.ToDeviceValue(boundable); var radius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
var transformRadiusVector = TransformVector(new PointF(radius, 0)); var transformRadiusVector = TransformVector(new PointF(radius, 0));
var transformedRadius = CalculateLength(transformRadiusVector); var transformedRadius = CalculateLength(transformRadiusVector);
return transformedRadius; return transformedRadius;
...@@ -191,9 +193,9 @@ namespace Svg ...@@ -191,9 +193,9 @@ namespace Svg
return path; return path;
} }
private ColorBlend CalculateColorBlend(SvgVisualElement renderingElement, float opacity, float specifiedRadius, float effectiveRadius) private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float specifiedRadius, float effectiveRadius)
{ {
var colorBlend = GetColorBlend(renderingElement, opacity, true); var colorBlend = GetColorBlend(renderer, opacity, true);
if (specifiedRadius >= effectiveRadius) if (specifiedRadius >= effectiveRadius)
{ {
......
...@@ -81,27 +81,20 @@ namespace Svg ...@@ -81,27 +81,20 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
public override GraphicsPath Path public override GraphicsPath Path(SvgRenderer renderer)
{ {
get if (this._path == null || this.IsPathDirty)
{ {
if (this._path == null || this.IsPathDirty) _path = new GraphicsPath();
{
_path = new GraphicsPath();
foreach (SvgPathSegment segment in this.PathData)
{
segment.AddToPath(_path);
}
this.IsPathDirty = false; foreach (SvgPathSegment segment in this.PathData)
{
segment.AddToPath(_path);
} }
return _path;
} this.IsPathDirty = false;
protected set
{
_path = value;
} }
return _path;
} }
internal void OnPathUpdated() internal void OnPathUpdated()
...@@ -124,7 +117,7 @@ namespace Svg ...@@ -124,7 +117,7 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
/// <summary> /// <summary>
...@@ -145,8 +138,8 @@ namespace Svg ...@@ -145,8 +138,8 @@ namespace Svg
{ {
if (this.Stroke != null) if (this.Stroke != null)
{ {
float strokeWidth = this.StrokeWidth.ToDeviceValue(this); float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth)) using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity), strokeWidth))
{ {
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0) if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{ {
...@@ -154,25 +147,26 @@ namespace Svg ...@@ -154,25 +147,26 @@ namespace Svg
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray(); pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
} }
renderer.DrawPath(pen, this.Path); var path = this.Path(renderer);
renderer.DrawPath(pen, path);
if (this.MarkerStart != null) if (this.MarkerStart != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[0], Path.PathPoints[0], Path.PathPoints[1]); marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
} }
if (this.MarkerMid != null) if (this.MarkerMid != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= Path.PathPoints.Length - 2; i++) 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]); marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
} }
if (this.MarkerEnd != null) if (this.MarkerEnd != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerEnd.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(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]); marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
} }
} }
} }
......
...@@ -36,3 +36,5 @@ using System.Runtime.InteropServices; ...@@ -36,3 +36,5 @@ using System.Runtime.InteropServices;
//[assembly: AssemblyFileVersion("1.0.1.*")] //[assembly: AssemblyFileVersion("1.0.1.*")]
[assembly: CLSCompliant(true)] [assembly: CLSCompliant(true)]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Svg.UnitTests")]
\ No newline at end of file
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