using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Linq;
namespace Svg
{
///
/// The class that all SVG elements should derive from when they are to be rendered.
///
public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvgStylable, ISvgClipable
{
private bool _requiresSmoothRendering;
private Region _previousClip;
///
/// Gets the for this element.
///
public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
get
{
return Bounds.Location;
}
}
SizeF ISvgBoundable.Size
{
get
{
return Bounds.Size;
}
}
///
/// Gets the bounds of the element.
///
/// The bounds.
public abstract RectangleF Bounds { get; }
///
/// Gets the associated if one has been specified.
///
[SvgAttribute("clip")]
public virtual string Clip
{
get { return this.Attributes.GetInheritedAttribute("clip"); }
set { this.Attributes["clip"] = value; }
}
///
/// Gets the associated if one has been specified.
///
[SvgAttribute("clip-path")]
public virtual Uri ClipPath
{
get { return this.Attributes.GetInheritedAttribute("clip-path"); }
set { this.Attributes["clip-path"] = value; }
}
///
/// Gets or sets the algorithm which is to be used to determine the clipping region.
///
[SvgAttribute("clip-rule")]
public SvgClipRule ClipRule
{
get { return this.Attributes.GetAttribute("clip-rule", SvgClipRule.NonZero); }
set { this.Attributes["clip-rule"] = value; }
}
///
/// Gets the associated if one has been specified.
///
[SvgAttribute("filter")]
public virtual Uri Filter
{
get { return this.Attributes.GetInheritedAttribute("filter"); }
set { this.Attributes["filter"] = value; }
}
///
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
///
protected virtual bool RequiresSmoothRendering
{
get { return this._requiresSmoothRendering; }
}
///
/// Initializes a new instance of the class.
///
public SvgVisualElement()
{
this.IsPathDirty = true;
this._requiresSmoothRendering = false;
}
protected virtual bool Renderable { get { return true; } }
///
/// Renders the and contents to the specified object.
///
/// The object to render to.
protected override void Render(ISvgRenderer renderer)
{
this.Render(renderer, true);
}
private void Render(ISvgRenderer renderer, bool renderFilter)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
(!Renderable || this.Path(renderer) != null))
{
bool renderNormal = true;
if (renderFilter && this.Filter != null)
{
var filterPath = this.Filter;
if (filterPath.ToString().StartsWith("url("))
{
filterPath = new Uri(filterPath.ToString().Substring(4, filterPath.ToString().Length - 5), UriKind.RelativeOrAbsolute);
}
var filter = this.OwnerDocument.IdManager.GetElementById(filterPath) as FilterEffects.SvgFilter;
if (filter != null)
{
this.PopTransforms(renderer);
try
{
filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
}
catch (Exception ex) { Debug.Print(ex.ToString()); }
renderNormal = false;
}
}
if (renderNormal)
{
this.SetClip(renderer);
if (Renderable)
{
// If this element needs smoothing enabled turn anti-aliasing on
if (this.RequiresSmoothRendering)
{
renderer.SmoothingMode = SmoothingMode.AntiAlias;
}
this.RenderFill(renderer);
this.RenderStroke(renderer);
// Reset the smoothing mode
if (this.RequiresSmoothRendering && renderer.SmoothingMode == SmoothingMode.AntiAlias)
{
renderer.SmoothingMode = SmoothingMode.Default;
}
}
else
{
base.RenderChildren(renderer);
}
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
}
///
/// Renders the fill of the to the specified
///
/// The object to render to.
protected internal virtual void RenderFill(ISvgRenderer renderer)
{
if (this.Fill != null)
{
using (var brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
{
if (brush != null)
{
this.Path(renderer).FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate;
renderer.FillPath(brush, this.Path(renderer));
}
}
}
}
///
/// Renders the stroke of the to the specified
///
/// The object to render to.
protected internal virtual bool RenderStroke(ISvgRenderer renderer)
{
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (var brush = this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1), true))
{
if (brush != null)
{
var path = this.Path(renderer);
var bounds = path.GetBounds();
if (path.PointCount < 1) return false;
if (bounds.Width <= 0 && bounds.Height <= 0)
{
switch (this.StrokeLineCap)
{
case SvgStrokeLineCap.Round:
using (var capPath = new GraphicsPath())
{
capPath.AddEllipse(path.PathPoints[0].X - strokeWidth / 2, path.PathPoints[0].Y - strokeWidth / 2, strokeWidth, strokeWidth);
renderer.FillPath(brush, capPath);
}
break;
case SvgStrokeLineCap.Square:
using (var capPath = new GraphicsPath())
{
capPath.AddRectangle(new RectangleF(path.PathPoints[0].X - strokeWidth / 2, path.PathPoints[0].Y - strokeWidth / 2, strokeWidth, strokeWidth));
renderer.FillPath(brush, capPath);
}
break;
}
}
else
{
using (var pen = new Pen(brush, strokeWidth))
{
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{
/* divide by stroke width - GDI behaviour that I don't quite understand yet.*/
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0) ? 1 : u.ToDeviceValue(renderer, UnitRenderingType.Other, this)) /
((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
}
switch (this.StrokeLineJoin)
{
case SvgStrokeLineJoin.Bevel:
pen.LineJoin = LineJoin.Bevel;
break;
case SvgStrokeLineJoin.Round:
pen.LineJoin = LineJoin.Round;
break;
default:
pen.LineJoin = LineJoin.Miter;
break;
}
pen.MiterLimit = this.StrokeMiterLimit;
switch (this.StrokeLineCap)
{
case SvgStrokeLineCap.Round:
pen.StartCap = LineCap.Round;
pen.EndCap = LineCap.Round;
break;
case SvgStrokeLineCap.Square:
pen.StartCap = LineCap.Square;
pen.EndCap = LineCap.Square;
break;
}
renderer.DrawPath(pen, path);
return true;
}
}
}
}
}
return false;
}
///
/// Sets the clipping region of the specified .
///
/// The to have its clipping region set.
protected internal virtual void SetClip(ISvgRenderer renderer)
{
if (this.ClipPath != null || !string.IsNullOrEmpty(this.Clip))
{
this._previousClip = renderer.GetClip();
if (this.ClipPath != null)
{
SvgClipPath clipPath = this.OwnerDocument.GetElementById(this.ClipPath.ToString());
if (clipPath != null) renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
}
var clip = this.Clip;
if (!string.IsNullOrEmpty(clip) && clip.StartsWith("rect("))
{
clip = clip.Trim();
var offsets = (from o in clip.Substring(5, clip.Length - 6).Split(',')
select float.Parse(o.Trim())).ToList();
var bounds = this.Bounds;
var clipRect = new RectangleF(bounds.Left + offsets[3], bounds.Top + offsets[0],
bounds.Width - (offsets[3] + offsets[1]),
bounds.Height - (offsets[2] + offsets[0]));
renderer.SetClip(new Region(clipRect), CombineMode.Intersect);
}
}
}
///
/// Resets the clipping region of the specified back to where it was before the method was called.
///
/// The to have its clipping region reset.
protected internal virtual void ResetClip(ISvgRenderer renderer)
{
if (this._previousClip != null)
{
renderer.SetClip(this._previousClip);
this._previousClip = null;
}
}
///
/// Sets the clipping region of the specified .
///
/// The to have its clipping region set.
void ISvgClipable.SetClip(ISvgRenderer renderer)
{
this.SetClip(renderer);
}
///
/// Resets the clipping region of the specified back to where it was before the method was called.
///
/// The to have its clipping region reset.
void ISvgClipable.ResetClip(ISvgRenderer renderer)
{
this.ResetClip(renderer);
}
public override SvgElement DeepCopy()
{
var newObj = base.DeepCopy() as SvgVisualElement;
newObj.ClipPath = this.ClipPath;
newObj.ClipRule = this.ClipRule;
newObj.Filter = this.Filter;
newObj.Visible = this.Visible;
if (this.Fill != null)
newObj.Fill = this.Fill;
if (this.Stroke != null)
newObj.Stroke = this.Stroke;
newObj.FillRule = this.FillRule;
newObj.FillOpacity = this.FillOpacity;
newObj.StrokeWidth = this.StrokeWidth;
newObj.StrokeLineCap = this.StrokeLineCap;
newObj.StrokeLineJoin = this.StrokeLineJoin;
newObj.StrokeMiterLimit = this.StrokeMiterLimit;
newObj.StrokeDashArray = this.StrokeDashArray;
newObj.StrokeDashOffset = this.StrokeDashOffset;
newObj.StrokeOpacity = this.StrokeOpacity;
newObj.Opacity = this.Opacity;
return newObj;
}
}
}