diff --git a/Source/Basic Shapes/SvgCircle.cs b/Source/Basic Shapes/SvgCircle.cs
index 93a4fcd989e174559ae5580a062fa6006fc4f334..d3fbb6df07b650c594177b9e05d4ed83109a6936 100644
--- a/Source/Basic Shapes/SvgCircle.cs
+++ b/Source/Basic Shapes/SvgCircle.cs
@@ -98,7 +98,7 @@ namespace Svg
///
/// Gets the representing this element.
///
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
@@ -117,7 +117,7 @@ namespace Svg
/// Renders the circle to the specified object.
///
/// The graphics object.
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
// Don't draw if there is no radius set
if (this.Radius.Value > 0.0f)
diff --git a/Source/Basic Shapes/SvgEllipse.cs b/Source/Basic Shapes/SvgEllipse.cs
index 07f93ad223a317131915f98be9c40e55d1df5fb0..0ab1d8e03ea1c304de05f0389e92f762510f1de4 100644
--- a/Source/Basic Shapes/SvgEllipse.cs
+++ b/Source/Basic Shapes/SvgEllipse.cs
@@ -102,7 +102,7 @@ namespace Svg
/// Gets the for this element.
///
///
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
@@ -122,7 +122,7 @@ namespace Svg
/// Renders the and contents to the specified object.
///
/// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if (this._radiusX.Value > 0.0f && this._radiusY.Value > 0.0f)
{
diff --git a/Source/Basic Shapes/SvgImage.cs b/Source/Basic Shapes/SvgImage.cs
index a8762c9b7eb02567ab528a65c92573204582c82d..d6b21a9962bfa1bbd16f340b9a18c1b27e791ac3 100644
--- a/Source/Basic Shapes/SvgImage.cs
+++ b/Source/Basic Shapes/SvgImage.cs
@@ -94,7 +94,7 @@ namespace Svg
///
/// Gets the for this element.
///
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
return null;
}
@@ -102,7 +102,7 @@ namespace Svg
///
/// Renders the and contents to the specified object.
///
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
@@ -120,7 +120,7 @@ namespace Svg
RectangleF destRect = destClip;
this.PushTransforms(renderer);
- renderer.AddClip(new Region(destClip));
+ renderer.SetClip(new Region(destClip), CombineMode.Intersect);
this.SetClip(renderer);
if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
@@ -223,6 +223,7 @@ namespace Svg
if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
{
var doc = SvgDocument.Open(ms);
+ doc.BaseUri = uri;
return doc.Draw();
}
else
diff --git a/Source/Basic Shapes/SvgLine.cs b/Source/Basic Shapes/SvgLine.cs
index 05c12a055f76d9abbd07767d2e0522f6823b057d..2d66a999b5081f404cc80d806d2143a4b9cf971d 100644
--- a/Source/Basic Shapes/SvgLine.cs
+++ b/Source/Basic Shapes/SvgLine.cs
@@ -92,7 +92,7 @@ namespace Svg
{
}
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
diff --git a/Source/Basic Shapes/SvgPolygon.cs b/Source/Basic Shapes/SvgPolygon.cs
index 5c6fc24cf18275b9ffa7943af9aa14c6afd34a60..b1c3e22dcd52cbf8b5e3a72f2b4724941885069b 100644
--- a/Source/Basic Shapes/SvgPolygon.cs
+++ b/Source/Basic Shapes/SvgPolygon.cs
@@ -32,7 +32,7 @@ namespace Svg
get { return true; }
}
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
diff --git a/Source/Basic Shapes/SvgPolyline.cs b/Source/Basic Shapes/SvgPolyline.cs
index c499dd6c2987f8221ddd8d0579e417d368b3d9c7..2b262f7135298f6a8303f23d8e34b5886310564a 100644
--- a/Source/Basic Shapes/SvgPolyline.cs
+++ b/Source/Basic Shapes/SvgPolyline.cs
@@ -14,7 +14,7 @@ namespace Svg
public class SvgPolyline : SvgPolygon
{
private GraphicsPath _Path;
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_Path == null || this.IsPathDirty)
{
diff --git a/Source/Basic Shapes/SvgRectangle.cs b/Source/Basic Shapes/SvgRectangle.cs
index 717e6c68897d6e8d2677ec94a98df96a338a44ba..c9d79c3ea698e54bb13aca5c7ea3fb2b9ad6cf86 100644
--- a/Source/Basic Shapes/SvgRectangle.cs
+++ b/Source/Basic Shapes/SvgRectangle.cs
@@ -174,7 +174,7 @@ namespace Svg
///
/// Gets the for this element.
///
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_path == null || IsPathDirty)
{
@@ -261,7 +261,7 @@ namespace Svg
///
/// Renders the and contents to the specified object.
///
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if (Width.Value > 0.0f && Height.Value > 0.0f)
{
diff --git a/Source/Basic Shapes/SvgVisualElement.cs b/Source/Basic Shapes/SvgVisualElement.cs
index 52169422564527a7f6a6abb18575e5fdc2efe4a9..99b14f31a118a144a0360f604996f90cc39879da 100644
--- a/Source/Basic Shapes/SvgVisualElement.cs
+++ b/Source/Basic Shapes/SvgVisualElement.cs
@@ -1,6 +1,7 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
+using System.Diagnostics;
namespace Svg
{
@@ -16,7 +17,7 @@ namespace Svg
///
/// Gets the for this element.
///
- public abstract GraphicsPath Path(SvgRenderer renderer);
+ public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
@@ -104,13 +105,13 @@ namespace Svg
///
/// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
this.Render(renderer, true);
}
- private void Render(SvgRenderer renderer, bool renderFilter)
+ private void Render(ISvgRenderer renderer, bool renderFilter)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
(!Renderable || this.Path(renderer) != null))
@@ -119,11 +120,20 @@ namespace Svg
if (renderFilter && this.Filter != null)
{
- var filter = this.OwnerDocument.IdManager.GetElementById(this.Filter) as FilterEffects.SvgFilter;
+ var filterPath = this.Filter;
+ if (filterPath.ToString().StartsWith("url("))
+ {
+ filterPath = new Uri(filterPath.ToString().Substring(4, filterPath.ToString().Length - 5), UriKind.RelativeOrAbsolute);
+ }
+ var filter = this.OwnerDocument.IdManager.GetElementById(filterPath) as FilterEffects.SvgFilter;
if (filter != null)
{
this.PopTransforms(renderer);
- filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
+ try
+ {
+ filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
+ }
+ catch (Exception ex) { Debug.Print(ex.ToString()); }
renderNormal = false;
}
}
@@ -163,10 +173,10 @@ namespace Svg
}
///
- /// Renders the fill of the to the specified
+ /// Renders the fill of the to the specified
///
- /// The object to render to.
- protected internal virtual void RenderFill(SvgRenderer renderer)
+ /// The object to render to.
+ protected internal virtual void RenderFill(ISvgRenderer renderer)
{
if (this.Fill != null)
{
@@ -182,12 +192,12 @@ namespace Svg
}
///
- /// Renders the stroke of the to the specified
+ /// Renders the stroke of the to the specified
///
- /// The object to render to.
- protected internal virtual void RenderStroke(SvgRenderer renderer)
+ /// The object to render to.
+ protected internal virtual void RenderStroke(ISvgRenderer renderer)
{
- if (this.Stroke != null)
+ if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (var pen = new Pen(this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1)), strokeWidth))
@@ -204,50 +214,50 @@ namespace Svg
}
///
- /// Sets the clipping region of the specified .
+ /// Sets the clipping region of the specified .
///
- /// The to have its clipping region set.
- protected internal virtual void SetClip(SvgRenderer renderer)
+ /// The to have its clipping region set.
+ protected internal virtual void SetClip(ISvgRenderer renderer)
{
if (this.ClipPath != null)
{
SvgClipPath clipPath = this.OwnerDocument.GetElementById(this.ClipPath.ToString());
- this._previousClip = renderer.Clip;
+ this._previousClip = renderer.GetClip();
if (clipPath != null)
{
- renderer.AddClip(clipPath.GetClipRegion(this));
+ renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
}
}
}
///
- /// Resets the clipping region of the specified back to where it was before the method was called.
+ /// Resets the clipping region of the specified back to where it was before the method was called.
///
- /// The to have its clipping region reset.
- protected internal virtual void ResetClip(SvgRenderer renderer)
+ /// The to have its clipping region reset.
+ protected internal virtual void ResetClip(ISvgRenderer renderer)
{
if (this._previousClip != null)
{
- renderer.Clip = this._previousClip;
+ renderer.SetClip(this._previousClip);
this._previousClip = null;
}
}
///
- /// Sets the clipping region of the specified .
+ /// Sets the clipping region of the specified .
///
- /// The to have its clipping region set.
- void ISvgClipable.SetClip(SvgRenderer renderer)
+ /// The to have its clipping region set.
+ void ISvgClipable.SetClip(ISvgRenderer renderer)
{
this.SetClip(renderer);
}
///
- /// Resets the clipping region of the specified back to where it was before the method was called.
+ /// Resets the clipping region of the specified back to where it was before the method was called.
///
- /// The to have its clipping region reset.
- void ISvgClipable.ResetClip(SvgRenderer renderer)
+ /// The to have its clipping region reset.
+ void ISvgClipable.ResetClip(ISvgRenderer renderer)
{
this.ResetClip(renderer);
}
diff --git a/Source/Basic Shapes/SvgVisualElementStyle.cs b/Source/Basic Shapes/SvgVisualElementStyle.cs
index 6433cbb683c7375533668ca721f86a4dd4376339..f2e08df7d2f01f022b3f0ba0e9f07215b7cfb231 100644
--- a/Source/Basic Shapes/SvgVisualElementStyle.cs
+++ b/Source/Basic Shapes/SvgVisualElementStyle.cs
@@ -187,7 +187,7 @@ namespace Svg
}
///
- /// Refers to the boldness of the font.
+ /// Refers to the style of the font.
///
[SvgAttribute("font-style")]
public virtual SvgFontStyle FontStyle
@@ -197,7 +197,7 @@ namespace Svg
}
///
- /// Refers to the boldness of the font.
+ /// Refers to the varient of the font.
///
[SvgAttribute("font-variant")]
public virtual SvgFontVariant FontVariant
@@ -323,7 +323,7 @@ namespace Svg
/// Get the font information based on data stored with the text object or inherited from the parent.
///
///
- internal System.Drawing.Font GetFont(SvgRenderer renderer)
+ internal IFontDefn GetFont(ISvgRenderer renderer)
{
// Get the font-size
float fontSize;
@@ -337,62 +337,81 @@ namespace Svg
fontSize = fontSizeUnit.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
}
- var fontStyle = System.Drawing.FontStyle.Regular;
+ var family = ValidateFontFamily(this.FontFamily, this.OwnerDocument);
+ var sFaces = family as IEnumerable;
- // Get the font-weight
- switch (this.FontWeight)
+ if (sFaces == null)
{
- case SvgFontWeight.bold:
- case SvgFontWeight.bolder:
- case SvgFontWeight.w600:
- case SvgFontWeight.w700:
- case SvgFontWeight.w800:
- case SvgFontWeight.w900:
- fontStyle |= System.Drawing.FontStyle.Bold;
- break;
- }
+ var fontStyle = System.Drawing.FontStyle.Regular;
- // Get the font-style
- switch (this.FontStyle)
- {
- case SvgFontStyle.italic:
- case SvgFontStyle.oblique:
- fontStyle |= System.Drawing.FontStyle.Italic;
- break;
- }
+ // Get the font-weight
+ switch (this.FontWeight)
+ {
+ case SvgFontWeight.bold:
+ case SvgFontWeight.bolder:
+ case SvgFontWeight.w600:
+ case SvgFontWeight.w700:
+ case SvgFontWeight.w800:
+ case SvgFontWeight.w900:
+ fontStyle |= System.Drawing.FontStyle.Bold;
+ break;
+ }
- // Get the text-decoration
- switch (this.TextDecoration)
- {
- case SvgTextDecoration.lineThrough:
- fontStyle |= System.Drawing.FontStyle.Strikeout;
- break;
- case SvgTextDecoration.underline:
- fontStyle |= System.Drawing.FontStyle.Underline;
- break;
- }
+ // Get the font-style
+ switch (this.FontStyle)
+ {
+ case SvgFontStyle.italic:
+ case SvgFontStyle.oblique:
+ fontStyle |= System.Drawing.FontStyle.Italic;
+ break;
+ }
- var family = ValidateFontFamily(this.FontFamily);
- if (!family.IsStyleAvailable(fontStyle))
+ // Get the text-decoration
+ switch (this.TextDecoration)
+ {
+ case SvgTextDecoration.lineThrough:
+ fontStyle |= System.Drawing.FontStyle.Strikeout;
+ break;
+ case SvgTextDecoration.underline:
+ fontStyle |= System.Drawing.FontStyle.Underline;
+ break;
+ }
+
+ var ff = family as FontFamily;
+ if (!ff.IsStyleAvailable(fontStyle))
+ {
+ // Do Something
+ }
+
+ // Get the font-family
+ return new GdiFontDefn(new System.Drawing.Font(ff, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel));
+ }
+ else
{
- // Do Something
+ var font = sFaces.First().Parent as SvgFont;
+ if (font == null)
+ {
+ var uri = sFaces.First().Descendants().OfType().First().ReferencedElement;
+ font = OwnerDocument.IdManager.GetElementById(uri) as SvgFont;
+ }
+ return new SvgFontDefn(font, fontSize, OwnerDocument.Ppi);
}
-
- // Get the font-family
- return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel);
}
- private static FontFamily ValidateFontFamily(string fontFamilyList)
+ public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var fontParts = (fontFamilyList ?? "").Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
var families = System.Drawing.FontFamily.Families;
FontFamily family;
+ IEnumerable sFaces;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach (var f in fontParts)
{
+ if (doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces;
+
family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
if (family != null) return family;
diff --git a/Source/Clipping and Masking/ISvgClipable.cs b/Source/Clipping and Masking/ISvgClipable.cs
index dc604fa3fe9f264a9b5d570b6d0e5c0db09e870e..2820ec1e51bf1c56bcb9b9807dd784f6c7c28e08 100644
--- a/Source/Clipping and Masking/ISvgClipable.cs
+++ b/Source/Clipping and Masking/ISvgClipable.cs
@@ -20,14 +20,14 @@ namespace Svg
///
SvgClipRule ClipRule { get; set; }
///
- /// Sets the clipping region of the specified .
+ /// Sets the clipping region of the specified .
///
- /// The to have its clipping region set.
- void SetClip(SvgRenderer renderer);
+ /// The to have its clipping region set.
+ void SetClip(ISvgRenderer renderer);
///
- /// Resets the clipping region of the specified back to where it was before the method was called.
+ /// Resets the clipping region of the specified back to where it was before the method was called.
///
- /// The to have its clipping region reset.
- void ResetClip(SvgRenderer renderer);
+ /// The to have its clipping region reset.
+ void ResetClip(ISvgRenderer renderer);
}
}
\ No newline at end of file
diff --git a/Source/Clipping and Masking/SvgClipPath.cs b/Source/Clipping and Masking/SvgClipPath.cs
index 72bc101dc0104a2025a2fb311d737082da09b2ba..feed3d38a78252d6a61e9eb25cb72ce3f6bfdd0e 100644
--- a/Source/Clipping and Masking/SvgClipPath.cs
+++ b/Source/Clipping and Masking/SvgClipPath.cs
@@ -75,7 +75,7 @@ namespace Svg
}
}
- path.AddPath(childPath, false);
+ if (childPath.PointCount > 0) path.AddPath(childPath, false);
}
foreach (SvgElement child in element.Children)
@@ -108,10 +108,10 @@ namespace Svg
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
// Do nothing
}
diff --git a/Source/DataTypes/SvgPoint.cs b/Source/DataTypes/SvgPoint.cs
index e15358d16aa19592f762d1f9e356a2d774bbe52b..00d744565e52d7617dccb2552fa3e54ba5105ad7 100644
--- a/Source/DataTypes/SvgPoint.cs
+++ b/Source/DataTypes/SvgPoint.cs
@@ -23,7 +23,7 @@ namespace Svg
set { this.y = value; }
}
- public PointF ToDeviceValue(SvgRenderer renderer, SvgElement owner)
+ public PointF ToDeviceValue(ISvgRenderer renderer, SvgElement owner)
{
return SvgUnit.GetDevicePoint(this.X, this.Y, renderer, owner);
}
diff --git a/Source/DataTypes/SvgTextLengthAdjust.cs b/Source/DataTypes/SvgTextLengthAdjust.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ab68d3ac4d6332a146debc1d371e752856aca8ac
--- /dev/null
+++ b/Source/DataTypes/SvgTextLengthAdjust.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ public enum SvgTextLengthAdjust
+ {
+ spacing,
+ spacingAndGlyphs
+ }
+}
diff --git a/Source/DataTypes/SvgTextPathMethod.cs b/Source/DataTypes/SvgTextPathMethod.cs
new file mode 100644
index 0000000000000000000000000000000000000000..64aac329c3858057259d62c1ecbfe5f1cc78a1d8
--- /dev/null
+++ b/Source/DataTypes/SvgTextPathMethod.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ public enum SvgTextPathMethod
+ {
+ align,
+ stretch
+ }
+}
diff --git a/Source/DataTypes/SvgTextPathSpacing.cs b/Source/DataTypes/SvgTextPathSpacing.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e53d8fa35d74e1e58f474a392ffc437fcd4124e6
--- /dev/null
+++ b/Source/DataTypes/SvgTextPathSpacing.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ public enum SvgTextPathSpacing
+ {
+ exact,
+ auto
+ }
+}
diff --git a/Source/DataTypes/SvgUnit.cs b/Source/DataTypes/SvgUnit.cs
index 9fb4f50c5a59de66ce0862f712ed79f93298cdc2..0130f247743b9315e9b2436d4c1287c377c7b616 100644
--- a/Source/DataTypes/SvgUnit.cs
+++ b/Source/DataTypes/SvgUnit.cs
@@ -64,7 +64,7 @@ namespace Svg
///
/// The container element used as the basis for calculations
/// The representation of the current unit in a device value (usually pixels).
- public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
+ public float ToDeviceValue(ISvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
{
// If it's already been calculated
if (this._deviceValue.HasValue)
@@ -107,7 +107,7 @@ namespace Svg
}
float points;
- Font currFont;
+ IFontDefn currFont;
switch (type)
{
@@ -158,7 +158,7 @@ namespace Svg
break;
case SvgUnitType.Percentage:
// Can't calculate if there is no style owner
- var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable());
+ var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.GetBoundable());
if (boundable == null)
{
_deviceValue = value;
@@ -193,7 +193,7 @@ namespace Svg
return this._deviceValue.Value;
}
- private Font GetFont(SvgRenderer renderer, SvgElement owner)
+ private IFontDefn GetFont(ISvgRenderer renderer, SvgElement owner)
{
if (owner == null) return null;
@@ -335,18 +335,18 @@ namespace Svg
this._deviceValue = null;
}
- public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
+ public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, ISvgRenderer 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)
+ public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, ISvgRenderer 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)
+ public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.SizeF(width.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
height.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
diff --git a/Source/DataTypes/SvgViewBox.cs b/Source/DataTypes/SvgViewBox.cs
index 401140a31b3fa80c3ae1dea89b520ed9caf17435..62d56abd12d9870ec1f2555cbd9790102a861466 100644
--- a/Source/DataTypes/SvgViewBox.cs
+++ b/Source/DataTypes/SvgViewBox.cs
@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
+using System.Drawing.Drawing2D;
namespace Svg
{
@@ -123,6 +124,81 @@ namespace Svg
}
#endregion
+ public void AddViewBoxTransform(SvgAspectRatio aspectRatio, ISvgRenderer renderer, SvgFragment frag)
+ {
+ if (this.Equals(SvgViewBox.Empty)) return;
+
+ var width = (frag == null ? this.Width : frag.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
+ var height = (frag == null ? this.Height : frag.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
+
+ var fScaleX = width / this.Width;
+ var fScaleY = height / this.Height; //(this.MinY < 0 ? -1 : 1) *
+ var fMinX = this.MinX;
+ var fMinY = this.MinY;
+
+ if (aspectRatio == null) aspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid, false);
+ if (aspectRatio.Align != SvgPreserveAspectRatio.none)
+ {
+ if (aspectRatio.Slice)
+ {
+ fScaleX = Math.Max(fScaleX, fScaleY);
+ fScaleY = Math.Max(fScaleX, fScaleY);
+ }
+ else
+ {
+ fScaleX = Math.Min(fScaleX, fScaleY);
+ fScaleY = Math.Min(fScaleX, fScaleY);
+ }
+ float fViewMidX = (this.Width / 2) * fScaleX;
+ float fViewMidY = (this.Height / 2) * fScaleY;
+ float fMidX = width / 2;
+ float fMidY = height / 2;
+
+ switch (aspectRatio.Align)
+ {
+ case SvgPreserveAspectRatio.xMinYMin:
+ break;
+ case SvgPreserveAspectRatio.xMidYMin:
+ fMinX += fMidX - fViewMidX;
+ break;
+ case SvgPreserveAspectRatio.xMaxYMin:
+ fMinX += width - this.Width * fScaleX;
+ break;
+ case SvgPreserveAspectRatio.xMinYMid:
+ fMinY += fMidY - fViewMidY;
+ break;
+ case SvgPreserveAspectRatio.xMidYMid:
+ fMinX += fMidX - fViewMidX;
+ fMinY += fMidY - fViewMidY;
+ break;
+ case SvgPreserveAspectRatio.xMaxYMid:
+ fMinX += width - this.Width * fScaleX;
+ fMinY += fMidY - fViewMidY;
+ break;
+ case SvgPreserveAspectRatio.xMinYMax:
+ fMinY += height - this.Height * fScaleY;
+ break;
+ case SvgPreserveAspectRatio.xMidYMax:
+ fMinX += fMidX - fViewMidX;
+ fMinY += height - this.Height * fScaleY;
+ break;
+ case SvgPreserveAspectRatio.xMaxYMax:
+ fMinX += width - this.Width * fScaleX;
+ fMinY += height - this.Height * fScaleY;
+ break;
+ default:
+ break;
+ }
+ }
+
+ var x = (frag == null ? 0 : frag.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
+ var y = (frag == null ? 0 : frag.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
+
+ renderer.SetClip(new Region(new RectangleF(x, y, width, height)), CombineMode.Intersect);
+ renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
+ renderer.TranslateTransform(x, y);
+ renderer.TranslateTransform(-fMinX, -fMinY);
+ }
}
internal class SvgViewBoxConverter : TypeConverter
diff --git a/Source/DataTypes/XmlSpaceHandling.cs b/Source/DataTypes/XmlSpaceHandling.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1febaf87c9033297c46736ff7f7d4f7658065ac4
--- /dev/null
+++ b/Source/DataTypes/XmlSpaceHandling.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ public enum XmlSpaceHandling
+ {
+ @default,
+ inherit,
+ preserve
+ }
+}
diff --git a/Source/Document Structure/SvgDefinitionList.cs b/Source/Document Structure/SvgDefinitionList.cs
index 9342773ef06a40b4cb4b0557b87d5a441811b108..8f6d96636e952a655957cf16e6e8d47a75857db6 100644
--- a/Source/Document Structure/SvgDefinitionList.cs
+++ b/Source/Document Structure/SvgDefinitionList.cs
@@ -18,10 +18,10 @@ namespace Svg
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
diff --git a/Source/Document Structure/SvgDocumentMetadata.cs b/Source/Document Structure/SvgDocumentMetadata.cs
index a52cb8800c4bc452856354053d12d9dfa224ac0c..680097f1d560e06c903df669cf86e8fb54958af3 100644
--- a/Source/Document Structure/SvgDocumentMetadata.cs
+++ b/Source/Document Structure/SvgDocumentMetadata.cs
@@ -31,10 +31,10 @@ namespace Svg
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
diff --git a/Source/Document Structure/SvgFragment.cs b/Source/Document Structure/SvgFragment.cs
index f23081f1a22960693540967e742d4ff4c8702ea7..36f151570dbcd4e4947a237d189eebe04148fd5c 100644
--- a/Source/Document Structure/SvgFragment.cs
+++ b/Source/Document Structure/SvgFragment.cs
@@ -148,86 +148,13 @@ namespace Svg
}
///
- /// Applies the required transforms to .
+ /// Applies the required transforms to .
///
- /// The to be transformed.
- protected internal override bool PushTransforms(SvgRenderer renderer)
+ /// The to be transformed.
+ protected internal override bool PushTransforms(ISvgRenderer renderer)
{
if (!base.PushTransforms(renderer)) return false;
-
- if (!this.ViewBox.Equals(SvgViewBox.Empty))
- {
- var width = this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
- var height = this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
-
- 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.Slice)
- {
- fScaleX = Math.Max(fScaleX, fScaleY);
- fScaleY = Math.Max(fScaleX, fScaleY);
- }
- else
- {
- 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 = width / 2;
- float fMidY = height / 2;
-
- switch (AspectRatio.Align)
- {
- case SvgPreserveAspectRatio.xMinYMin:
- break;
- case SvgPreserveAspectRatio.xMidYMin:
- fMinX += fMidX - fViewMidX;
- break;
- case SvgPreserveAspectRatio.xMaxYMin:
- fMinX += width - this.ViewBox.Width * fScaleX;
- break;
- case SvgPreserveAspectRatio.xMinYMid:
- fMinY += fMidY - fViewMidY;
- break;
- case SvgPreserveAspectRatio.xMidYMid:
- fMinX += fMidX - fViewMidX;
- fMinY += fMidY - fViewMidY;
- break;
- case SvgPreserveAspectRatio.xMaxYMid:
- fMinX += width - this.ViewBox.Width * fScaleX;
- fMinY += fMidY - fViewMidY;
- break;
- case SvgPreserveAspectRatio.xMinYMax:
- fMinY += height - this.ViewBox.Height * fScaleY;
- break;
- case SvgPreserveAspectRatio.xMidYMax:
- fMinX += fMidX - fViewMidX;
- fMinY += height - this.ViewBox.Height * fScaleY;
- break;
- case SvgPreserveAspectRatio.xMaxYMax:
- fMinX += width - this.ViewBox.Width * fScaleX;
- fMinY += height - this.ViewBox.Height * fScaleY;
- break;
- default:
- break;
- }
- }
-
- 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);
- }
-
+ this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, this);
return true;
}
diff --git a/Source/Document Structure/SvgGroup.cs b/Source/Document Structure/SvgGroup.cs
index f4826f80236a00f5a679bf23bd62a999955327e4..63889c67a7d11e89695ca807492e6cb1f8c15e22 100644
--- a/Source/Document Structure/SvgGroup.cs
+++ b/Source/Document Structure/SvgGroup.cs
@@ -9,15 +9,11 @@ namespace Svg
[SvgElement("g")]
public class SvgGroup : SvgVisualElement
{
- public SvgGroup()
- {
- }
-
///
/// Gets the for this element.
///
///
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
@@ -57,26 +53,7 @@ namespace Svg
}
protected override bool Renderable { get { return false; } }
-
- /////
- ///// Renders the and contents to the specified object.
- /////
- ///// The object to render to.
- //protected override void Render(SvgRenderer renderer)
- //{
- // if (!Visible || !Displayable)
- // return;
-
- // if (this.PushTransforms(renderer))
- // {
- // this.SetClip(renderer);
- // base.RenderChildren(renderer);
- // this.ResetClip(renderer);
- // this.PopTransforms(renderer);
- // }
- //}
-
-
+
public override SvgElement DeepCopy()
{
return DeepCopy();
diff --git a/Source/Document Structure/SvgSwitch.cs b/Source/Document Structure/SvgSwitch.cs
index d3700306e19ce0de75605d3a590256f47cc50582..d07dabb00825c4ad9ca558ba3b140557ae1c0ec4 100644
--- a/Source/Document Structure/SvgSwitch.cs
+++ b/Source/Document Structure/SvgSwitch.cs
@@ -17,7 +17,7 @@ namespace Svg
/// Gets the for this element.
///
///
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
@@ -60,7 +60,7 @@ namespace Svg
/// Renders the and contents to the specified object.
///
/// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
diff --git a/Source/Document Structure/SvgSymbol.cs b/Source/Document Structure/SvgSymbol.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f0ebd2f7ecc6f628d70d601f4ba2b0953c7c3787
--- /dev/null
+++ b/Source/Document Structure/SvgSymbol.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+
+namespace Svg.Document_Structure
+{
+ ///
+ /// An element used to group SVG shapes.
+ ///
+ [SvgElement("symbol")]
+ public class SvgSymbol : SvgVisualElement
+ {
+
+ ///
+ /// Gets or sets the viewport of the element.
+ ///
+ ///
+ [SvgAttribute("viewBox")]
+ public SvgViewBox ViewBox
+ {
+ get { return this.Attributes.GetAttribute("viewBox"); }
+ set { this.Attributes["viewBox"] = value; }
+ }
+
+ ///
+ /// Gets or sets the aspect of the viewport.
+ ///
+ ///
+ [SvgAttribute("preserveAspectRatio")]
+ public SvgAspectRatio AspectRatio
+ {
+ get { return this.Attributes.GetAttribute("preserveAspectRatio"); }
+ set { this.Attributes["preserveAspectRatio"] = value; }
+ }
+
+ ///
+ /// Gets the for this element.
+ ///
+ ///
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
+ {
+ return GetPaths(this, renderer);
+ }
+
+ ///
+ /// Gets the bounds of the element.
+ ///
+ /// The bounds.
+ public override System.Drawing.RectangleF Bounds
+ {
+ get
+ {
+ var r = new RectangleF();
+ foreach (var c in this.Children)
+ {
+ if (c is SvgVisualElement)
+ {
+ // First it should check if rectangle is empty or it will return the wrong Bounds.
+ // This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
+ if (r.IsEmpty)
+ {
+ r = ((SvgVisualElement)c).Bounds;
+ }
+ else
+ {
+ var childBounds = ((SvgVisualElement)c).Bounds;
+ if (!childBounds.IsEmpty)
+ {
+ r = RectangleF.Union(r, childBounds);
+ }
+ }
+ }
+ }
+
+ return r;
+ }
+ }
+
+ protected override bool Renderable { get { return false; } }
+
+ ///
+ /// Applies the required transforms to .
+ ///
+ /// The to be transformed.
+ protected internal override bool PushTransforms(ISvgRenderer renderer)
+ {
+ if (!base.PushTransforms(renderer)) return false;
+ this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, null);
+ return true;
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return DeepCopy();
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ var newObj = base.DeepCopy() as SvgSymbol;
+ if (this.Fill != null)
+ newObj.Fill = this.Fill.DeepCopy() as SvgPaintServer;
+ return newObj;
+ }
+ }
+}
diff --git a/Source/Document Structure/SvgUse.cs b/Source/Document Structure/SvgUse.cs
index 89dbc54767d5d597bb26c7fb7aace67559170b1d..ba2b6ab39294c5e68cd1bec5e6538980345c7223 100644
--- a/Source/Document Structure/SvgUse.cs
+++ b/Source/Document Structure/SvgUse.cs
@@ -35,10 +35,10 @@ namespace Svg
}
///
- /// Applies the required transforms to .
+ /// Applies the required transforms to .
///
- /// The to be transformed.
- protected internal override bool PushTransforms(SvgRenderer renderer)
+ /// The to be transformed.
+ protected internal override bool PushTransforms(ISvgRenderer renderer)
{
if (!base.PushTransforms(renderer)) return false;
renderer.TranslateTransform(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
@@ -55,7 +55,7 @@ namespace Svg
this.Y = 0;
}
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
return (element != null) ? element.Path(renderer) : null;
@@ -66,20 +66,9 @@ namespace Svg
get { return new System.Drawing.RectangleF(); }
}
- // public override SvgElementCollection Children
- // {
- // get
- // {
- // SvgElement element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
- // SvgElementCollection elements = new SvgElementCollection(this, true);
- // elements.Add(element);
- // return elements;
- // }
- // }
-
protected override bool Renderable { get { return false; } }
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
diff --git a/Source/Extensibility/SvgForeignObject.cs b/Source/Extensibility/SvgForeignObject.cs
index d61a95484130fd9b1bcd05edc2e4bd433e5f0eac..dd19031a3ca34b1ebba4fb948a04e3ed2202f92f 100644
--- a/Source/Extensibility/SvgForeignObject.cs
+++ b/Source/Extensibility/SvgForeignObject.cs
@@ -17,7 +17,7 @@ namespace Svg
/// Gets the for this element.
///
///
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
diff --git a/Source/Filter Effects/ImageBuffer.cs b/Source/Filter Effects/ImageBuffer.cs
index 5a8ec9983c45876fed0125bb7cdfc0d8cb639817..11b721d240ab2ee09c0f371ec1ff9baa3a2a0bc0 100644
--- a/Source/Filter Effects/ImageBuffer.cs
+++ b/Source/Filter Effects/ImageBuffer.cs
@@ -14,8 +14,8 @@ namespace Svg.FilterEffects
private Dictionary _images;
private RectangleF _bounds;
- private SvgRenderer _renderer;
- private Action _renderMethod;
+ private ISvgRenderer _renderer;
+ private Action _renderMethod;
private float _inflate;
public Matrix Transform { get; set; }
@@ -41,7 +41,7 @@ namespace Svg.FilterEffects
}
}
- public ImageBuffer(RectangleF bounds, float inflate, SvgRenderer renderer, Action renderMethod)
+ public ImageBuffer(RectangleF bounds, float inflate, ISvgRenderer renderer, Action renderMethod)
{
_bounds = bounds;
_inflate = inflate;
@@ -124,7 +124,7 @@ namespace Svg.FilterEffects
}
private string ProcessKey(string key)
{
- if (string.IsNullOrEmpty(key)) return BufferKey;
+ if (string.IsNullOrEmpty(key)) return _images.ContainsKey(BufferKey) ? BufferKey : SvgFilterPrimitive.SourceGraphic;
return key;
}
@@ -136,7 +136,7 @@ namespace Svg.FilterEffects
(int)(_bounds.Height + 2 * _inflate * _bounds.Height + _bounds.Y));
using (var renderer = SvgRenderer.FromImage(graphic))
{
- renderer.Boundable(_renderer.Boundable());
+ renderer.SetBoundable(_renderer.GetBoundable());
var transform = new Matrix();
transform.Translate(_bounds.Width * _inflate, _bounds.Height * _inflate);
renderer.Transform = transform;
diff --git a/Source/Filter Effects/SvgFilter.cs b/Source/Filter Effects/SvgFilter.cs
index 51c3cccba086edb5af855a8609ec86f2d9a1f59b..2bfc247325b66f4fc15a1b15d011a44db4edce61 100644
--- a/Source/Filter Effects/SvgFilter.cs
+++ b/Source/Filter Effects/SvgFilter.cs
@@ -80,10 +80,10 @@ namespace Svg.FilterEffects
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
base.RenderChildren(renderer);
}
@@ -109,7 +109,7 @@ namespace Svg.FilterEffects
return transformMatrix;
}
- private RectangleF GetPathBounds(SvgVisualElement element, SvgRenderer renderer, Matrix transform)
+ private RectangleF GetPathBounds(SvgVisualElement element, ISvgRenderer renderer, Matrix transform)
{
var bounds = element.Path(renderer).GetBounds();
var pts = new PointF[] { bounds.Location, new PointF(bounds.Right, bounds.Bottom) };
@@ -119,7 +119,7 @@ namespace Svg.FilterEffects
Math.Abs(pts[0].X - pts[1].X), Math.Abs(pts[0].Y - pts[1].Y));
}
- public void ApplyFilter(SvgVisualElement element, SvgRenderer renderer, Action renderMethod)
+ public void ApplyFilter(SvgVisualElement element, ISvgRenderer renderer, Action renderMethod)
{
var inflate = 0.5f;
var transform = GetTransform(element);
@@ -139,11 +139,10 @@ namespace Svg.FilterEffects
var bufferImg = buffer.Buffer;
bufferImg.Save(@"C:\test.png");
var imgDraw = RectangleF.Inflate(bounds, inflate * bounds.Width, inflate * bounds.Height);
- var prevClip = renderer.Clip;
- renderer.Clip = new Region(imgDraw);
+ var prevClip = renderer.GetClip();
+ renderer.SetClip(new Region(imgDraw));
renderer.DrawImage(bufferImg, imgDraw, new RectangleF(bounds.X, bounds.Y, imgDraw.Width, imgDraw.Height), GraphicsUnit.Pixel);
- renderer.Clip = prevClip;
- //renderer.DrawImage(bufferImg, bounds, bounds, GraphicsUnit.Pixel);
+ renderer.SetClip(prevClip);
}
}
diff --git a/Source/Painting/ISvgStylable.cs b/Source/Painting/ISvgStylable.cs
index 47274168bb89245f98e496c0bed2d3466e41b574..fb8a19e7f23ecda874eb922cf8571ebd6e0401be 100644
--- a/Source/Painting/ISvgStylable.cs
+++ b/Source/Painting/ISvgStylable.cs
@@ -19,6 +19,6 @@ namespace Svg
float StrokeMiterLimit { get; set; }
SvgUnitCollection StrokeDashArray { get; set; }
SvgUnit StrokeDashOffset { get; set; }
- GraphicsPath Path(SvgRenderer renderer);
+ GraphicsPath Path(ISvgRenderer renderer);
}
}
\ No newline at end of file
diff --git a/Source/Painting/SvgColourServer.cs b/Source/Painting/SvgColourServer.cs
index 99fdf7c7b67aa5d1c92f1b837b961ee9919541c8..67fa17d0886f77aeddd817d6771530445c3ada78 100644
--- a/Source/Painting/SvgColourServer.cs
+++ b/Source/Painting/SvgColourServer.cs
@@ -35,7 +35,7 @@ namespace Svg
set { this._colour = value; }
}
- public override Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity)
{
//is none?
if (this == SvgPaintServer.None) return new SolidBrush(System.Drawing.Color.Transparent);
diff --git a/Source/Painting/SvgDeferredPaintServer.cs b/Source/Painting/SvgDeferredPaintServer.cs
index 22adfc87cfa53f823892c890e98423000a8d2a22..cb675180d10e413e3bd68842f3ada5e02bc743c0 100644
--- a/Source/Painting/SvgDeferredPaintServer.cs
+++ b/Source/Painting/SvgDeferredPaintServer.cs
@@ -44,7 +44,7 @@ namespace Svg
}
}
- public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
+ public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity)
{
EnsureServer(styleOwner);
return _concreteServer.GetBrush(styleOwner, renderer, opacity);
diff --git a/Source/Painting/SvgGradientServer.cs b/Source/Painting/SvgGradientServer.cs
index 155286937ce3673cab778e910f597a808af0e964..aea48926dcbb618f54560e3b5c163db910289e61 100644
--- a/Source/Painting/SvgGradientServer.cs
+++ b/Source/Painting/SvgGradientServer.cs
@@ -130,7 +130,7 @@ namespace Svg
///
/// The parent .
/// The opacity of the colour blend.
- protected ColorBlend GetColorBlend(SvgRenderer renderer, float opacity, bool radial)
+ protected ColorBlend GetColorBlend(ISvgRenderer renderer, float opacity, bool radial)
{
int colourBlends = this.Stops.Count;
bool insertStart = false;
@@ -184,7 +184,7 @@ namespace Svg
for (int i = 0; i < colourBlends; i++)
{
var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
- var boundWidth = renderer.Boundable().Bounds.Width;
+ var boundWidth = renderer.GetBoundable().Bounds.Width;
mergedOpacity = opacity * currentStop.Opacity;
position =
diff --git a/Source/Painting/SvgLinearGradientServer.cs b/Source/Painting/SvgLinearGradientServer.cs
index cc791efa9084c376d7aa8012b5637e174cc844c7..f419e769697a77585d5833f511d1a5654f74e596 100644
--- a/Source/Painting/SvgLinearGradientServer.cs
+++ b/Source/Painting/SvgLinearGradientServer.cs
@@ -79,7 +79,7 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
}
- public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
{
LoadStops(renderingElement);
if (IsInvalid)
@@ -89,7 +89,7 @@ namespace Svg
try
{
- if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
+ if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
var specifiedStart = CalculateStart(renderer);
var specifiedEnd = CalculateEnd(renderer);
@@ -116,12 +116,12 @@ namespace Svg
}
}
- private PointF CalculateStart(SvgRenderer renderer)
+ private PointF CalculateStart(ISvgRenderer renderer)
{
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
}
- private PointF CalculateEnd(SvgRenderer renderer)
+ private PointF CalculateEnd(ISvgRenderer renderer)
{
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
}
@@ -181,7 +181,7 @@ namespace Svg
return new GradientPoints(effectiveStart, effectiveEnd);
}
- private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
+ private ColorBlend CalculateColorBlend(ISvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{
var colorBlend = GetColorBlend(renderer, opacity, false);
diff --git a/Source/Painting/SvgMarker.cs b/Source/Painting/SvgMarker.cs
index 12b4f80e9ed096eeffbf8d06f73a1efccb6f429d..a959e3d83f122bf7e7a578ae8ed2c5fa366a0d3a 100644
--- a/Source/Painting/SvgMarker.cs
+++ b/Source/Painting/SvgMarker.cs
@@ -87,7 +87,7 @@ namespace Svg
Overflow = SvgOverflow.hidden;
}
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
var path = this.Children.FirstOrDefault(x => x is SvgPath);
if (path != null)
@@ -131,7 +131,7 @@ namespace Svg
///
///
///
- public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
+ public void RenderMarker(ISvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2)
{
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
@@ -148,7 +148,7 @@ namespace Svg
///
///
///
- public void RenderMarker(SvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
+ public void RenderMarker(ISvgRenderer pRenderer, SvgPath pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
{
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
@@ -168,7 +168,7 @@ namespace Svg
///
///
///
- private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
+ private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{
Pen pRenderPen = CreatePen(pOwner, pRenderer);
@@ -216,7 +216,7 @@ namespace Svg
///
///
///
- private Pen CreatePen(SvgPath pPath, SvgRenderer renderer)
+ private Pen CreatePen(SvgPath pPath, ISvgRenderer renderer)
{
Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
switch (MarkerUnits)
diff --git a/Source/Painting/SvgPaintServer.cs b/Source/Painting/SvgPaintServer.cs
index 11c342e5045f6e06d23fcb716a88dfd07c385291..417024ce7acff4a6113fc873b4a00046a1ee67d0 100644
--- a/Source/Painting/SvgPaintServer.cs
+++ b/Source/Painting/SvgPaintServer.cs
@@ -26,10 +26,10 @@ namespace Svg
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected override void Render(ISvgRenderer renderer)
{
// Never render paint servers or their children
}
@@ -39,7 +39,7 @@ namespace Svg
///
/// The owner .
/// The opacity of the brush.
- public abstract Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity);
+ public abstract Brush GetBrush(SvgVisualElement styleOwner, ISvgRenderer renderer, float opacity);
///
/// Returns a that represents the current .
diff --git a/Source/Painting/SvgPatternServer.cs b/Source/Painting/SvgPatternServer.cs
index 9afa695d35c2b6ad84e0fe6cbec142068eab203f..48adb39d3cd2a8f84d892da21f12ed9d245ef4e0 100644
--- a/Source/Painting/SvgPatternServer.cs
+++ b/Source/Painting/SvgPatternServer.cs
@@ -20,8 +20,8 @@ namespace Svg
private SvgUnit _x;
private SvgUnit _y;
private SvgViewBox _viewBox;
- private SvgCoordinateUnits _patternUnits;
- private SvgCoordinateUnits _patternContentUnits;
+ private SvgCoordinateUnits _patternUnits = SvgCoordinateUnits.ObjectBoundingBox;
+ private SvgCoordinateUnits _patternContentUnits = SvgCoordinateUnits.UserSpaceOnUse;
[SvgAttribute("overflow")]
public SvgOverflow Overflow
@@ -129,7 +129,7 @@ namespace Svg
///
/// The owner .
/// The opacity of the brush.
- public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
{
// If there aren't any children, return null
if (this.Children.Count == 0)
@@ -141,7 +141,7 @@ namespace Svg
try
{
- if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
+ if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
@@ -153,11 +153,7 @@ namespace Svg
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
- {
- patternMatrix.Translate(-1, -1);
+ patternMatrix.Translate(x, y);
}
if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
@@ -167,20 +163,16 @@ namespace Svg
}
Bitmap image = new Bitmap((int)width, (int)height);
- using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
+ using (var iRenderer = SvgRenderer.FromImage(image))
{
- iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
+ iRenderer.SetBoundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.GetBoundable());
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();
}
image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
diff --git a/Source/Painting/SvgRadialGradientServer.cs b/Source/Painting/SvgRadialGradientServer.cs
index 5a1a6cd443c7211079475a3df32870a719b086b7..240f87ea34e7fabcb606c8cdbdbef2b73d0e7c93 100644
--- a/Source/Painting/SvgRadialGradientServer.cs
+++ b/Source/Painting/SvgRadialGradientServer.cs
@@ -98,16 +98,16 @@ namespace Svg
private object _lockObj = new Object();
- public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
+ public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity)
{
LoadStops(renderingElement);
try
{
- if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
+ if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
// Calculate the path and transform it appropriately
- var origin = renderer.Boundable().Location;
+ var origin = renderer.GetBoundable().Location;
var center = new PointF(origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this),
origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this));
var specifiedRadius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
@@ -181,7 +181,7 @@ namespace Svg
return bounds.Height / (points[2].Y - points[1].Y);
}
- private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
+ private PointF CalculateFocalPoint(ISvgRenderer renderer, PointF origin)
{
var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
@@ -203,7 +203,7 @@ namespace Svg
return path;
}
- private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float scale, out float outScale)
+ private ColorBlend CalculateColorBlend(ISvgRenderer renderer, float opacity, float scale, out float outScale)
{
var colorBlend = GetColorBlend(renderer, opacity, true);
float newScale;
diff --git a/Source/Paths/SvgPath.cs b/Source/Paths/SvgPath.cs
index 95757d2b98b8cb80a29f7e9b4dead90595a6aea1..25de7c228d817bb5d3dda3d1f75f8b78ff19f3ee 100644
--- a/Source/Paths/SvgPath.cs
+++ b/Source/Paths/SvgPath.cs
@@ -38,9 +38,9 @@ namespace Svg
/// Gets or sets the length of the path.
///
[SvgAttribute("pathLength")]
- public int PathLength
+ public float PathLength
{
- get { return this.Attributes.GetAttribute("pathLength"); }
+ get { return this.Attributes.GetAttribute("pathLength"); }
set { this.Attributes["pathLength"] = value; }
}
@@ -81,7 +81,7 @@ namespace Svg
///
/// Gets the for this element.
///
- public override GraphicsPath Path(SvgRenderer renderer)
+ public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
@@ -131,15 +131,15 @@ namespace Svg
}
///
- /// Renders the stroke of the to the specified
+ /// Renders the stroke of the to the specified
///
- /// The object to render to.
- protected internal override void RenderStroke(SvgRenderer renderer)
+ /// The object to render to.
+ protected internal override void RenderStroke(ISvgRenderer renderer)
{
- if (this.Stroke != null)
+ if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
- using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity), strokeWidth))
+ using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity * this.Opacity), strokeWidth))
{
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{
diff --git a/Source/Rendering/IGraphicsProvider.cs b/Source/Rendering/IGraphicsProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..743c50f63e54f166c96c9310ff3b81d59a668eca
--- /dev/null
+++ b/Source/Rendering/IGraphicsProvider.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+
+namespace Svg
+{
+ public interface IGraphicsProvider
+ {
+ Graphics GetGraphics();
+ }
+}
diff --git a/Source/Rendering/ISvgRenderer.cs b/Source/Rendering/ISvgRenderer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0488abd94c81bf0af0b2a4fc357c38327f3c590c
--- /dev/null
+++ b/Source/Rendering/ISvgRenderer.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Drawing.Drawing2D;
+using System.Drawing;
+using System.Collections.Generic;
+
+namespace Svg
+{
+ public interface ISvgRenderer : IDisposable
+ {
+ float DpiY { get; }
+ void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit);
+ void DrawImageUnscaled(Image image, Point location);
+ void DrawPath(Pen pen, GraphicsPath path);
+ void FillPath(Brush brush, GraphicsPath path);
+ float FontBaselineOffset(IFontDefn font);
+ ISvgBoundable GetBoundable();
+ Region GetClip();
+ IList MeasureCharacters(string text, IFontDefn font);
+ SizeF MeasureString(string text, IFontDefn font);
+ ISvgBoundable PopBoundable();
+ void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append);
+ void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append);
+ void SetBoundable(ISvgBoundable boundable);
+ void SetClip(Region region, CombineMode combineMode = CombineMode.Replace);
+ SmoothingMode SmoothingMode { get; set; }
+ Matrix Transform { get; set; }
+ void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append);
+ }
+}
diff --git a/Source/Rendering/SvgRenderer.cs b/Source/Rendering/SvgRenderer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2c03b01d59cfdbd205ad61122812a07af5f20511
--- /dev/null
+++ b/Source/Rendering/SvgRenderer.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Text;
+
+namespace Svg
+{
+ ///
+ /// Convenience wrapper around a graphics object
+ ///
+ public sealed class SvgRenderer : IDisposable, IGraphicsProvider, ISvgRenderer
+ {
+ private Graphics _innerGraphics;
+ private Stack _boundables = new Stack();
+
+ public void SetBoundable(ISvgBoundable boundable)
+ {
+ _boundables.Push(boundable);
+ }
+ public ISvgBoundable GetBoundable()
+ {
+ return _boundables.Peek();
+ }
+ public ISvgBoundable PopBoundable()
+ {
+ return _boundables.Pop();
+ }
+
+ public float DpiY
+ {
+ get { return _innerGraphics.DpiY; }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private SvgRenderer(Graphics graphics)
+ {
+ this._innerGraphics = graphics;
+ }
+
+ public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit)
+ {
+ _innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit);
+ }
+ public void DrawImageUnscaled(Image image, Point location)
+ {
+ this._innerGraphics.DrawImageUnscaled(image, location);
+ }
+ public void DrawPath(Pen pen, GraphicsPath path)
+ {
+ this._innerGraphics.DrawPath(pen, path);
+ }
+ public void FillPath(Brush brush, GraphicsPath path)
+ {
+ this._innerGraphics.FillPath(brush, path);
+ }
+ public Region GetClip()
+ {
+ return this._innerGraphics.Clip;
+ }
+ public void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append)
+ {
+ this._innerGraphics.RotateTransform(fAngle, order);
+ }
+ public void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append)
+ {
+ this._innerGraphics.ScaleTransform(sx, sy, order);
+ }
+ public void SetClip(Region region, CombineMode combineMode = CombineMode.Replace)
+ {
+ this._innerGraphics.SetClip(region, combineMode);
+ }
+ public void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append)
+ {
+ this._innerGraphics.TranslateTransform(dx, dy, order);
+ }
+
+
+
+ public SmoothingMode SmoothingMode
+ {
+ get { return this._innerGraphics.SmoothingMode; }
+ set { this._innerGraphics.SmoothingMode = value; }
+ }
+
+ public Matrix Transform
+ {
+ get { return this._innerGraphics.Transform; }
+ set { this._innerGraphics.Transform = value; }
+ }
+
+ public void Dispose()
+ {
+ this._innerGraphics.Dispose();
+ }
+
+ public float FontBaselineOffset(IFontDefn font)
+ {
+ return font.Ascent(this);
+ }
+
+ public IList MeasureCharacters(string text, IFontDefn font)
+ {
+ return font.MeasureCharacters(this, text);
+ }
+
+ public SizeF MeasureString(string text, IFontDefn font)
+ {
+ return font.MeasureString(this, text);
+ }
+
+ Graphics IGraphicsProvider.GetGraphics()
+ {
+ return _innerGraphics;
+ }
+
+ ///
+ /// Creates a new from the specified .
+ ///
+ /// from which to create the new .
+ public static ISvgRenderer FromImage(Image image)
+ {
+ var g = Graphics.FromImage(image);
+ g.TextRenderingHint = TextRenderingHint.AntiAlias;
+ g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
+ g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
+ g.TextContrast = 1;
+ return new SvgRenderer(g);
+ }
+
+ ///
+ /// Creates a new from the specified .
+ ///
+ /// The to create the renderer from.
+ public static ISvgRenderer FromGraphics(Graphics graphics)
+ {
+ return new SvgRenderer(graphics);
+ }
+
+ public static ISvgRenderer FromNull()
+ {
+ var img = new Bitmap(1, 1);
+ return SvgRenderer.FromImage(img);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Svg.csproj b/Source/Svg.csproj
index 7e2ee28c3dc68fcf372139d6c699a103bc2ed1da..56a1bb4c18dbc68660539c8a15ee37c5861de0a2 100644
--- a/Source/Svg.csproj
+++ b/Source/Svg.csproj
@@ -101,8 +101,15 @@
+
+
+
+
+
+
+
@@ -235,7 +242,7 @@
-
+
@@ -276,10 +283,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/SvgAttributeAttribute.cs b/Source/SvgAttributeAttribute.cs
index cc8c72e47eeb01c1fd3b44870c024b6605e34b2e..eca8f18082ae400e5c6e8497f25c9dce8f9809be 100644
--- a/Source/SvgAttributeAttribute.cs
+++ b/Source/SvgAttributeAttribute.cs
@@ -15,13 +15,14 @@ namespace Svg
///
/// Gets a containing the XLink namespace (http://www.w3.org/1999/xlink).
///
- public const string SVG_NAMESPACE = "http://www.w3.org/2000/svg";
+ public const string SvgNamespace = "http://www.w3.org/2000/svg";
public const string XLinkPrefix = "xlink";
public const string XLinkNamespace = "http://www.w3.org/1999/xlink";
+ public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
public static readonly List> Namespaces = new List>()
{
- new KeyValuePair("", SVG_NAMESPACE),
+ new KeyValuePair("", SvgNamespace),
new KeyValuePair(XLinkPrefix, XLinkNamespace)
};
private string _name;
@@ -55,7 +56,7 @@ namespace Svg
{
get
{
- if (_namespace == SVG_NAMESPACE)
+ if (_namespace == SvgNamespace)
return _name;
return Namespaces.First(x => x.Value == _namespace).Key + ":" + _name;
}
@@ -93,7 +94,7 @@ namespace Svg
internal SvgAttributeAttribute(string name)
{
this._name = name;
- this._namespace = SVG_NAMESPACE;
+ this._namespace = SvgNamespace;
}
///
diff --git a/Source/SvgAttributeCollection.cs b/Source/SvgAttributeCollection.cs
index db60897651b574f6c60ff777c33028d8e551c430..39e85f64de85069dc18f27dc01bebcd6a88f39d2 100644
--- a/Source/SvgAttributeCollection.cs
+++ b/Source/SvgAttributeCollection.cs
@@ -89,8 +89,9 @@ namespace Svg
(value is SvgFontWeight && (SvgFontWeight)value == SvgFontWeight.inherit) ||
(value is SvgTextAnchor && (SvgTextAnchor)value == SvgTextAnchor.inherit) ||
(value is SvgFontVariant && (SvgFontVariant)value == SvgFontVariant.inherit) ||
- (value is SvgTextDecoration && (SvgTextDecoration)value == SvgTextDecoration.inherit) ||
- (value == "inherit")
+ (value is SvgTextDecoration && (SvgTextDecoration)value == SvgTextDecoration.inherit) ||
+ (value is XmlSpaceHandling && (XmlSpaceHandling)value == XmlSpaceHandling.inherit) ||
+ (value is string && (string)value == "inherit")
);
}
diff --git a/Source/SvgDocument.cs b/Source/SvgDocument.cs
index c5fd0dcbb266f1724667364b2e7eb7fe0613a463..14f1ef8e9668f610505ca47b820f0d55f5829f88 100644
--- a/Source/SvgDocument.cs
+++ b/Source/SvgDocument.cs
@@ -22,6 +22,18 @@ namespace Svg
public static readonly int PointsPerInch = 96;
private SvgElementIdManager _idManager;
+ private Dictionary> _fontDefns = null;
+ internal Dictionary> FontDefns()
+ {
+ if (_fontDefns == null)
+ {
+ _fontDefns = (from f in Descendants().OfType()
+ group f by f.FontFamily into family
+ select family).ToDictionary(f => f.Key, f => (IEnumerable)f);
+ }
+ return _fontDefns;
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -379,18 +391,18 @@ namespace Svg
}
///
- /// Renders the to the specified .
+ /// Renders the to the specified .
///
- /// The to render the document with.
+ /// The to render the document with.
/// The parameter cannot be null.
- public void Draw(SvgRenderer renderer)
+ public void Draw(ISvgRenderer renderer)
{
if (renderer == null)
{
throw new ArgumentNullException("renderer");
}
- renderer.Boundable(this);
+ renderer.SetBoundable(this);
this.Render(renderer);
}
@@ -407,7 +419,7 @@ namespace Svg
}
var renderer = SvgRenderer.FromGraphics(graphics);
- renderer.Boundable(this);
+ renderer.SetBoundable(this);
this.Render(renderer);
}
@@ -447,12 +459,8 @@ namespace Svg
{
using (var renderer = SvgRenderer.FromImage(bitmap))
{
- renderer.Boundable(new GenericBoundable(0, 0, bitmap.Width, bitmap.Height));
- renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
- renderer.TextContrast = 1;
- renderer.PixelOffsetMode = PixelOffsetMode.Half;
+ renderer.SetBoundable(new GenericBoundable(0, 0, bitmap.Width, bitmap.Height));
this.Render(renderer);
- renderer.Save();
}
}
catch
diff --git a/Source/SvgElement.cs b/Source/SvgElement.cs
index b2ceb6cb40f85970602ffb76879981fe3d3834c0..d808b9c50803c16af83514de1384c2466bd864c8 100644
--- a/Source/SvgElement.cs
+++ b/Source/SvgElement.cs
@@ -255,13 +255,13 @@ namespace Svg
}
///
- /// Applies the required transforms to .
+ /// Applies the required transforms to .
///
- /// The to be transformed.
- protected internal virtual bool PushTransforms(SvgRenderer renderer)
+ /// The to be transformed.
+ protected internal virtual bool PushTransforms(ISvgRenderer renderer)
{
_graphicsMatrix = renderer.Transform;
- _graphicsClip = renderer.Clip;
+ _graphicsClip = renderer.GetClip();
// Return if there are no transforms
if (this.Transforms == null || this.Transforms.Count == 0)
@@ -283,10 +283,10 @@ namespace Svg
}
///
- /// Removes any previously applied transforms from the specified .
+ /// Removes any previously applied transforms from the specified .
///
- /// The that should have transforms removed.
- protected internal virtual void PopTransforms(SvgRenderer renderer)
+ /// The that should have transforms removed.
+ protected internal virtual void PopTransforms(ISvgRenderer renderer)
{
renderer.Transform = _graphicsMatrix;
_graphicsMatrix = null;
@@ -295,19 +295,19 @@ namespace Svg
}
///
- /// Applies the required transforms to .
+ /// Applies the required transforms to .
///
- /// The to be transformed.
- void ISvgTransformable.PushTransforms(SvgRenderer renderer)
+ /// The to be transformed.
+ void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
{
this.PushTransforms(renderer);
}
///
- /// Removes any previously applied transforms from the specified .
+ /// Removes any previously applied transforms from the specified .
///
- /// The that should have transforms removed.
- void ISvgTransformable.PopTransforms(SvgRenderer renderer)
+ /// The that should have transforms removed.
+ void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
{
this.PopTransforms(renderer);
}
@@ -344,6 +344,17 @@ namespace Svg
}
}
+ ///
+ /// Gets or sets the text anchor.
+ ///
+ /// The text anchor.
+ [SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
+ public virtual XmlSpaceHandling SpaceHandling
+ {
+ get { return (this.Attributes["space"] == null) ? XmlSpaceHandling.inherit : (XmlSpaceHandling)this.Attributes["space"]; }
+ set { this.Attributes["space"] = value; }
+ }
+
public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action logElementOldIDNewID = null)
{
// Don't do anything if it hasn't changed
@@ -469,10 +480,10 @@ namespace Svg
///
- /// Renders this element to the .
+ /// Renders this element to the .
///
- /// The that the element should use to render itself.
- public void RenderElement(SvgRenderer renderer)
+ /// The that the element should use to render itself.
+ public void RenderElement(ISvgRenderer renderer)
{
this.Render(renderer);
}
@@ -632,10 +643,10 @@ namespace Svg
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- protected virtual void Render(SvgRenderer renderer)
+ /// The object to render to.
+ protected virtual void Render(ISvgRenderer renderer)
{
this.PushTransforms(renderer);
this.RenderChildren(renderer);
@@ -645,8 +656,8 @@ namespace Svg
///
/// Renders the children of this .
///
- /// The to render the child s to.
- protected virtual void RenderChildren(SvgRenderer renderer)
+ /// The to render the child s to.
+ protected virtual void RenderChildren(ISvgRenderer renderer)
{
foreach (SvgElement element in this.Children)
{
@@ -655,10 +666,10 @@ namespace Svg
}
///
- /// Renders the and contents to the specified object.
+ /// Renders the and contents to the specified object.
///
- /// The object to render to.
- void ISvgElement.Render(SvgRenderer renderer)
+ /// The object to render to.
+ void ISvgElement.Render(ISvgRenderer renderer)
{
this.Render(renderer);
}
@@ -698,7 +709,7 @@ namespace Svg
///
///
///
- protected GraphicsPath GetPaths(SvgElement elem, SvgRenderer renderer)
+ protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
{
var ret = new GraphicsPath();
@@ -1117,6 +1128,6 @@ namespace Svg
SvgElementCollection Children { get; }
IList Nodes { get; }
- void Render(SvgRenderer renderer);
+ void Render(ISvgRenderer renderer);
}
}
\ No newline at end of file
diff --git a/Source/SvgElementFactory.cs b/Source/SvgElementFactory.cs
index 8f812bf25234184fccf77ef6f2e5f7737268ed95..aaef9ddfba478d03c55d1af82799393daf19118b 100644
--- a/Source/SvgElementFactory.cs
+++ b/Source/SvgElementFactory.cs
@@ -15,7 +15,6 @@ namespace Svg
internal class SvgElementFactory
{
private static List availableElements;
- private const string svgNS = "http://www.w3.org/2000/svg";
private static Parser cssParser = new Parser();
///
@@ -84,7 +83,7 @@ namespace Svg
//Trace.TraceInformation("Begin CreateElement: {0}", elementName);
- if (elementNS == svgNS)
+ if (elementNS == SvgAttributeAttribute.SvgNamespace || string.IsNullOrEmpty(elementNS))
{
if (elementName == "svg")
{
diff --git a/Source/SvgElementIdManager.cs b/Source/SvgElementIdManager.cs
index a2d92b08c61ee1a0e5e018f2c49ad38eada1924a..a4d10b787517ef89391d04849efeab19b4082bcb 100644
--- a/Source/SvgElementIdManager.cs
+++ b/Source/SvgElementIdManager.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
+using System.Net;
+using System.IO;
namespace Svg
{
@@ -39,6 +41,29 @@ namespace Svg
public virtual SvgElement GetElementById(Uri uri)
{
+ if (!uri.IsAbsoluteUri && this._document.BaseUri != null && !uri.ToString().StartsWith("#"))
+ {
+ var fullUri = new Uri(this._document.BaseUri, uri);
+ var hash = fullUri.OriginalString.Substring(fullUri.OriginalString.LastIndexOf('#'));
+ SvgDocument doc;
+ switch (fullUri.Scheme.ToLowerInvariant())
+ {
+ case "file":
+ doc = SvgDocument.Open(fullUri.LocalPath.Substring(0, fullUri.LocalPath.Length - hash.Length));
+ return doc.IdManager.GetElementById(hash);
+ case "http":
+ case "https":
+ var httpRequest = WebRequest.Create(uri);
+ using (WebResponse webResponse = httpRequest.GetResponse())
+ {
+ doc = SvgDocument.Open(webResponse.GetResponseStream());
+ return doc.IdManager.GetElementById(hash);
+ }
+ default:
+ throw new NotSupportedException();
+ }
+
+ }
return this.GetElementById(uri.ToString());
}
diff --git a/Source/SvgRenderer.cs b/Source/SvgRenderer.cs
deleted file mode 100644
index 8ed934cb55d190cd7f94cff09941efd0e15b6a4c..0000000000000000000000000000000000000000
--- a/Source/SvgRenderer.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Text;
-
-namespace Svg
-{
- ///
- /// Convenience wrapper around a graphics object
- ///
- public sealed class SvgRenderer : IDisposable
- {
- private Graphics _innerGraphics;
- private Stack _boundables = new Stack();
-
- public void Boundable(ISvgBoundable boundable)
- {
- _boundables.Push(boundable);
- }
- public ISvgBoundable Boundable()
- {
- return _boundables.Peek();
- }
- public ISvgBoundable PopBoundable()
- {
- return _boundables.Pop();
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- private SvgRenderer()
- {
- }
-
- public Region Clip
- {
- get { return this._innerGraphics.Clip; }
- set { this._innerGraphics.Clip = value; }
- }
-
- ///
- /// Creates a new from the specified .
- ///
- /// from which to create the new .
- public static SvgRenderer FromImage(Image image)
- {
- SvgRenderer renderer = new SvgRenderer();
- renderer._innerGraphics = Graphics.FromImage(image);
- return renderer;
- }
-
- ///
- /// Creates a new from the specified .
- ///
- /// The to create the renderer from.
- public static SvgRenderer FromGraphics(Graphics graphics)
- {
- SvgRenderer renderer = new SvgRenderer();
- renderer._innerGraphics = graphics;
- 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)
- {
- this._innerGraphics.DrawImageUnscaled(image, location);
- }
-
- public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit)
- {
- _innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit);
- }
-
- public void AddClip(Region region)
- {
- this._innerGraphics.SetClip(region, CombineMode.Intersect);
- }
- public void SetClip(Region region)
- {
- this._innerGraphics.SetClip(region, CombineMode.Replace);
- }
-
- public void FillPath(Brush brush, GraphicsPath path)
- {
- this._innerGraphics.FillPath(brush, path);
- }
-
- public void DrawPath(Pen pen, GraphicsPath 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)
- {
- this._innerGraphics.TranslateTransform(dx, dy, order);
- }
-
- public void TranslateTransform(float dx, float dy)
- {
- this.TranslateTransform(dx, dy, MatrixOrder.Append);
- }
-
- public void ScaleTransform(float sx, float sy, MatrixOrder order)
- {
- this._innerGraphics.ScaleTransform(sx, sy, order);
- }
-
- public void ScaleTransform(float sx, float sy)
- {
- this.ScaleTransform(sx, sy, MatrixOrder.Append);
- }
-
- public SmoothingMode SmoothingMode
- {
- get { return this._innerGraphics.SmoothingMode; }
- set { this._innerGraphics.SmoothingMode = value; }
- }
-
- public PixelOffsetMode PixelOffsetMode
- {
- get { return this._innerGraphics.PixelOffsetMode; }
- set { this._innerGraphics.PixelOffsetMode = value; }
- }
-
- public CompositingQuality CompositingQuality
- {
- get { return this._innerGraphics.CompositingQuality; }
- set { this._innerGraphics.CompositingQuality = value; }
- }
-
- public TextRenderingHint TextRenderingHint
- {
- get { return this._innerGraphics.TextRenderingHint; }
- set { this._innerGraphics.TextRenderingHint = value; }
- }
-
- public int TextContrast
- {
- get { return this._innerGraphics.TextContrast; }
- set { this._innerGraphics.TextContrast = value; }
- }
-
- public Matrix Transform
- {
- get { return this._innerGraphics.Transform; }
- set { this._innerGraphics.Transform = value; }
- }
-
- public void Save()
- {
- this._innerGraphics.Save();
- }
-
- public void Dispose()
- {
- this._innerGraphics.Dispose();
- }
-
- public SizeF MeasureString(string text, Font font)
- {
- var ff = font.FontFamily;
- //Baseline calculation to match http://bobpowell.net/formattingtext.aspx
- float ascent = ff.GetCellAscent(font.Style);
- float baselineOffset = font.SizeInPoints / ff.GetEmHeight(font.Style) * ascent;
- float baselineOffsetPixels = this._innerGraphics.DpiY / 72f * baselineOffset;
-
- StringFormat format = StringFormat.GenericTypographic;
- format.SetMeasurableCharacterRanges(new CharacterRange[]{new CharacterRange(0, text.Length)});
- format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
- Region[] r = this._innerGraphics.MeasureCharacterRanges(text, font, new Rectangle(0, 0, 1000, 1000), format);
- RectangleF rect = r[0].GetBounds(this._innerGraphics);
-
- return new SizeF(rect.Width, baselineOffsetPixels);
- }
- }
-}
\ No newline at end of file
diff --git a/Source/SvgTextReader.cs b/Source/SvgTextReader.cs
index 7ef556cb6f48222de0963acd7891059ab74b9a14..b04f48c9b8d6b89c6edde782801badb54a4f4d17 100644
--- a/Source/SvgTextReader.cs
+++ b/Source/SvgTextReader.cs
@@ -123,7 +123,6 @@ namespace Svg
{
const string entityText = "
+ /// http://stackoverflow.com/questions/3633000/net-enumerate-winforms-font-styles
+ ///
+ public class FontFamily
+ {
+ #region InstalledFont Parameters
+
+ string _fontName = string.Empty;
+ string _fontSubFamily = string.Empty;
+ string _fontPath = string.Empty;
+
+ #endregion
+
+ #region InstalledFont Constructor
+
+ private FontFamily(string fontName, string fontSubFamily, string fontPath)
+ {
+ _fontName = fontName;
+ _fontSubFamily = fontSubFamily;
+ _fontPath = fontPath;
+ }
+
+ #endregion
+
+ #region InstalledFont Properties
+
+ public string FontName { get { return _fontName; } set { _fontName = value; } }
+ public string FontSubFamily { get { return _fontSubFamily; } set { _fontSubFamily = value; } }
+ public string FontPath { get { return _fontPath; } set { _fontPath = value; } }
+
+ #endregion
+
+ static public FontFamily FromPath(string fontFilePath)
+ {
+ string FontName = string.Empty;
+ string FontSubFamily = string.Empty;
+ string encStr = "UTF-8";
+ string strRet = string.Empty;
+
+ using (FileStream fs = new FileStream(fontFilePath, FileMode.Open, FileAccess.Read))
+ {
+
+ TT_OFFSET_TABLE ttOffsetTable = new TT_OFFSET_TABLE()
+ {
+ uMajorVersion = ReadUShort(fs),
+ uMinorVersion = ReadUShort(fs),
+ uNumOfTables = ReadUShort(fs),
+ uSearchRange = ReadUShort(fs),
+ uEntrySelector = ReadUShort(fs),
+ uRangeShift = ReadUShort(fs),
+ };
+
+ TT_TABLE_DIRECTORY tblDir = new TT_TABLE_DIRECTORY();
+ bool found = false;
+ for (int i = 0; i <= ttOffsetTable.uNumOfTables; i++)
+ {
+ tblDir = new TT_TABLE_DIRECTORY();
+ tblDir.Initialize();
+ fs.Read(tblDir.szTag, 0, tblDir.szTag.Length);
+ tblDir.uCheckSum = ReadULong(fs);
+ tblDir.uOffset = ReadULong(fs);
+ tblDir.uLength = ReadULong(fs);
+
+ Encoding enc = Encoding.GetEncoding(encStr);
+ string s = enc.GetString(tblDir.szTag);
+
+ if (s.CompareTo("name") == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) return null;
+
+ fs.Seek(tblDir.uOffset, SeekOrigin.Begin);
+
+ TT_NAME_TABLE_HEADER ttNTHeader = new TT_NAME_TABLE_HEADER
+ {
+ uFSelector = ReadUShort(fs),
+ uNRCount = ReadUShort(fs),
+ uStorageOffset = ReadUShort(fs)
+ };
+
+ TT_NAME_RECORD ttRecord = new TT_NAME_RECORD();
+
+ for (int j = 0; j <= ttNTHeader.uNRCount; j++)
+ {
+ ttRecord = new TT_NAME_RECORD()
+ {
+ uPlatformID = ReadUShort(fs),
+ uEncodingID = ReadUShort(fs),
+ uLanguageID = ReadUShort(fs),
+ uNameID = ReadUShort(fs),
+ uStringLength = ReadUShort(fs),
+ uStringOffset = ReadUShort(fs)
+ };
+
+ if (ttRecord.uNameID > 2) { break; }
+
+ long nPos = fs.Position;
+ fs.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, SeekOrigin.Begin);
+
+ byte[] buf = new byte[ttRecord.uStringLength];
+ fs.Read(buf, 0, ttRecord.uStringLength);
+
+ Encoding enc;
+ if (ttRecord.uEncodingID == 3 || ttRecord.uEncodingID == 1)
+ {
+ enc = Encoding.BigEndianUnicode;
+ }
+ else
+ {
+ enc = Encoding.UTF8;
+ }
+
+ strRet = enc.GetString(buf);
+ if (ttRecord.uNameID == 1) { FontName = strRet; }
+ if (ttRecord.uNameID == 2) { FontSubFamily = strRet; }
+
+ fs.Seek(nPos, SeekOrigin.Begin);
+ }
+
+ return new FontFamily(FontName, FontSubFamily, fontFilePath);
+ }
+ }
+
+ public struct TT_OFFSET_TABLE
+ {
+ public ushort uMajorVersion;
+ public ushort uMinorVersion;
+ public ushort uNumOfTables;
+ public ushort uSearchRange;
+ public ushort uEntrySelector;
+ public ushort uRangeShift;
+ }
+
+ public struct TT_TABLE_DIRECTORY
+ {
+ public byte[] szTag;
+ public UInt32 uCheckSum;
+ public UInt32 uOffset;
+ public UInt32 uLength;
+ public void Initialize()
+ {
+ szTag = new byte[4];
+ }
+ }
+
+ public struct TT_NAME_TABLE_HEADER
+ {
+ public ushort uFSelector;
+ public ushort uNRCount;
+ public ushort uStorageOffset;
+ }
+
+ public struct TT_NAME_RECORD
+ {
+ public ushort uPlatformID;
+ public ushort uEncodingID;
+ public ushort uLanguageID;
+ public ushort uNameID;
+ public ushort uStringLength;
+ public ushort uStringOffset;
+ }
+
+ static private UInt16 ReadChar(FileStream fs, int characters)
+ {
+ string[] s = new string[characters];
+ byte[] buf = new byte[Convert.ToByte(s.Length)];
+
+ buf = ReadAndSwap(fs, buf.Length);
+ return BitConverter.ToUInt16(buf, 0);
+ }
+
+ static private UInt16 ReadByte(FileStream fs)
+ {
+ byte[] buf = new byte[11];
+ buf = ReadAndSwap(fs, buf.Length);
+ return BitConverter.ToUInt16(buf, 0);
+ }
+
+ static private UInt16 ReadUShort(FileStream fs)
+ {
+ byte[] buf = new byte[2];
+ buf = ReadAndSwap(fs, buf.Length);
+ return BitConverter.ToUInt16(buf, 0);
+ }
+
+ static private UInt32 ReadULong(FileStream fs)
+ {
+ byte[] buf = new byte[4];
+ buf = ReadAndSwap(fs, buf.Length);
+ return BitConverter.ToUInt32(buf, 0);
+ }
+
+ static private byte[] ReadAndSwap(FileStream fs, int size)
+ {
+ byte[] buf = new byte[size];
+ fs.Read(buf, 0, buf.Length);
+ Array.Reverse(buf);
+ return buf;
+ }
+ }
+
+ class Program
+ {
+ //static void Main(string[] args)
+ //{
+ // System.Drawing.FontFamily fam;
+ // var allInstalledFonts = from e in Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", false).GetValueNames()
+ // select Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts").GetValue(e);
+
+ // var ttfFonts = from e in allInstalledFonts.Where(e => (e.ToString().EndsWith(".ttf") || e.ToString().EndsWith(".otf"))) select e;
+ // var ttfFontsPaths = from e in ttfFonts.Select(e => (Path.GetPathRoot(e.ToString()) == "") ? Environment.GetFolderPath(Environment.SpecialFolder.Fonts) + "\\" + e.ToString() : e.ToString()) select e;
+ // var fonts = from e in ttfFontsPaths.Select(e => GetFontDetails(e.ToString())) select e;
+
+ // foreach (InstalledFont f in fonts)
+ // {
+ // if (f != null)
+ // Console.WriteLine("Name: " + f.FontName + ", SubFamily: " + f.FontSubFamily + ", Path: " + f.FontPath);
+ // }
+
+ // Console.ReadLine();
+ //}
+
+
+ }
+}
diff --git a/Source/Text/GdiFontDefn.cs b/Source/Text/GdiFontDefn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ef8d99ddd4830008d49bbbae3eeaf64021d747e8
--- /dev/null
+++ b/Source/Text/GdiFontDefn.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace Svg
+{
+ public class GdiFontDefn : IFontDefn
+ {
+ private Font _font;
+
+ public float Size
+ {
+ get { return _font.Size; }
+ }
+ public float SizeInPoints
+ {
+ get { return _font.SizeInPoints; }
+ }
+
+ public GdiFontDefn(Font font)
+ {
+ _font = font;
+ }
+
+ public void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location)
+ {
+ path.AddString(text, _font.FontFamily, (int)_font.Style, _font.Size, location, StringFormat.GenericTypographic);
+ }
+
+ //Baseline calculation to match http://bobpowell.net/formattingtext.aspx
+ public float Ascent(ISvgRenderer renderer)
+ {
+ var ff = _font.FontFamily;
+ float ascent = ff.GetCellAscent(_font.Style);
+ float baselineOffset = _font.SizeInPoints / ff.GetEmHeight(_font.Style) * ascent;
+ return renderer.DpiY / 72f * baselineOffset;
+ }
+
+ public IList MeasureCharacters(ISvgRenderer renderer, string text)
+ {
+ var g = GetGraphics(renderer);
+ var regions = new List();
+ StringFormat format;
+ for (int s = 0; s <= (text.Length - 1) / 32; s++)
+ {
+ format = StringFormat.GenericTypographic;
+ format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
+ format.SetMeasurableCharacterRanges((from r in Enumerable.Range(32 * s, Math.Min(32, text.Length - 32 * s))
+ select new CharacterRange(r, 1)).ToArray());
+ regions.AddRange(from r in g.MeasureCharacterRanges(text, _font, new Rectangle(0, 0, 1000, 1000), format)
+ select r.GetBounds(g));
+ }
+ return regions;
+ }
+
+ public SizeF MeasureString(ISvgRenderer renderer, string text)
+ {
+ var g = GetGraphics(renderer);
+ StringFormat format = StringFormat.GenericTypographic;
+ format.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, text.Length) });
+ format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
+ Region[] r = g.MeasureCharacterRanges(text, _font, new Rectangle(0, 0, 1000, 1000), format);
+ RectangleF rect = r[0].GetBounds(g);
+
+ return new SizeF(rect.Width, Ascent(renderer));
+ }
+
+ private static Graphics _graphics;
+ private static Graphics GetGraphics(object renderer)
+ {
+ var provider = renderer as IGraphicsProvider;
+ if (provider == null)
+ {
+ if (_graphics == null)
+ {
+ var bmp = new Bitmap(1, 1);
+ _graphics = Graphics.FromImage(bmp);
+ }
+ return _graphics;
+ }
+ else
+ {
+ return provider.GetGraphics();
+ }
+ }
+ }
+}
diff --git a/Source/Text/IFontDefn.cs b/Source/Text/IFontDefn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3b53896d3826835fcb2f3535422d06804968ecd7
--- /dev/null
+++ b/Source/Text/IFontDefn.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace Svg
+{
+ public interface IFontDefn
+ {
+ float Size { get; }
+ float SizeInPoints { get; }
+ void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location);
+ float Ascent(ISvgRenderer renderer);
+ IList MeasureCharacters(ISvgRenderer renderer, string text);
+ SizeF MeasureString(ISvgRenderer renderer, string text);
+ }
+}
diff --git a/Source/Text/PathStatistics.cs b/Source/Text/PathStatistics.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a02d968a2a3a13e75b53bf4f6265b8afd7657780
--- /dev/null
+++ b/Source/Text/PathStatistics.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing.Drawing2D;
+using System.Drawing;
+
+namespace Svg
+{
+ public class PathStatistics
+ {
+ private const double GqBreak_TwoPoint = 0.57735026918962573;
+ private const double GqBreak_ThreePoint = 0.7745966692414834;
+ private const double GqBreak_FourPoint_01 = 0.33998104358485631;
+ private const double GqBreak_FourPoint_02 = 0.86113631159405257;
+ private const double GqWeight_FourPoint_01 = 0.65214515486254621;
+ private const double GqWeight_FourPoint_02 = 0.34785484513745385;
+
+ private PathData _data;
+ private double _totalLength;
+ private List _segments = new List();
+
+ public double TotalLength { get { return _totalLength; } }
+
+ public PathStatistics(PathData data)
+ {
+ _data = data;
+ int i = 1;
+ _totalLength = 0;
+ ISegment newSegment;
+ while (i < _data.Points.Length)
+ {
+ switch (_data.Types[i])
+ {
+ case 1:
+ newSegment = new LineSegment(_data.Points[i - 1], _data.Points[i]);
+ i++;
+ break;
+ case 3:
+ newSegment = new CubicBezierSegment(_data.Points[i - 1], _data.Points[i], _data.Points[i + 1], _data.Points[i + 2]);
+ i+= 3;
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ newSegment.StartOffset = _totalLength;
+ _segments.Add(newSegment);
+ _totalLength += newSegment.Length;
+ }
+ }
+
+
+ public void LocationAngleAtOffset(double offset, out PointF point, out float angle)
+ {
+ _segments[BinarySearchForSegment(offset, 0, _segments.Count - 1)].LocationAngleAtOffset(offset, out point, out angle);
+ }
+ public bool OffsetOnPath(double offset)
+ {
+ var seg = _segments[BinarySearchForSegment(offset, 0, _segments.Count - 1)];
+ offset -= seg.StartOffset;
+ return (offset >= 0 && offset <= seg.Length);
+ }
+
+ private int BinarySearchForSegment(double offset, int first, int last)
+ {
+ if (last == first)
+ {
+ return first;
+ }
+ else if ((last - first) == 1)
+ {
+ return (offset >= _segments[last].StartOffset ? last : first);
+ }
+ else
+ {
+ var mid = (last + first) / 2;
+ if (offset < _segments[mid].StartOffset)
+ {
+ return BinarySearchForSegment(offset, first, mid);
+ }
+ else
+ {
+ return BinarySearchForSegment(offset, mid, last);
+ }
+ }
+ }
+
+ private interface ISegment
+ {
+ double StartOffset { get; set; }
+ double Length { get; }
+ void LocationAngleAtOffset(double offset, out PointF point, out float rotation);
+ }
+
+ private class LineSegment : ISegment
+ {
+ private double _length;
+ private double _rotation;
+ private PointF _start;
+ private PointF _end;
+
+ public double StartOffset { get; set; }
+ public double Length { get { return _length; } }
+
+ public LineSegment(PointF start, PointF end)
+ {
+ _start = start;
+ _end = end;
+ _length = Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
+ _rotation = Math.Atan2(end.Y - start.Y, end.X - start.X) * 180 / Math.PI;
+ }
+
+ public void LocationAngleAtOffset(double offset, out PointF point, out float rotation)
+ {
+ offset -= StartOffset;
+ if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException();
+ point = new PointF((float)(_start.X + (offset / _length) * (_end.X - _start.X)),
+ (float)(_start.Y + (offset / _length) * (_end.Y - _start.Y)));
+ rotation = (float)_rotation;
+ }
+ }
+
+ private class CubicBezierSegment : ISegment
+ {
+ private PointF _p0;
+ private PointF _p1;
+ private PointF _p2;
+ private PointF _p3;
+ private double _length;
+ private Func _integral;
+ private SortedList _lengths = new SortedList();
+
+ public double StartOffset { get; set; }
+ public double Length { get { return _length; } }
+
+ public CubicBezierSegment(PointF p0, PointF p1, PointF p2, PointF p3)
+ {
+ _p0 = p0;
+ _p1 = p1;
+ _p2 = p2;
+ _p3 = p3;
+ _integral = (t) => CubicBezierArcLengthIntegrand(_p0, _p1, _p2, _p3, t);
+ _length = GetLength(0, 1, 0.00000001f);
+ _lengths.Add(0, 0);
+ _lengths.Add(_length, 1);
+ }
+
+ private double GetLength(double left, double right, double epsilon)
+ {
+ var fullInt = GaussianQuadrature(_integral, left, right, 4);
+ return Subdivide(left, right, fullInt, 0, epsilon);
+ }
+ private double Subdivide(double left, double right, double fullInt, double totalLength, double epsilon)
+ {
+ var mid = (left + right) / 2;
+ var leftValue = GaussianQuadrature(_integral, left, mid, 4);
+ var rightValue = GaussianQuadrature(_integral, mid, right, 4);
+ if (Math.Abs(fullInt - (leftValue + rightValue)) > epsilon) {
+ var leftSub = Subdivide(left, mid, leftValue, totalLength, epsilon / 2.0);
+ totalLength += leftSub;
+ AddElementToTable(mid, totalLength);
+ return Subdivide(mid, right, rightValue, totalLength, epsilon / 2.0) + leftSub;
+ }
+ else
+ {
+ return leftValue + rightValue;
+ }
+ }
+ private void AddElementToTable(double position, double totalLength)
+ {
+ _lengths.Add(totalLength, position);
+ }
+
+ public void LocationAngleAtOffset(double offset, out PointF point, out float rotation)
+ {
+ offset -= StartOffset;
+ if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException();
+
+ var t = BinarySearchForParam(offset, 0, _lengths.Count - 1);
+ point = CubicBezierCurve(_p0, _p1, _p2, _p3, t);
+ var deriv = CubicBezierDerivative(_p0, _p1, _p2, _p3, t);
+ rotation = (float)(Math.Atan2(deriv.Y, deriv.X) * 180.0 / Math.PI);
+ }
+ private double BinarySearchForParam(double length, int first, int last)
+ {
+ if (last == first)
+ {
+ return _lengths.Values[last];
+ }
+ else if ((last - first) == 1)
+ {
+ return _lengths.Values[first] + (_lengths.Values[last] - _lengths.Values[first]) *
+ (length - _lengths.Keys[first]) / (_lengths.Keys[last] - _lengths.Keys[first]);
+ }
+ else
+ {
+ var mid = (last + first) / 2;
+ if (length < _lengths.Keys[mid])
+ {
+ return BinarySearchForParam(length, first, mid);
+ }
+ else
+ {
+ return BinarySearchForParam(length, mid, last);
+ }
+ }
+ }
+
+ ///
+ /// Evaluates the integral of the function over the integral using the specified number of points
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double GaussianQuadrature(Func func, double a, double b, int points)
+ {
+ switch (points)
+ {
+ case 1:
+ return (b - a) * func.Invoke((a + b) / 2.0);
+ case 2:
+ return (b - a) / 2.0 * (func.Invoke((b - a) / 2.0 * -1 * GqBreak_TwoPoint + (a + b) / 2.0) +
+ func.Invoke((b - a) / 2.0 * GqBreak_TwoPoint + (a + b) / 2.0));
+ case 3:
+ return (b - a) / 2.0 * (5.0 / 9 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_ThreePoint + (a + b) / 2.0) +
+ 8.0 / 9 * func.Invoke((a + b) / 2.0) +
+ 5.0 / 9 * func.Invoke((b - a) / 2.0 * GqBreak_ThreePoint + (a + b) / 2.0));
+ case 4:
+ return (b - a) / 2.0 * (GqWeight_FourPoint_01 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_FourPoint_01 + (a + b) / 2.0) +
+ GqWeight_FourPoint_01 * func.Invoke((b - a) / 2.0 * GqBreak_FourPoint_01 + (a + b) / 2.0) +
+ GqWeight_FourPoint_02 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_FourPoint_02 + (a + b) / 2.0) +
+ GqWeight_FourPoint_02 * func.Invoke((b - a) / 2.0 * GqBreak_FourPoint_02 + (a + b) / 2.0));
+ }
+ throw new NotSupportedException();
+ }
+
+ /// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+ private PointF CubicBezierCurve(PointF p0, PointF p1, PointF p2, PointF p3, double t)
+ {
+ return new PointF((float)(Math.Pow(1 - t, 3) * p0.X + 3 * Math.Pow(1 - t, 2) * t * p1.X +
+ 3 * (1 - t) * Math.Pow(t, 2) * p2.X + Math.Pow(t, 3) * p3.X),
+ (float)(Math.Pow(1 - t, 3) * p0.Y + 3 * Math.Pow(1 - t, 2) * t * p1.Y +
+ 3 * (1 - t) * Math.Pow(t, 2) * p2.Y + Math.Pow(t, 3) * p3.Y));
+ }
+
+ /// http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html
+ private PointF CubicBezierDerivative(PointF p0, PointF p1, PointF p2, PointF p3, double t)
+ {
+ return new PointF((float)(3 * Math.Pow(1 - t, 2) * (p1.X - p0.X) + 6 * (1 - t) * t * (p2.X - p1.X) + 3 * Math.Pow(t, 2) * (p3.X - p2.X)),
+ (float)(3 * Math.Pow(1 - t, 2) * (p1.Y - p0.Y) + 6 * (1 - t) * t * (p2.Y - p1.Y) + 3 * Math.Pow(t, 2) * (p3.Y - p2.Y)));
+ }
+
+
+ private double CubicBezierArcLengthIntegrand(PointF p0, PointF p1, PointF p2, PointF p3, double t)
+ {
+ return Math.Sqrt(Math.Pow(3 * Math.Pow(1 - t, 2) * (p1.X - p0.X) + 6 * (1 - t) * t * (p2.X - p1.X) + 3 * Math.Pow(t, 2) * (p3.X - p2.X), 2) +
+ Math.Pow(3 * Math.Pow(1 - t, 2) * (p1.Y - p0.Y) + 6 * (1 - t) * t * (p2.Y - p1.Y) + 3 * Math.Pow(t, 2) * (p3.Y - p2.Y), 2));
+ }
+ }
+ }
+}
diff --git a/Source/Text/SvgFont.cs b/Source/Text/SvgFont.cs
new file mode 100644
index 0000000000000000000000000000000000000000..da38bc73da1ec53d566201ce48de901e5b8395d6
--- /dev/null
+++ b/Source/Text/SvgFont.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("font")]
+ public class SvgFont : SvgElement
+ {
+ [SvgAttribute("horiz-adv-x")]
+ public float HorizAdvX
+ {
+ get { return (this.Attributes["horiz-adv-x"] == null ? 0 : (float)this.Attributes["horiz-adv-x"]); }
+ set { this.Attributes["horiz-adv-x"] = value; }
+ }
+ [SvgAttribute("horiz-origin-x")]
+ public float HorizOriginX
+ {
+ get { return (this.Attributes["horiz-origin-x"] == null ? 0 : (float)this.Attributes["horiz-origin-x"]); }
+ set { this.Attributes["horiz-origin-x"] = value; }
+ }
+ [SvgAttribute("horiz-origin-y")]
+ public float HorizOriginY
+ {
+ get { return (this.Attributes["horiz-origin-y"] == null ? 0 : (float)this.Attributes["horiz-origin-y"]); }
+ set { this.Attributes["horiz-origin-y"] = value; }
+ }
+ [SvgAttribute("vert-adv-y")]
+ public float VertAdvY
+ {
+ get { return (this.Attributes["vert-adv-y"] == null ? this.Children.OfType().First().UnitsPerEm : (float)this.Attributes["vert-adv-y"]); }
+ set { this.Attributes["vert-adv-y"] = value; }
+ }
+ [SvgAttribute("vert-origin-x")]
+ public float VertOriginX
+ {
+ get { return (this.Attributes["vert-origin-x"] == null ? this.HorizAdvX / 2 : (float)this.Attributes["vert-origin-x"]); }
+ set { this.Attributes["vert-origin-x"] = value; }
+ }
+ [SvgAttribute("vert-origin-y")]
+ public float VertOriginY
+ {
+ get { return (this.Attributes["vert-origin-y"] == null ?
+ (this.Children.OfType().First().Attributes["ascent"] == null ? 0 : this.Children.OfType().First().Ascent) :
+ (float)this.Attributes["vert-origin-y"]); }
+ set { this.Attributes["vert-origin-y"] = value; }
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ }
+}
diff --git a/Source/Text/SvgFontDefn.cs b/Source/Text/SvgFontDefn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d26f3b6963d8607bd1253ace5a843d128333962a
--- /dev/null
+++ b/Source/Text/SvgFontDefn.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace Svg
+{
+ public class SvgFontDefn : IFontDefn
+ {
+ private SvgFont _font;
+ private float _emScale;
+ private float _ppi;
+ private float _size;
+ private Dictionary _glyphs;
+ private Dictionary _kerning;
+
+ public float Size
+ {
+ get { return _size; }
+ }
+
+ public float SizeInPoints
+ {
+ get { return _size * 72.0f / _ppi; }
+ }
+
+ public SvgFontDefn (SvgFont font, float size, float ppi)
+ {
+ _font = font;
+ _size = size;
+ _ppi = ppi;
+ var face = _font.Children.OfType().First();
+ _emScale = _size / face.UnitsPerEm;
+ }
+
+ public float Ascent(ISvgRenderer renderer)
+ {
+ float ascent = _font.Descendants().OfType().First().Ascent;
+ float baselineOffset = this.SizeInPoints * (_emScale / _size) * ascent;
+ return renderer.DpiY / 72f * baselineOffset;
+ }
+
+ public IList MeasureCharacters(ISvgRenderer renderer, string text)
+ {
+ var result = new List();
+ GetPath(renderer, text, result, false);
+ return result;
+ }
+
+ public System.Drawing.SizeF MeasureString(ISvgRenderer renderer, string text)
+ {
+ var result = new List();
+ GetPath(renderer, text, result, true);
+ var nonEmpty = result.Where(r => r != RectangleF.Empty);
+ if (!nonEmpty.Any()) return SizeF.Empty;
+ return new SizeF(nonEmpty.Last().Right - nonEmpty.First().Left, Ascent(renderer));
+ }
+
+ public void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location)
+ {
+ var textPath = GetPath(renderer, text, null, false);
+ if (textPath.PointCount > 0)
+ {
+ var translate = new Matrix();
+ translate.Translate(location.X, location.Y);
+ textPath.Transform(translate);
+ path.AddPath(textPath, false);
+ }
+ }
+
+ private GraphicsPath GetPath(ISvgRenderer renderer, string text, IList ranges, bool measureSpaces)
+ {
+ EnsureDictionaries();
+
+ RectangleF bounds;
+ SvgGlyph glyph;
+ SvgKern kern;
+ GraphicsPath path;
+ SvgGlyph prevGlyph = null;
+ Matrix scaleMatrix;
+ float xPos = 0;
+
+ var ascent = Ascent(renderer);
+
+ var result = new GraphicsPath();
+ if (string.IsNullOrEmpty(text)) return result;
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (!_glyphs.TryGetValue(text.Substring(i, 1), out glyph)) glyph = _font.Descendants().OfType().First();
+ if (prevGlyph != null && _kerning.TryGetValue(prevGlyph.GlyphName + "|" + glyph.GlyphName, out kern))
+ {
+ xPos -= kern.Kerning * _emScale;
+ }
+ path = (GraphicsPath)glyph.Path(renderer).Clone();
+ scaleMatrix = new Matrix();
+ scaleMatrix.Scale(_emScale, -1 * _emScale, MatrixOrder.Append);
+ scaleMatrix.Translate(xPos, ascent, MatrixOrder.Append);
+ path.Transform(scaleMatrix);
+
+ bounds = path.GetBounds();
+ if (ranges != null)
+ {
+ if (measureSpaces && bounds == RectangleF.Empty)
+ {
+ ranges.Add(new RectangleF(xPos, 0, glyph.HorizAdvX * _emScale, ascent));
+ }
+ else
+ {
+ ranges.Add(bounds);
+ }
+ }
+ if (path.PointCount > 0) result.AddPath(path, false);
+
+ xPos += glyph.HorizAdvX * _emScale;
+ prevGlyph = glyph;
+ }
+
+ return result;
+ }
+
+ private void EnsureDictionaries()
+ {
+ if (_glyphs == null) _glyphs = _font.Descendants().OfType().ToDictionary(g => g.Unicode ?? g.GlyphName ?? g.ID);
+ if (_kerning == null) _kerning = _font.Descendants().OfType().ToDictionary(k => k.Glyph1 + "|" + k.Glyph2);
+ }
+ }
+}
diff --git a/Source/Text/SvgFontFace.cs b/Source/Text/SvgFontFace.cs
new file mode 100644
index 0000000000000000000000000000000000000000..09aacaef46462b62d8c661327490ff047b2c96e9
--- /dev/null
+++ b/Source/Text/SvgFontFace.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("font-face")]
+ public class SvgFontFace : SvgElement
+ {
+ [SvgAttribute("alphabetic")]
+ public float Alphabetic
+ {
+ get { return (this.Attributes["alphabetic"] == null ? 0 : (float)this.Attributes["alphabetic"]); }
+ set { this.Attributes["alphabetic"] = value; }
+ }
+
+ [SvgAttribute("ascent")]
+ public float Ascent
+ {
+ get { return (this.Attributes["ascent"] == null ? this.UnitsPerEm - ((SvgFont)this.Parent).VertOriginY : (float)this.Attributes["ascent"]); }
+ set { this.Attributes["ascent"] = value; }
+ }
+
+ [SvgAttribute("ascent-height")]
+ public float AscentHeight
+ {
+ get { return (this.Attributes["ascent-height"] == null ? this.Ascent : (float)this.Attributes["ascent-height"]); }
+ set { this.Attributes["ascent-height"] = value; }
+ }
+
+ [SvgAttribute("descent")]
+ public float Descent
+ {
+ get { return (this.Attributes["descent"] == null ? ((SvgFont)this.Parent).VertOriginY : (float)this.Attributes["descent"]); }
+ set { this.Attributes["descent"] = value; }
+ }
+
+ ///
+ /// Indicates which font family is to be used to render the text.
+ ///
+ [SvgAttribute("font-family")]
+ public virtual string FontFamily
+ {
+ get { return this.Attributes["font-family"] as string; }
+ set { this.Attributes["font-family"] = value; }
+ }
+
+ ///
+ /// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
+ ///
+ [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; }
+ }
+
+ ///
+ /// Refers to the style of the font.
+ ///
+ [SvgAttribute("font-style")]
+ public virtual SvgFontStyle FontStyle
+ {
+ get { return (this.Attributes["font-style"] == null) ? SvgFontStyle.inherit : (SvgFontStyle)this.Attributes["font-style"]; }
+ set { this.Attributes["font-style"] = value; }
+ }
+
+ ///
+ /// Refers to the varient of the font.
+ ///
+ [SvgAttribute("font-variant")]
+ public virtual SvgFontVariant FontVariant
+ {
+ get { return (this.Attributes["font-variant"] == null) ? SvgFontVariant.inherit : (SvgFontVariant)this.Attributes["font-variant"]; }
+ set { this.Attributes["font-variant"] = value; }
+ }
+
+ ///
+ /// Refers to the boldness of the font.
+ ///
+ [SvgAttribute("font-weight")]
+ public virtual SvgFontWeight FontWeight
+ {
+ get { return (this.Attributes["font-weight"] == null) ? SvgFontWeight.inherit : (SvgFontWeight)this.Attributes["font-weight"]; }
+ set { this.Attributes["font-weight"] = value; }
+ }
+
+ [SvgAttribute("panose-1")]
+ public string Panose1
+ {
+ get { return this.Attributes["panose-1"] as string; }
+ set { this.Attributes["panose-1"] = value; }
+ }
+
+ [SvgAttribute("units-per-em")]
+ public float UnitsPerEm
+ {
+ get { return (this.Attributes["units-per-em"] == null ? 1000 : (float)this.Attributes["units-per-em"]); }
+ set { this.Attributes["units-per-em"] = value; }
+ }
+
+ [SvgAttribute("x-height")]
+ public float XHeight
+ {
+ get { return (this.Attributes["x-height"] == null ? float.MinValue : (float)this.Attributes["x-height"]); }
+ set { this.Attributes["x-height"] = value; }
+ }
+
+
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ }
+}
diff --git a/Source/Text/SvgFontFaceSrc.cs b/Source/Text/SvgFontFaceSrc.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5b0fdb6852d07a3bee2cc9819340b5ec3679fd6a
--- /dev/null
+++ b/Source/Text/SvgFontFaceSrc.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("font-face-src")]
+ public class SvgFontFaceSrc : SvgElement
+ {
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ }
+}
diff --git a/Source/Text/SvgFontFaceUri.cs b/Source/Text/SvgFontFaceUri.cs
new file mode 100644
index 0000000000000000000000000000000000000000..58d155d6aedbb321f7eb9cf46a9eee82be629020
--- /dev/null
+++ b/Source/Text/SvgFontFaceUri.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("font-face-uri")]
+ public class SvgFontFaceUri : SvgElement
+ {
+ private Uri _referencedElement;
+
+ [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
+ public virtual Uri ReferencedElement
+ {
+ get { return this._referencedElement; }
+ set { this._referencedElement = value; }
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return DeepCopy();
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ var newObj = base.DeepCopy() as SvgFontFaceUri;
+ newObj.ReferencedElement = this.ReferencedElement;
+
+ return newObj;
+ }
+ }
+}
diff --git a/Source/Text/SvgGlyph.cs b/Source/Text/SvgGlyph.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bd1045b7b1b27911c3424c16e46424db665ad1c8
--- /dev/null
+++ b/Source/Text/SvgGlyph.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Svg.Pathing;
+using System.Drawing.Drawing2D;
+
+namespace Svg
+{
+ [SvgElement("glyph")]
+ public class SvgGlyph : SvgVisualElement
+ {
+ private GraphicsPath _path;
+
+ ///
+ /// Gets or sets a of path data.
+ ///
+ [SvgAttribute("d")]
+ public SvgPathSegmentList PathData
+ {
+ get { return this.Attributes.GetAttribute("d"); }
+ set { this.Attributes["d"] = value; }
+ }
+
+ [SvgAttribute("glyph-name")]
+ public virtual string GlyphName
+ {
+ get { return this.Attributes["glyph-name"] as string; }
+ set { this.Attributes["glyph-name"] = value; }
+ }
+ [SvgAttribute("horiz-adv-x")]
+ public float HorizAdvX
+ {
+ get { return (this.Attributes["horiz-adv-x"] == null ? this.Parents.OfType().First().HorizAdvX : (float)this.Attributes["horiz-adv-x"]); }
+ set { this.Attributes["horiz-adv-x"] = value; }
+ }
+ [SvgAttribute("unicode")]
+ public string Unicode
+ {
+ get { return this.Attributes["unicode"] as string; }
+ set { this.Attributes["unicode"] = value; }
+ }
+ [SvgAttribute("vert-adv-y")]
+ public float VertAdvY
+ {
+ get { return (this.Attributes["vert-adv-y"] == null ? this.Parents.OfType().First().VertAdvY : (float)this.Attributes["vert-adv-y"]); }
+ set { this.Attributes["vert-adv-y"] = value; }
+ }
+ [SvgAttribute("vert-origin-x")]
+ public float VertOriginX
+ {
+ get { return (this.Attributes["vert-origin-x"] == null ? this.Parents.OfType().First().VertOriginX : (float)this.Attributes["vert-origin-x"]); }
+ set { this.Attributes["vert-origin-x"] = value; }
+ }
+ [SvgAttribute("vert-origin-y")]
+ public float VertOriginY
+ {
+ get { return (this.Attributes["vert-origin-y"] == null ? this.Parents.OfType().First().VertOriginY : (float)this.Attributes["vert-origin-y"]); }
+ set { this.Attributes["vert-origin-y"] = value; }
+ }
+
+
+ ///
+ /// Gets the for this element.
+ ///
+ public override GraphicsPath Path(ISvgRenderer renderer)
+ {
+ if (this._path == null || this.IsPathDirty)
+ {
+ _path = new GraphicsPath();
+
+ foreach (SvgPathSegment segment in this.PathData)
+ {
+ segment.AddToPath(_path);
+ }
+
+ this.IsPathDirty = false;
+ }
+ return _path;
+ }
+
+
+ ///
+ /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
+ ///
+ protected override bool RequiresSmoothRendering
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Gets the bounds of the element.
+ ///
+ /// The bounds.
+ public override System.Drawing.RectangleF Bounds
+ {
+ get { return this.Path(null).GetBounds(); }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SvgGlyph()
+ {
+ var pathData = new SvgPathSegmentList();
+ this.Attributes["d"] = pathData;
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return DeepCopy();
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ var newObj = base.DeepCopy() as SvgGlyph;
+ foreach (var pathData in this.PathData)
+ newObj.PathData.Add(pathData.Clone());
+ return newObj;
+
+ }
+ }
+}
diff --git a/Source/Text/SvgKern.cs b/Source/Text/SvgKern.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bdc5bd1128a4d6c763b2178e2d644e87e46773f6
--- /dev/null
+++ b/Source/Text/SvgKern.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ public abstract class SvgKern : SvgElement
+ {
+ [SvgAttribute("g1")]
+ public string Glyph1
+ {
+ get { return this.Attributes["g1"] as string; }
+ set { this.Attributes["g1"] = value; }
+ }
+ [SvgAttribute("g2")]
+ public string Glyph2
+ {
+ get { return this.Attributes["g2"] as string; }
+ set { this.Attributes["g2"] = value; }
+ }
+ [SvgAttribute("u1")]
+ public string Unicode1
+ {
+ get { return this.Attributes["u1"] as string; }
+ set { this.Attributes["u1"] = value; }
+ }
+ [SvgAttribute("u2")]
+ public string Unicode2
+ {
+ get { return this.Attributes["u2"] as string; }
+ set { this.Attributes["u2"] = value; }
+ }
+ [SvgAttribute("k")]
+ public float Kerning
+ {
+ get { return (this.Attributes["k"] == null ? 0 : (float)this.Attributes["k"]); }
+ set { this.Attributes["k"] = value; }
+ }
+ }
+
+ [SvgElement("vkern")]
+ public class SvgVerticalKern : SvgKern
+ {
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ }
+ [SvgElement("hkern")]
+ public class SvgHorizontalKern : SvgKern
+ {
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+ }
+}
diff --git a/Source/Text/SvgMissingGlyph.cs b/Source/Text/SvgMissingGlyph.cs
new file mode 100644
index 0000000000000000000000000000000000000000..22173acf58628ecc544e167e8167cecf8ea33d19
--- /dev/null
+++ b/Source/Text/SvgMissingGlyph.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("missing-glyph")]
+ public class SvgMissingGlyph : SvgGlyph
+ {
+ [SvgAttribute("glyph-name")]
+ public override string GlyphName
+ {
+ get { return this.Attributes["glyph-name"] as string ?? "__MISSING_GLYPH__"; }
+ set { this.Attributes["glyph-name"] = value; }
+ }
+ }
+}
diff --git a/Source/Text/SvgTextBase.cs b/Source/Text/SvgTextBase.cs
index 8729f8ed35e036ba2f7c2e5bdaaf9a3e75076459..7e90db6e184b18653b893ef62f99e27d46c52337 100644
--- a/Source/Text/SvgTextBase.cs
+++ b/Source/Text/SvgTextBase.cs
@@ -11,34 +11,15 @@ using System.Linq;
namespace Svg
{
- public enum XmlSpaceHandling
- {
- @default,
- preserve
- }
-
public abstract class SvgTextBase : SvgVisualElement
{
- private SvgUnitCollection _x = new SvgUnitCollection();
- private SvgUnitCollection _y = new SvgUnitCollection();
- private SvgUnitCollection _dy = new SvgUnitCollection();
- private SvgUnitCollection _dx = new SvgUnitCollection();
- private SvgUnit _letterSpacing;
- private SvgUnit _wordSpacing;
- private static readonly SvgRenderer _stringMeasure;
+ protected SvgUnitCollection _x = new SvgUnitCollection();
+ protected SvgUnitCollection _y = new SvgUnitCollection();
+ protected SvgUnitCollection _dy = new SvgUnitCollection();
+ protected SvgUnitCollection _dx = new SvgUnitCollection();
+ private string _rotate;
+ private List _rotations = new List();
- private XmlSpaceHandling _space = XmlSpaceHandling.@default;
-
- ///
- /// Initializes the class.
- ///
- static SvgTextBase()
- {
- Bitmap bitmap = new Bitmap(1, 1);
- _stringMeasure = SvgRenderer.FromImage(bitmap);
- _stringMeasure.TextRenderingHint = TextRenderingHint.AntiAlias;
- }
-
///
/// Gets or sets the text to be rendered.
///
@@ -66,6 +47,12 @@ namespace Svg
set { this.Attributes["baseline-shift"] = value; this.IsPathDirty = true; }
}
+ public override XmlSpaceHandling SpaceHandling
+ {
+ get { return base.SpaceHandling; }
+ set { base.SpaceHandling = value; this.IsPathDirty = true; }
+ }
+
///
/// Gets or sets the X.
///
@@ -142,14 +129,56 @@ namespace Svg
}
}
+ ///
+ /// Gets or sets the rotate.
+ ///
+ /// The rotate.
+ [SvgAttribute("rotate")]
+ public virtual string Rotate
+ {
+ get { return this._rotate; }
+ set
+ {
+ if (_rotate != value)
+ {
+ this._rotate = value;
+ this._rotations.Clear();
+ this._rotations.AddRange(from r in _rotate.Split(new char[] {',', ' ', '\r', '\n', '\t'}, StringSplitOptions.RemoveEmptyEntries) select float.Parse(r));
+ this.IsPathDirty = true;
+ OnAttributeChanged(new AttributeEventArgs { Attribute = "rotate", Value = value });
+ }
+ }
+ }
+
+ ///
+ /// The pre-calculated length of the text
+ ///
+ [SvgAttribute("textLength")]
+ public virtual SvgUnit TextLength
+ {
+ get { return (this.Attributes["textLength"] == null ? SvgUnit.None : (SvgUnit)this.Attributes["textLength"]); }
+ set { this.Attributes["textLength"] = value; this.IsPathDirty = true; }
+ }
+
+ ///
+ /// Gets or sets the text anchor.
+ ///
+ /// The text anchor.
+ [SvgAttribute("lengthAdjust")]
+ public virtual SvgTextLengthAdjust LengthAdjust
+ {
+ get { return (this.Attributes["lengthAdjust"] == null) ? SvgTextLengthAdjust.spacing : (SvgTextLengthAdjust)this.Attributes["lengthAdjust"]; }
+ set { this.Attributes["lengthAdjust"] = value; this.IsPathDirty = true; }
+ }
+
///
/// Specifies spacing behavior between text characters.
///
[SvgAttribute("letter-spacing")]
public virtual SvgUnit LetterSpacing
{
- get { return this._letterSpacing; }
- set { this._letterSpacing = value; this.IsPathDirty = true; }
+ get { return (this.Attributes["letter-spacing"] == null ? SvgUnit.None : (SvgUnit)this.Attributes["letter-spacing"]); }
+ set { this.Attributes["letter-spacing"] = value; this.IsPathDirty = true; }
}
///
@@ -158,8 +187,8 @@ namespace Svg
[SvgAttribute("word-spacing")]
public virtual SvgUnit WordSpacing
{
- get { return this._wordSpacing; }
- set { this._wordSpacing = value; this.IsPathDirty = true; }
+ get { return (this.Attributes["word-spacing"] == null ? SvgUnit.None : (SvgUnit)this.Attributes["word-spacing"]); }
+ set { this.Attributes["word-spacing"] = value; this.IsPathDirty = true; }
}
///
@@ -215,9 +244,9 @@ namespace Svg
///
/// Renders the and contents to the specified object.
///
- /// The object to render to.
+ /// The object to render to.
/// Necessary to make sure that any internal tspan elements get rendered as well
- protected override void Render(SvgRenderer renderer)
+ protected override void Render(ISvgRenderer renderer)
{
if ((this.Path(renderer) != null) && this.Visible && this.Displayable)
{
@@ -245,192 +274,135 @@ namespace Svg
}
}
- private GraphicsPath _path;
-
- protected class NodeBounds
+ internal virtual IEnumerable GetContentNodes()
{
- public float xOffset { get; set; }
- public SizeF Bounds { get; set; }
- public ISvgNode Node { get; set; }
+ return (this.Nodes == null || this.Nodes.Count < 1 ? this.Children.OfType() : this.Nodes);
}
- protected class BoundsData
+ protected virtual GraphicsPath GetBaselinePath(ISvgRenderer renderer)
{
- private List _nodes = new List();
- public IList Nodes
- {
- get { return _nodes; }
- }
- public SizeF Bounds { get; set; }
+ return null;
}
- protected BoundsData GetTextBounds(SvgRenderer renderer)
+ protected virtual float GetAuthorPathLength()
{
- var font = GetFont(renderer);
- SvgTextBase innerText;
- SizeF stringBounds;
- float totalHeight = 0;
- float totalWidth = 0;
-
- var result = new BoundsData();
- var nodes = (from n in this.Nodes
- where (n is SvgContentNode || n is SvgTextBase) && !string.IsNullOrEmpty(n.Content)
- select n).ToList();
-
- // Individual character spacing
- if (nodes.FirstOrDefault() is SvgContentNode && _x.Count > 1)
- {
- string ch;
- var content = nodes.First() as SvgContentNode;
- nodes.RemoveAt(0);
- int posCount = Math.Min(content.Content.Length, _x.Count);
- var text = PrepareText(content.Content, false, (nodes.Count > 1 && nodes[1] is SvgTextBase));
-
- for (var i = 0; i < posCount; i++)
- {
- ch = (i == posCount - 1 ? text.Substring(i) : text.Substring(i, 1));
- stringBounds = _stringMeasure.MeasureString(ch, font);
- totalHeight = Math.Max(totalHeight, stringBounds.Height);
- result.Nodes.Add(new NodeBounds()
- {
- Bounds = stringBounds,
- Node = new SvgContentNode() { Content = ch },
- xOffset = (i == 0 ? 0 : _x[i].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) -
- _x[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this))
- });
- }
- }
-
- // Calculate the bounds of the text
- ISvgNode node;
- var accumulateDims = true;
- for (var i = 0; i < nodes.Count; i++)
- {
- node = nodes[i];
- lock (_stringMeasure)
- {
- innerText = node as SvgTextBase;
- if (innerText == null)
- {
- stringBounds = _stringMeasure.MeasureString(PrepareText(node.Content,
- i > 0 && nodes[i - 1] is SvgTextBase,
- i < nodes.Count - 1 && nodes[i + 1] is SvgTextBase), font);
- result.Nodes.Add(new NodeBounds() { Bounds = stringBounds, Node = node, xOffset = totalWidth });
- }
- else
- {
- stringBounds = innerText.GetTextBounds(renderer).Bounds;
- result.Nodes.Add(new NodeBounds() { Bounds = stringBounds, Node = node, xOffset = totalWidth });
- accumulateDims = accumulateDims && SvgUnitCollection.IsNullOrEmpty(innerText.X) && SvgUnitCollection.IsNullOrEmpty(innerText.Y);
- if (accumulateDims && innerText.Dx.Count == 1) totalWidth += innerText.Dx[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
- }
-
- if (accumulateDims)
- {
- totalHeight = Math.Max(totalHeight, stringBounds.Height);
- totalWidth += stringBounds.Width;
- }
- }
- }
- result.Bounds = new SizeF(totalWidth, totalHeight);
- return result;
+ return 0;
}
- protected float _calcX = 0;
- protected float _calcY = 0;
+ private GraphicsPath _path;
///
/// Gets the for this element.
///
///
- public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
+ public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
// Make sure the path is always null if there is no text
//if there is a TSpan inside of this text element then path should not be null (even if this text is empty!)
- if ((string.IsNullOrEmpty(this.Text) || this.Text.Trim().Length < 1) && this.Children.Where(x => x is SvgTextSpan).Select(x => x as SvgTextSpan).Count() == 0)
- return _path = null;
- //NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF?
- // When an empty string is passed to GraphicsPath, it rises an InvalidArgumentException...
-
+ var nodes = this.GetContentNodes().ToList();
+ if (nodes.Count < 1) return _path = null;
+ if (nodes.Count == 1 && nodes[0] is SvgContentNode &&
+ (string.IsNullOrEmpty(nodes[0].Content) || nodes[0].Content.Trim().Length < 1)) return _path = null;
+
if (_path == null || this.IsPathDirty)
{
renderer = (renderer ?? SvgRenderer.FromNull());
- // Measure the overall bounds of all the text
- var boundsData = GetTextBounds(renderer);
-
- var font = GetFont(renderer);
- SvgTextBase innerText;
- float x = (_x.Count < 1 ? _calcX : _x[0].ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this)) +
- (_dx.Count < 1 ? 0 : _dx[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this));
- float y = (_y.Count < 1 ? _calcY : _y[0].ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this)) +
- (_dy.Count < 1 ? 0 : _dy[0].ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
+ this.SetPath(new TextDrawingState(renderer, this));
+ }
+ return _path;
+ }
- _path = new GraphicsPath();
- _path.StartFigure();
+ private void SetPath(TextDrawingState state)
+ {
+ SetPath(state, true);
+ }
- // Determine the location of the start point
- switch (this.TextAnchor)
+ ///
+ /// Sets the path on this element and all child elements. Uses the state
+ /// object to track the state of the drawing
+ ///
+ /// State of the drawing operation
+ private void SetPath(TextDrawingState state, bool doMeasurements)
+ {
+ SvgTextBase inner;
+ TextDrawingState newState;
+ TextDrawingState origState = null;
+ bool alignOnBaseline = state.BaselinePath != null && (this.TextAnchor == SvgTextAnchor.Middle || this.TextAnchor == SvgTextAnchor.End);
+ if (doMeasurements)
+ {
+ if (this.TextLength != SvgUnit.None)
+ {
+ origState = state.Clone();
+ }
+ else if (alignOnBaseline)
{
- case SvgTextAnchor.Middle:
- x -= (boundsData.Bounds.Width / 2);
- break;
- case SvgTextAnchor.End:
- x -= boundsData.Bounds.Width;
- break;
+ origState = state.Clone();
+ state.BaselinePath = null;
}
+ }
- try
+ foreach (var node in GetContentNodes())
+ {
+ inner = node as SvgTextBase;
+ if (inner == null)
{
- renderer.Boundable(new FontBoundable(font));
- switch (this.BaselineShift)
- {
- case null:
- case "":
- case "baseline":
- case "inherit":
- // do nothing
- break;
- case "sub":
- y += new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- break;
- case "super":
- y -= new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- break;
- default:
- var convert = new SvgUnitConverter();
- var shift = (SvgUnit)convert.ConvertFromInvariantString(this.BaselineShift);
- y -= shift.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
- break;
- }
+ if (!string.IsNullOrEmpty(node.Content)) state.DrawString(PrepareText(node.Content));
}
- finally
+ else
{
- renderer.PopBoundable();
+ newState = new TextDrawingState(state, inner);
+ inner.SetPath(newState);
+ state.NumChars += newState.NumChars;
+ state.Current = newState.Current;
}
+ }
+
+ var path = state.GetPath() ?? new GraphicsPath();
- NodeBounds data;
- var yCummOffset = 0.0f;
- for (var i = 0; i < boundsData.Nodes.Count; i++)
+ // Apply any text length adjustments
+ if (doMeasurements)
+ {
+ if (this.TextLength != SvgUnit.None)
+ {
+ var bounds = path.GetBounds();
+ var specLength = this.TextLength.ToDeviceValue(state.Renderer, UnitRenderingType.Horizontal, this);
+ var actLength = bounds.Width;
+ var diff = (actLength - specLength);
+ if (Math.Abs(diff) > 1.5)
+ {
+ if (this.LengthAdjust == SvgTextLengthAdjust.spacing)
+ {
+ origState.LetterSpacingAdjust = -1 * diff / (state.NumChars - origState.NumChars - 1);
+ SetPath(origState, false);
+ return;
+ }
+ else
+ {
+ var matrix = new Matrix();
+ matrix.Translate(-1 * bounds.X, 0, MatrixOrder.Append);
+ matrix.Scale(specLength / actLength, 1, MatrixOrder.Append);
+ matrix.Translate(bounds.X, 0, MatrixOrder.Append);
+ path.Transform(matrix);
+ }
+ }
+ }
+ else if (alignOnBaseline)
{
- data = boundsData.Nodes[i];
- innerText = data.Node as SvgTextBase;
- if (innerText == null)
+ var bounds = path.GetBounds();
+ if (this.TextAnchor == SvgTextAnchor.Middle)
{
- // Minus FontSize because the x/y coords mark the bottom left, not bottom top.
- DrawString(renderer, _path, x + data.xOffset, y - boundsData.Bounds.Height, font,
- PrepareText(data.Node.Content, i > 0 && boundsData.Nodes[i - 1].Node is SvgTextBase,
- i < boundsData.Nodes.Count - 1 && boundsData.Nodes[i + 1].Node is SvgTextBase));
+ origState.StartOffsetAdjust = -1 * bounds.Width / 2;
}
else
{
- innerText._calcX = x + data.xOffset;
- innerText._calcY = y + yCummOffset;
- if (innerText.Dy.Count == 1) yCummOffset += innerText.Dy[0].ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
+ origState.StartOffsetAdjust = -1 * bounds.Width;
}
+ SetPath(origState, false);
+ return;
}
-
- _path.CloseFigure();
- this.IsPathDirty = false;
}
- return _path;
+
+
+ _path = path;
+ this.IsPathDirty = false;
}
private static readonly Regex MultipleSpaces = new Regex(@" {2,}", RegexOptions.Compiled);
@@ -440,67 +412,19 @@ namespace Svg
///
/// Text to be prepared
/// Prepared text
- protected string PrepareText(string value, bool leadingSpace, bool trailingSpace)
+ protected string PrepareText(string value)
{
- if (_space == XmlSpaceHandling.preserve)
+ if (this.SpaceHandling == XmlSpaceHandling.preserve)
{
return value.Replace('\t', ' ').Replace("\r\n", " ").Replace('\r', ' ').Replace('\n', ' ');
}
else
{
var convValue = MultipleSpaces.Replace(value.Replace("\r", "").Replace("\n", "").Replace('\t', ' '), " ");
- //if (!leadingSpace) convValue = convValue.TrimStart();
- //if (!trailingSpace) convValue = convValue.TrimEnd();
return convValue;
}
}
- ///
- /// Draws a string on a path at a specified location and with a specified font.
- ///
- internal void DrawString(SvgRenderer renderer, GraphicsPath path, float x, float y, Font font, string text)
- {
- PointF location = new PointF(x, y);
-
- // No way to do letter-spacing or word-spacing, so do manually
- if (this.LetterSpacing.Value > 0.0f || this.WordSpacing.Value > 0.0f)
- {
- // Cut up into words, or just leave as required
- string[] words = (this.WordSpacing.Value > 0.0f) ? text.Split(' ') : new string[] { text };
- float wordSpacing = this.WordSpacing.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
- float letterSpacing = this.LetterSpacing.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
- float start = x;
-
- foreach (string word in words)
- {
- // Only do if there is line spacing, just write the word otherwise
- if (this.LetterSpacing.Value > 0.0f)
- {
- char[] characters = word.ToCharArray();
- foreach (char currentCharacter in characters)
- {
- path.AddString(currentCharacter.ToString(), font.FontFamily, (int)font.Style, font.Size, location, StringFormat.GenericTypographic);
- location = new PointF(path.GetBounds().Width + start + letterSpacing, location.Y);
- }
- }
- else
- {
- path.AddString(word, font.FontFamily, (int)font.Style, font.Size, location, StringFormat.GenericTypographic);
- }
-
- // Move the location of the word to be written along
- location = new PointF(path.GetBounds().Width + start + wordSpacing, location.Y);
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(text))
- {
- path.AddString(text, font.FontFamily, (int)font.Style, font.Size, location, StringFormat.GenericTypographic);
- }
- }
- }
-
[SvgAttribute("onchange")]
public event EventHandler Change;
@@ -519,6 +443,23 @@ namespace Svg
}
}
+
+
+ //private static GraphicsPath GetPath(string text, Font font)
+ //{
+ // var fontMetrics = (from c in text.Distinct()
+ // select new { Char = c, Metrics = Metrics(c, font) }).
+ // ToDictionary(c => c.Char, c=> c.Metrics);
+ // // Measure each character and check the metrics against the overall metrics of rendering
+ // // an entire word with kerning.
+ //}
+ //private static RectangleF Metrics(char c, Font font)
+ //{
+ // var path = new GraphicsPath();
+ // path.AddString(c.ToString(), font.FontFamily, (int)font.Style, font.Size, new Point(0, 0), StringFormat.GenericTypographic);
+ // return path.GetBounds();
+ //}
+
#if Net4
public override void RegisterEvents(ISvgEventCaller caller)
{
@@ -542,11 +483,17 @@ namespace Svg
private class FontBoundable : ISvgBoundable
{
- private Font _font;
+ private IFontDefn _font;
+ private float _width = 1;
- public FontBoundable(Font font)
+ public FontBoundable(IFontDefn font)
+ {
+ _font = font;
+ }
+ public FontBoundable(IFontDefn font, float width)
{
_font = font;
+ _width = width;
}
public PointF Location
@@ -556,7 +503,7 @@ namespace Svg
public SizeF Size
{
- get { return new SizeF(1, _font.Size); }
+ get { return new SizeF(_width, _font.Size); }
}
public RectangleF Bounds
@@ -564,5 +511,402 @@ namespace Svg
get { return new RectangleF(this.Location, this.Size); }
}
}
+
+ private class TextDrawingState
+ {
+ private float _xAnchor = float.MinValue;
+ private IList _anchoredPaths = new List();
+ private GraphicsPath _currPath = null;
+ private GraphicsPath _finalPath = null;
+ private float _authorPathLength = 0;
+
+ public GraphicsPath BaselinePath { get; set; }
+ public PointF Current { get; set; }
+ public SvgTextBase Element { get; set; }
+ public float LetterSpacingAdjust { get; set; }
+ public int NumChars { get; set; }
+ public TextDrawingState Parent { get; set; }
+ public ISvgRenderer Renderer { get; set; }
+ public float StartOffsetAdjust { get; set; }
+
+ private TextDrawingState() { }
+ public TextDrawingState(ISvgRenderer renderer, SvgTextBase element)
+ {
+ this.Element = element;
+ this.Renderer = renderer;
+ this.Current = PointF.Empty;
+ _xAnchor = 0;
+ this.BaselinePath = element.GetBaselinePath(renderer);
+ _authorPathLength = element.GetAuthorPathLength();
+ }
+ public TextDrawingState(TextDrawingState parent, SvgTextBase element)
+ {
+ this.Element = element;
+ this.Renderer = parent.Renderer;
+ this.Parent = parent;
+ this.Current = parent.Current;
+ this.BaselinePath = element.GetBaselinePath(parent.Renderer) ?? parent.BaselinePath;
+ var currPathLength = element.GetAuthorPathLength();
+ _authorPathLength = currPathLength == 0 ? parent._authorPathLength : currPathLength;
+ }
+
+ public GraphicsPath GetPath()
+ {
+ FlushPath();
+ return _finalPath;
+ }
+
+ public TextDrawingState Clone()
+ {
+ var result = new TextDrawingState();
+ result._anchoredPaths = this._anchoredPaths.ToList();
+ result.BaselinePath = this.BaselinePath;
+ result._xAnchor = this._xAnchor;
+ result.Current = this.Current;
+ result.Element = this.Element;
+ result.NumChars = this.NumChars;
+ result.Parent = this.Parent;
+ result.Renderer = this.Renderer;
+ return result;
+ }
+
+ public void DrawString(string value)
+ {
+ // Get any defined anchors
+ var xAnchors = GetValues(value.Length, e => e._x, UnitRenderingType.HorizontalOffset);
+ var yAnchors = GetValues(value.Length, e => e._y, UnitRenderingType.VerticalOffset);
+ var font = this.Element.GetFont(this.Renderer);
+ var fontBaselineHeight = this.Renderer.FontBaselineOffset(font);
+ PathStatistics pathStats = null;
+ var pathScale = 1.0;
+ if (BaselinePath != null)
+ {
+ pathStats = new PathStatistics(BaselinePath.PathData);
+ if (_authorPathLength > 0) pathScale = _authorPathLength / pathStats.TotalLength;
+ }
+
+ // Get all of the offsets (explicit and defined by spacing)
+ IList xOffsets;
+ IList yOffsets;
+ IList rotations;
+ float baselineShift = 0.0f;
+
+ try
+ {
+ this.Renderer.SetBoundable(new FontBoundable(font, (float)(pathStats == null ? 1 : pathStats.TotalLength)));
+ xOffsets = GetValues(value.Length, e => e._dx, UnitRenderingType.Horizontal);
+ yOffsets = GetValues(value.Length, e => e._dy, UnitRenderingType.Vertical);
+ if (StartOffsetAdjust != 0.0f)
+ {
+ if (xOffsets.Count < 1)
+ {
+ xOffsets.Add(StartOffsetAdjust);
+ }
+ else
+ {
+ xOffsets[0] += StartOffsetAdjust;
+ }
+ }
+
+ if (this.Element.LetterSpacing.Value != 0.0f || this.Element.WordSpacing.Value != 0.0f || this.LetterSpacingAdjust != 0.0f)
+ {
+ var spacing = this.Element.LetterSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element) + this.LetterSpacingAdjust;
+ var wordSpacing = this.Element.WordSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element);
+ if (this.Parent == null && this.NumChars == 0 && xOffsets.Count < 1) xOffsets.Add(0);
+ for (int i = (this.Parent == null && this.NumChars == 0 ? 1 : 0); i < value.Length; i++)
+ {
+ if (i >= xOffsets.Count)
+ {
+ xOffsets.Add(spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0));
+ }
+ else
+ {
+ xOffsets[i] += spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0);
+ }
+ }
+ }
+
+ rotations = GetValues(value.Length, e => e._rotations);
+
+ // Calculate Y-offset due to baseline shift. Don't inherit the value so that it is not accumulated multiple times.
+ var baselineShiftText = this.Element.Attributes.GetAttribute("baseline-shift");
+
+ switch (baselineShiftText)
+ {
+ case null:
+ case "":
+ case "baseline":
+ case "inherit":
+ // do nothing
+ break;
+ case "sub":
+ baselineShift = new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
+ break;
+ case "super":
+ baselineShift = -1 * new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
+ break;
+ default:
+ var convert = new SvgUnitConverter();
+ var shiftUnit = (SvgUnit)convert.ConvertFromInvariantString(baselineShiftText);
+ baselineShift = -1 * shiftUnit.ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
+ break;
+ }
+
+ if (baselineShift != 0.0f)
+ {
+ if (yOffsets.Any())
+ {
+ yOffsets[0] += baselineShift;
+ }
+ else
+ {
+ yOffsets.Add(baselineShift);
+ }
+ }
+ }
+ finally
+ {
+ this.Renderer.PopBoundable();
+ }
+
+ // NOTE: Assuming a horizontal left-to-right font
+ // Render absolutely positioned items in the horizontal direction
+ var yPos = Current.Y;
+ for (int i = 0; i < xAnchors.Count - 1; i++)
+ {
+ FlushPath();
+ _xAnchor = xAnchors[i] + (xOffsets.Count > i ? xOffsets[i] : 0);
+ EnsurePath();
+ yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);
+
+ DrawStringOnCurrPath(value[i].ToString(), font, new PointF(_xAnchor, yPos),
+ fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
+ }
+
+ // Render any remaining characters
+ var renderChar = 0;
+ var xPos = this.Current.X;
+ if (xAnchors.Any())
+ {
+ FlushPath();
+ renderChar = xAnchors.Count - 1;
+ xPos = xAnchors.Last();
+ _xAnchor = xPos;
+ }
+ EnsurePath();
+
+
+ // Render individual characters as necessary
+ var lastIndividualChar = renderChar + Math.Max(Math.Max(Math.Max(Math.Max(xOffsets.Count, yOffsets.Count), yAnchors.Count), rotations.Count) - renderChar - 1, 0);
+ if (rotations.LastOrDefault() != 0.0f || pathStats != null) lastIndividualChar = value.Length;
+ if (lastIndividualChar > renderChar)
+ {
+ var charBounds = this.Renderer.MeasureCharacters(value.Substring(renderChar, Math.Min(lastIndividualChar + 1, value.Length) - renderChar), font);
+ PointF pathPoint;
+ float rotation;
+ float halfWidth;
+ for (int i = renderChar; i < lastIndividualChar; i++)
+ {
+ xPos += (float)pathScale * (xOffsets.Count > i ? xOffsets[i] : 0) + (charBounds[i - renderChar].X - (i == renderChar ? 0 : charBounds[i - renderChar - 1].X));
+ yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);
+ if (pathStats == null)
+ {
+ DrawStringOnCurrPath(value[i].ToString(), font, new PointF(xPos, yPos),
+ fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
+ }
+ else
+ {
+ xPos = Math.Max(xPos, 0);
+ halfWidth = charBounds[i-renderChar].Width / 2;
+ if (pathStats.OffsetOnPath(xPos + halfWidth))
+ {
+ pathStats.LocationAngleAtOffset(xPos + halfWidth, out pathPoint, out rotation);
+ pathPoint = new PointF((float)(pathPoint.X - halfWidth * Math.Cos(rotation * Math.PI / 180) - (float)pathScale * yPos * Math.Sin(rotation * Math.PI / 180)),
+ (float)(pathPoint.Y - halfWidth * Math.Sin(rotation * Math.PI / 180) + (float)pathScale * yPos * Math.Cos(rotation * Math.PI / 180)));
+ DrawStringOnCurrPath(value[i].ToString(), font, pathPoint, fontBaselineHeight, rotation);
+ }
+ }
+ }
+
+ // Add the kerning to the next character
+ if (lastIndividualChar < value.Length)
+ {
+ xPos += charBounds[charBounds.Count - 1].X - charBounds[charBounds.Count - 2].X;
+ }
+ else
+ {
+ xPos += charBounds.Last().Width;
+ }
+ }
+
+ // Render the string normally
+ if (lastIndividualChar < value.Length)
+ {
+ xPos += (xOffsets.Count > lastIndividualChar ? xOffsets[lastIndividualChar] : 0);
+ yPos = (yAnchors.Count > lastIndividualChar ? yAnchors[lastIndividualChar] : yPos) +
+ (yOffsets.Count > lastIndividualChar ? yOffsets[lastIndividualChar] : 0);
+ DrawStringOnCurrPath(value.Substring(lastIndividualChar), font, new PointF(xPos, yPos),
+ fontBaselineHeight, rotations.LastOrDefault());
+ var bounds = this.Renderer.MeasureString(value.Substring(lastIndividualChar), font);
+ xPos += bounds.Width;
+ }
+
+
+ NumChars += value.Length;
+ // Undo any baseline shift. This is not persisted, unlike normal vertical offsets.
+ this.Current = new PointF(xPos, yPos - baselineShift);
+ }
+
+ private void DrawStringOnCurrPath(string value, IFontDefn font, PointF location, float fontBaselineHeight, float rotation)
+ {
+ var drawPath = _currPath;
+ if (rotation != 0.0f) drawPath = new GraphicsPath();
+ font.AddStringToPath(this.Renderer, drawPath, value, new PointF(location.X, location.Y - fontBaselineHeight));
+ if (rotation != 0.0f && drawPath.PointCount > 0)
+ {
+ var matrix = new Matrix();
+ matrix.Translate(-1 * location.X, -1 * location.Y, MatrixOrder.Append);
+ matrix.Rotate(rotation, MatrixOrder.Append);
+ matrix.Translate(location.X, location.Y, MatrixOrder.Append);
+ drawPath.Transform(matrix);
+ _currPath.AddPath(drawPath, false);
+ }
+
+ }
+
+ private void EnsurePath()
+ {
+ if (_currPath == null)
+ {
+ _currPath = new GraphicsPath();
+ _currPath.StartFigure();
+
+ var currState = this;
+ while (currState != null && currState._xAnchor <= float.MinValue)
+ {
+ currState = currState.Parent;
+ }
+ currState._anchoredPaths.Add(_currPath);
+ }
+ }
+
+ private void FlushPath()
+ {
+ if (_currPath != null)
+ {
+ _currPath.CloseFigure();
+
+ // Abort on empty paths (e.g. rendering a space)
+ if (_currPath.PointCount < 1)
+ {
+ _anchoredPaths.Clear();
+ _xAnchor = float.MinValue;
+ _currPath = null;
+ return;
+ }
+
+ if (_xAnchor > float.MinValue)
+ {
+ float minX = float.MaxValue;
+ float maxX = float.MinValue;
+ RectangleF bounds;
+ foreach (var path in _anchoredPaths)
+ {
+ bounds = path.GetBounds();
+ if (bounds.Left < minX) minX = bounds.Left;
+ if (bounds.Right > maxX) maxX = bounds.Right;
+ }
+
+ var xOffset = _xAnchor - minX;
+ switch (Element.TextAnchor)
+ {
+ case SvgTextAnchor.Middle:
+ xOffset -= (maxX - minX) / 2;
+ break;
+ case SvgTextAnchor.End:
+ xOffset -= (maxX - minX);
+ break;
+ }
+
+ if (xOffset != 0)
+ {
+ var matrix = new Matrix();
+ matrix.Translate(xOffset, 0);
+ foreach (var path in _anchoredPaths)
+ {
+ path.Transform(matrix);
+ }
+ }
+
+ _anchoredPaths.Clear();
+ _xAnchor = float.MinValue;
+
+ }
+
+ if (_finalPath == null)
+ {
+ _finalPath = _currPath;
+ }
+ else
+ {
+ _finalPath.AddPath(_currPath, false);
+ }
+
+ _currPath = null;
+ }
+ }
+
+ private IList GetValues(int maxCount, Func> listGetter)
+ {
+ var currState = this;
+ int charCount = 0;
+ var results = new List();
+ int resultCount = 0;
+
+ while (currState != null)
+ {
+ charCount += currState.NumChars;
+ results.AddRange(listGetter.Invoke(currState.Element).Skip(charCount).Take(maxCount));
+ if (results.Count > resultCount)
+ {
+ maxCount -= results.Count - resultCount;
+ charCount += results.Count - resultCount;
+ resultCount = results.Count;
+ }
+
+ if (maxCount < 1) return results;
+
+ currState = currState.Parent;
+ }
+
+ return results;
+ }
+ private IList GetValues(int maxCount, Func> listGetter, UnitRenderingType renderingType)
+ {
+ var currState = this;
+ int charCount = 0;
+ var results = new List();
+ int resultCount = 0;
+
+ while (currState != null)
+ {
+ charCount += currState.NumChars;
+ results.AddRange(listGetter.Invoke(currState.Element).Skip(charCount).Take(maxCount).Select(p => p.ToDeviceValue(currState.Renderer, renderingType, currState.Element)));
+ if (results.Count > resultCount)
+ {
+ maxCount -= results.Count - resultCount;
+ charCount += results.Count - resultCount;
+ resultCount = results.Count;
+ }
+
+ if (maxCount < 1) return results;
+
+ currState = currState.Parent;
+ }
+
+ return results;
+ }
+ }
+
}
}
diff --git a/Source/Text/SvgTextPath.cs b/Source/Text/SvgTextPath.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e80b866a3403c8a705c7a8226f7a4b07f1eaa0e0
--- /dev/null
+++ b/Source/Text/SvgTextPath.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Diagnostics;
+
+namespace Svg
+{
+ ///
+ /// The element defines a graphics element consisting of text.
+ ///
+ [SvgElement("textPath")]
+ public class SvgTextPath : SvgTextBase
+ {
+ private Uri _referencedPath;
+
+ public override SvgUnitCollection Dx
+ {
+ get { return null; }
+ set { /* do nothing */ }
+ }
+
+ [SvgAttribute("startOffset")]
+ public virtual SvgUnit StartOffset
+ {
+ get { return (_dx.Count < 1 ? SvgUnit.None : _dx[0]); }
+ set
+ {
+ if (_dx.Count < 1)
+ {
+ _dx.Add(value);
+ }
+ else
+ {
+ _dx[0] = value;
+ }
+ }
+ }
+
+ [SvgAttribute("method")]
+ public virtual SvgTextPathMethod Method
+ {
+ get { return (this.Attributes["method"] == null ? SvgTextPathMethod.align : (SvgTextPathMethod)this.Attributes["method"]); }
+ set { this.Attributes["method"] = value; }
+ }
+
+ [SvgAttribute("spacing")]
+ public virtual SvgTextPathSpacing Spacing
+ {
+ get { return (this.Attributes["spacing"] == null ? SvgTextPathSpacing.exact : (SvgTextPathSpacing)this.Attributes["spacing"]); }
+ set { this.Attributes["spacing"] = value; }
+ }
+
+ [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
+ public virtual Uri ReferencedPath
+ {
+ get { return this._referencedPath; }
+ set { this._referencedPath = value; }
+ }
+
+ protected override GraphicsPath GetBaselinePath(ISvgRenderer renderer)
+ {
+ var path = this.OwnerDocument.IdManager.GetElementById(this.ReferencedPath) as SvgVisualElement;
+ if (path == null) return null;
+ var pathData = (GraphicsPath)path.Path(renderer).Clone();
+ if (path.Transforms.Count > 0)
+ {
+ Matrix transformMatrix = new Matrix(1, 0, 0, 1, 0, 0);
+
+ foreach (var transformation in path.Transforms)
+ {
+ transformMatrix.Multiply(transformation.Matrix);
+ }
+
+ pathData.Transform(transformMatrix);
+ }
+ return pathData;
+ }
+ protected override float GetAuthorPathLength()
+ {
+ var path = this.OwnerDocument.IdManager.GetElementById(this.ReferencedPath) as SvgPath;
+ if (path == null) return 0;
+ return path.PathLength;
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return base.DeepCopy();
+ }
+
+
+
+
+ }
+}
diff --git a/Source/Text/SvgTextRef.cs b/Source/Text/SvgTextRef.cs
new file mode 100644
index 0000000000000000000000000000000000000000..45a878dba42c9f5fa7f23c144218aac4830acea7
--- /dev/null
+++ b/Source/Text/SvgTextRef.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Svg
+{
+ [SvgElement("tref")]
+ public class SvgTextRef : SvgTextBase
+ {
+ private Uri _referencedElement;
+
+ [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
+ public virtual Uri ReferencedElement
+ {
+ get { return this._referencedElement; }
+ set { this._referencedElement = value; }
+ }
+
+ internal override IEnumerable GetContentNodes()
+ {
+ var refText = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement) as SvgTextBase;
+ if (refText == null)
+ {
+ return base.GetContentNodes();
+ }
+ else
+ {
+ return refText.GetContentNodes();
+ }
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ return DeepCopy();
+ }
+
+ public override SvgElement DeepCopy()
+ {
+ var newObj = base.DeepCopy() as SvgTextRef;
+ newObj.X = this.X;
+ newObj.Y = this.Y;
+ newObj.Dx = this.Dx;
+ newObj.Dy = this.Dy;
+ newObj.Text = this.Text;
+ newObj.ReferencedElement = this.ReferencedElement;
+
+ return newObj;
+ }
+
+
+ }
+}
diff --git a/Source/Transforms/ISvgTransformable.cs b/Source/Transforms/ISvgTransformable.cs
index ed1a63e1952513b5d6f08be03fcf6d4f8447c082..348c049dc8c0fd11bc366ea0221525c43bf347c9 100644
--- a/Source/Transforms/ISvgTransformable.cs
+++ b/Source/Transforms/ISvgTransformable.cs
@@ -18,14 +18,14 @@ namespace Svg
///
SvgTransformCollection Transforms { get; set; }
///
- /// Applies the required transforms to .
+ /// Applies the required transforms to .
///
- /// The to be transformed.
- void PushTransforms(SvgRenderer renderer);
+ /// The to be transformed.
+ void PushTransforms(ISvgRenderer renderer);
///
- /// Removes any previously applied transforms from the specified .
+ /// Removes any previously applied transforms from the specified .
///
- /// The that should have transforms removed.
- void PopTransforms(SvgRenderer renderer);
+ /// The that should have transforms removed.
+ void PopTransforms(ISvgRenderer renderer);
}
}
\ No newline at end of file
diff --git a/Tests/SvgW3CTestRunner/View.cs b/Tests/SvgW3CTestRunner/View.cs
index c39a31dc7e583087f42363bb97a91e77b8b81948..c8faec4fe66951cccd436135d0783bf8a0b7b4fd 100644
--- a/Tests/SvgW3CTestRunner/View.cs
+++ b/Tests/SvgW3CTestRunner/View.cs
@@ -5,6 +5,7 @@ using System.Windows.Forms;
using System.Drawing;
using System.IO;
using Svg;
+using System.Diagnostics;
namespace SvgW3CTestRunner
{
diff --git a/Tests/W3CTestSuite/png/__issue-015-01.png b/Tests/W3CTestSuite/png/__issue-015-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d8dd4a83eed5886fe2e1a8cbaa06e59115e42e5
Binary files /dev/null and b/Tests/W3CTestSuite/png/__issue-015-01.png differ
diff --git a/Tests/W3CTestSuite/png/__issue-34-02.png b/Tests/W3CTestSuite/png/__issue-034-02.png
similarity index 100%
rename from Tests/W3CTestSuite/png/__issue-34-02.png
rename to Tests/W3CTestSuite/png/__issue-034-02.png
diff --git a/Tests/W3CTestSuite/png/__issue-036-01.png b/Tests/W3CTestSuite/png/__issue-036-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd2aefdbc4c98df85e9096a9dc4f59ac24d98585
Binary files /dev/null and b/Tests/W3CTestSuite/png/__issue-036-01.png differ
diff --git a/Tests/W3CTestSuite/png/__issue-064-01.png b/Tests/W3CTestSuite/png/__issue-064-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..125aad6afdda6a85f3fb48ba7fa7a2dca57145cf
Binary files /dev/null and b/Tests/W3CTestSuite/png/__issue-064-01.png differ
diff --git a/Tests/W3CTestSuite/png/__issue-064-02.png b/Tests/W3CTestSuite/png/__issue-064-02.png
new file mode 100644
index 0000000000000000000000000000000000000000..c014043602423404ab304c74f9c0c23519286a6d
Binary files /dev/null and b/Tests/W3CTestSuite/png/__issue-064-02.png differ
diff --git a/Tests/W3CTestSuite/png/__issue-83-01.png b/Tests/W3CTestSuite/png/__issue-083-01.png
similarity index 100%
rename from Tests/W3CTestSuite/png/__issue-83-01.png
rename to Tests/W3CTestSuite/png/__issue-083-01.png
diff --git a/Tests/W3CTestSuite/svg/__issue-015-01.svg b/Tests/W3CTestSuite/svg/__issue-015-01.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cdcceec13b76fba0e709e6eec5930b8de3c56d89
--- /dev/null
+++ b/Tests/W3CTestSuite/svg/__issue-015-01.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/Tests/W3CTestSuite/svg/__issue-34-02.svg b/Tests/W3CTestSuite/svg/__issue-034-02.svg
similarity index 100%
rename from Tests/W3CTestSuite/svg/__issue-34-02.svg
rename to Tests/W3CTestSuite/svg/__issue-034-02.svg
diff --git a/Tests/W3CTestSuite/svg/__issue-036-01.svg b/Tests/W3CTestSuite/svg/__issue-036-01.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cbd03734f4b4cb80859953111ec30c60f9bc4eb0
--- /dev/null
+++ b/Tests/W3CTestSuite/svg/__issue-036-01.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Tests/W3CTestSuite/svg/__issue-064-01.svg b/Tests/W3CTestSuite/svg/__issue-064-01.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a55bb613f56b68f3b3ed25029fe068656f097887
--- /dev/null
+++ b/Tests/W3CTestSuite/svg/__issue-064-01.svg
@@ -0,0 +1,42 @@
+
+
+
+
\ No newline at end of file
diff --git a/Tests/W3CTestSuite/svg/__issue-064-02.svg b/Tests/W3CTestSuite/svg/__issue-064-02.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f29eae3bc4094ae8aad39a4eb439f5717d9784fe
--- /dev/null
+++ b/Tests/W3CTestSuite/svg/__issue-064-02.svg
@@ -0,0 +1,57 @@
+
+
+
+
\ No newline at end of file
diff --git a/Tests/W3CTestSuite/svg/__issue-83-01.svg b/Tests/W3CTestSuite/svg/__issue-083-01.svg
similarity index 100%
rename from Tests/W3CTestSuite/svg/__issue-83-01.svg
rename to Tests/W3CTestSuite/svg/__issue-083-01.svg
diff --git a/Tests/W3CTestSuite/svg/text-path-02-b.svg b/Tests/W3CTestSuite/svg/text-path-02-b.svg
index 3513226c26728cd966a58f147355099b7925ed11..1e00df5013a3979476049528ba76ac74d5aef6c2 100644
--- a/Tests/W3CTestSuite/svg/text-path-02-b.svg
+++ b/Tests/W3CTestSuite/svg/text-path-02-b.svg
@@ -63,26 +63,26 @@
- Positive offset Negative offset
+ Negative offset
- Positive offset Negative offset
+ Negative offset
- Positive offset Negative offset
+ Positive offset
- Positive offset Negative offset
+ Positive offset