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
Source/bin/ Source/**/bin/
Source/obj/ Source/**/obj/
Source/Svg.csproj.user Source/**/*.csproj.user
Source/Svg.suo Source/**/*.suo
Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Samples/SVGViewer/obj/ Samples/SVGViewer/obj/
Samples/SVGViewer/bin/ Samples/SVGViewer/bin/
Samples/SVGViewer/SVGViewer.OpenCover.Settings Samples/SVGViewer/SVGViewer.OpenCover.Settings
Source/**/*.dll
Source/**/*.pdb
Tests/**/bin/
Tests/**/obj/
Tests/**/*.csproj.user
Tests/**/*.suo
*.trx
Source/TestResults/
\ No newline at end of file
This diff is collapsed.
...@@ -81,7 +81,7 @@ namespace Svg ...@@ -81,7 +81,7 @@ namespace Svg
/// <value>The rectangular bounds of the circle.</value> /// <value>The rectangular bounds of the circle.</value>
public override RectangleF Bounds public override RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
/// <summary> /// <summary>
...@@ -98,24 +98,19 @@ namespace Svg ...@@ -98,24 +98,19 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> representing this element. /// Gets the <see cref="GraphicsPath"/> representing 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.StartFigure();
_path = new GraphicsPath(); var center = this.Center.ToDeviceValue(renderer, this);
_path.StartFigure(); var radius = this.Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
_path.AddEllipse(this.Center.ToDeviceValue().X - this.Radius.ToDeviceValue(), this.Center.ToDeviceValue().Y - this.Radius.ToDeviceValue(), 2 * this.Radius.ToDeviceValue(), 2 * this.Radius.ToDeviceValue()); _path.AddEllipse(center.X - radius, center.Y - radius, 2 * radius, 2 * radius);
_path.CloseFigure(); _path.CloseFigure();
this.IsPathDirty = false; this.IsPathDirty = false;
}
return _path;
}
protected set
{
_path = value;
} }
return _path;
} }
/// <summary> /// <summary>
......
...@@ -95,34 +95,27 @@ namespace Svg ...@@ -95,34 +95,27 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override RectangleF Bounds public override RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
public override GraphicsPath Path public override GraphicsPath Path(SvgRenderer renderer)
{ {
get if (this._path == null || this.IsPathDirty)
{ {
if (this._path == null || this.IsPathDirty) var center = SvgUnit.GetDevicePoint(this._centerX, this._centerY, renderer, this);
{ var radius = SvgUnit.GetDevicePoint(this._radiusX, this._radiusY, renderer, this);
PointF center = new PointF(this._centerX.ToDeviceValue(this), this._centerY.ToDeviceValue(this, true));
PointF radius = new PointF(this._radiusX.ToDeviceValue(this), this._radiusY.ToDeviceValue(this, true));
this._path = new GraphicsPath(); this._path = new GraphicsPath();
_path.StartFigure(); _path.StartFigure();
_path.AddEllipse(center.X - radius.X, center.Y - radius.Y, 2 * radius.X, 2 * radius.Y); _path.AddEllipse(center.X - radius.X, center.Y - radius.Y, 2 * radius.X, 2 * radius.Y);
_path.CloseFigure(); _path.CloseFigure();
this.IsPathDirty = false; this.IsPathDirty = false;
}
return _path;
}
protected set
{
_path = value;
} }
return _path;
} }
/// <summary> /// <summary>
......
...@@ -29,6 +29,17 @@ namespace Svg ...@@ -29,6 +29,17 @@ namespace Svg
public SvgPoint Location public SvgPoint Location
{ {
get { return new SvgPoint(X, Y); } get { return new SvgPoint(X, Y); }
}
/// <summary>
/// Gets or sets the aspect of the viewport.
/// </summary>
/// <value></value>
[SvgAttribute("preserveAspectRatio")]
public SvgAspectRatio AspectRatio
{
get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
set { this.Attributes["preserveAspectRatio"] = value; }
} }
[SvgAttribute("x")] [SvgAttribute("x")]
...@@ -75,21 +86,17 @@ namespace Svg ...@@ -75,21 +86,17 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override RectangleF Bounds public override RectangleF Bounds
{ {
get { return new RectangleF(this.Location.ToDeviceValue(), new SizeF(this.Width, this.Height)); } get { return new RectangleF(this.Location.ToDeviceValue(null, this),
new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this),
this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this))); }
} }
/// <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 return null;
{
return null;
}
protected set
{
}
} }
/// <summary> /// <summary>
...@@ -106,13 +113,72 @@ namespace Svg ...@@ -106,13 +113,72 @@ namespace Svg
{ {
if (b != null) if (b != null)
{ {
var srcRect = new RectangleF(0, 0, b.Width, b.Height);
var destClip = new RectangleF(this.Location.ToDeviceValue(renderer, this),
new SizeF(Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this)));
RectangleF destRect = destClip;
this.PushTransforms(renderer); this.PushTransforms(renderer);
renderer.AddClip(new Region(destClip));
this.SetClip(renderer); this.SetClip(renderer);
RectangleF srcRect = new RectangleF(0, 0, b.Width, b.Height); if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
var destRect = new RectangleF(this.Location.ToDeviceValue(), {
new SizeF(Width.ToDeviceValue(), Height.ToDeviceValue())); var fScaleX = destClip.Width / srcRect.Width;
var fScaleY = destClip.Height / srcRect.Height;
var xOffset = 0.0f;
var yOffset = 0.0f;
if (AspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
}
switch (AspectRatio.Align)
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMin:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
break;
case SvgPreserveAspectRatio.xMinYMid:
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMidYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMaxYMid:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
break;
case SvgPreserveAspectRatio.xMinYMax:
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMidYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
case SvgPreserveAspectRatio.xMaxYMax:
xOffset = (destClip.Width - srcRect.Width * fScaleX);
yOffset = (destClip.Height - srcRect.Height * fScaleY);
break;
}
destRect = new RectangleF(destClip.X + xOffset, destClip.Y + yOffset,
srcRect.Width * fScaleX, srcRect.Height * fScaleY);
}
renderer.DrawImage(b, destRect, srcRect, GraphicsUnit.Pixel); renderer.DrawImage(b, destRect, srcRect, GraphicsUnit.Pixel);
this.ResetClip(renderer); this.ResetClip(renderer);
...@@ -129,7 +195,7 @@ namespace Svg ...@@ -129,7 +195,7 @@ namespace Svg
try try
{ {
// handle data/uri embedded images (http://en.wikipedia.org/wiki/Data_URI_scheme) // handle data/uri embedded images (http://en.wikipedia.org/wiki/Data_URI_scheme)
if (uri.Scheme == "data") if (uri.IsAbsoluteUri && uri.Scheme == "data")
{ {
string uriString = uri.OriginalString; string uriString = uri.OriginalString;
int dataIdx = uriString.IndexOf(",") + 1; int dataIdx = uriString.IndexOf(",") + 1;
...@@ -143,14 +209,26 @@ namespace Svg ...@@ -143,14 +209,26 @@ namespace Svg
return image; return image;
} }
if (!uri.IsAbsoluteUri)
{
uri = new Uri(OwnerDocument.BaseUri, uri);
}
// should work with http: and file: protocol urls // should work with http: and file: protocol urls
var httpRequest = WebRequest.Create(uri); var httpRequest = WebRequest.Create(uri);
using (WebResponse webResponse = httpRequest.GetResponse()) using (WebResponse webResponse = httpRequest.GetResponse())
{ {
MemoryStream ms = BufferToMemoryStream(webResponse.GetResponseStream()); MemoryStream ms = BufferToMemoryStream(webResponse.GetResponseStream());
Image image = Bitmap.FromStream(ms); if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
return image; {
var doc = SvgDocument.Open<SvgDocument>(ms);
return doc.Draw();
}
else
{
return Bitmap.FromStream(ms);
}
} }
} }
catch (Exception ex) catch (Exception ex)
......
...@@ -92,30 +92,25 @@ namespace Svg ...@@ -92,30 +92,25 @@ namespace Svg
{ {
} }
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{ {
get if (this._path == null || this.IsPathDirty)
{ {
if (this._path == null || this.IsPathDirty) PointF start = new PointF(this.StartX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
{ this.StartY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
PointF start = new PointF(this.StartX.ToDeviceValue(this), this.StartY.ToDeviceValue(this, true)); PointF end = new PointF(this.EndX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
PointF end = new PointF(this.EndX.ToDeviceValue(this), this.EndY.ToDeviceValue(this, true)); this.EndY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
this._path = new GraphicsPath(); this._path = new GraphicsPath();
this._path.AddLine(start, end); this._path.AddLine(start, end);
this.IsPathDirty = false; this.IsPathDirty = false;
}
return this._path;
}
protected set
{
_path = value;
} }
return this._path;
} }
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
......
...@@ -32,51 +32,44 @@ namespace Svg ...@@ -32,51 +32,44 @@ namespace Svg
get { return true; } get { return true; }
} }
public override GraphicsPath Path public override GraphicsPath Path(SvgRenderer renderer)
{ {
get if (this._path == null || this.IsPathDirty)
{ {
if (this._path == null || this.IsPathDirty) this._path = new GraphicsPath();
{ this._path.StartFigure();
this._path = new GraphicsPath();
this._path.StartFigure();
try try
{
for (int i = 2; i < this._points.Count; i+=2)
{ {
for (int i = 2; i < this._points.Count; i+=2) var endPoint = SvgUnit.GetDevicePoint(this._points[i], this._points[i+1], renderer, this);
{
PointF endPoint = new PointF(this._points[i].ToDeviceValue(this), this._points[i+1].ToDeviceValue(this));
//first line //first line
if (_path.PointCount == 0) if (_path.PointCount == 0)
{ {
_path.AddLine(new PointF(this._points[i-2].ToDeviceValue(this), this._points[i-1].ToDeviceValue(this)), endPoint); _path.AddLine(SvgUnit.GetDevicePoint(this._points[i - 2], this._points[i - 1], renderer, this), endPoint);
} }
else else
{ {
_path.AddLine(_path.GetLastPoint(), endPoint); _path.AddLine(_path.GetLastPoint(), endPoint);
}
} }
} }
catch
{
Trace.TraceError("Error parsing points");
}
this._path.CloseFigure();
this.IsPathDirty = false;
} }
return this._path; catch
} {
protected set Trace.TraceError("Error parsing points");
{ }
_path = value;
this._path.CloseFigure();
this.IsPathDirty = false;
} }
return this._path;
} }
public override RectangleF Bounds public override RectangleF Bounds
{ {
get { return this.Path.GetBounds(); } get { return this.Path(null).GetBounds(); }
} }
......
...@@ -14,39 +14,37 @@ namespace Svg ...@@ -14,39 +14,37 @@ namespace Svg
public class SvgPolyline : SvgPolygon public class SvgPolyline : SvgPolygon
{ {
private GraphicsPath _Path; private GraphicsPath _Path;
public override GraphicsPath Path public override GraphicsPath Path(SvgRenderer renderer)
{ {
get if (_Path == null || this.IsPathDirty)
{ {
if (_Path == null || this.IsPathDirty) _Path = new GraphicsPath();
{
_Path = new GraphicsPath();
try try
{
for (int i = 0; i < Points.Count; i += 2)
{ {
for (int i = 0; i < Points.Count; i += 2) PointF endPoint = new PointF(Points[i].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
{ Points[i + 1].ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
PointF endPoint = new PointF(Points[i].ToDeviceValue(this), Points[i + 1].ToDeviceValue(this));
// TODO: Remove unrequired first line // TODO: Remove unrequired first line
if (_Path.PointCount == 0) if (_Path.PointCount == 0)
{ {
_Path.AddLine(endPoint, endPoint); _Path.AddLine(endPoint, endPoint);
} }
else else
{ {
_Path.AddLine(_Path.GetLastPoint(), endPoint); _Path.AddLine(_Path.GetLastPoint(), endPoint);
}
} }
} }
catch (Exception exc)
{
Trace.TraceError("Error rendering points: " + exc.Message);
}
this.IsPathDirty = false;
} }
return _Path; catch (Exception exc)
{
Trace.TraceError("Error rendering points: " + exc.Message);
}
this.IsPathDirty = false;
} }
return _Path;
} }
} }
} }
\ No newline at end of file
...@@ -168,101 +168,94 @@ namespace Svg ...@@ -168,101 +168,94 @@ namespace Svg
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override RectangleF Bounds public override RectangleF Bounds
{ {
get { return Path.GetBounds(); } get { return Path(null).GetBounds(); }
} }
/// <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 (_path == null || IsPathDirty)
{ {
if (_path == null || IsPathDirty) // If the corners aren't to be rounded just create a rectangle
if (CornerRadiusX.Value == 0.0f && CornerRadiusY.Value == 0.0f)
{ {
// If the corners aren't to be rounded just create a rectangle var rectangle = new RectangleF(Location.ToDeviceValue(renderer, this),
if (CornerRadiusX.Value == 0.0f && CornerRadiusY.Value == 0.0f) SvgUnit.GetDeviceSize(this.Width, this.Height, renderer, this));
{
var rectangle = new RectangleF(Location.ToDeviceValue(),
new SizeF(Width.ToDeviceValue(), Height.ToDeviceValue()));
_path = new GraphicsPath(); _path = new GraphicsPath();
_path.StartFigure(); _path.StartFigure();
_path.AddRectangle(rectangle); _path.AddRectangle(rectangle);
_path.CloseFigure(); _path.CloseFigure();
} }
else else
{ {
_path = new GraphicsPath(); _path = new GraphicsPath();
var arcBounds = new RectangleF(); var arcBounds = new RectangleF();
var lineStart = new PointF(); var lineStart = new PointF();
var lineEnd = new PointF(); var lineEnd = new PointF();
var width = Width.ToDeviceValue(); var width = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var height = Height.ToDeviceValue(); var height = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
var rx = CornerRadiusX.ToDeviceValue() * 2; var rx = CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2;
var ry = CornerRadiusY.ToDeviceValue() * 2; var ry = CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2;
var location = Location.ToDeviceValue(); var location = Location.ToDeviceValue(renderer, this);
// Start // Start
_path.StartFigure(); _path.StartFigure();
// Add first arc // Add first arc
arcBounds.Location = location; arcBounds.Location = location;
arcBounds.Width = rx; arcBounds.Width = rx;
arcBounds.Height = ry; arcBounds.Height = ry;
_path.AddArc(arcBounds, 180, 90); _path.AddArc(arcBounds, 180, 90);
// Add first line // Add first line
lineStart.X = Math.Min(location.X + rx, location.X + width * 0.5f); lineStart.X = Math.Min(location.X + rx, location.X + width * 0.5f);
lineStart.Y = location.Y; lineStart.Y = location.Y;
lineEnd.X = Math.Max(location.X + width - rx, location.X + width * 0.5f); lineEnd.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
lineEnd.Y = lineStart.Y; lineEnd.Y = lineStart.Y;
_path.AddLine(lineStart, lineEnd); _path.AddLine(lineStart, lineEnd);
// Add second arc // Add second arc
arcBounds.Location = new PointF(location.X + width - rx, location.Y); arcBounds.Location = new PointF(location.X + width - rx, location.Y);
_path.AddArc(arcBounds, 270, 90); _path.AddArc(arcBounds, 270, 90);
// Add second line // Add second line
lineStart.X = location.X + width; lineStart.X = location.X + width;
lineStart.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f); lineStart.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
lineEnd.X = lineStart.X; lineEnd.X = lineStart.X;
lineEnd.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f); lineEnd.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
_path.AddLine(lineStart, lineEnd); _path.AddLine(lineStart, lineEnd);
// Add third arc // Add third arc
arcBounds.Location = new PointF(location.X + width - rx, location.Y + height - ry); arcBounds.Location = new PointF(location.X + width - rx, location.Y + height - ry);
_path.AddArc(arcBounds, 0, 90); _path.AddArc(arcBounds, 0, 90);
// Add third line // Add third line
lineStart.X = Math.Max(location.X + width - rx, location.X + width * 0.5f); lineStart.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
lineStart.Y = location.Y + height; lineStart.Y = location.Y + height;
lineEnd.X = Math.Min(location.X + rx, location.X + width * 0.5f); lineEnd.X = Math.Min(location.X + rx, location.X + width * 0.5f);
lineEnd.Y = lineStart.Y; lineEnd.Y = lineStart.Y;
_path.AddLine(lineStart, lineEnd); _path.AddLine(lineStart, lineEnd);
// Add third arc // Add third arc
arcBounds.Location = new PointF(location.X, location.Y + height - ry); arcBounds.Location = new PointF(location.X, location.Y + height - ry);
_path.AddArc(arcBounds, 90, 90); _path.AddArc(arcBounds, 90, 90);
// Add fourth line // Add fourth line
lineStart.X = location.X; lineStart.X = location.X;
lineStart.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f); lineStart.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
lineEnd.X = lineStart.X; lineEnd.X = lineStart.X;
lineEnd.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f); lineEnd.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
_path.AddLine(lineStart, lineEnd); _path.AddLine(lineStart, lineEnd);
// Close // Close
_path.CloseFigure(); _path.CloseFigure();
}
IsPathDirty = false;
} }
return _path; IsPathDirty = false;
}
protected set
{
_path = value;
} }
return _path;
} }
/// <summary> /// <summary>
......
...@@ -16,7 +16,7 @@ namespace Svg ...@@ -16,7 +16,7 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
public abstract GraphicsPath Path { get; protected set; } public abstract GraphicsPath Path(SvgRenderer renderer);
PointF ISvgBoundable.Location PointF ISvgBoundable.Location
{ {
...@@ -105,7 +105,7 @@ namespace Svg ...@@ -105,7 +105,7 @@ namespace Svg
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer) protected override void Render(SvgRenderer renderer)
{ {
if ((this.Path != null) && this.Visible && this.Displayable) if ((this.Path(renderer) != null) && this.Visible && this.Displayable)
{ {
this.PushTransforms(renderer); this.PushTransforms(renderer);
this.SetClip(renderer); this.SetClip(renderer);
...@@ -138,12 +138,12 @@ namespace Svg ...@@ -138,12 +138,12 @@ namespace Svg
{ {
if (this.Fill != null) if (this.Fill != null)
{ {
using (Brush brush = this.Fill.GetBrush(this, this.FillOpacity)) using (Brush brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
{ {
if (brush != null) if (brush != null)
{ {
this.Path.FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate; this.Path(renderer).FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate;
renderer.FillPath(brush, this.Path); renderer.FillPath(brush, this.Path(renderer));
} }
} }
} }
...@@ -157,8 +157,8 @@ namespace Svg ...@@ -157,8 +157,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 (var pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth)) using (var pen = new Pen(this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1)), strokeWidth))
{ {
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0) if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{ {
...@@ -166,7 +166,7 @@ namespace Svg ...@@ -166,7 +166,7 @@ namespace Svg
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.Value <= 0) ? 1 : u.Value) / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray(); pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.Value <= 0) ? 1 : u.Value) / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
} }
renderer.DrawPath(pen, this.Path); renderer.DrawPath(pen, this.Path(renderer));
} }
} }
} }
...@@ -184,7 +184,7 @@ namespace Svg ...@@ -184,7 +184,7 @@ namespace Svg
if (clipPath != null) if (clipPath != null)
{ {
renderer.Clip = clipPath.GetClipRegion(this); renderer.AddClip(clipPath.GetClipRegion(this));
} }
} }
} }
......
...@@ -162,11 +162,7 @@ namespace Svg ...@@ -162,11 +162,7 @@ namespace Svg
public virtual string FontFamily public virtual string FontFamily
{ {
get { return this.Attributes["font-family"] as string; } get { return this.Attributes["font-family"] as string; }
set set { this.Attributes["font-family"] = value; this.IsPathDirty = true; }
{
this.Attributes["font-family"] = value;
this.IsPathDirty = true;
}
} }
/// <summary> /// <summary>
...@@ -179,15 +175,6 @@ namespace Svg ...@@ -179,15 +175,6 @@ namespace Svg
set { this.Attributes["font-size"] = value; this.IsPathDirty = true; } set { this.Attributes["font-size"] = value; this.IsPathDirty = true; }
} }
public SvgUnit GetInheritedFontSize()
{
var fontSizeElement = (from e in this.ParentsAndSelf.OfType<SvgVisualElement>()
where e.FontSize != SvgUnit.Empty && e.FontSize != SvgUnit.None
select e).FirstOrDefault();
return (fontSizeElement == null ? SvgUnit.None : fontSizeElement.FontSize);
}
/// <summary> /// <summary>
/// Refers to the boldness of the font. /// Refers to the boldness of the font.
/// </summary> /// </summary>
...@@ -308,5 +295,71 @@ namespace Svg ...@@ -308,5 +295,71 @@ namespace Svg
this.IsPathDirty = true; this.IsPathDirty = true;
} }
} }
private const string DefaultFontFamily = "Times New Roman";
/// <summary>
/// Get the font information based on data stored with the text object or inherited from the parent.
/// </summary>
/// <returns></returns>
internal System.Drawing.Font GetFont(SvgRenderer renderer)
{
// Get the font-size
float fontSize;
var fontSizeUnit = this.FontSize;
if (fontSizeUnit == SvgUnit.None)
{
fontSize = 1.0f;
}
else
{
fontSize = fontSizeUnit.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
}
var fontStyle = System.Drawing.FontStyle.Regular;
// Get the font-weight
switch (this.FontWeight)
{
case SvgFontWeight.bold:
case SvgFontWeight.bolder:
case SvgFontWeight.w700:
case SvgFontWeight.w800:
case SvgFontWeight.w900:
fontStyle |= System.Drawing.FontStyle.Bold;
break;
}
// Get the font-style
switch (this.FontStyle)
{
case SvgFontStyle.italic:
case SvgFontStyle.oblique:
fontStyle |= System.Drawing.FontStyle.Italic;
break;
}
// Get the font-family
string family = ValidateFontFamily(this.FontFamily) ?? DefaultFontFamily;
return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel);
}
private static string ValidateFontFamily(string fontFamilyList)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var fontParts = (fontFamilyList ?? "").Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
var families = System.Drawing.FontFamily.Families;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach (var f in fontParts.Where(f => families.Any(family => family.Name.ToLower() == f.ToLower())))
{
return f;
}
// No valid font family found from the list requested.
return null;
}
} }
} }
\ No newline at end of file
...@@ -61,11 +61,11 @@ namespace Svg ...@@ -61,11 +61,11 @@ namespace Svg
{ {
var graphicsElement = element as SvgVisualElement; var graphicsElement = element as SvgVisualElement;
if (graphicsElement != null && graphicsElement.Path != null) if (graphicsElement != null && graphicsElement.Path(null) != null)
{ {
path.FillMode = (graphicsElement.ClipRule == SvgClipRule.NonZero) ? FillMode.Winding : FillMode.Alternate; path.FillMode = (graphicsElement.ClipRule == SvgClipRule.NonZero) ? FillMode.Winding : FillMode.Alternate;
GraphicsPath childPath = graphicsElement.Path; GraphicsPath childPath = graphicsElement.Path(null);
if (graphicsElement.Transforms != null) if (graphicsElement.Transforms != null)
{ {
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fizzler;
using ExCSS;
namespace Svg.Css
{
internal static class CssQuery
{
public static IEnumerable<SvgElement> QuerySelectorAll(this SvgElement elem, string selector)
{
var generator = new SelectorGenerator<SvgElement>(new SvgElementOps());
Fizzler.Parser.Parse(selector, generator);
return generator.Selector(Enumerable.Repeat(elem, 1));
}
public static int GetSpecificity(this BaseSelector selector)
{
if (selector is SimpleSelector)
{
var simpleCode = selector.ToString().ToLowerInvariant();
if (simpleCode.StartsWith(":not("))
{
simpleCode = simpleCode.Substring(5, simpleCode.Length - 6);
return GetSpecificity(new SimpleSelector(simpleCode));
}
else if (simpleCode.StartsWith("#"))
{
// ID selector
return 1 << 12;
}
else if (simpleCode.StartsWith("::") || simpleCode == ":after" || simpleCode == ":before" ||
simpleCode == ":first-letter" || simpleCode == ":first-line" || simpleCode == ":selection")
{
// pseudo-element
return 1 << 4;
}
else if (simpleCode.StartsWith(".") || simpleCode.StartsWith(":") || simpleCode.StartsWith("["))
{
// class, pseudo-class, attribute
return 1 << 8;
}
else if (selector == SimpleSelector.All)
{
// all selector
return 0;
}
else
{
// element selector
return 1 << 4;
}
}
else
{
var list = selector as IEnumerable<BaseSelector>;
if (list != null)
{
return (from s in list select GetSpecificity(s)).Aggregate((p, c) => p + c);
}
else
{
var complex = selector as IEnumerable<CombinatorSelector>;
if (complex != null)
{
return (from s in complex select GetSpecificity(s.Selector)).Aggregate((p, c) => p + c);
}
else
{
return 0;
}
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fizzler;
namespace Svg.Css
{
internal class SvgElementOps : IElementOps<SvgElement>
{
public Selector<SvgElement> Type(NamespacePrefix prefix, string name)
{
var type = SvgElementFactory.AvailableElements.SingleOrDefault(e => e.ElementName == name);
return nodes => nodes.Where(n => n.GetType() == type.ElementType);
}
public Selector<SvgElement> Universal(NamespacePrefix prefix)
{
return nodes => nodes;
}
public Selector<SvgElement> Id(string id)
{
return nodes => nodes.Where(n => n.ID == id);
}
public Selector<SvgElement> Class(string clazz)
{
return AttributeIncludes(NamespacePrefix.None, "class", clazz);
}
public Selector<SvgElement> AttributeExists(NamespacePrefix prefix, string name)
{
return nodes => nodes.Where(n => n.Attributes.ContainsKey(name) || n.CustomAttributes.ContainsKey(name));
}
public Selector<SvgElement> AttributeExact(NamespacePrefix prefix, string name, string value)
{
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val == value) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString() == value);
});
}
public Selector<SvgElement> AttributeIncludes(NamespacePrefix prefix, string name, string value)
{
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split(' ').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split(' ').Contains(value));
});
}
public Selector<SvgElement> AttributeDashMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split('-').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split('-').Contains(value));
}));
}
public Selector<SvgElement> AttributePrefixMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.StartsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().StartsWith(value));
}));
}
public Selector<SvgElement> AttributeSuffixMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.EndsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().EndsWith(value));
}));
}
public Selector<SvgElement> AttributeSubstring(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Contains(value));
}));
}
public Selector<SvgElement> FirstChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.First() == n);
}
public Selector<SvgElement> LastChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.Last() == n);
}
private IEnumerable<T> GetByIds<T>(IList<T> items, IEnumerable<int> indices)
{
foreach (var i in indices)
{
if (i >= 0 && i < items.Count) yield return items[i];
}
}
public Selector<SvgElement> NthChild(int a, int b)
{
return nodes => nodes.Where(n => n.Parent != null && GetByIds(n.Parent.Children, (from i in Enumerable.Range(0, n.Parent.Children.Count / a) select a * i + b)).Contains(n));
}
public Selector<SvgElement> OnlyChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.Count == 1);
}
public Selector<SvgElement> Empty()
{
return nodes => nodes.Where(n => n.Children.Count == 0);
}
public Selector<SvgElement> Child()
{
return nodes => nodes.SelectMany(n => n.Children);
}
public Selector<SvgElement> Descendant()
{
return nodes => nodes.SelectMany(n => Descendants(n));
}
private IEnumerable<SvgElement> Descendants(SvgElement elem)
{
foreach (var child in elem.Children)
{
yield return child;
foreach (var descendant in child.Descendants())
{
yield return descendant;
}
}
}
public Selector<SvgElement> Adjacent()
{
return nodes => nodes.SelectMany(n => ElementsAfterSelf(n).Take(1));
}
public Selector<SvgElement> GeneralSibling()
{
return nodes => nodes.SelectMany(n => ElementsAfterSelf(n));
}
private IEnumerable<SvgElement> ElementsAfterSelf(SvgElement self)
{
return (self.Parent == null ? Enumerable.Empty<SvgElement>() : self.Parent.Children.Skip(self.Parent.Children.IndexOf(self) + 1));
}
public Selector<SvgElement> NthLastChild(int a, int b)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
internal interface ISvgSupportsCoordinateUnits
{
SvgCoordinateUnits GetUnits();
}
}
...@@ -11,12 +11,12 @@ namespace Svg ...@@ -11,12 +11,12 @@ namespace Svg
public enum SvgCoordinateUnits public enum SvgCoordinateUnits
{ {
/// <summary> /// <summary>
/// Indicates that the coordinate system of the entire document is to be used. /// Indicates that the coordinate system of the owner element is to be used.
/// </summary> /// </summary>
UserSpaceOnUse, ObjectBoundingBox,
/// <summary> /// <summary>
/// Indicates that the coordinate system of the owner element is to be used. /// Indicates that the coordinate system of the entire document is to be used.
/// </summary> /// </summary>
ObjectBoundingBox UserSpaceOnUse
} }
} }
...@@ -23,9 +23,9 @@ namespace Svg ...@@ -23,9 +23,9 @@ namespace Svg
set { this.y = value; } set { this.y = value; }
} }
public PointF ToDeviceValue() public PointF ToDeviceValue(SvgRenderer renderer, SvgElement owner)
{ {
return new PointF(this.X.ToDeviceValue(), this.Y.ToDeviceValue()); return SvgUnit.GetDevicePoint(this.X, this.Y, renderer, owner);
} }
public bool IsEmpty() public bool IsEmpty()
......
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Drawing;
namespace Svg namespace Svg
{ {
...@@ -60,26 +62,9 @@ namespace Svg ...@@ -60,26 +62,9 @@ namespace Svg
/// <summary> /// <summary>
/// Converts the current unit to one that can be used at render time. /// Converts the current unit to one that can be used at render time.
/// </summary> /// </summary>
/// <param name="boundable">The container element used as the basis for calculations</param>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns> /// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue() public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
{
return this.ToDeviceValue(null);
}
/// <summary>
/// Converts the current unit to one that can be used at render time.
/// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(ISvgBoundable boundable)
{
return this.ToDeviceValue(boundable, false);
}
/// <summary>
/// Converts the current unit to one that can be used at render time.
/// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(ISvgBoundable boundable, bool vertical)
{ {
// If it's already been calculated // If it's already been calculated
if (this._deviceValue.HasValue) if (this._deviceValue.HasValue)
...@@ -99,60 +84,135 @@ namespace Svg ...@@ -99,60 +84,135 @@ namespace Svg
const float cmInInch = 2.54f; const float cmInInch = 2.54f;
int ppi = SvgDocument.PointsPerInch; int ppi = SvgDocument.PointsPerInch;
switch (this.Type) var type = this.Type;
var value = this.Value;
// Deal with fractional pattern units
var coordElem = owner as ISvgSupportsCoordinateUnits;
if (coordElem != null && coordElem.GetUnits() == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
{
type = SvgUnitType.Percentage;
value *= 100;
}
var element = owner as SvgElement;
if (element != null)
{
var pattern = element.Parents.OfType<SvgPatternServer>().FirstOrDefault();
if (pattern != null && pattern.PatternContentUnits == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
{
type = SvgUnitType.Percentage;
value *= 100;
}
}
float points;
Font currFont;
switch (type)
{ {
case SvgUnitType.Em: case SvgUnitType.Em:
var visualElem = boundable as SvgVisualElement; currFont = GetFont(renderer, owner);
if (visualElem == null) if (currFont == null)
{ {
float points = (float)(this.Value * 9); points = (float)(value * 9);
_deviceValue = (points / 72) * ppi; _deviceValue = (points / 72) * ppi;
} }
else else
{ {
_deviceValue = this.Value * visualElem.GetInheritedFontSize().ToDeviceValue(boundable); _deviceValue = value * (currFont.SizeInPoints / 72) * ppi;
}
break;
case SvgUnitType.Ex:
currFont = GetFont(renderer, owner);
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points / 72) * ppi / 2;
}
else
{
_deviceValue = value * (currFont.SizeInPoints / 72) * ppi * RelativeXHeight(currFont);
} }
break; break;
case SvgUnitType.Centimeter: case SvgUnitType.Centimeter:
_deviceValue = (float)((this.Value / cmInInch) * ppi); _deviceValue = (float)((value / cmInInch) * ppi);
break; break;
case SvgUnitType.Inch: case SvgUnitType.Inch:
_deviceValue = this.Value * ppi; _deviceValue = value * ppi;
break; break;
case SvgUnitType.Millimeter: case SvgUnitType.Millimeter:
_deviceValue = (float)((this.Value / 10) / cmInInch) * ppi; _deviceValue = (float)((value / 10) / cmInInch) * ppi;
break; break;
case SvgUnitType.Pica: case SvgUnitType.Pica:
_deviceValue = ((this.Value * 12) / 72) * ppi; _deviceValue = ((value * 12) / 72) * ppi;
break; break;
case SvgUnitType.Point: case SvgUnitType.Point:
_deviceValue = (this.Value / 72) * ppi; _deviceValue = (value / 72) * ppi;
break; break;
case SvgUnitType.Pixel: case SvgUnitType.Pixel:
_deviceValue = this.Value; _deviceValue = value;
break; break;
case SvgUnitType.User: case SvgUnitType.User:
_deviceValue = this.Value; _deviceValue = value;
break; break;
case SvgUnitType.Percentage: case SvgUnitType.Percentage:
// Can't calculate if there is no style owner // Can't calculate if there is no style owner
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable());
if (boundable == null) if (boundable == null)
{ {
_deviceValue = this.Value; _deviceValue = value;
break; break;
} }
// TODO : Support height percentages
System.Drawing.SizeF size = boundable.Bounds.Size; System.Drawing.SizeF size = boundable.Bounds.Size;
_deviceValue = (((vertical) ? size.Height : size.Width) / 100) * this.Value;
switch (renderType)
{
case UnitRenderingType.Horizontal:
_deviceValue = (size.Width / 100) * value;
break;
case UnitRenderingType.HorizontalOffset:
_deviceValue = (size.Width / 100) * value + boundable.Location.X;
break;
case UnitRenderingType.Vertical:
_deviceValue = (size.Height / 100) * value;
break;
case UnitRenderingType.VerticalOffset:
_deviceValue = (size.Height / 100) * value + boundable.Location.Y;
break;
default:
_deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0);
break;
}
break; break;
default: default:
_deviceValue = this.Value; _deviceValue = value;
break; break;
} }
return this._deviceValue.Value; return this._deviceValue.Value;
} }
private Font GetFont(SvgRenderer renderer, SvgElement owner)
{
if (owner == null) return null;
var visual = owner.ParentsAndSelf.OfType<SvgVisualElement>().FirstOrDefault();
return visual.GetFont(renderer);
}
private float RelativeXHeight(Font font)
{
var mediaFont = new System.Windows.Media.FontFamily(font.Name);
var sum = 0.0;
var cnt = 0;
foreach (var tf in mediaFont.FamilyTypefaces)
{
sum += tf.XHeight;
cnt += 1;
}
return (float)(sum / cnt);
}
/// <summary> /// <summary>
/// Converts the current unit to a percentage, if applicable. /// Converts the current unit to a percentage, if applicable.
/// </summary> /// </summary>
...@@ -249,7 +309,7 @@ namespace Svg ...@@ -249,7 +309,7 @@ namespace Svg
/// <returns>The result of the conversion.</returns> /// <returns>The result of the conversion.</returns>
public static implicit operator float(SvgUnit value) public static implicit operator float(SvgUnit value)
{ {
return value.ToDeviceValue(); return value.ToDeviceValue(null, UnitRenderingType.Other, null);
} }
/// <summary> /// <summary>
...@@ -286,6 +346,32 @@ namespace Svg ...@@ -286,6 +346,32 @@ namespace Svg
this._isEmpty = (this._value == 0.0f); this._isEmpty = (this._value == 0.0f);
this._deviceValue = null; this._deviceValue = null;
} }
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, owner),
y.ToDeviceValue(renderer, UnitRenderingType.Vertical, owner));
}
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, SvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.SizeF(width.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
height.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
}
public enum UnitRenderingType
{
Other,
Horizontal,
HorizontalOffset,
Vertical,
VerticalOffset
} }
/// <summary> /// <summary>
...@@ -306,6 +392,10 @@ namespace Svg ...@@ -306,6 +392,10 @@ namespace Svg
/// </summary> /// </summary>
Em, Em,
/// <summary> /// <summary>
/// Indicates that the unit is equal to the x-height of the current font.
/// </summary>
Ex,
/// <summary>
/// Indicates that the unit is a percentage. /// Indicates that the unit is a percentage.
/// </summary> /// </summary>
Percentage, Percentage,
......
...@@ -68,6 +68,8 @@ namespace Svg ...@@ -68,6 +68,8 @@ namespace Svg
return new SvgUnit(SvgUnitType.Percentage, val); return new SvgUnit(SvgUnitType.Percentage, val);
case "em": case "em":
return new SvgUnit(SvgUnitType.Em, val); return new SvgUnit(SvgUnitType.Em, val);
case "ex":
return new SvgUnit(SvgUnitType.Ex, val);
default: default:
throw new FormatException("Unit is in an invalid format '" + unit + "'."); throw new FormatException("Unit is in an invalid format '" + unit + "'.");
} }
......
...@@ -127,20 +127,43 @@ namespace Svg ...@@ -127,20 +127,43 @@ namespace Svg
set { this.Attributes["preserveAspectRatio"] = value; } set { this.Attributes["preserveAspectRatio"] = value; }
} }
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[SvgAttribute("font-size")]
public virtual SvgUnit FontSize
{
get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
set { this.Attributes["font-size"] = value; }
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[SvgAttribute("font-family")]
public virtual string FontFamily
{
get { return this.Attributes["font-family"] as string; }
set { this.Attributes["font-family"] = value; }
}
/// <summary> /// <summary>
/// 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 override void PushTransforms(SvgRenderer renderer) protected internal override bool PushTransforms(SvgRenderer renderer)
{ {
base.PushTransforms(renderer); if (!base.PushTransforms(renderer)) return false;
if (!this.ViewBox.Equals(SvgViewBox.Empty)) if (!this.ViewBox.Equals(SvgViewBox.Empty))
{ {
float fScaleX = this.Width.ToDeviceValue(this, false) / this.ViewBox.Width; var width = this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float fScaleY = this.Height.ToDeviceValue(this, true) / this.ViewBox.Height; var height = this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
float fMinX = -this.ViewBox.MinX;
float fMinY = -this.ViewBox.MinY; var fScaleX = width / this.ViewBox.Width;
var fScaleY = height / this.ViewBox.Height;
var fMinX = -this.ViewBox.MinX;
var fMinY = -this.ViewBox.MinY;
if (AspectRatio.Align != SvgPreserveAspectRatio.none) if (AspectRatio.Align != SvgPreserveAspectRatio.none)
{ {
...@@ -156,50 +179,56 @@ namespace Svg ...@@ -156,50 +179,56 @@ namespace Svg
} }
float fViewMidX = (this.ViewBox.Width / 2) * fScaleX; float fViewMidX = (this.ViewBox.Width / 2) * fScaleX;
float fViewMidY = (this.ViewBox.Height / 2) * fScaleY; float fViewMidY = (this.ViewBox.Height / 2) * fScaleY;
float fMidX = this.Width.ToDeviceValue(this, false) / 2; float fMidX = width / 2;
float fMidY = this.Height.ToDeviceValue(this, true) / 2; float fMidY = height / 2;
switch (AspectRatio.Align) switch (AspectRatio.Align)
{ {
case SvgPreserveAspectRatio.xMinYMin: case SvgPreserveAspectRatio.xMinYMin:
break; break;
case SvgPreserveAspectRatio.xMidYMin: case SvgPreserveAspectRatio.xMidYMin:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += fMidX - fViewMidX;
break; break;
case SvgPreserveAspectRatio.xMaxYMin: case SvgPreserveAspectRatio.xMaxYMin:
fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width; fMinX += width - this.ViewBox.Width * fScaleX;
break; break;
case SvgPreserveAspectRatio.xMinYMid: case SvgPreserveAspectRatio.xMinYMid:
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += fMidY - fViewMidY;
break; break;
case SvgPreserveAspectRatio.xMidYMid: case SvgPreserveAspectRatio.xMidYMid:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += fMidX - fViewMidX;
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += fMidY - fViewMidY;
break; break;
case SvgPreserveAspectRatio.xMaxYMid: case SvgPreserveAspectRatio.xMaxYMid:
fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width; fMinX += width - this.ViewBox.Width * fScaleX;
fMinY += (fMidY - fViewMidY) / fScaleY; fMinY += fMidY - fViewMidY;
break; break;
case SvgPreserveAspectRatio.xMinYMax: case SvgPreserveAspectRatio.xMinYMax:
fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height; fMinY += height - this.ViewBox.Height * fScaleY;
break; break;
case SvgPreserveAspectRatio.xMidYMax: case SvgPreserveAspectRatio.xMidYMax:
fMinX += (fMidX - fViewMidX) / fScaleX; fMinX += fMidX - fViewMidX;
fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height; fMinY += height - this.ViewBox.Height * fScaleY;
break; break;
case SvgPreserveAspectRatio.xMaxYMax: case SvgPreserveAspectRatio.xMaxYMax:
fMinX += (this.Width.ToDeviceValue(this, false) / fScaleX) - this.ViewBox.Width; fMinX += width - this.ViewBox.Width * fScaleX;
fMinY += (this.Height.ToDeviceValue(this, true) / fScaleY) - this.ViewBox.Height; fMinY += height - this.ViewBox.Height * fScaleY;
break; break;
default: default:
break; break;
} }
} }
renderer.TranslateTransform(_x, _y); var x = _x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var y = _y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
renderer.AddClip(new Region(new RectangleF(x, y, width, height)));
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
renderer.TranslateTransform(x,y);
renderer.TranslateTransform(fMinX, fMinY); renderer.TranslateTransform(fMinX, fMinY);
renderer.ScaleTransform(fScaleX, fScaleY);
} }
return true;
} }
/// <summary> /// <summary>
...@@ -245,18 +274,38 @@ namespace Svg ...@@ -245,18 +274,38 @@ namespace Svg
public SizeF GetDimensions() public SizeF GetDimensions()
{ {
var w = Width.ToDeviceValue(); float w, h;
var h = Height.ToDeviceValue();
RectangleF bounds = new RectangleF();
var isWidthperc = Width.Type == SvgUnitType.Percentage; var isWidthperc = Width.Type == SvgUnitType.Percentage;
var isHeightperc = Height.Type == SvgUnitType.Percentage; var isHeightperc = Height.Type == SvgUnitType.Percentage;
RectangleF bounds = new RectangleF();
if (isWidthperc || isHeightperc) if (isWidthperc || isHeightperc)
{ {
bounds = this.Bounds; //do just one call to the recursive bounds property if (ViewBox.Width > 0 && ViewBox.Height > 0)
if (isWidthperc) w = (bounds.Width + bounds.X) * (w * 0.01f); {
if (isHeightperc) h = (bounds.Height + bounds.Y) * (h * 0.01f); bounds = new RectangleF(ViewBox.MinX, ViewBox.MinY, ViewBox.Width, ViewBox.Height);
}
else
{
bounds = this.Bounds; //do just one call to the recursive bounds property
}
}
if (isWidthperc)
{
w = (bounds.Width + bounds.X) * (Width.Value * 0.01f);
}
else
{
w = Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this);
}
if (isHeightperc)
{
h = (bounds.Height + bounds.Y) * (Height.Value * 0.01f);
}
else
{
h = Height.ToDeviceValue(null, UnitRenderingType.Vertical, this);
} }
return new SizeF(w, h); return new SizeF(w, h);
......
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