зеркало из https://github.com/wieslawsoltes/SVG.git
Bug Fixes
- Improving rendering of SVG images - Allow markers on lines, polygons, and polylines - Implement different line joins and line caps - Improve CSS handling
This commit is contained in:
Родитель
3f4ee33309
Коммит
4200d30212
|
@ -109,88 +109,116 @@ namespace Svg
|
|||
|
||||
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.SetClip(new Region(destClip), CombineMode.Intersect);
|
||||
this.SetClip(renderer);
|
||||
|
||||
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;
|
||||
|
||||
if (AspectRatio.Slice)
|
||||
{
|
||||
fScaleX = Math.Max(fScaleX, fScaleY);
|
||||
fScaleY = Math.Max(fScaleX, fScaleY);
|
||||
}
|
||||
else
|
||||
{
|
||||
fScaleX = Math.Min(fScaleX, fScaleY);
|
||||
fScaleY = Math.Min(fScaleX, 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;
|
||||
}
|
||||
|
||||
destRect = new RectangleF(destClip.X + xOffset, destClip.Y + yOffset,
|
||||
srcRect.Width * fScaleX, srcRect.Height * fScaleY);
|
||||
}
|
||||
|
||||
renderer.DrawImage(b, destRect, srcRect, GraphicsUnit.Pixel);
|
||||
|
||||
this.ResetClip(renderer);
|
||||
this.PopTransforms(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;
|
||||
}
|
||||
|
||||
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 != 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;
|
||||
|
||||
if (AspectRatio.Slice)
|
||||
{
|
||||
fScaleX = Math.Max(fScaleX, fScaleY);
|
||||
fScaleY = Math.Max(fScaleX, fScaleY);
|
||||
}
|
||||
else
|
||||
{
|
||||
fScaleX = Math.Min(fScaleX, fScaleY);
|
||||
fScaleY = Math.Min(fScaleX, 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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -228,7 +256,7 @@ namespace Svg
|
|||
{
|
||||
var doc = SvgDocument.Open<SvgDocument>(stream);
|
||||
doc.BaseUri = uri;
|
||||
return doc.Draw();
|
||||
return doc;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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 */ }
|
||||
|
@ -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 SvgPointCollection _points;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The points that make up the SvgPolygon
|
||||
/// </summary>
|
||||
[SvgAttribute("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
|
||||
|
@ -41,14 +72,15 @@ namespace Svg
|
|||
|
||||
try
|
||||
{
|
||||
for (int i = 2; (i + 1) < 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(); }
|
||||
|
|
|
@ -13,6 +13,38 @@ 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(ISvgRenderer renderer)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ namespace Svg
|
|||
/// </summary>
|
||||
public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvgStylable, ISvgClipable
|
||||
{
|
||||
private bool _dirty;
|
||||
private bool _requiresSmoothRendering;
|
||||
private Region _previousClip;
|
||||
|
||||
|
@ -42,18 +41,6 @@ namespace Svg
|
|||
/// <value>The bounds.</value>
|
||||
public abstract RectangleF Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this element's <see cref="Path"/> is dirty.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the path is dirty; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
protected virtual bool IsPathDirty
|
||||
{
|
||||
get { return this._dirty; }
|
||||
set { this._dirty = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
|
||||
/// </summary>
|
||||
|
@ -107,7 +94,7 @@ namespace Svg
|
|||
/// </summary>
|
||||
public SvgVisualElement()
|
||||
{
|
||||
this._dirty = true;
|
||||
this.IsPathDirty = true;
|
||||
this._requiresSmoothRendering = false;
|
||||
}
|
||||
|
||||
|
@ -206,7 +193,7 @@ namespace Svg
|
|||
/// 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 virtual void RenderStroke(ISvgRenderer renderer)
|
||||
protected internal virtual bool RenderStroke(ISvgRenderer renderer)
|
||||
{
|
||||
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
|
||||
{
|
||||
|
@ -215,20 +202,74 @@ namespace Svg
|
|||
{
|
||||
if (brush != null)
|
||||
{
|
||||
using (var pen = new Pen(brush, strokeWidth))
|
||||
var path = this.Path(renderer);
|
||||
var bounds = path.GetBounds();
|
||||
if (path.PointCount < 1) return false;
|
||||
if (bounds.Width <= 0 && bounds.Height <= 0)
|
||||
{
|
||||
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
|
||||
switch (this.StrokeLineCap)
|
||||
{
|
||||
/* 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();
|
||||
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>
|
||||
|
|
|
@ -12,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>
|
||||
|
@ -64,383 +57,5 @@ namespace Svg
|
|||
set { this.Attributes["enable-background"] = value; }
|
||||
}
|
||||
|
||||
/// <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 colour of the gradient stop.
|
||||
/// </summary>
|
||||
/// <remarks>Apparently this can be set on non-sensical elements. Don't ask; just check the tests.</remarks>
|
||||
[SvgAttribute("stop-color")]
|
||||
[TypeConverter(typeof(SvgPaintServerFactory))]
|
||||
public SvgPaintServer StopColor
|
||||
{
|
||||
get { return this.Attributes["stop-color"] as SvgPaintServer; }
|
||||
set { this.Attributes["stop-color"] = 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
|
||||
{
|
||||
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 style 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 varient 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 IFontDefn GetFont(ISvgRenderer 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 family = ValidateFontFamily(this.FontFamily, this.OwnerDocument);
|
||||
var sFaces = family as IEnumerable<SvgFontFace>;
|
||||
|
||||
if (sFaces == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
var ff = family as FontFamily;
|
||||
if (!ff.IsStyleAvailable(fontStyle))
|
||||
{
|
||||
// Do Something
|
||||
}
|
||||
|
||||
// Get the font-family
|
||||
return new GdiFontDefn(new System.Drawing.Font(ff, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel));
|
||||
}
|
||||
else
|
||||
{
|
||||
var font = sFaces.First().Parent as SvgFont;
|
||||
if (font == null)
|
||||
{
|
||||
var uri = sFaces.First().Descendants().OfType<SvgFontFaceUri>().First().ReferencedElement;
|
||||
font = OwnerDocument.IdManager.GetElementById(uri) as SvgFont;
|
||||
}
|
||||
return new SvgFontDefn(font, fontSize, OwnerDocument.Ppi);
|
||||
}
|
||||
}
|
||||
|
||||
public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc)
|
||||
{
|
||||
// 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;
|
||||
FontFamily family;
|
||||
IEnumerable<SvgFontFace> sFaces;
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces;
|
||||
|
||||
family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
|
||||
if (family != null) return family;
|
||||
|
||||
switch (f)
|
||||
{
|
||||
case "serif":
|
||||
return System.Drawing.FontFamily.GenericSerif;
|
||||
case "sans-serif":
|
||||
return System.Drawing.FontFamily.GenericSansSerif;
|
||||
case "monospace":
|
||||
return System.Drawing.FontFamily.GenericMonospace;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid font family found from the list requested.
|
||||
return System.Drawing.FontFamily.GenericSansSerif;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -35,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)
|
||||
|
@ -43,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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -54,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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,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));
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -80,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));
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -93,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));
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -106,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));
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -89,9 +89,9 @@ namespace Svg
|
|||
|
||||
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
|
||||
{
|
||||
var path = this.Children.FirstOrDefault(x => x is SvgPath);
|
||||
var path = this.Children.FirstOrDefault(x => x is SvgVisualElement);
|
||||
if (path != null)
|
||||
return (path as SvgPath).Path(renderer);
|
||||
return (path as SvgVisualElement).Path(renderer);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ namespace Svg
|
|||
/// <param name="pOwner"></param>
|
||||
/// <param name="pMarkerPoint1"></param>
|
||||
/// <param name="pMarkerPoint2"></param>
|
||||
public void RenderMarker(ISvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
|
||||
public void RenderMarker(ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
|
||||
{
|
||||
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
|
||||
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
|
||||
|
@ -148,7 +148,7 @@ namespace Svg
|
|||
/// <param name="pMarkerPoint1"></param>
|
||||
/// <param name="pMarkerPoint2"></param>
|
||||
/// <param name="pMarkerPoint3"></param>
|
||||
public void RenderMarker(ISvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
|
||||
public void RenderMarker(ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
|
||||
{
|
||||
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
|
||||
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
|
||||
|
@ -168,7 +168,7 @@ namespace Svg
|
|||
/// <param name="pRenderer"></param>
|
||||
/// <param name="pOwner"></param>
|
||||
/// <param name="pMarkerPoint"></param>
|
||||
private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
|
||||
private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pMarkerPoint)
|
||||
{
|
||||
using (var pRenderPen = CreatePen(pOwner, pRenderer))
|
||||
{
|
||||
|
@ -195,9 +195,9 @@ namespace Svg
|
|||
break;
|
||||
}
|
||||
markerPath.Transform(transMatrix);
|
||||
pRenderer.DrawPath(pRenderPen, markerPath);
|
||||
if (pRenderPen != null) pRenderer.DrawPath(pRenderPen, markerPath);
|
||||
|
||||
SvgPaintServer pFill = Fill;
|
||||
SvgPaintServer pFill = this.Children.First().Fill;
|
||||
SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for?
|
||||
float fOpacity = FillOpacity;
|
||||
|
||||
|
@ -218,8 +218,9 @@ namespace Svg
|
|||
/// </summary>
|
||||
/// <param name="pStroke"></param>
|
||||
/// <returns></returns>
|
||||
private Pen CreatePen(SvgPath pPath, ISvgRenderer renderer)
|
||||
private Pen CreatePen(SvgVisualElement pPath, ISvgRenderer renderer)
|
||||
{
|
||||
if (pPath.Stroke == null) return null;
|
||||
Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
|
||||
switch (MarkerUnits)
|
||||
{
|
||||
|
@ -233,10 +234,10 @@ namespace Svg
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a clone of the current path, scaled for the stroke with
|
||||
/// Get a clone of the current path, scaled for the stroke width
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private GraphicsPath GetClone(SvgPath pPath)
|
||||
private GraphicsPath GetClone(SvgVisualElement pPath)
|
||||
{
|
||||
GraphicsPath pRet = Path(null).Clone() as GraphicsPath;
|
||||
switch (MarkerUnits)
|
||||
|
@ -262,7 +263,7 @@ namespace Svg
|
|||
private float AdjustForViewBoxWidth(float fWidth)
|
||||
{
|
||||
// TODO: We know this isn't correct
|
||||
return (fWidth / ViewBox.Width);
|
||||
return (ViewBox.Width <= 0 ? 1 : fWidth / ViewBox.Width);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -273,7 +274,7 @@ namespace Svg
|
|||
private float AdjustForViewBoxHeight(float fHeight)
|
||||
{
|
||||
// TODO: We know this isn't correct
|
||||
return (fHeight / ViewBox.Height);
|
||||
return (ViewBox.Height <= 0 ? 1 : fHeight / ViewBox.Height);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -134,48 +134,31 @@ namespace Svg
|
|||
/// 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 void RenderStroke(ISvgRenderer renderer)
|
||||
protected internal override 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, this.StrokeOpacity * this.Opacity))
|
||||
{
|
||||
if (brush != null)
|
||||
{
|
||||
using (Pen pen = new Pen(brush, strokeWidth))
|
||||
{
|
||||
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
|
||||
{
|
||||
/* divide by stroke width - GDI behaviour that I don't quite understand yet.*/
|
||||
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
|
||||
}
|
||||
var result = base.RenderStroke(renderer);
|
||||
var path = this.Path(renderer);
|
||||
|
||||
var path = this.Path(renderer);
|
||||
renderer.DrawPath(pen, path);
|
||||
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.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.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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 SvgElement DeepCopy()
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
<Compile Include="Paths\CoordinateParser.cs" />
|
||||
<Compile Include="Rendering\IGraphicsProvider.cs" />
|
||||
<Compile Include="Rendering\ISvgRenderer.cs" />
|
||||
<Compile Include="SvgElementStyle.cs" />
|
||||
<Compile Include="SvgNodeReader.cs" />
|
||||
<Compile Include="Css\CssQuery.cs" />
|
||||
<Compile Include="Css\SvgElementOps.cs" />
|
||||
|
|
|
@ -15,8 +15,11 @@ namespace Svg
|
|||
/// <summary>
|
||||
/// The base class of which all SVG elements are derived from.
|
||||
/// </summary>
|
||||
public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
|
||||
public abstract partial class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
|
||||
{
|
||||
internal const int StyleSpecificity_PresAttribute = 0;
|
||||
internal const int StyleSpecificity_InlineStyle = 1 << 16;
|
||||
|
||||
//optimization
|
||||
protected class PropertyAttributeTuple
|
||||
{
|
||||
|
@ -48,6 +51,7 @@ namespace Svg
|
|||
|
||||
private Dictionary<string, SortedDictionary<int, string>> _styles = new Dictionary<string, SortedDictionary<int, string>>();
|
||||
|
||||
|
||||
public void AddStyle(string name, string value, int specificity)
|
||||
{
|
||||
SortedDictionary<int, string> rules;
|
||||
|
@ -68,6 +72,32 @@ namespace Svg
|
|||
_styles = null;
|
||||
}
|
||||
|
||||
|
||||
public bool ContainsAttribute(string name)
|
||||
{
|
||||
SortedDictionary<int, string> rules;
|
||||
return (this.Attributes.ContainsKey(name) || this.CustomAttributes.ContainsKey(name) ||
|
||||
(_styles.TryGetValue(name, out rules) && (rules.ContainsKey(StyleSpecificity_InlineStyle) || rules.ContainsKey(StyleSpecificity_PresAttribute))));
|
||||
}
|
||||
public bool TryGetAttribute(string name, out string value)
|
||||
{
|
||||
object objValue;
|
||||
if (this.Attributes.TryGetValue(name, out objValue))
|
||||
{
|
||||
value = objValue.ToString();
|
||||
return true;
|
||||
}
|
||||
if (this.CustomAttributes.TryGetValue(name, out value)) return true;
|
||||
SortedDictionary<int, string> rules;
|
||||
if (_styles.TryGetValue(name, out rules))
|
||||
{
|
||||
// Get staged styles that are
|
||||
if (rules.TryGetValue(StyleSpecificity_InlineStyle, out value)) return true;
|
||||
if (rules.TryGetValue(StyleSpecificity_PresAttribute, out value)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the element.
|
||||
/// </summary>
|
||||
|
|
|
@ -139,13 +139,13 @@ namespace Svg
|
|||
{
|
||||
foreach (var decl in rule.Declarations)
|
||||
{
|
||||
element.AddStyle(decl.Name, decl.Term.ToString(), 1 << 16);
|
||||
element.AddStyle(decl.Name, decl.Term.ToString(), SvgElement.StyleSpecificity_InlineStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsStyleAttribute(reader.LocalName))
|
||||
{
|
||||
element.AddStyle(reader.LocalName, reader.Value, 2 << 16);
|
||||
element.AddStyle(reader.LocalName, reader.Value, SvgElement.StyleSpecificity_PresAttribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using Svg.DataTypes;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Svg
|
||||
{
|
||||
public partial class SvgElement
|
||||
{
|
||||
private bool _dirty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this element's <see cref="Path"/> is dirty.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the path is dirty; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
protected virtual bool IsPathDirty
|
||||
{
|
||||
get { return this._dirty; }
|
||||
set { this._dirty = value; }
|
||||
}
|
||||
|
||||
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 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) ? 4f : (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 colour of the gradient stop.
|
||||
/// </summary>
|
||||
/// <remarks>Apparently this can be set on non-sensical elements. Don't ask; just check the tests.</remarks>
|
||||
[SvgAttribute("stop-color")]
|
||||
[TypeConverter(typeof(SvgPaintServerFactory))]
|
||||
public SvgPaintServer StopColor
|
||||
{
|
||||
get { return this.Attributes["stop-color"] as SvgPaintServer; }
|
||||
set { this.Attributes["stop-color"] = 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
|
||||
{
|
||||
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 style 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 varient 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 IFontDefn GetFont(ISvgRenderer 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 family = ValidateFontFamily(this.FontFamily, this.OwnerDocument);
|
||||
var sFaces = family as IEnumerable<SvgFontFace>;
|
||||
|
||||
if (sFaces == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
var ff = family as FontFamily;
|
||||
if (!ff.IsStyleAvailable(fontStyle))
|
||||
{
|
||||
// Do Something
|
||||
}
|
||||
|
||||
// Get the font-family
|
||||
return new GdiFontDefn(new System.Drawing.Font(ff, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel));
|
||||
}
|
||||
else
|
||||
{
|
||||
var font = sFaces.First().Parent as SvgFont;
|
||||
if (font == null)
|
||||
{
|
||||
var uri = sFaces.First().Descendants().OfType<SvgFontFaceUri>().First().ReferencedElement;
|
||||
font = OwnerDocument.IdManager.GetElementById(uri) as SvgFont;
|
||||
}
|
||||
return new SvgFontDefn(font, fontSize, OwnerDocument.Ppi);
|
||||
}
|
||||
}
|
||||
|
||||
public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc)
|
||||
{
|
||||
// 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;
|
||||
FontFamily family;
|
||||
IEnumerable<SvgFontFace> sFaces;
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces;
|
||||
|
||||
family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
|
||||
if (family != null) return family;
|
||||
|
||||
switch (f)
|
||||
{
|
||||
case "serif":
|
||||
return System.Drawing.FontFamily.GenericSerif;
|
||||
case "sans-serif":
|
||||
return System.Drawing.FontFamily.GenericSansSerif;
|
||||
case "monospace":
|
||||
return System.Drawing.FontFamily.GenericMonospace;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid font family found from the list requested.
|
||||
return System.Drawing.FontFamily.GenericSansSerif;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -823,7 +823,7 @@ namespace Svg
|
|||
if (bounds.Right > maxX) maxX = bounds.Right;
|
||||
}
|
||||
|
||||
var xOffset = _xAnchor - minX;
|
||||
var xOffset = 0f; //_xAnchor - minX;
|
||||
switch (Element.TextAnchor)
|
||||
{
|
||||
case SvgTextAnchor.Middle:
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Svg.Transforms
|
|||
|
||||
public override string WriteToString()
|
||||
{
|
||||
if (this.X == this.Y) return string.Format(CultureInfo.InvariantCulture, "scale({0})", this.X);
|
||||
return string.Format(CultureInfo.InvariantCulture, "scale({0}, {1})", this.X, this.Y);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,5 +54,9 @@ namespace Svg.Transforms
|
|||
}
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return WriteToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -109,5 +109,11 @@ namespace Svg.Transforms
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.Count < 1) return string.Empty;
|
||||
return (from t in this select t.ToString()).Aggregate((p,c) => p + " " + c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,4 +156,14 @@ text-tref-01-b.svg
|
|||
text-tspan-01-b.svg
|
||||
text-ws-01-t.svg
|
||||
text-ws-02-t.svg
|
||||
text-ws-03-t.svg
|
||||
text-ws-03-t.svg
|
||||
painting-stroke-02-t.svg
|
||||
coords-viewattr-04-f.svg
|
||||
fonts-desc-01-t.svg
|
||||
fonts-elem-05-t.svg
|
||||
fonts-elem-06-t.svg
|
||||
paths-data-10-t.svg
|
||||
paths-data-16-t.svg
|
||||
shapes-rect-05-f.svg
|
||||
styling-css-02-b.svg
|
||||
styling-css-04-f.svg
|
Загрузка…
Ссылка в новой задаче