Commit 1585c700 authored by Ritch Melton's avatar Ritch Melton
Browse files

merged

parents cc4f2940 1818255b
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Svg;
using System.Drawing.Drawing2D;
using System.Drawing;
namespace SVGViewer
{
class DebugRenderer : ISvgRenderer
{
private Region _clip = new Region();
private Matrix _transform = new Matrix();
private Stack<ISvgBoundable> _boundables = new Stack<ISvgBoundable>();
public void SetBoundable(ISvgBoundable boundable)
{
_boundables.Push(boundable);
}
public ISvgBoundable GetBoundable()
{
return _boundables.Peek();
}
public ISvgBoundable PopBoundable()
{
return _boundables.Pop();
}
public float DpiY
{
get { return 96; }
}
public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit)
{
}
public void DrawImageUnscaled(Image image, Point location)
{
}
public void DrawPath(Pen pen, GraphicsPath path)
{
var newPath = (GraphicsPath)path.Clone();
newPath.Transform(_transform);
}
public void FillPath(Brush brush, GraphicsPath path)
{
var newPath = (GraphicsPath)path.Clone();
newPath.Transform(_transform);
}
public Region GetClip()
{
return _clip;
}
public void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append)
{
_transform.Rotate(fAngle, order);
}
public void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append)
{
_transform.Scale(sx, sy, order);
}
public void SetClip(Region region, CombineMode combineMode = CombineMode.Replace)
{
switch (combineMode)
{
case CombineMode.Intersect:
_clip.Intersect(region);
break;
case CombineMode.Complement:
_clip.Complement(region);
break;
case CombineMode.Exclude:
_clip.Exclude(region);
break;
case CombineMode.Union:
_clip.Union(region);
break;
case CombineMode.Xor:
_clip.Xor(region);
break;
default:
_clip = region;
break;
}
}
public void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append)
{
_transform.Translate(dx, dy, order);
}
public SmoothingMode SmoothingMode
{
get { return SmoothingMode.Default; }
set { /* Do Nothing */ }
}
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
public void Dispose()
{
}
}
}
......@@ -93,6 +93,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DebugRenderer.cs" />
<Compile Include="SvgViewer.cs">
<SubType>Form</SubType>
</Compile>
......
......@@ -43,7 +43,10 @@ namespace SVGViewer
private void RenderSvg(SvgDocument svgDoc)
{
//var render = new DebugRenderer();
//svgDoc.Draw(render);
svgImage.Image = svgDoc.Draw();
svgImage.Image.Save(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(svgDoc.BaseUri.LocalPath), "output.png"));
}
}
}
......@@ -98,7 +98,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> representing this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -117,7 +117,7 @@ namespace Svg
/// Renders the circle to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The graphics object.</param>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
// Don't draw if there is no radius set
if (this.Radius.Value > 0.0f)
......
......@@ -102,7 +102,7 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -122,7 +122,7 @@ namespace Svg
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (this._radiusX.Value > 0.0f && this._radiusY.Value > 0.0f)
{
......
......@@ -94,7 +94,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
return null;
}
......@@ -102,95 +102,123 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
if (Width.Value > 0.0f && Height.Value > 0.0f && this.Href != null)
{
using (Image b = GetImage(this.Href))
var img = GetImage(this.Href);
if (img != null)
{
if (b != null)
RectangleF srcRect;
var bmp = img as Image;
var svg = img as SvgFragment;
if (bmp != null)
{
var srcRect = new RectangleF(0, 0, b.Width, b.Height);
var destClip = new RectangleF(this.Location.ToDeviceValue(renderer, this),
new SizeF(Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this)));
RectangleF destRect = destClip;
this.PushTransforms(renderer);
renderer.AddClip(new Region(destClip));
this.SetClip(renderer);
srcRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
}
else if (svg != null)
{
srcRect = new RectangleF(new PointF(0, 0), svg.GetDimensions());
}
else
{
return;
}
if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
{
var fScaleX = destClip.Width / srcRect.Width;
var fScaleY = destClip.Height / srcRect.Height;
var xOffset = 0.0f;
var yOffset = 0.0f;
var destClip = new RectangleF(this.Location.ToDeviceValue(renderer, this),
new SizeF(Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this)));
RectangleF destRect = destClip;
this.PushTransforms(renderer);
renderer.SetClip(new Region(destClip), CombineMode.Intersect);
this.SetClip(renderer);
if (AspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
}
if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
{
var fScaleX = destClip.Width / srcRect.Width;
var fScaleY = destClip.Height / srcRect.Height;
var xOffset = 0.0f;
var yOffset = 0.0f;
switch (AspectRatio.Align)
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
break;
case SvgPreserveAspectRatio.xMinYMid:
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMidYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMinYMax:
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMidYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMaxYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
}
if (AspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
}
destRect = new RectangleF(destClip.X + xOffset, destClip.Y + yOffset,
srcRect.Width * fScaleX, srcRect.Height * fScaleY);
switch (AspectRatio.Align)
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
break;
case SvgPreserveAspectRatio.xMinYMid:
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMidYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMinYMax:
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMidYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMaxYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
}
renderer.DrawImage(b, destRect, srcRect, GraphicsUnit.Pixel);
this.ResetClip(renderer);
this.PopTransforms(renderer);
destRect = new RectangleF(destClip.X + xOffset, destClip.Y + yOffset,
srcRect.Width * fScaleX, srcRect.Height * fScaleY);
}
if (bmp != null)
{
renderer.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel);
bmp.Dispose();
}
else if (svg != null)
{
var currOffset = new PointF(renderer.Transform.OffsetX, renderer.Transform.OffsetY);
renderer.TranslateTransform(-currOffset.X, -currOffset.Y);
renderer.ScaleTransform(destRect.Width / srcRect.Width, destRect.Height / srcRect.Height);
renderer.TranslateTransform(currOffset.X + destRect.X, currOffset.Y + destRect.Y);
renderer.SetBoundable(new GenericBoundable(srcRect));
svg.RenderElement(renderer);
renderer.PopBoundable();
}
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
// TODO: cache images... will need a shared context for this
// TODO: support preserveAspectRatio, etc
}
}
protected Image GetImage(Uri uri)
protected object GetImage(Uri uri)
{
try
{
......@@ -205,8 +233,10 @@ namespace Svg
// we're assuming base64, as ascii encoding would be *highly* unsusual for images
// also assuming it's png or jpeg mimetype
byte[] imageBytes = Convert.FromBase64String(uriString.Substring(dataIdx));
Image image = Image.FromStream(new MemoryStream(imageBytes));
return image;
using (var stream = new MemoryStream(imageBytes))
{
return Image.FromStream(stream);
}
}
if (!uri.IsAbsoluteUri)
......@@ -219,15 +249,19 @@ namespace Svg
using (WebResponse webResponse = httpRequest.GetResponse())
{
MemoryStream ms = BufferToMemoryStream(webResponse.GetResponseStream());
if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
using (var stream = webResponse.GetResponseStream())
{
var doc = SvgDocument.Open<SvgDocument>(ms);
return doc.Draw();
}
else
{
return Bitmap.FromStream(ms);
stream.Position = 0;
if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
{
var doc = SvgDocument.Open<SvgDocument>(stream);
doc.BaseUri = uri;
return doc;
}
else
{
return Bitmap.FromStream(stream);
}
}
}
}
......
......@@ -79,6 +79,38 @@ namespace Svg
}
}
/// <summary>
/// Gets or sets the marker (end cap) of the path.
/// </summary>
[SvgAttribute("marker-end")]
public Uri MarkerEnd
{
get { return this.Attributes.GetAttribute<Uri>("marker-end"); }
set { this.Attributes["marker-end"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-mid")]
public Uri MarkerMid
{
get { return this.Attributes.GetAttribute<Uri>("marker-mid"); }
set { this.Attributes["marker-mid"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-start")]
public Uri MarkerStart
{
get { return this.Attributes.GetAttribute<Uri>("marker-start"); }
set { this.Attributes["marker-start"] = value; }
}
public override SvgPaintServer Fill
{
get { return null; /* Line can't have a fill */ }
......@@ -92,7 +124,7 @@ namespace Svg
{
}
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -108,6 +140,37 @@ namespace Svg
return this._path;
}
/// <summary>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal override bool RenderStroke(ISvgRenderer renderer)
{
var result = base.RenderStroke(renderer);
var path = this.Path(renderer);
if (this.MarkerStart != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
}
if (this.MarkerMid != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
}
if (this.MarkerEnd != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<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]);
}
return result;
}
public override System.Drawing.RectangleF Bounds
{
get { return this.Path(null).GetBounds(); }
......
......@@ -15,16 +15,47 @@ namespace Svg
public class SvgPolygon : SvgVisualElement
{
private GraphicsPath _path;
private SvgUnitCollection _points;
/// <summary>
/// The points that make up the SvgPolygon
/// </summary>
[SvgAttribute("points")]
public SvgUnitCollection Points
public SvgPointCollection Points
{
get { return this._points; }
set { this._points = value; this.IsPathDirty = true; }
get { return this.Attributes["points"] as SvgPointCollection; }
set { this.Attributes["points"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Gets or sets the marker (end cap) of the path.
/// </summary>
[SvgAttribute("marker-end")]
public Uri MarkerEnd
{
get { return this.Attributes.GetAttribute<Uri>("marker-end"); }
set { this.Attributes["marker-end"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-mid")]
public Uri MarkerMid
{
get { return this.Attributes.GetAttribute<Uri>("marker-mid"); }
set { this.Attributes["marker-mid"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-start")]
public Uri MarkerStart
{
get { return this.Attributes.GetAttribute<Uri>("marker-start"); }
set { this.Attributes["marker-start"] = value; }
}
protected override bool RequiresSmoothRendering
......@@ -32,7 +63,7 @@ namespace Svg
get { return true; }
}
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -41,14 +72,15 @@ namespace Svg
try
{
for (int i = 2; i < this._points.Count; i+=2)
var points = this.Points;
for (int i = 2; (i + 1) < points.Count; i += 2)
{
var endPoint = SvgUnit.GetDevicePoint(this._points[i], this._points[i+1], renderer, this);
var endPoint = SvgUnit.GetDevicePoint(points[i], points[i + 1], renderer, this);
//first line
if (_path.PointCount == 0)
{
_path.AddLine(SvgUnit.GetDevicePoint(this._points[i - 2], this._points[i - 1], renderer, this), endPoint);
_path.AddLine(SvgUnit.GetDevicePoint(points[i - 2], points[i - 1], renderer, this), endPoint);
}
else
{
......@@ -67,6 +99,37 @@ namespace Svg
return this._path;
}
/// <summary>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal override bool RenderStroke(ISvgRenderer renderer)
{
var result = base.RenderStroke(renderer);
var path = this.Path(renderer);
if (this.MarkerStart != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
}
if (this.MarkerMid != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
}
if (this.MarkerEnd != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<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]);
}
return result;
}
public override RectangleF Bounds
{
get { return this.Path(null).GetBounds(); }
......@@ -81,7 +144,7 @@ namespace Svg
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgPolygon;
newObj.Points = new SvgUnitCollection();
newObj.Points = new SvgPointCollection();
foreach (var pt in this.Points)
newObj.Points.Add(pt);
return newObj;
......
......@@ -13,8 +13,40 @@ namespace Svg
[SvgElement("polyline")]
public class SvgPolyline : SvgPolygon
{
/// <summary>
/// Gets or sets the marker (end cap) of the path.
/// </summary>
[SvgAttribute("marker-end")]
public Uri MarkerEnd
{
get { return this.Attributes.GetAttribute<Uri>("marker-end"); }
set { this.Attributes["marker-end"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-mid")]
public Uri MarkerMid
{
get { return this.Attributes.GetAttribute<Uri>("marker-mid"); }
set { this.Attributes["marker-mid"] = value; }
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[SvgAttribute("marker-start")]
public Uri MarkerStart
{
get { return this.Attributes.GetAttribute<Uri>("marker-start"); }
set { this.Attributes["marker-start"] = value; }
}
private GraphicsPath _Path;
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_Path == null || this.IsPathDirty)
{
......@@ -22,7 +54,7 @@ namespace Svg
try
{
for (int i = 0; i < Points.Count; i += 2)
for (int i = 0; (i + 1) < Points.Count; i += 2)
{
PointF endPoint = new PointF(Points[i].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Points[i + 1].ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
......@@ -46,5 +78,36 @@ namespace Svg
}
return _Path;
}
/// <summary>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal override bool RenderStroke(ISvgRenderer renderer)
{
var result = base.RenderStroke(renderer);
var path = this.Path(renderer);
if (this.MarkerStart != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
}
if (this.MarkerMid != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
}
if (this.MarkerEnd != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<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]);
}
return result;
}
}
}
\ No newline at end of file
......@@ -174,7 +174,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_path == null || IsPathDirty)
{
......@@ -197,8 +197,8 @@ namespace Svg
var lineEnd = new PointF();
var width = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var height = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
var rx = CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2;
var ry = CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2;
var rx = Math.Min(CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2, width);
var ry = Math.Min(CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2, height);
var location = Location.ToDeviceValue(renderer, this);
// Start
......@@ -261,7 +261,7 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (Width.Value > 0.0f && Height.Value > 0.0f)
{
......
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Linq;
namespace Svg
{
......@@ -9,14 +11,13 @@ namespace Svg
/// </summary>
public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvgStylable, ISvgClipable
{
private bool _dirty;
private bool _requiresSmoothRendering;
private Region _previousClip;
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public abstract GraphicsPath Path(SvgRenderer renderer);
public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
......@@ -41,15 +42,13 @@ namespace Svg
public abstract RectangleF Bounds { get; }
/// <summary>
/// Gets or sets a value indicating whether this element's <see cref="Path"/> is dirty.
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
/// </summary>
/// <value>
/// <c>true</c> if the path is dirty; otherwise, <c>false</c>.
/// </value>
protected virtual bool IsPathDirty
[SvgAttribute("clip")]
public virtual string Clip
{
get { return this._dirty; }
set { this._dirty = value; }
get { return this.Attributes.GetInheritedAttribute<string>("clip"); }
set { this.Attributes["clip"] = value; }
}
/// <summary>
......@@ -58,7 +57,7 @@ namespace Svg
[SvgAttribute("clip-path")]
public virtual Uri ClipPath
{
get { return this.Attributes.GetAttribute<Uri>("clip-path"); }
get { return this.Attributes.GetInheritedAttribute<Uri>("clip-path"); }
set { this.Attributes["clip-path"] = value; }
}
......@@ -78,7 +77,7 @@ namespace Svg
[SvgAttribute("filter")]
public virtual Uri Filter
{
get { return this.Attributes.GetAttribute<Uri>("filter"); }
get { return this.Attributes.GetInheritedAttribute<Uri>("filter"); }
set { this.Attributes["filter"] = value; }
}
......@@ -95,50 +94,91 @@ namespace Svg
/// </summary>
public SvgVisualElement()
{
this._dirty = true;
this.IsPathDirty = true;
this._requiresSmoothRendering = false;
}
protected virtual bool Renderable { get { return true; } }
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
if ((this.Path(renderer) != null) && this.Visible && this.Displayable)
this.Render(renderer, true);
}
private void Render(ISvgRenderer renderer, bool renderFilter)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
(!Renderable || this.Path(renderer) != null))
{
this.PushTransforms(renderer);
this.SetClip(renderer);
bool renderNormal = true;
// If this element needs smoothing enabled turn anti-aliasing on
if (this.RequiresSmoothRendering)
if (renderFilter && this.Filter != null)
{
renderer.SmoothingMode = SmoothingMode.AntiAlias;
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;
}
}
this.RenderFill(renderer);
this.RenderStroke(renderer);
// Reset the smoothing mode
if (this.RequiresSmoothRendering && renderer.SmoothingMode == SmoothingMode.AntiAlias)
if (renderNormal)
{
renderer.SmoothingMode = SmoothingMode.Default;
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);
}
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
/// <summary>
/// Renders the fill of the <see cref="SvgVisualElement"/> to the specified <see cref="SvgRenderer"/>
/// Renders the fill of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected internal virtual void RenderFill(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal virtual void RenderFill(ISvgRenderer renderer)
{
if (this.Fill != null)
{
using (Brush brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
using (var brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
{
if (brush != null)
{
......@@ -150,72 +190,146 @@ namespace Svg
}
/// <summary>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="SvgRenderer"/>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected internal virtual void RenderStroke(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal virtual bool RenderStroke(ISvgRenderer renderer)
{
if (this.Stroke != null)
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (var pen = new Pen(this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1)), strokeWidth))
using (var brush = this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1), true))
{
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
if (brush != null)
{
/* divide by stroke width - GDI behaviour that I don't quite understand yet.*/
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.Value <= 0) ? 1 : u.Value) / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
}
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, this.Path(renderer));
renderer.DrawPath(pen, path);
return true;
}
}
}
}
}
return false;
}
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
protected internal virtual void SetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
protected internal virtual void SetClip(ISvgRenderer renderer)
{
if (this.ClipPath != null)
if (this.ClipPath != null || !string.IsNullOrEmpty(this.Clip))
{
SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString());
this._previousClip = renderer.Clip;
this._previousClip = renderer.GetClip();
if (this.ClipPath != null)
{
SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString());
if (clipPath != null) renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
}
if (clipPath != null)
var clip = this.Clip;
if (!string.IsNullOrEmpty(clip) && clip.StartsWith("rect("))
{
renderer.AddClip(clipPath.GetClipRegion(this));
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);
}
}
}
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(ISvgRenderer renderer)
{
if (this._previousClip != null)
{
renderer.Clip = this._previousClip;
renderer.SetClip(this._previousClip);
this._previousClip = null;
}
}
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
void ISvgClipable.SetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
void ISvgClipable.SetClip(ISvgRenderer renderer)
{
this.SetClip(renderer);
}
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
void ISvgClipable.ResetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
void ISvgClipable.ResetClip(ISvgRenderer renderer)
{
this.ResetClip(renderer);
}
......
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Reflection;
using System.ComponentModel;
......@@ -11,13 +12,6 @@ namespace Svg
{
public abstract partial class SvgVisualElement
{
private static float FixOpacityValue(float value)
{
const float max = 1.0f;
const float min = 0.0f;
return Math.Min(Math.Max(value, min), max);
}
/// <summary>
/// Gets or sets a value to determine whether the element will be rendered.
/// </summary>
......@@ -56,331 +50,11 @@ namespace Svg
/// <summary>
/// Gets or sets the fill <see cref="SvgPaintServer"/> of this element.
/// </summary>
[SvgAttribute("fill")]
public virtual SvgPaintServer Fill
{
get { return (this.Attributes["fill"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["fill"]; }
set { this.Attributes["fill"] = value; }
}
/// <summary>
/// Gets or sets the <see cref="SvgPaintServer"/> to be used when rendering a stroke around this element.
/// </summary>
[SvgAttribute("stroke")]
public virtual SvgPaintServer Stroke
{
get { return (this.Attributes["stroke"] == null) ? null : (SvgPaintServer)this.Attributes["stroke"]; }
set { this.Attributes["stroke"] = value; }
}
[SvgAttribute("fill-rule")]
public virtual SvgFillRule FillRule
{
get { return (this.Attributes["fill-rule"] == null) ? SvgFillRule.NonZero : (SvgFillRule)this.Attributes["fill-rule"]; }
set { this.Attributes["fill-rule"] = value; }
}
/// <summary>
/// Gets or sets the opacity of this element's <see cref="Fill"/>.
/// </summary>
[SvgAttribute("fill-opacity")]
public virtual float FillOpacity
{
get { return (this.Attributes["fill-opacity"] == null) ? this.Opacity : (float)this.Attributes["fill-opacity"]; }
set { this.Attributes["fill-opacity"] = FixOpacityValue(value); }
}
/// <summary>
/// Gets or sets the width of the stroke (if the <see cref="Stroke"/> property has a valid value specified.
/// </summary>
[SvgAttribute("stroke-width")]
public virtual SvgUnit StrokeWidth
{
get { return (this.Attributes["stroke-width"] == null) ? new SvgUnit(1.0f) : (SvgUnit)this.Attributes["stroke-width"]; }
set { this.Attributes["stroke-width"] = value; }
}
[SvgAttribute("stroke-linecap")]
public virtual SvgStrokeLineCap StrokeLineCap
{
get { return (this.Attributes["stroke-linecap"] == null) ? SvgStrokeLineCap.Butt : (SvgStrokeLineCap)this.Attributes["stroke-linecap"]; }
set { this.Attributes["stroke-linecap"] = value; }
}
[SvgAttribute("stroke-linejoin")]
public virtual SvgStrokeLineJoin StrokeLineJoin
{
get { return (this.Attributes["stroke-linejoin"] == null) ? SvgStrokeLineJoin.Miter : (SvgStrokeLineJoin)this.Attributes["stroke-linejoin"]; }
set { this.Attributes["stroke-linejoin"] = value; }
}
[SvgAttribute("stroke-miterlimit")]
public virtual float StrokeMiterLimit
{
get { return (this.Attributes["stroke-miterlimit"] == null) ? 4.0f : (float)this.Attributes["stroke-miterlimit"]; }
set { this.Attributes["stroke-miterlimit"] = value; }
}
[SvgAttribute("stroke-dasharray")]
public virtual SvgUnitCollection StrokeDashArray
{
get { return this.Attributes["stroke-dasharray"] as SvgUnitCollection; }
set { this.Attributes["stroke-dasharray"] = value; }
}
[SvgAttribute("stroke-dashoffset")]
public virtual SvgUnit StrokeDashOffset
{
get { return (this.Attributes["stroke-dashoffset"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["stroke-dashoffset"]; }
set { this.Attributes["stroke-dashoffset"] = value; }
}
/// <summary>
/// Gets or sets the opacity of the stroke, if the <see cref="Stroke"/> property has been specified. 1.0 is fully opaque; 0.0 is transparent.
/// </summary>
[SvgAttribute("stroke-opacity")]
public virtual float StrokeOpacity
{
get { return (this.Attributes["stroke-opacity"] == null) ? this.Opacity : (float)this.Attributes["stroke-opacity"]; }
set { this.Attributes["stroke-opacity"] = FixOpacityValue(value); }
}
/// <summary>
/// Gets or sets the opacity of the element. 1.0 is fully opaque; 0.0 is transparent.
/// </summary>
[SvgAttribute("opacity")]
public virtual float Opacity
{
get { return (this.Attributes["opacity"] == null) ? 1.0f : (float)this.Attributes["opacity"]; }
set { this.Attributes["opacity"] = FixOpacityValue(value); }
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[SvgAttribute("font-family")]
public virtual string FontFamily
[SvgAttribute("enable-background")]
public virtual string EnableBackground
{
get { return this.Attributes["font-family"] as string; }
set { this.Attributes["font-family"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[SvgAttribute("font-size")]
public virtual SvgUnit FontSize
{
get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
set { this.Attributes["font-size"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-style")]
public virtual SvgFontStyle FontStyle
{
get { return (this.Attributes["font-style"] == null) ? SvgFontStyle.inherit : (SvgFontStyle)this.Attributes["font-style"]; }
set { this.Attributes["font-style"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-variant")]
public virtual SvgFontVariant FontVariant
{
get { return (this.Attributes["font-variant"] == null) ? SvgFontVariant.inherit : (SvgFontVariant)this.Attributes["font-variant"]; }
set { this.Attributes["font-variant"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("text-decoration")]
public virtual SvgTextDecoration TextDecoration
{
get { return (this.Attributes["text-decoration"] == null) ? SvgTextDecoration.inherit : (SvgTextDecoration)this.Attributes["text-decoration"]; }
set { this.Attributes["text-decoration"] = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-weight")]
public virtual SvgFontWeight FontWeight
{
get { return (this.Attributes["font-weight"] == null) ? SvgFontWeight.inherit : (SvgFontWeight)this.Attributes["font-weight"]; }
set { this.Attributes["font-weight"] = value; this.IsPathDirty = true; }
}
private enum FontParseState
{
fontStyle,
fontVariant,
fontWeight,
fontSize,
fontFamilyNext,
fontFamilyCurr
}
/// <summary>
/// Set all font information.
/// </summary>
[SvgAttribute("font")]
public virtual string Font
{
get { return (this.Attributes["font"] == null ? "" : this.Attributes["font"] as string); }
set
{
var state = FontParseState.fontStyle;
var parts = value.Split(' ');
SvgFontStyle fontStyle;
SvgFontVariant fontVariant;
SvgFontWeight fontWeight;
SvgUnit fontSize;
bool success;
string[] sizes;
string part;
for (int i = 0; i < parts.Length; i++)
{
part = parts[i];
success = false;
while (!success)
{
switch (state)
{
case FontParseState.fontStyle:
success = Enums.TryParse<SvgFontStyle>(part, out fontStyle);
if (success) this.FontStyle = fontStyle;
state++;
break;
case FontParseState.fontVariant:
success = Enums.TryParse<SvgFontVariant>(part, out fontVariant);
if (success) this.FontVariant = fontVariant;
state++;
break;
case FontParseState.fontWeight:
success = Enums.TryParse<SvgFontWeight>(part, out fontWeight);
if (success) this.FontWeight = fontWeight;
state++;
break;
case FontParseState.fontSize:
sizes = part.Split('/');
try
{
fontSize = (SvgUnit)(new SvgUnitConverter().ConvertFromInvariantString(sizes[0]));
success = true;
this.FontSize = fontSize;
}
catch { }
state++;
break;
case FontParseState.fontFamilyNext:
state++;
success = true;
break;
}
}
switch (state)
{
case FontParseState.fontFamilyNext:
this.FontFamily = string.Join(" ", parts, i + 1, parts.Length - (i + 1));
i = int.MaxValue - 2;
break;
case FontParseState.fontFamilyCurr:
this.FontFamily = string.Join(" ", parts, i, parts.Length - (i));
i = int.MaxValue - 2;
break;
}
}
this.Attributes["font"] = value;
this.IsPathDirty = true;
}
}
private const string DefaultFontFamily = "Times New Roman";
/// <summary>
/// Get the font information based on data stored with the text object or inherited from the parent.
/// </summary>
/// <returns></returns>
internal System.Drawing.Font GetFont(SvgRenderer renderer)
{
// Get the font-size
float fontSize;
var fontSizeUnit = this.FontSize;
if (fontSizeUnit == SvgUnit.None)
{
fontSize = 1.0f;
}
else
{
fontSize = fontSizeUnit.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
}
var fontStyle = System.Drawing.FontStyle.Regular;
// Get the font-weight
switch (this.FontWeight)
{
case SvgFontWeight.bold:
case SvgFontWeight.bolder:
case SvgFontWeight.w600:
case SvgFontWeight.w700:
case SvgFontWeight.w800:
case SvgFontWeight.w900:
fontStyle |= System.Drawing.FontStyle.Bold;
break;
}
// Get the font-style
switch (this.FontStyle)
{
case SvgFontStyle.italic:
case SvgFontStyle.oblique:
fontStyle |= System.Drawing.FontStyle.Italic;
break;
}
// Get the text-decoration
switch (this.TextDecoration)
{
case SvgTextDecoration.lineThrough:
fontStyle |= System.Drawing.FontStyle.Strikeout;
break;
case SvgTextDecoration.underline:
fontStyle |= System.Drawing.FontStyle.Underline;
break;
}
// Get the font-family
string family = ValidateFontFamily(this.FontFamily) ?? DefaultFontFamily;
return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel);
}
private static string ValidateFontFamily(string fontFamilyList)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var fontParts = (fontFamilyList ?? "").Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
var families = System.Drawing.FontFamily.Families;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach (var f in fontParts.Where(f => families.Any(family => family.Name.ToLower() == f.ToLower())))
{
return f;
}
// No valid font family found from the list requested.
return null;
get { return this.Attributes["enable-background"] as string; }
set { this.Attributes["enable-background"] = value; }
}
}
......
......@@ -20,14 +20,14 @@ namespace Svg
/// </summary>
SvgClipRule ClipRule { get; set; }
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
void SetClip(SvgRenderer renderer);
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
void SetClip(ISvgRenderer renderer);
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
void ResetClip(SvgRenderer renderer);
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
void ResetClip(ISvgRenderer renderer);
}
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ namespace Svg
/// </summary>
public SvgClipPath()
{
this.ClipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
this.ClipPathUnits = SvgCoordinateUnits.Inherit;
}
private GraphicsPath cachedClipPath = null;
......@@ -49,7 +49,20 @@ namespace Svg
this._pathDirty = false;
}
return new Region(cachedClipPath);
var result = cachedClipPath;
if (ClipPathUnits == SvgCoordinateUnits.ObjectBoundingBox)
{
result = (GraphicsPath)cachedClipPath.Clone();
using (var transform = new Matrix())
{
var bounds = owner.Bounds;
transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Append);
transform.Translate(bounds.Left, bounds.Top, MatrixOrder.Append);
result.Transform(transform);
}
}
return new Region(result);
}
/// <summary>
......@@ -75,7 +88,7 @@ namespace Svg
}
}
path.AddPath(childPath, false);
if (childPath.PointCount > 0) path.AddPath(childPath, false);
}
foreach (SvgElement child in element.Children)
......@@ -108,10 +121,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing
}
......
......@@ -10,8 +10,12 @@ namespace Svg.Css
{
public Selector<SvgElement> Type(NamespacePrefix prefix, string name)
{
var type = SvgElementFactory.AvailableElements.SingleOrDefault(e => e.ElementName == name);
return nodes => nodes.Where(n => n.GetType() == type.ElementType);
SvgElementFactory.ElementInfo type = null;
if (SvgElementFactory.AvailableElements.TryGetValue(name, out type))
{
return nodes => nodes.Where(n => n.GetType() == type.ElementType);
}
return nodes => Enumerable.Empty<SvgElement>();
}
public Selector<SvgElement> Universal(NamespacePrefix prefix)
......@@ -31,7 +35,7 @@ namespace Svg.Css
public Selector<SvgElement> AttributeExists(NamespacePrefix prefix, string name)
{
return nodes => nodes.Where(n => n.Attributes.ContainsKey(name) || n.CustomAttributes.ContainsKey(name));
return nodes => nodes.Where(n => n.ContainsAttribute(name));
}
public Selector<SvgElement> AttributeExact(NamespacePrefix prefix, string name, string value)
......@@ -39,9 +43,7 @@ namespace Svg.Css
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val == value) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString() == value);
return (n.TryGetAttribute(name, out val) && val == value);
});
}
......@@ -50,9 +52,7 @@ namespace Svg.Css
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split(' ').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split(' ').Contains(value));
return (n.TryGetAttribute(name, out val) && val.Split(' ').Contains(value));
});
}
......@@ -63,9 +63,7 @@ namespace Svg.Css
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split('-').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split('-').Contains(value));
return (n.TryGetAttribute(name, out val) && val.Split('-').Contains(value));
}));
}
......@@ -76,9 +74,7 @@ namespace Svg.Css
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.StartsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().StartsWith(value));
return (n.TryGetAttribute(name, out val) && val.StartsWith(value));
}));
}
......@@ -89,9 +85,7 @@ namespace Svg.Css
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.EndsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().EndsWith(value));
return (n.TryGetAttribute(name, out val) && val.EndsWith(value));
}));
}
......@@ -102,9 +96,7 @@ namespace Svg.Css
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Contains(value));
return (n.TryGetAttribute(name, out val) && val.Contains(value));
}));
}
......
......@@ -2,14 +2,17 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace Svg
{
/// <summary>
/// Defines the various coordinate units certain SVG elements may use.
/// </summary>
[TypeConverter(typeof(SvgCoordinateUnitsConverter))]
public enum SvgCoordinateUnits
{
Inherit,
/// <summary>
/// Indicates that the coordinate system of the owner element is to be used.
/// </summary>
......
......@@ -7,10 +7,10 @@ namespace Svg
{
public enum SvgOverflow
{
inherit,
auto,
visible,
hidden,
scroll,
inherit
scroll
}
}
......@@ -23,7 +23,7 @@ namespace Svg
set { this.y = value; }
}
public PointF ToDeviceValue(SvgRenderer renderer, SvgElement owner)
public PointF ToDeviceValue(ISvgRenderer renderer, SvgElement owner)
{
return SvgUnit.GetDevicePoint(this.X, this.Y, renderer, owner);
}
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Globalization;
namespace Svg
{
/// <summary>
/// Represents a list of <see cref="SvgUnits"/> used with the <see cref="SvgPolyline"/> and <see cref="SvgPolygon"/>.
/// </summary>
[TypeConverter(typeof(SvgPointCollectionConverter))]
public class SvgPointCollection : List<SvgUnit>
{
public override string ToString()
{
string ret = "";
foreach (var unit in this)
{
ret += unit.ToString() + " ";
}
return ret;
}
}
/// <summary>
/// A class to convert string into <see cref="SvgUnitCollection"/> instances.
/// </summary>
internal class SvgPointCollectionConverter : TypeConverter
{
//private static readonly SvgUnitConverter _unitConverter = new SvgUnitConverter();
/// <summary>
/// Converts the given object to the type of this converter, using the specified context and culture information.
/// </summary>
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
/// <param name="culture">The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.</param>
/// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
/// <returns>
/// An <see cref="T:System.Object"/> that represents the converted value.
/// </returns>
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed. </exception>
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
var strValue = ((string)value).Trim();
if (string.Compare(strValue, "none", StringComparison.InvariantCultureIgnoreCase) == 0) return null;
var parser = new CoordinateParser(strValue);
var pointValue = 0.0f;
var result = new SvgPointCollection();
while (parser.TryGetFloat(out pointValue))
{
result.Add(new SvgUnit(SvgUnitType.User, pointValue));
}
return result;
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((SvgPointCollection)value).ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextLengthAdjust
{
spacing,
spacingAndGlyphs
}
}
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