Commit 4b19ecb4 authored by Brian C. Barnes's avatar Brian C. Barnes
Browse files

Marker support. perserveAspectRatio Align support

parent 2f2a1e02
using System; using Svg.DataTypes;
using System.ComponentModel; using System.ComponentModel;
namespace Svg namespace Svg
...@@ -6,9 +6,10 @@ namespace Svg ...@@ -6,9 +6,10 @@ namespace Svg
/// <summary> /// <summary>
/// Description of SvgAspectRatio. /// Description of SvgAspectRatio.
/// </summary> /// </summary>
[TypeConverter(typeof(SvgPreserveAspectRatioConverter))]
public class SvgAspectRatio public class SvgAspectRatio
{ {
public SvgAspectRatio() public SvgAspectRatio() : this(SvgPreserveAspectRatio.none)
{ {
} }
...@@ -42,11 +43,10 @@ namespace Svg ...@@ -42,11 +43,10 @@ namespace Svg
} }
[TypeConverter(typeof(SvgPreserverAspectRatioConverter))]
public enum SvgPreserveAspectRatio public enum SvgPreserveAspectRatio
{ {
XMidYMid, //default XMidYMid, //default
None, none,
XMinYMin, XMinYMin,
XMidYMin, XMidYMin,
XMaxYMin, XMaxYMin,
......
using System; using Svg.DataTypes;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel; using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Globalization;
namespace Svg namespace Svg
{ {
/// <summary> /// <summary>
/// Represents an orientation in an Scalable Vector Graphics document. /// Represents an orientation in an Scalable Vector Graphics document.
/// </summary> /// </summary>
public class SvgOrient [TypeConverter(typeof(SvgOrientConverter))]
public class SvgOrient
{ {
private bool _isAuto = true; private bool _isAuto = true;
private float _angle; private float _angle;
......
...@@ -100,11 +100,11 @@ namespace Svg ...@@ -100,11 +100,11 @@ namespace Svg
/// Gets or sets the aspect of the viewport. /// Gets or sets the aspect of the viewport.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
[SvgAttribute("preserveAspectRatio")] [SvgAttribute("preserveAspectRatio")]
public SvgAspectRatio AspectRatio public SvgAspectRatio AspectRatio
{ {
get {return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); } get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
set { this.Attributes["preserveAspectRatio"] = value; } set { this.Attributes["preserveAspectRatio"] = value; }
} }
/// <summary> /// <summary>
...@@ -117,10 +117,60 @@ namespace Svg ...@@ -117,10 +117,60 @@ namespace Svg
if (!this.ViewBox.Equals(SvgViewBox.Empty)) if (!this.ViewBox.Equals(SvgViewBox.Empty))
{ {
renderer.TranslateTransform(_x, _y, MatrixOrder.Append); float fScaleX = this.Width.ToDeviceValue() / this.ViewBox.Width;
renderer.TranslateTransform(-this.ViewBox.MinX, -this.ViewBox.MinY, MatrixOrder.Append); float fScaleY = this.Height.ToDeviceValue() / this.ViewBox.Height;
float fMinX = -this.ViewBox.MinX;
float fMinY = -this.ViewBox.MinY;
if (AspectRatio.Align != SvgPreserveAspectRatio.none)
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
float fViewMidX = (this.ViewBox.Width / 2) * fScaleX;
float fViewMidY = (this.ViewBox.Height / 2) * fScaleY;
float fMidX = this.Width.ToDeviceValue() / 2;
float fMidY = this.Height.ToDeviceValue() / 2;
renderer.ScaleTransform(this.Width.ToDeviceValue() / this.ViewBox.Width, this.Height.ToDeviceValue() / this.ViewBox.Height, MatrixOrder.Append); switch (AspectRatio.Align)
{
case SvgPreserveAspectRatio.XMinYMin:
break;
case SvgPreserveAspectRatio.XMidYMin:
fMinX += (fMidX - fViewMidX) / fScaleX;
break;
case SvgPreserveAspectRatio.XMaxYMin:
fMinX += this.ViewBox.Width - this.Width.ToDeviceValue();
break;
case SvgPreserveAspectRatio.XMinYMid:
fMinY += (fMidY - fViewMidY) / fScaleY;
break;
case SvgPreserveAspectRatio.XMidYMid:
fMinX += (fMidX - fViewMidX) / fScaleX;
fMinY += (fMidY - fViewMidY) / fScaleY;
break;
case SvgPreserveAspectRatio.XMaxYMid:
fMinX += this.ViewBox.Width - this.Width.ToDeviceValue();
fMinY += (fMidY - fViewMidY) / fScaleY;
break;
case SvgPreserveAspectRatio.XMinYMax:
fMinY += this.ViewBox.Height - this.Height.ToDeviceValue();
break;
case SvgPreserveAspectRatio.XMidYMax:
fMinX += (fMidX - fViewMidX) / fScaleX;
fMinY += this.ViewBox.Height - this.Height.ToDeviceValue();
break;
case SvgPreserveAspectRatio.XMaxYMax:
fMinX += this.ViewBox.Width - this.Width.ToDeviceValue();
fMinY += this.ViewBox.Height - this.Height.ToDeviceValue();
break;
default:
break;
}
}
renderer.TranslateTransform(_x, _y, MatrixOrder.Append);
renderer.TranslateTransform(fMinX, fMinY, MatrixOrder.Append);
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Append);
} }
} }
...@@ -162,7 +212,7 @@ namespace Svg ...@@ -162,7 +212,7 @@ namespace Svg
this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f); this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f);
this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f); this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f);
this.ViewBox = SvgViewBox.Empty; this.ViewBox = SvgViewBox.Empty;
this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.None); this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.XMidYMid);
} }
......
using System; using Svg.DataTypes;
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
...@@ -108,11 +109,6 @@ namespace Svg ...@@ -108,11 +109,6 @@ namespace Svg
{ {
} }
//implementaton for preserve aspect ratio
public sealed class SvgPreserverAspectRatioConverter : EnumBaseConverter<SvgPreserveAspectRatio>
{
}
public sealed class SvgStrokeLineCapConverter : EnumBaseConverter<SvgStrokeLineCap> public sealed class SvgStrokeLineCapConverter : EnumBaseConverter<SvgStrokeLineCap>
{ {
} }
...@@ -120,5 +116,8 @@ namespace Svg ...@@ -120,5 +116,8 @@ namespace Svg
public sealed class SvgStrokeLineJoinConverter : EnumBaseConverter<SvgStrokeLineJoin> public sealed class SvgStrokeLineJoinConverter : EnumBaseConverter<SvgStrokeLineJoin>
{ {
} }
public sealed class SvgMarkerUnitsConverter : EnumBaseConverter<SvgMarkerUnits>
{
}
} }
...@@ -6,6 +6,8 @@ using System.Web; ...@@ -6,6 +6,8 @@ using System.Web;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing;
using Svg.DataTypes;
namespace Svg namespace Svg
{ {
...@@ -61,6 +63,35 @@ namespace Svg ...@@ -61,6 +63,35 @@ namespace Svg
} }
[SvgAttribute("markerWidth")]
public virtual SvgUnit MarkerWidth
{
get { return this.Attributes.GetAttribute<SvgUnit>("markerWidth"); }
set { this.Attributes["markerWidth"] = value; }
}
[SvgAttribute("markerHeight")]
public virtual SvgUnit MarkerHeight
{
get { return this.Attributes.GetAttribute<SvgUnit>("markerHeight"); }
set { this.Attributes["markerHeight"] = value; }
}
[SvgAttribute("markerUnits")]
public virtual SvgMarkerUnits MarkerUnits
{
get { return this.Attributes.GetAttribute<SvgMarkerUnits>("markerUnits"); }
set { this.Attributes["markerUnits"] = value; }
}
public SvgMarker()
{
MarkerUnits = SvgMarkerUnits.strokeWidth;
MarkerHeight = 3;
MarkerWidth = 3;
Overflow = SvgOverflow.hidden;
}
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path
{ {
get get
...@@ -87,19 +118,6 @@ namespace Svg ...@@ -87,19 +118,6 @@ namespace Svg
} }
} }
//protected internal override void RenderStroke(SvgRenderer renderer)
//{
// this.PushTransforms(renderer);
// SvgElement parent = element._parent;
// element._parent = this;
// element.RenderElement(renderer);
// element._parent = parent;
// this.PopTransforms(renderer);
//}
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
{ {
return DeepCopy<SvgMarker>(); return DeepCopy<SvgMarker>();
...@@ -117,5 +135,148 @@ namespace Svg ...@@ -117,5 +135,148 @@ namespace Svg
return newObj; return newObj;
} }
}
/// <summary>
/// Render this marker using the slope of the given line segment
/// </summary>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param>
public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
{
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
RenderPart2(fAngle1, pRenderer, pOwner, pRefPoint);
}
/// <summary>
/// Render this marker using the average of the slopes of the two given line segments
/// </summary>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param>
/// <param name="pMarkerPoint3"></param>
public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
{
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
xDiff = pMarkerPoint3.X - pMarkerPoint2.X;
yDiff = pMarkerPoint3.Y - pMarkerPoint2.Y;
float fAngle2 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
RenderPart2((fAngle1 + fAngle2) / 2, pRenderer, pOwner, pRefPoint);
}
/// <summary>
/// Common code for rendering a marker once the orientation angle has been calculated
/// </summary>
/// <param name="fAngle"></param>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint"></param>
private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{
Pen pRenderPen = CreatePen(pOwner);
GraphicsPath markerPath = GetClone(pOwner);
Matrix transMatrix = new Matrix();
transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y);
if (Orient.IsAuto)
transMatrix.Rotate(fAngle);
else
transMatrix.Rotate(Orient.Angle);
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth));
break;
case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY);
break;
}
markerPath.Transform(transMatrix);
pRenderer.DrawPath(pRenderPen, markerPath);
SvgPaintServer pFill = Fill;
SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for?
float fOpacity = FillOpacity;
if (pFill != null)
{
Brush pBrush = pFill.GetBrush(this, fOpacity);
pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose();
}
pRenderPen.Dispose();
markerPath.Dispose();
transMatrix.Dispose();
}
/// <summary>
/// Create a pen that can be used to render this marker
/// </summary>
/// <param name="pStroke"></param>
/// <returns></returns>
private Pen CreatePen(SvgPath pPath)
{
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity);
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth));
case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth));
}
return (new Pen(pBrush, StrokeWidth));
}
/// <summary>
/// Get a clone of the current path, scaled for the stroke with
/// </summary>
/// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath)
{
GraphicsPath pRet = Path.Clone() as GraphicsPath;
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
Matrix transMatrix = new Matrix();
transMatrix.Scale(AdjustForViewBoxWidth(pPath.StrokeWidth), AdjustForViewBoxHeight(pPath.StrokeWidth));
pRet.Transform(transMatrix);
break;
case SvgMarkerUnits.userSpaceOnUse:
break;
}
return (pRet);
}
/// <summary>
/// Adjust the given value to account for the width of the viewbox in the viewport
/// </summary>
/// <param name="fWidth"></param>
/// <returns></returns>
private float AdjustForViewBoxWidth(float fWidth)
{
// TODO: We know this isn't correct
return (fWidth / ViewBox.Width);
}
/// <summary>
/// Adjust the given value to account for the height of the viewbox in the viewport
/// </summary>
/// <param name="fWidth"></param>
/// <returns></returns>
private float AdjustForViewBoxHeight(float fHeight)
{
// TODO: We know this isn't correct
return (fHeight / ViewBox.Height);
}
}
} }
\ No newline at end of file
...@@ -56,6 +56,17 @@ namespace Svg ...@@ -56,6 +56,17 @@ namespace Svg
} }
/// <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> /// <summary>
/// Gets or sets the marker (start cap) of the path. /// Gets or sets the marker (start cap) of the path.
/// </summary> /// </summary>
...@@ -135,7 +146,7 @@ namespace Svg ...@@ -135,7 +146,7 @@ namespace Svg
if (this.Stroke != null) if (this.Stroke != null)
{ {
float strokeWidth = this.StrokeWidth.ToDeviceValue(this); float strokeWidth = this.StrokeWidth.ToDeviceValue(this);
using (var pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth)) using (Pen pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth))
{ {
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0) if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{ {
...@@ -143,33 +154,30 @@ namespace Svg ...@@ -143,33 +154,30 @@ 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();
} }
//hardcoded transformation matrix. I am not sure why this is not in proportion or rotated correctly (something to do with how the endcaps are determined in GDI) renderer.DrawPath(pen, this.Path);
var transMatrix = new Matrix();
transMatrix.Rotate(-90f);
transMatrix.Scale(.6f, .6f);
if (this.MarkerStart != null) if (this.MarkerStart != null)
{ {
var marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
var markerPath = marker.Path.Clone() as GraphicsPath; marker.RenderMarker(renderer, this, Path.PathPoints[0], Path.PathPoints[0], Path.PathPoints[1]);
markerPath.Transform(transMatrix);
pen.CustomStartCap = new CustomLineCap(markerPath, null);
} }
if (this.MarkerEnd != null) if (this.MarkerMid != null)
{ {
var marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerEnd.ToString()); SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
var markerPath = marker.Path.Clone() as GraphicsPath; for (int i = 1; i <= Path.PathPoints.Length - 2; i++)
markerPath.Transform(transMatrix); marker.RenderMarker(renderer, this, Path.PathPoints[i], Path.PathPoints[i - 1], Path.PathPoints[i], Path.PathPoints[i + 1]);
pen.CustomEndCap = new CustomLineCap(markerPath, null);
} }
renderer.DrawPath(pen, this.Path); 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]);
}
} }
} }
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
{ {
return DeepCopy<SvgPath>(); return DeepCopy<SvgPath>();
...@@ -186,9 +194,5 @@ namespace Svg ...@@ -186,9 +194,5 @@ namespace Svg
return newObj; return newObj;
} }
} }
} }
\ No newline at end of file
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\..\vvvv\public\common\src\thirdparty\</OutputPath> <OutputPath>..\..\vvvv\public\common\src\thirdparty\</OutputPath>
<DefineConstants>TRACE;DEBUG;REFLECTION</DefineConstants> <DefineConstants>TRACE;DEBUG;REFLECTION</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
...@@ -99,6 +99,8 @@ ...@@ -99,6 +99,8 @@
<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\SvgAspectRatioConverter.cs" />
<Compile Include="DataTypes\SvgMarkerUnits.cs" />
<Compile Include="DataTypes\SvgOrient.cs" /> <Compile Include="DataTypes\SvgOrient.cs" />
<Compile Include="DataTypes\ISvgViewPort.cs" /> <Compile Include="DataTypes\ISvgViewPort.cs" />
<Compile Include="DataTypes\SvgAspectRatio.cs" /> <Compile Include="DataTypes\SvgAspectRatio.cs" />
...@@ -106,6 +108,7 @@ ...@@ -106,6 +108,7 @@
<Compile Include="DataTypes\SvgElementStyle.cs" /> <Compile Include="DataTypes\SvgElementStyle.cs" />
<Compile Include="DataTypes\SvgCoordinateUnits.cs" /> <Compile Include="DataTypes\SvgCoordinateUnits.cs" />
<Compile Include="DataTypes\SvgFontWeight.cs" /> <Compile Include="DataTypes\SvgFontWeight.cs" />
<Compile Include="DataTypes\SvgOrientConverter.cs" />
<Compile Include="DataTypes\SvgOverflow.cs" /> <Compile Include="DataTypes\SvgOverflow.cs" />
<Compile Include="DataTypes\SvgUnitCollection.cs" /> <Compile Include="DataTypes\SvgUnitCollection.cs" />
<Compile Include="DataTypes\SvgViewBox.cs" /> <Compile Include="DataTypes\SvgViewBox.cs" />
......
...@@ -26,7 +26,7 @@ namespace Svg ...@@ -26,7 +26,7 @@ namespace Svg
/// </summary> /// </summary>
public SvgDocument() public SvgDocument()
{ {
Ppi = 96; Ppi = PointsPerInch;
} }
/// <summary> /// <summary>
......
...@@ -72,6 +72,16 @@ namespace Svg ...@@ -72,6 +72,16 @@ namespace Svg
this._innerGraphics.DrawPath(pen, path); this._innerGraphics.DrawPath(pen, path);
} }
public void RotateTransform(float fAngle, MatrixOrder order)
{
this._innerGraphics.RotateTransform(fAngle, order);
}
public void RotateTransform(float fAngle)
{
this.RotateTransform(fAngle, MatrixOrder.Append);
}
public void TranslateTransform(float dx, float dy, MatrixOrder order) public void TranslateTransform(float dx, float dy, MatrixOrder order)
{ {
this._innerGraphics.TranslateTransform(dx, dy, order); this._innerGraphics.TranslateTransform(dx, dy, order);
......
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