Commit 535abaf8 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #90 from erdomke/master

Initial Work on W3C Test Compliance
parents 4b1ff3d4 bd05ecbc
...@@ -79,44 +79,51 @@ namespace Svg ...@@ -79,44 +79,51 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F); Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
} }
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
LoadStops(); LoadStops(renderingElement);
if (IsInvalid) if (IsInvalid)
{ {
return null; return null;
} }
var boundable = CalculateBoundable(renderingElement); try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var specifiedStart = CalculateStart(boundable); var specifiedStart = CalculateStart(renderer);
var specifiedEnd = CalculateEnd(boundable); var specifiedEnd = CalculateEnd(renderer);
var effectiveStart = specifiedStart; var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd; var effectiveEnd = specifiedEnd;
if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd)) if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd))
{ {
var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd); var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd);
effectiveStart = expansion.StartPoint; effectiveStart = expansion.StartPoint;
effectiveEnd = expansion.EndPoint; effectiveEnd = expansion.EndPoint;
} }
return new LinearGradientBrush(effectiveStart, effectiveEnd, Color.Transparent, Color.Transparent) return new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
{
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
}
finally
{ {
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd), if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
WrapMode = WrapMode.TileFlipX }
};
} }
private PointF CalculateStart(ISvgBoundable boundable) private PointF CalculateStart(SvgRenderer renderer)
{ {
return TransformPoint(new PointF(this.X1.ToDeviceValue(boundable), this.Y1.ToDeviceValue(boundable, true))); return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
} }
private PointF CalculateEnd(ISvgBoundable boundable) private PointF CalculateEnd(SvgRenderer renderer)
{ {
return TransformPoint(new PointF(this.X2.ToDeviceValue(boundable), this.Y2.ToDeviceValue(boundable, true))); return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
} }
private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd) private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
...@@ -174,9 +181,9 @@ namespace Svg ...@@ -174,9 +181,9 @@ namespace Svg
return new GradientPoints(effectiveStart, effectiveEnd); return new GradientPoints(effectiveStart, effectiveEnd);
} }
private ColorBlend CalculateColorBlend(SvgVisualElement owner, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd) private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{ {
var colorBlend = GetColorBlend(owner, opacity, false); var colorBlend = GetColorBlend(renderer, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart); var startDelta = CalculateDistance(specifiedStart, effectiveStart);
var endDelta = CalculateDistance(specifiedEnd, effectiveEnd); var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
......
...@@ -87,26 +87,19 @@ namespace Svg ...@@ -87,26 +87,19 @@ namespace Svg
Overflow = SvgOverflow.hidden; Overflow = SvgOverflow.hidden;
} }
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get var path = this.Children.FirstOrDefault(x => x is SvgPath);
{ if (path != null)
var path = this.Children.FirstOrDefault(x => x is SvgPath); return (path as SvgPath).Path(renderer);
if (path != null) return null;
return (path as SvgPath).Path;
return null;
}
protected set
{
// No-op
}
} }
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
{ {
get get
{ {
var path = this.Path; var path = this.Path(null);
if (path != null) if (path != null)
return path.GetBounds(); return path.GetBounds();
return new System.Drawing.RectangleF(); return new System.Drawing.RectangleF();
...@@ -177,7 +170,7 @@ namespace Svg ...@@ -177,7 +170,7 @@ namespace Svg
/// <param name="pMarkerPoint"></param> /// <param name="pMarkerPoint"></param>
private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint) private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{ {
Pen pRenderPen = CreatePen(pOwner); Pen pRenderPen = CreatePen(pOwner, pRenderer);
GraphicsPath markerPath = GetClone(pOwner); GraphicsPath markerPath = GetClone(pOwner);
...@@ -190,10 +183,14 @@ namespace Svg ...@@ -190,10 +183,14 @@ namespace Svg
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth)); transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)),
AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)));
break; break;
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY); transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
break; break;
} }
markerPath.Transform(transMatrix); markerPath.Transform(transMatrix);
...@@ -205,7 +202,7 @@ namespace Svg ...@@ -205,7 +202,7 @@ namespace Svg
if (pFill != null) if (pFill != null)
{ {
Brush pBrush = pFill.GetBrush(this, fOpacity); Brush pBrush = pFill.GetBrush(this, pRenderer, fOpacity);
pRenderer.FillPath(pBrush, markerPath); pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose(); pBrush.Dispose();
} }
...@@ -219,17 +216,18 @@ namespace Svg ...@@ -219,17 +216,18 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="pStroke"></param> /// <param name="pStroke"></param>
/// <returns></returns> /// <returns></returns>
private Pen CreatePen(SvgPath pPath) private Pen CreatePen(SvgPath pPath, SvgRenderer renderer)
{ {
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity); Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this) *
pPath.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
case SvgMarkerUnits.userSpaceOnUse: case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
} }
return (new Pen(pBrush, StrokeWidth)); return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
} }
/// <summary> /// <summary>
...@@ -238,7 +236,7 @@ namespace Svg ...@@ -238,7 +236,7 @@ namespace Svg
/// <returns></returns> /// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath) private GraphicsPath GetClone(SvgPath pPath)
{ {
GraphicsPath pRet = Path.Clone() as GraphicsPath; GraphicsPath pRet = Path(null).Clone() as GraphicsPath;
switch (MarkerUnits) switch (MarkerUnits)
{ {
case SvgMarkerUnits.strokeWidth: case SvgMarkerUnits.strokeWidth:
......
...@@ -39,7 +39,7 @@ namespace Svg ...@@ -39,7 +39,7 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param> /// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param> /// <param name="opacity">The opacity of the brush.</param>
public abstract Brush GetBrush(SvgVisualElement styleOwner, float opacity); public abstract Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity);
/// <summary> /// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
......
...@@ -27,6 +27,14 @@ namespace Svg ...@@ -27,6 +27,14 @@ namespace Svg
{ {
return SvgColourServer.NotSet; return SvgColourServer.NotSet;
} }
else if (value == "inherit")
{
return SvgColourServer.Inherit;
}
else if (value == "currentColor")
{
return new SvgDeferredPaintServer(document, value);
}
else if (value.IndexOf("url(#") > -1) else if (value.IndexOf("url(#") > -1)
{ {
Match match = _urlRefPattern.Match(value); Match match = _urlRefPattern.Match(value);
......
...@@ -13,13 +13,15 @@ namespace Svg ...@@ -13,13 +13,15 @@ namespace Svg
/// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted. /// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
/// </summary> /// </summary>
[SvgElement("pattern")] [SvgElement("pattern")]
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort, ISvgSupportsCoordinateUnits
{ {
private SvgUnit _width; private SvgUnit _width;
private SvgUnit _height; private SvgUnit _height;
private SvgUnit _x; private SvgUnit _x;
private SvgUnit _y; private SvgUnit _y;
private SvgViewBox _viewBox; private SvgViewBox _viewBox;
private SvgCoordinateUnits _patternUnits;
private SvgCoordinateUnits _patternContentUnits;
[SvgAttribute("overflow")] [SvgAttribute("overflow")]
public SvgOverflow Overflow public SvgOverflow Overflow
...@@ -61,6 +63,26 @@ namespace Svg ...@@ -61,6 +63,26 @@ namespace Svg
set { this._width = value; } set { this._width = value; }
} }
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternUnits
{
get { return this._patternUnits; }
set { this._patternUnits = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternContentUnits
{
get { return this._patternContentUnits; }
set { this._patternContentUnits = value; }
}
/// <summary> /// <summary>
/// Gets or sets the height of the pattern. /// Gets or sets the height of the pattern.
/// </summary> /// </summary>
...@@ -107,7 +129,7 @@ namespace Svg ...@@ -107,7 +129,7 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param> /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param> /// <param name="opacity">The opacity of the brush.</param>
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
// If there aren't any children, return null // If there aren't any children, return null
if (this.Children.Count == 0) if (this.Children.Count == 0)
...@@ -117,18 +139,21 @@ namespace Svg ...@@ -117,18 +139,21 @@ namespace Svg
if (this._width.Value == 0.0f || this._height.Value == 0.0f) if (this._width.Value == 0.0f || this._height.Value == 0.0f)
return null; return null;
float width = this._width.ToDeviceValue(renderingElement); try
float height = this._height.ToDeviceValue(renderingElement, true);
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer renderer = SvgRenderer.FromImage(image))
{ {
Matrix patternMatrix = new Matrix(); if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
Matrix patternMatrix = new Matrix();
// Apply a translate if needed // Apply a translate if needed
if (this._x.Value > 0.0f || this._y.Value > 0.0f) if (this._x.Value > 0.0f || this._y.Value > 0.0f)
{ {
patternMatrix.Translate(this._x.ToDeviceValue(renderingElement) + -1.0f, this._y.ToDeviceValue(renderingElement, true) + -1.0f); float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
patternMatrix.Translate(x + -1.0f, y + -1.0f);
} }
else else
{ {
...@@ -137,27 +162,40 @@ namespace Svg ...@@ -137,27 +162,40 @@ namespace Svg
if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0) if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
{ {
patternMatrix.Scale(this.Width.ToDeviceValue() / this.ViewBox.Width, this.Height.ToDeviceValue() / this.ViewBox.Height); patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
} }
renderer.Transform = patternMatrix; Bitmap image = new Bitmap((int)width, (int)height);
renderer.CompositingQuality = CompositingQuality.HighQuality; using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
renderer.SmoothingMode = SmoothingMode.AntiAlias;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{ {
child.RenderElement(renderer); iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
iRenderer.Transform = patternMatrix;
iRenderer.CompositingQuality = CompositingQuality.HighQuality;
iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
iRenderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{
child.RenderElement(iRenderer);
}
iRenderer.Save();
} }
renderer.Save(); image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
}
TextureBrush textureBrush = new TextureBrush(image); TextureBrush textureBrush = new TextureBrush(image);
return textureBrush; return textureBrush;
}
finally
{
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
} }
private static int imgNumber = 0;
...@@ -180,5 +218,10 @@ namespace Svg ...@@ -180,5 +218,10 @@ namespace Svg
return newObj; return newObj;
} }
public SvgCoordinateUnits GetUnits()
{
return _patternUnits;
}
} }
} }
\ No newline at end of file
...@@ -95,53 +95,55 @@ namespace Svg ...@@ -95,53 +95,55 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F); Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
} }
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
LoadStops(); LoadStops(renderingElement);
var origin = CalculateOrigin(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin); try
var focalPoint = CalculateFocalPoint(renderingElement, origin);
var specifiedRadius = CalculateRadius(renderingElement);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
{ {
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedRadius, effectiveRadius), if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
CenterPoint = focalPoint var origin = renderer.Boundable().Location;
}; var centerPoint = CalculateCenterPoint(renderer, origin);
var focalPoint = CalculateFocalPoint(renderer, origin);
Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!"); var specifiedRadius = CalculateRadius(renderer);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
return brush; var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
} {
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius),
CenterPoint = focalPoint
};
private PointF CalculateOrigin(SvgVisualElement renderingElement) Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!");
{
return CalculateBoundable(renderingElement).Location; return brush;
}
finally
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
} }
private PointF CalculateCenterPoint(ISvgBoundable boundable, PointF origin) private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin)
{ {
var deviceCenterX = origin.X + CenterX.ToDeviceValue(boundable); var deviceCenterX = origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(boundable, true); var deviceCenterY = origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY)); var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY));
return transformedCenterPoint; return transformedCenterPoint;
} }
private PointF CalculateFocalPoint(ISvgBoundable boundable, PointF origin) private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
{ {
var deviceFocalX = origin.X + FocalX.ToDeviceValue(boundable); var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(boundable, true); var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY)); var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
return transformedFocalPoint; return transformedFocalPoint;
} }
private float CalculateRadius(ISvgBoundable boundable) private float CalculateRadius(SvgRenderer renderer)
{ {
var radius = Radius.ToDeviceValue(boundable); var radius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
var transformRadiusVector = TransformVector(new PointF(radius, 0)); var transformRadiusVector = TransformVector(new PointF(radius, 0));
var transformedRadius = CalculateLength(transformRadiusVector); var transformedRadius = CalculateLength(transformRadiusVector);
return transformedRadius; return transformedRadius;
...@@ -191,9 +193,9 @@ namespace Svg ...@@ -191,9 +193,9 @@ namespace Svg
return path; return path;
} }
private ColorBlend CalculateColorBlend(SvgVisualElement renderingElement, float opacity, float specifiedRadius, float effectiveRadius) private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float specifiedRadius, float effectiveRadius)
{ {
var colorBlend = GetColorBlend(renderingElement, opacity, true); var colorBlend = GetColorBlend(renderer, opacity, true);
if (specifiedRadius >= effectiveRadius) if (specifiedRadius >= effectiveRadius)
{ {
......
...@@ -81,27 +81,20 @@ namespace Svg ...@@ -81,27 +81,20 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
public override GraphicsPath Path public override GraphicsPath Path(SvgRenderer renderer)
{ {
get if (this._path == null || this.IsPathDirty)
{ {
if (this._path == null || this.IsPathDirty) _path = new GraphicsPath();
{
_path = new GraphicsPath();
foreach (SvgPathSegment segment in this.PathData)
{
segment.AddToPath(_path);
}
this.IsPathDirty = false; foreach (SvgPathSegment segment in this.PathData)
{
segment.AddToPath(_path);
} }
return _path;
} this.IsPathDirty = false;
protected set
{
_path = value;
} }
return _path;
} }
internal void OnPathUpdated() internal void OnPathUpdated()
...@@ -124,7 +117,7 @@ namespace Svg ...@@ -124,7 +117,7 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
/// <summary> /// <summary>
...@@ -145,8 +138,8 @@ namespace Svg ...@@ -145,8 +138,8 @@ namespace Svg
{ {
if (this.Stroke != null) if (this.Stroke != null)
{ {
float strokeWidth = this.StrokeWidth.ToDeviceValue(this); float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth)) using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity), strokeWidth))
{ {
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0) if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{ {
...@@ -154,25 +147,26 @@ namespace Svg ...@@ -154,25 +147,26 @@ namespace Svg
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray(); pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
} }
renderer.DrawPath(pen, this.Path); var path = this.Path(renderer);
renderer.DrawPath(pen, path);
if (this.MarkerStart != null) if (this.MarkerStart != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[0], Path.PathPoints[0], Path.PathPoints[1]); marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
} }
if (this.MarkerMid != null) if (this.MarkerMid != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= Path.PathPoints.Length - 2; i++) for (int i = 1; i <= path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, Path.PathPoints[i], Path.PathPoints[i - 1], Path.PathPoints[i], Path.PathPoints[i + 1]); marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
} }
if (this.MarkerEnd != null) if (this.MarkerEnd != null)
{ {
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerEnd.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerEnd.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[Path.PathPoints.Length - 1], Path.PathPoints[Path.PathPoints.Length - 2], Path.PathPoints[Path.PathPoints.Length - 1]); marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
} }
} }
} }
......
...@@ -223,8 +223,7 @@ namespace Svg ...@@ -223,8 +223,7 @@ namespace Svg
var lastSegment = segments.Last; var lastSegment = segments.Last;
// if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0
if (lastSegment is SvgClosePathSegment) if (lastSegment is SvgClosePathSegment) lastSegment = segments.Reverse().OfType<SvgMoveToSegment>().First();
lastSegment = segments[segments.Count - 2];
if (isRelativeX) if (isRelativeX)
{ {
......
...@@ -36,3 +36,5 @@ using System.Runtime.InteropServices; ...@@ -36,3 +36,5 @@ using System.Runtime.InteropServices;
//[assembly: AssemblyFileVersion("1.0.1.*")] //[assembly: AssemblyFileVersion("1.0.1.*")]
[assembly: CLSCompliant(true)] [assembly: CLSCompliant(true)]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Svg.UnitTests")]
\ No newline at end of file
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DocumentationFile> <DocumentationFile>
</DocumentationFile> </DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>PdbOnly</DebugType> <DebugType>PdbOnly</DebugType>
...@@ -76,12 +76,13 @@ ...@@ -76,12 +76,13 @@
<DocumentationFile>bin\Release\Svg.XML</DocumentationFile> <DocumentationFile>bin\Release\Svg.XML</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<SignAssembly>True</SignAssembly> <SignAssembly>false</SignAssembly>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AssemblyOriginatorKeyFile>svgkey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>svgkey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Web" /> <Reference Include="System.Web" />
...@@ -99,6 +100,11 @@ ...@@ -99,6 +100,11 @@
<Compile Include="Clipping and Masking\SvgClipRule.cs" /> <Compile Include="Clipping and Masking\SvgClipRule.cs" />
<Compile Include="Clipping and Masking\SvgClipPath.cs" /> <Compile Include="Clipping and Masking\SvgClipPath.cs" />
<Compile Include="Clipping and Masking\SvgMask.cs" /> <Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\ISvgSupportsCoordinateUnits.cs" />
<Compile Include="Painting\GenericBoundable.cs" />
<Compile Include="SvgNodeReader.cs" />
<Compile Include="Css\CssQuery.cs" />
<Compile Include="Css\SvgElementOps.cs" />
<Compile Include="DataTypes\SvgAspectRatioConverter.cs" /> <Compile Include="DataTypes\SvgAspectRatioConverter.cs" />
<Compile Include="DataTypes\SvgFontStyle.cs" /> <Compile Include="DataTypes\SvgFontStyle.cs" />
<Compile Include="DataTypes\SvgFontVariant.cs" /> <Compile Include="DataTypes\SvgFontVariant.cs" />
...@@ -117,8 +123,92 @@ ...@@ -117,8 +123,92 @@
<Compile Include="Document Structure\SvgSwitch.cs" /> <Compile Include="Document Structure\SvgSwitch.cs" />
<Compile Include="Document Structure\SvgTitle.cs" /> <Compile Include="Document Structure\SvgTitle.cs" />
<Compile Include="Document Structure\SvgDocumentMetadata.cs" /> <Compile Include="Document Structure\SvgDocumentMetadata.cs" />
<Compile Include="External\ExCSS\IToString.cs" />
<Compile Include="External\ExCSS\Lexer.cs" />
<Compile Include="External\ExCSS\Model\Enumerations.cs" />
<Compile Include="External\ExCSS\Model\Extensions\CharacterExtensions.cs" />
<Compile Include="External\ExCSS\Model\Extensions\StringExtensions.cs" />
<Compile Include="External\ExCSS\Model\FunctionBuffer.cs" />
<Compile Include="External\ExCSS\Model\HtmlEncoding.cs" />
<Compile Include="External\ExCSS\Model\ICssRules.cs" />
<Compile Include="External\ExCSS\Model\ICssSelector.cs" />
<Compile Include="External\ExCSS\Model\IStyleDeclaration.cs" />
<Compile Include="External\ExCSS\Model\ISupportsMedia.cs" />
<Compile Include="External\ExCSS\Model\MediaTypeList.cs" />
<Compile Include="External\ExCSS\Model\Rules\AggregateRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\CharacterSetRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\ConditionalRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\DocumentRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\FontFaceRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\GenericRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\ImportRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\IRuleContainer.cs" />
<Compile Include="External\ExCSS\Model\Rules\KeyframeRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\KeyframesRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\MediaRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\NamespaceRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\PageRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\RuleSet.cs" />
<Compile Include="External\ExCSS\Model\Rules\StyleDeclaration.cs" />
<Compile Include="External\ExCSS\Model\Rules\StyleRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\SupportsRule.cs" />
<Compile Include="External\ExCSS\Model\Selector\AggregateSelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\BaseSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\CombinatorSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\ComplexSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\FirstChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\LastChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\MultipleSelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthFirstChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthLastChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthLastOfTypeSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthOfTypeSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\SelectorFactory.cs" />
<Compile Include="External\ExCSS\Model\Selector\SelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\SimpleSelector.cs" />
<Compile Include="External\ExCSS\Model\Specification.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\Block.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\BracketBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\CharacterBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\CommentBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\DelimiterBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\MatchBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\NumericBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\PipeBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\RangeBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\SpecialCharacter.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\StringBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\SymbolBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\UnitBlock.cs" />
<Compile Include="External\ExCSS\Model\Values\GenericFunction.cs" />
<Compile Include="External\ExCSS\Model\Values\HtmlColor.cs" />
<Compile Include="External\ExCSS\Model\Values\InheritTerm.cs" />
<Compile Include="External\ExCSS\Model\Values\PrimitiveTerm.cs" />
<Compile Include="External\ExCSS\Model\Values\Property.cs" />
<Compile Include="External\ExCSS\Model\Values\Term.cs" />
<Compile Include="External\ExCSS\Model\Values\TermList.cs" />
<Compile Include="External\ExCSS\Parser.Blocks.cs" />
<Compile Include="External\ExCSS\Parser.cs" />
<Compile Include="External\ExCSS\StyleSheet.cs" />
<Compile Include="External\ExCSS\StylesheetParseError.cs" />
<Compile Include="External\ExCSS\StylesheetReader.cs" />
<Compile Include="Extensibility\SvgForeignObject.cs" /> <Compile Include="Extensibility\SvgForeignObject.cs" />
<Compile Include="Extensions.cs" /> <Compile Include="Extensions.cs" />
<Compile Include="External\Fizzler\Either.cs" />
<Compile Include="External\Fizzler\HumanReadableSelectorGenerator.cs" />
<Compile Include="External\Fizzler\IElementOps.cs" />
<Compile Include="External\Fizzler\ISelectorGenerator.cs" />
<Compile Include="External\Fizzler\NamespacePrefix.cs" />
<Compile Include="External\Fizzler\Parser.cs" />
<Compile Include="External\Fizzler\Reader.cs" />
<Compile Include="External\Fizzler\Selector.cs" />
<Compile Include="External\Fizzler\SelectorGenerator.cs" />
<Compile Include="External\Fizzler\SelectorGeneratorTee.cs" />
<Compile Include="External\Fizzler\SelectorsCachingCompiler.cs" />
<Compile Include="External\Fizzler\Token.cs" />
<Compile Include="External\Fizzler\Tokener.cs" />
<Compile Include="External\Fizzler\TokenKind.cs" />
<Compile Include="Painting\ISvgBoundable.cs" /> <Compile Include="Painting\ISvgBoundable.cs" />
<Compile Include="Painting\SvgDeferredPaintServer.cs" /> <Compile Include="Painting\SvgDeferredPaintServer.cs" />
<Compile Include="Painting\SvgMarker.cs" /> <Compile Include="Painting\SvgMarker.cs" />
...@@ -141,6 +231,7 @@ ...@@ -141,6 +231,7 @@
<Compile Include="SvgContentNode.cs" /> <Compile Include="SvgContentNode.cs" />
<Compile Include="SvgDefinitionDefaults.cs" /> <Compile Include="SvgDefinitionDefaults.cs" />
<Compile Include="NonSvgElement.cs" /> <Compile Include="NonSvgElement.cs" />
<Compile Include="SvgReader.cs" />
<Compile Include="SvgUnknownElement.cs" /> <Compile Include="SvgUnknownElement.cs" />
<Compile Include="SvgElementAttribute.cs" /> <Compile Include="SvgElementAttribute.cs" />
<Compile Include="SvgExtentions.cs" /> <Compile Include="SvgExtentions.cs" />
......
 
Microsoft Visual Studio Solution File, Format Version 11.00 Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010 # Visual Studio 2010
# SharpDevelop 4.4
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg", "Svg.csproj", "{886A98C5-37C0-4E8B-885E-30C1D2F98B47}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg", "Svg.csproj", "{886A98C5-37C0-4E8B-885E-30C1D2F98B47}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVGViewer", "..\Samples\SVGViewer\SVGViewer.csproj", "{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVGViewer", "..\Samples\SVGViewer\SVGViewer.csproj", "{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.UnitTests", "..\Tests\Svg.UnitTests\Svg.UnitTests.csproj", "{E702EB7D-D01D-438A-BADD-E72D4E49109F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A417AF1E-BEDB-4715-B4FD-D795579217F9}"
ProjectSection(SolutionItems) = preProject
Local.testsettings = Local.testsettings
Svg.vsmdi = Svg.vsmdi
TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgW3CTestRunner", "..\Tests\SvgW3CTestRunner\SvgW3CTestRunner.csproj", "{8ED74C39-6CFF-421E-952A-30F9E6957108}"
EndProject
Global Global
GlobalSection(TestCaseManagementSettings) = postSolution
CategoryFile = Svg.vsmdi
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.Build.0 = Debug|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|x86.ActiveCfg = Debug|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|x86.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.ActiveCfg = Release|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.Build.0 = Release|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.Build.0 = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|x86.ActiveCfg = Release|Any CPU {886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|x86.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.Build.0 = Debug|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Mixed Platforms.Build.0 = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.ActiveCfg = Debug|x86 {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.ActiveCfg = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.Build.0 = Release|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.Build.0 = Release|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.Build.0 = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Mixed Platforms.ActiveCfg = Release|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Mixed Platforms.Build.0 = Release|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.ActiveCfg = Release|Any CPU {1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|x86.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Any CPU.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|x86.ActiveCfg = Release|Any CPU
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Any CPU.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Mixed Platforms.Build.0 = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|x86.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|x86.Build.0 = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Any CPU.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Mixed Platforms.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Mixed Platforms.Build.0 = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|x86.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
......
<?xml version="1.0" encoding="UTF-8"?>
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
<RunConfiguration id="616d39c5-86e4-40f8-97e9-b8a423ce7322" name="Local" storage="local.testsettings" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</TestList>
</TestLists>
\ No newline at end of file
...@@ -63,9 +63,12 @@ namespace Svg ...@@ -63,9 +63,12 @@ namespace Svg
/// <returns>The attribute value if available; otherwise the ancestors value for the same attribute; otherwise the default value of <typeparamref name="TAttributeType"/>.</returns> /// <returns>The attribute value if available; otherwise the ancestors value for the same attribute; otherwise the default value of <typeparamref name="TAttributeType"/>.</returns>
public TAttributeType GetInheritedAttribute<TAttributeType>(string attributeName) public TAttributeType GetInheritedAttribute<TAttributeType>(string attributeName)
{ {
if (this.ContainsKey(attributeName) /*&& base[attributeName] != null*/) if (this.ContainsKey(attributeName) && !IsInheritValue(base[attributeName]))
{ {
return (TAttributeType)base[attributeName]; var result = (TAttributeType)base[attributeName];
var deferred = result as SvgDeferredPaintServer;
if (deferred != null) deferred.EnsureServer(_owner);
return result;
} }
if (this._owner.Parent != null) if (this._owner.Parent != null)
...@@ -79,6 +82,16 @@ namespace Svg ...@@ -79,6 +82,16 @@ namespace Svg
return default(TAttributeType); return default(TAttributeType);
} }
private bool IsInheritValue(object value)
{
return (value == null ||
(value is SvgFontStyle && (SvgFontStyle)value == SvgFontStyle.inherit) ||
(value is SvgFontWeight && (SvgFontWeight)value == SvgFontWeight.inherit) ||
(value is SvgTextAnchor && (SvgTextAnchor)value == SvgTextAnchor.inherit) ||
(value == "inherit")
);
}
/// <summary> /// <summary>
/// Gets the attribute with the specified name. /// Gets the attribute with the specified name.
/// </summary> /// </summary>
......
...@@ -9,6 +9,8 @@ using System.IO; ...@@ -9,6 +9,8 @@ using System.IO;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using System.Linq; using System.Linq;
using ExCSS;
using Svg.Css;
namespace Svg namespace Svg
{ {
...@@ -28,6 +30,8 @@ namespace Svg ...@@ -28,6 +30,8 @@ namespace Svg
Ppi = PointsPerInch; Ppi = PointsPerInch;
} }
public Uri BaseUri { get; set; }
/// <summary> /// <summary>
/// Gets an <see cref="SvgElementIdManager"/> for this document. /// Gets an <see cref="SvgElementIdManager"/> for this document.
/// </summary> /// </summary>
...@@ -43,7 +47,7 @@ namespace Svg ...@@ -43,7 +47,7 @@ namespace Svg
return _idManager; return _idManager;
} }
} }
/// <summary> /// <summary>
/// Overwrites the current IdManager with a custom implementation. /// Overwrites the current IdManager with a custom implementation.
/// Be careful with this: If elements have been inserted into the document before, /// Be careful with this: If elements have been inserted into the document before,
...@@ -113,8 +117,8 @@ namespace Svg ...@@ -113,8 +117,8 @@ namespace Svg
{ {
return (this.GetElementById(id) as TSvgElement); return (this.GetElementById(id) as TSvgElement);
} }
/// <summary> /// <summary>
/// Opens the document at the specified path and loads the SVG contents. /// Opens the document at the specified path and loads the SVG contents.
/// </summary> /// </summary>
/// <param name="path">A <see cref="string"/> containing the path of the file to open.</param> /// <param name="path">A <see cref="string"/> containing the path of the file to open.</param>
...@@ -155,7 +159,9 @@ namespace Svg ...@@ -155,7 +159,9 @@ namespace Svg
throw new FileNotFoundException("The specified document cannot be found.", path); throw new FileNotFoundException("The specified document cannot be found.", path);
} }
return Open<T>(File.OpenRead(path), entities); var doc = Open<T>(File.OpenRead(path), entities);
doc.BaseUri = new Uri(System.IO.Path.GetFullPath(path));
return doc;
} }
/// <summary> /// <summary>
...@@ -167,6 +173,27 @@ namespace Svg ...@@ -167,6 +173,27 @@ namespace Svg
return Open<T>(stream, null); return Open<T>(stream, null);
} }
/// <summary>
/// Attempts to create an SVG document from the specified string data.
/// </summary>
/// <param name="svg">The SVG data.</param>
public static T FromSvg<T>(string svg) where T : SvgDocument, new()
{
if (string.IsNullOrEmpty(svg))
{
throw new ArgumentNullException("svg");
}
using (var strReader = new System.IO.StringReader(svg))
{
var reader = new SvgTextReader(strReader, null);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open<T>(reader);
}
}
/// <summary> /// <summary>
/// Opens an SVG document from the specified <see cref="Stream"/> and adds the specified entities. /// Opens an SVG document from the specified <see cref="Stream"/> and adds the specified entities.
/// </summary> /// </summary>
...@@ -180,94 +207,148 @@ namespace Svg ...@@ -180,94 +207,148 @@ namespace Svg
throw new ArgumentNullException("stream"); throw new ArgumentNullException("stream");
} }
//Trace.TraceInformation("Begin Read"); // Don't close the stream via a dispose: that is the client's job.
var reader = new SvgTextReader(stream, entities);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open<T>(reader);
}
using (var reader = new SvgTextReader(stream, entities)) private static T Open<T>(XmlReader reader) where T : SvgDocument, new()
{
var elementStack = new Stack<SvgElement>();
bool elementEmpty;
SvgElement element = null;
SvgElement parent;
T svgDocument = null;
var styles = new List<ISvgNode>();
while (reader.Read())
{ {
var elementStack = new Stack<SvgElement>(); try
bool elementEmpty;
SvgElement element = null;
SvgElement parent;
T svgDocument = null;
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
while (reader.Read())
{ {
try switch (reader.NodeType)
{ {
switch (reader.NodeType) case XmlNodeType.Element:
{ // Does this element have a value or children
case XmlNodeType.Element: // (Must do this check here before we progress to another node)
// Does this element have a value or children elementEmpty = reader.IsEmptyElement;
// (Must do this check here before we progress to another node) // Create element
elementEmpty = reader.IsEmptyElement; if (elementStack.Count > 0)
// Create element {
if (elementStack.Count > 0) element = SvgElementFactory.CreateElement(reader, svgDocument);
{ }
element = SvgElementFactory.CreateElement(reader, svgDocument); else
} {
else svgDocument = SvgElementFactory.CreateDocument<T>(reader);
{ element = svgDocument;
svgDocument = SvgElementFactory.CreateDocument<T>(reader); }
element = svgDocument;
} // Add to the parents children
if (elementStack.Count > 0)
// Add to the parents children {
if (elementStack.Count > 0) parent = elementStack.Peek();
{ if (parent != null && element != null)
parent = elementStack.Peek();
if (parent != null && element != null)
{
parent.Children.Add(element);
parent.Nodes.Add(element);
}
}
// Push element into stack
elementStack.Push(element);
// Need to process if the element is empty
if (elementEmpty)
{ {
goto case XmlNodeType.EndElement; parent.Children.Add(element);
parent.Nodes.Add(element);
} }
}
// Push element into stack
elementStack.Push(element);
// Need to process if the element is empty
if (elementEmpty)
{
goto case XmlNodeType.EndElement;
}
break;
case XmlNodeType.EndElement:
// Pop the element out of the stack
element = elementStack.Pop();
if (element.Nodes.OfType<SvgContentNode>().Any())
{
element.Content = (from e in element.Nodes select e.Content).Aggregate((p, c) => p + c);
}
else
{
element.Nodes.Clear(); // No sense wasting the space where it isn't needed
}
var unknown = element as SvgUnknownElement;
if (unknown != null && unknown.ElementName == "style")
{
styles.Add(unknown);
}
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
}
}
catch (Exception exc)
{
Trace.TraceError(exc.Message);
}
}
break; if (styles.Any())
case XmlNodeType.EndElement: {
var cssTotal = styles.Select((s) => s.Content).Aggregate((p, c) => p + Environment.NewLine + c);
// Pop the element out of the stack var cssParser = new Parser();
element = elementStack.Pop(); var sheet = cssParser.Parse(cssTotal);
AggregateSelectorList aggList;
if (element.Nodes.OfType<SvgContentNode>().Any()) IEnumerable<BaseSelector> selectors;
{ IEnumerable<SvgElement> elemsToStyle;
element.Content = (from e in element.Nodes select e.Content).Aggregate((p, c) => p + c);
} foreach (var rule in sheet.StyleRules)
else {
{ aggList = rule.Selector as AggregateSelectorList;
element.Nodes.Clear(); // No sense wasting the space where it isn't needed if (aggList != null && aggList.Delimiter == ",")
} {
break; selectors = aggList;
case XmlNodeType.CDATA: }
case XmlNodeType.Text: else
element = elementStack.Peek(); {
element.Nodes.Add(new SvgContentNode() { Content = reader.Value }); selectors = Enumerable.Repeat(rule.Selector, 1);
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
}
} }
catch (Exception exc)
foreach (var selector in selectors)
{ {
Trace.TraceError(exc.Message); elemsToStyle = svgDocument.QuerySelectorAll(rule.Selector.ToString());
foreach (var elem in elemsToStyle)
{
foreach (var decl in rule.Declarations)
{
elem.AddStyle(decl.Name, decl.Term.ToString(), rule.Selector.GetSpecificity());
}
}
} }
} }
}
//Trace.TraceInformation("End Read"); if (svgDocument != null) FlushStyles(svgDocument);
return svgDocument; return svgDocument;
}
private static void FlushStyles(SvgElement elem)
{
elem.FlushStyles();
foreach (var child in elem.Children)
{
FlushStyles(child);
} }
} }
...@@ -283,10 +364,8 @@ namespace Svg ...@@ -283,10 +364,8 @@ namespace Svg
throw new ArgumentNullException("document"); throw new ArgumentNullException("document");
} }
using (var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml))) var reader = new SvgNodeReader(document.DocumentElement, null);
{ return Open<SvgDocument>(reader);
return Open<SvgDocument>(stream, null);
}
} }
public static Bitmap OpenAsBitmap(string path) public static Bitmap OpenAsBitmap(string path)
...@@ -311,6 +390,7 @@ namespace Svg ...@@ -311,6 +390,7 @@ namespace Svg
throw new ArgumentNullException("renderer"); throw new ArgumentNullException("renderer");
} }
renderer.Boundable(this);
this.Render(renderer); this.Render(renderer);
} }
...@@ -326,7 +406,9 @@ namespace Svg ...@@ -326,7 +406,9 @@ namespace Svg
throw new ArgumentNullException("graphics"); throw new ArgumentNullException("graphics");
} }
this.Render(SvgRenderer.FromGraphics(graphics)); var renderer = SvgRenderer.FromGraphics(graphics);
renderer.Boundable(this);
this.Render(renderer);
} }
/// <summary> /// <summary>
...@@ -339,7 +421,7 @@ namespace Svg ...@@ -339,7 +421,7 @@ namespace Svg
var size = GetDimensions(); var size = GetDimensions();
var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height)); var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
// bitmap.SetResolution(300, 300); // bitmap.SetResolution(300, 300);
try try
{ {
Draw(bitmap); Draw(bitmap);
...@@ -365,6 +447,7 @@ namespace Svg ...@@ -365,6 +447,7 @@ namespace Svg
{ {
using (var renderer = SvgRenderer.FromImage(bitmap)) using (var renderer = SvgRenderer.FromImage(bitmap))
{ {
renderer.Boundable(this);
renderer.TextRenderingHint = TextRenderingHint.AntiAlias; renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
renderer.TextContrast = 1; renderer.TextContrast = 1;
renderer.PixelOffsetMode = PixelOffsetMode.Half; renderer.PixelOffsetMode = PixelOffsetMode.Half;
...@@ -395,7 +478,7 @@ namespace Svg ...@@ -395,7 +478,7 @@ namespace Svg
public void Write(string path) public void Write(string path)
{ {
using(var fs = new FileStream(path, FileMode.Create, FileAccess.Write)) using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{ {
this.Write(fs); this.Write(fs);
} }
......
...@@ -40,10 +40,34 @@ namespace Svg ...@@ -40,10 +40,34 @@ namespace Svg
private EventHandlerList _eventHandlers; private EventHandlerList _eventHandlers;
private SvgElementCollection _children; private SvgElementCollection _children;
private static readonly object _loadEventKey = new object(); private static readonly object _loadEventKey = new object();
private Region _graphicsClip;
private Matrix _graphicsMatrix; private Matrix _graphicsMatrix;
private SvgCustomAttributeCollection _customAttributes; private SvgCustomAttributeCollection _customAttributes;
private List<ISvgNode> _nodes = new List<ISvgNode>(); private List<ISvgNode> _nodes = new List<ISvgNode>();
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;
if (!_styles.TryGetValue(name, out rules))
{
rules = new SortedDictionary<int, string>();
_styles[name] = rules;
}
while (rules.ContainsKey(specificity)) specificity++;
rules[specificity] = value;
}
public void FlushStyles()
{
foreach (var s in _styles)
{
SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, this.OwnerDocument);
}
_styles = null;
}
/// <summary> /// <summary>
/// Gets the name of the element. /// Gets the name of the element.
/// </summary> /// </summary>
...@@ -66,6 +90,16 @@ namespace Svg ...@@ -66,6 +90,16 @@ namespace Svg
internal set { this._elementName = value; } internal set { this._elementName = value; }
} }
/// <summary>
/// Gets or sets the color <see cref="SvgPaintServer"/> of this element which drives the currentColor property.
/// </summary>
[SvgAttribute("color")]
public virtual SvgPaintServer Color
{
get { return (this.Attributes["color"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["color"]; }
set { this.Attributes["color"] = value; }
}
/// <summary> /// <summary>
/// Gets or sets the content of the element. /// Gets or sets the content of the element.
/// </summary> /// </summary>
...@@ -224,15 +258,17 @@ namespace Svg ...@@ -224,15 +258,17 @@ namespace Svg
/// Applies the required transforms to <see cref="SvgRenderer"/>. /// Applies the required transforms to <see cref="SvgRenderer"/>.
/// </summary> /// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal virtual void PushTransforms(SvgRenderer renderer) protected internal virtual bool PushTransforms(SvgRenderer renderer)
{ {
_graphicsMatrix = renderer.Transform; _graphicsMatrix = renderer.Transform;
_graphicsClip = renderer.Clip;
// Return if there are no transforms // Return if there are no transforms
if (this.Transforms == null || this.Transforms.Count == 0) if (this.Transforms == null || this.Transforms.Count == 0)
{ {
return; return true;
} }
if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(new Matrix(0, 0, 0, 0, 0, 0))) return false;
Matrix transformMatrix = renderer.Transform; Matrix transformMatrix = renderer.Transform;
...@@ -242,6 +278,8 @@ namespace Svg ...@@ -242,6 +278,8 @@ namespace Svg
} }
renderer.Transform = transformMatrix; renderer.Transform = transformMatrix;
return true;
} }
/// <summary> /// <summary>
...@@ -252,6 +290,8 @@ namespace Svg ...@@ -252,6 +290,8 @@ namespace Svg
{ {
renderer.Transform = _graphicsMatrix; renderer.Transform = _graphicsMatrix;
_graphicsMatrix = null; _graphicsMatrix = null;
renderer.SetClip(_graphicsClip);
_graphicsClip = null;
} }
/// <summary> /// <summary>
...@@ -636,7 +676,7 @@ namespace Svg ...@@ -636,7 +676,7 @@ namespace Svg
{ {
if(!(child is SvgGroup)) if(!(child is SvgGroup))
{ {
var childPath = ((SvgVisualElement)child).Path; var childPath = ((SvgVisualElement)child).Path(null);
if (childPath != null) if (childPath != null)
{ {
...@@ -648,8 +688,8 @@ namespace Svg ...@@ -648,8 +688,8 @@ namespace Svg
} }
} }
} }
AddPaths(child, path); if (!(child is SvgPaintServer)) AddPaths(child, path);
} }
} }
...@@ -658,7 +698,7 @@ namespace Svg ...@@ -658,7 +698,7 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="elem"></param> /// <param name="elem"></param>
/// <param name="path"></param> /// <param name="path"></param>
protected GraphicsPath GetPaths(SvgElement elem) protected GraphicsPath GetPaths(SvgElement elem, SvgRenderer renderer)
{ {
var ret = new GraphicsPath(); var ret = new GraphicsPath();
...@@ -668,7 +708,7 @@ namespace Svg ...@@ -668,7 +708,7 @@ namespace Svg
{ {
if(!(child is SvgGroup)) if(!(child is SvgGroup))
{ {
var childPath = ((SvgVisualElement)child).Path; var childPath = ((SvgVisualElement)child).Path(renderer);
if (childPath != null) if (childPath != null)
{ {
...@@ -681,7 +721,7 @@ namespace Svg ...@@ -681,7 +721,7 @@ namespace Svg
} }
else else
{ {
var childPath = GetPaths(child); var childPath = GetPaths(child, renderer);
if(child.Transforms != null) if(child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix()); childPath.Transform(child.Transforms.GetMatrix());
} }
......
...@@ -5,6 +5,7 @@ using System.Xml; ...@@ -5,6 +5,7 @@ using System.Xml;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ExCSS;
namespace Svg namespace Svg
{ {
...@@ -15,11 +16,12 @@ namespace Svg ...@@ -15,11 +16,12 @@ namespace Svg
{ {
private static List<ElementInfo> availableElements; private static List<ElementInfo> availableElements;
private const string svgNS = "http://www.w3.org/2000/svg"; private const string svgNS = "http://www.w3.org/2000/svg";
private static Parser cssParser = new Parser();
/// <summary> /// <summary>
/// Gets a list of available types that can be used when creating an <see cref="SvgElement"/>. /// Gets a list of available types that can be used when creating an <see cref="SvgElement"/>.
/// </summary> /// </summary>
private static List<ElementInfo> AvailableElements public static List<ElementInfo> AvailableElements
{ {
get get
{ {
...@@ -43,7 +45,7 @@ namespace Svg ...@@ -43,7 +45,7 @@ namespace Svg
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into an <see cref="SvgDocument"/>.</param> /// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into an <see cref="SvgDocument"/>.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> parameter cannot be <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="reader"/> parameter cannot be <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">The CreateDocument method can only be used to parse root &lt;svg&gt; elements.</exception> /// <exception cref="InvalidOperationException">The CreateDocument method can only be used to parse root &lt;svg&gt; elements.</exception>
public static T CreateDocument<T>(XmlTextReader reader) where T : SvgDocument, new() public static T CreateDocument<T>(XmlReader reader) where T : SvgDocument, new()
{ {
if (reader == null) if (reader == null)
{ {
...@@ -64,7 +66,7 @@ namespace Svg ...@@ -64,7 +66,7 @@ namespace Svg
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into a subclass of <see cref="SvgElement"/>.</param> /// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into a subclass of <see cref="SvgElement"/>.</param>
/// <param name="document">The <see cref="SvgDocument"/> that the created element belongs to.</param> /// <param name="document">The <see cref="SvgDocument"/> that the created element belongs to.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> and <paramref name="document"/> parameters cannot be <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="reader"/> and <paramref name="document"/> parameters cannot be <c>null</c>.</exception>
public static SvgElement CreateElement(XmlTextReader reader, SvgDocument document) public static SvgElement CreateElement(XmlReader reader, SvgDocument document)
{ {
if (reader == null) if (reader == null)
{ {
...@@ -74,7 +76,7 @@ namespace Svg ...@@ -74,7 +76,7 @@ namespace Svg
return CreateElement<SvgDocument>(reader, false, document); return CreateElement<SvgDocument>(reader, false, document);
} }
private static SvgElement CreateElement<T>(XmlTextReader reader, bool fragmentIsDocument, SvgDocument document) where T : SvgDocument, new() private static SvgElement CreateElement<T>(XmlReader reader, bool fragmentIsDocument, SvgDocument document) where T : SvgDocument, new()
{ {
SvgElement createdElement = null; SvgElement createdElement = null;
string elementName = reader.LocalName; string elementName = reader.LocalName;
...@@ -118,58 +120,114 @@ namespace Svg ...@@ -118,58 +120,114 @@ namespace Svg
return createdElement; return createdElement;
} }
private static void SetAttributes(SvgElement element, XmlTextReader reader, SvgDocument document) private static void SetAttributes(SvgElement element, XmlReader reader, SvgDocument document)
{ {
//Trace.TraceInformation("Begin SetAttributes"); //Trace.TraceInformation("Begin SetAttributes");
string[] styles = null; //string[] styles = null;
string[] style = null; //string[] style = null;
int i = 0; //int i = 0;
while (reader.MoveToNextAttribute()) while (reader.MoveToNextAttribute())
{ {
// Special treatment for "style" if (reader.LocalName.Equals("style") && !(element is NonSvgElement))
if (reader.LocalName.Equals("style") && !(element is NonSvgElement))
{ {
styles = reader.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); var inlineSheet = cssParser.Parse("#a{" + reader.Value + "}");
foreach (var rule in inlineSheet.StyleRules)
for (i = 0; i < styles.Length; i++)
{ {
if (!styles[i].Contains(":")) foreach (var decl in rule.Declarations)
{ {
continue; element.AddStyle(decl.Name, decl.Term.ToString(), 1 << 16);
} }
style = styles[i].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
SetPropertyValue(element, style[0].Trim(), style[1].Trim(), document);
} }
//defaults for text can come from the document
if (element.ElementName == "text")
{
if (!styles.Contains("font-size") && document.CustomAttributes.ContainsKey("font-size") && document.CustomAttributes["font-size"] != null)
{
SetPropertyValue(element, "font-size", document.CustomAttributes["font-size"], document);
}
if (!styles.Contains("font-family") && document.CustomAttributes.ContainsKey("font-family") && document.CustomAttributes["font-family"] != null)
{
SetPropertyValue(element, "font-family", document.CustomAttributes["font-family"], document);
}
}
continue;
} }
else if (IsStyleAttribute(reader.LocalName))
SetPropertyValue(element, reader.LocalName, reader.Value, document); {
element.AddStyle(reader.LocalName, reader.Value, 2 << 16);
}
else
{
SetPropertyValue(element, reader.LocalName, reader.Value, document);
}
} }
//Trace.TraceInformation("End SetAttributes"); //Trace.TraceInformation("End SetAttributes");
} }
private static bool IsStyleAttribute(string name)
{
switch (name)
{
case "alignment-baseline":
case "baseline-shift":
case "clip":
case "clip-path":
case "clip-rule":
case "color":
case "color-interpolation":
case "color-interpolation-filters":
case "color-profile":
case "color-rendering":
case "cursor":
case "direction":
case "display":
case "dominant-baseline":
case "enable-background":
case "fill":
case "fill-opacity":
case "fill-rule":
case "filter":
case "flood-color":
case "flood-opacity":
case "font":
case "font-family":
case "font-size":
case "font-size-adjust":
case "font-stretch":
case "font-style":
case "font-variant":
case "font-weight":
case "glyph-orientation-horizontal":
case "glyph-orientation-vertical":
case "image-rendering":
case "kerning":
case "letter-spacing":
case "lighting-color":
case "marker":
case "marker-end":
case "marker-mid":
case "marker-start":
case "mask":
case "opacity":
case "overflow":
case "pointer-events":
case "shape-rendering":
case "stop-color":
case "stop-opacity":
case "stroke":
case "stroke-dasharray":
case "stroke-dashoffset":
case "stroke-linecap":
case "stroke-linejoin":
case "stroke-miterlimit":
case "stroke-opacity":
case "stroke-width":
case "text-anchor":
case "text-decoration":
case "text-rendering":
case "unicode-bidi":
case "visibility":
case "word-spacing":
case "writing-mode":
return true;
}
return false;
}
private static Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>> _propertyDescriptors = new Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>>(); private static Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>> _propertyDescriptors = new Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>>();
private static object syncLock = new object(); private static object syncLock = new object();
private static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document) internal static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
{ {
var elementType = element.GetType(); var elementType = element.GetType();
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Collections.Specialized;
namespace Svg
{
internal sealed class SvgNodeReader : XmlNodeReader
{
private Dictionary<string, string> _entities;
private string _value;
private bool _customValue = false;
private string _localName;
public SvgNodeReader(XmlNode node, Dictionary<string, string> entities)
: base(node)
{
this._entities = entities;
}
/// <summary>
/// Gets the text value of the current node.
/// </summary>
/// <value></value>
/// <returns>The value returned depends on the <see cref="P:System.Xml.XmlTextReader.NodeType"/> of the node. The following table lists node types that have a value to return. All other node types return String.Empty.Node Type Value AttributeThe value of the attribute. CDATAThe content of the CDATA section. CommentThe content of the comment. DocumentTypeThe internal subset. ProcessingInstructionThe entire content, excluding the target. SignificantWhitespaceThe white space within an xml:space= 'preserve' scope. TextThe content of the text node. WhitespaceThe white space between markup. XmlDeclarationThe content of the declaration. </returns>
public override string Value
{
get
{
return (this._customValue) ? this._value : base.Value;
}
}
/// <summary>
/// Gets the local name of the current node.
/// </summary>
/// <value></value>
/// <returns>The name of the current node with the prefix removed. For example, LocalName is book for the element &lt;bk:book&gt;.For node types that do not have a name (like Text, Comment, and so on), this property returns String.Empty.</returns>
public override string LocalName
{
get
{
return (this._customValue) ? this._localName : base.LocalName;
}
}
private IDictionary<string, string> Entities
{
get
{
if (this._entities == null)
{
this._entities = new Dictionary<string, string>();
}
return this._entities;
}
}
/// <summary>
/// Moves to the next attribute.
/// </summary>
/// <returns>
/// true if there is a next attribute; false if there are no more attributes.
/// </returns>
public override bool MoveToNextAttribute()
{
bool moved = base.MoveToNextAttribute();
if (moved)
{
this._localName = base.LocalName;
if (this.ReadAttributeValue())
{
if (this.NodeType == XmlNodeType.EntityReference)
{
this.ResolveEntity();
}
else
{
this._value = base.Value;
}
}
this._customValue = true;
}
return moved;
}
/// <summary>
/// Reads the next node from the stream.
/// </summary>
/// <returns>
/// true if the next node was read successfully; false if there are no more nodes to read.
/// </returns>
/// <exception cref="T:System.Xml.XmlException">An error occurred while parsing the XML. </exception>
public override bool Read()
{
this._customValue = false;
bool read = base.Read();
if (this.NodeType == XmlNodeType.DocumentType)
{
this.ParseEntities();
}
return read;
}
private void ParseEntities()
{
const string entityText = "<!ENTITY";
string[] entities = this.Value.Split(new string[]{entityText}, StringSplitOptions.None);
string[] parts = null;
string name = null;
string value = null;
foreach (string entity in entities)
{
if (string.IsNullOrEmpty(entity.Trim()))
{
continue;
}
parts = entity.Trim().Split(new char[]{' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
name = parts[0];
value = parts[1].Split(new char[] { this.QuoteChar }, StringSplitOptions.RemoveEmptyEntries)[0];
this.Entities.Add(name, value);
}
}
/// <summary>
/// Resolves the entity reference for EntityReference nodes.
/// </summary>
public override void ResolveEntity()
{
if (this.NodeType == XmlNodeType.EntityReference)
{
if (this._entities.ContainsKey(this.Name))
{
this._value = this._entities[this.Name];
}
else
{
this._value = string.Empty;
}
this._customValue = true;
}
}
}
}
\ No newline at end of file
...@@ -8,9 +8,26 @@ using System.Drawing.Text; ...@@ -8,9 +8,26 @@ using System.Drawing.Text;
namespace Svg namespace Svg
{ {
/// <summary>
/// Convenience wrapper around a graphics object
/// </summary>
public sealed class SvgRenderer : IDisposable public sealed class SvgRenderer : IDisposable
{ {
private Graphics _innerGraphics; private Graphics _innerGraphics;
private Stack<ISvgBoundable> _boundables = new Stack<ISvgBoundable>();
public void Boundable(ISvgBoundable boundable)
{
_boundables.Push(boundable);
}
public ISvgBoundable Boundable()
{
return _boundables.Peek();
}
public ISvgBoundable PopBoundable()
{
return _boundables.Pop();
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SvgRenderer"/> class. /// Initializes a new instance of the <see cref="SvgRenderer"/> class.
...@@ -47,6 +64,14 @@ namespace Svg ...@@ -47,6 +64,14 @@ namespace Svg
return renderer; return renderer;
} }
public static SvgRenderer FromNull()
{
SvgRenderer renderer = new SvgRenderer();
var img = new Bitmap(1, 1);
renderer._innerGraphics = Graphics.FromImage(img);
return renderer;
}
public void DrawImageUnscaled(Image image, Point location) public void DrawImageUnscaled(Image image, Point location)
{ {
this._innerGraphics.DrawImageUnscaled(image, location); this._innerGraphics.DrawImageUnscaled(image, location);
...@@ -57,9 +82,13 @@ namespace Svg ...@@ -57,9 +82,13 @@ namespace Svg
_innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit); _innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit);
} }
public void AddClip(Region region)
{
this._innerGraphics.SetClip(region, CombineMode.Intersect);
}
public void SetClip(Region region) public void SetClip(Region region)
{ {
this._innerGraphics.SetClip(region, CombineMode.Complement); this._innerGraphics.SetClip(region, CombineMode.Replace);
} }
public void FillPath(Brush brush, GraphicsPath path) public void FillPath(Brush brush, GraphicsPath path)
...@@ -151,17 +180,18 @@ namespace Svg ...@@ -151,17 +180,18 @@ namespace Svg
public SizeF MeasureString(string text, Font font) public SizeF MeasureString(string text, Font font)
{ {
var ff = font.FontFamily; var ff = font.FontFamily;
float lineSpace = ff.GetLineSpacing(font.Style); //Baseline calculation to match http://bobpowell.net/formattingtext.aspx
float ascent = ff.GetCellAscent(font.Style); float ascent = ff.GetCellAscent(font.Style);
float baseline = font.GetHeight(this._innerGraphics) * ascent / lineSpace; float baselineOffset = font.SizeInPoints / ff.GetEmHeight(font.Style) * ascent;
float baselineOffsetPixels = this._innerGraphics.DpiY / 72f * baselineOffset;
StringFormat format = StringFormat.GenericTypographic; StringFormat format = StringFormat.GenericTypographic;
format.SetMeasurableCharacterRanges(new CharacterRange[]{new CharacterRange(0, text.Length)}); format.SetMeasurableCharacterRanges(new CharacterRange[]{new CharacterRange(0, text.Length)});
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
Region[] r = this._innerGraphics.MeasureCharacterRanges(text, font, new Rectangle(0, 0, 1000, 1000), format); Region[] r = this._innerGraphics.MeasureCharacterRanges(text, font, new Rectangle(0, 0, 1000, 1000), format);
RectangleF rect = r[0].GetBounds(this._innerGraphics); RectangleF rect = r[0].GetBounds(this._innerGraphics);
return new SizeF(rect.Width, baseline); return new SizeF(rect.Width, baselineOffsetPixels);
} }
} }
} }
\ No newline at end of file
...@@ -18,7 +18,14 @@ namespace Svg ...@@ -18,7 +18,14 @@ namespace Svg
public SvgTextReader(Stream stream, Dictionary<string, string> entities) public SvgTextReader(Stream stream, Dictionary<string, string> entities)
: base(stream) : base(stream)
{ {
this.EntityHandling = EntityHandling.ExpandCharEntities; this.EntityHandling = EntityHandling.ExpandEntities;
this._entities = entities;
}
public SvgTextReader(TextReader reader, Dictionary<string, string> entities)
: base(reader)
{
this.EntityHandling = EntityHandling.ExpandEntities;
this._entities = entities; this._entities = entities;
} }
...@@ -119,6 +126,7 @@ namespace Svg ...@@ -119,6 +126,7 @@ namespace Svg
string[] parts = null; string[] parts = null;
string name = null; string name = null;
string value = null; string value = null;
int quoteIndex;
foreach (string entity in entities) foreach (string entity in entities)
{ {
...@@ -127,11 +135,14 @@ namespace Svg ...@@ -127,11 +135,14 @@ namespace Svg
continue; continue;
} }
parts = entity.Trim().Split(new char[]{' ', '\t'}, StringSplitOptions.RemoveEmptyEntries); name = entity.Trim();
name = parts[0]; quoteIndex = name.IndexOf(this.QuoteChar);
value = parts[1].Split(new char[] { this.QuoteChar }, StringSplitOptions.RemoveEmptyEntries)[0]; if (quoteIndex > 0)
{
this.Entities.Add(name, value); value = name.Substring(quoteIndex + 1, name.LastIndexOf(this.QuoteChar) - quoteIndex - 1);
name = name.Substring(0, quoteIndex).Trim();
this.Entities.Add(name, value);
}
} }
} }
......
...@@ -12,6 +12,7 @@ namespace Svg ...@@ -12,6 +12,7 @@ namespace Svg
[TypeConverter(typeof(SvgTextAnchorConverter))] [TypeConverter(typeof(SvgTextAnchorConverter))]
public enum SvgTextAnchor public enum SvgTextAnchor
{ {
inherit,
/// <summary> /// <summary>
/// The rendered characters are aligned such that the start of the text string is at the initial current text position. /// The rendered characters are aligned such that the start of the text string is at the initial current text position.
/// </summary> /// </summary>
......
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