Commit 41581021 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge remote-tracking branch 'origin/master'

parents 79c12b63 220cac7f
......@@ -19,13 +19,9 @@ namespace Svg
public class SvgDocument : SvgFragment, ITypeDescriptorContext
{
public static readonly int PPI = 96;
private SvgElementIdManager _idManager;
/// <summary>
/// Gets a <see cref="string"/> containing the XLink namespace (http://www.w3.org/1999/xlink).
/// </summary>
public static readonly string XLinkNamespace = "http://www.w3.org/1999/xlink";
private SvgElementIdManager _idManager;
/// <summary>
/// Initializes a new instance of the <see cref="SvgDocument"/> class.
......@@ -340,7 +336,7 @@ namespace Svg
var size = GetDimensions();
var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
// bitmap.SetResolution(300, 300);
try
{
Draw(bitmap);
......@@ -406,5 +402,6 @@ namespace Svg
this.Write(fs);
}
}
}
}
\ No newline at end of file
......@@ -196,7 +196,7 @@ namespace Svg
[SvgAttribute("transform")]
public SvgTransformCollection Transforms
{
get { return this.Attributes.GetAttribute<SvgTransformCollection>("Transforms"); }
get { return (this.Attributes.GetAttribute<SvgTransformCollection>("Transforms") ?? new SvgTransformCollection()); }
set { this.Attributes["Transforms"] = value; }
}
......@@ -271,13 +271,20 @@ namespace Svg
/// <summary>
/// Initializes a new instance of the <see cref="SvgElement"/> class.
/// </summary>
internal SvgElement()
public SvgElement()
{
this._children = new SvgElementCollection(this);
this._eventHandlers = new EventHandlerList();
this._elementName = string.Empty;
}
public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
{
}
/// <summary>
/// Renders this element to the <see cref="SvgRenderer"/>.
/// </summary>
......@@ -299,7 +306,13 @@ namespace Svg
writer.WriteStartElement(this.ElementName);
if (this.ElementName == "svg")
{
writer.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg");
foreach (var ns in SvgAttributeAttribute.Namespaces)
{
if (string.IsNullOrEmpty(ns.Key))
writer.WriteAttributeString("xmlns", ns.Value);
else
writer.WriteAttributeString("xmlns:" + ns.Key, ns.Value);
}
writer.WriteAttributeString("version", "1.1");
}
}
......@@ -339,13 +352,13 @@ namespace Svg
{
string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
writer.WriteAttributeString(attr.Attribute.Name, value);
writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
}
}
else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
{
string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
writer.WriteAttributeString(attr.Attribute.Name, value);
writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
}
}
}
......@@ -485,10 +498,39 @@ namespace Svg
{
return this.MemberwiseClone();
}
public abstract SvgElement DeepCopy();
public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
{
var newObj = new T();
newObj.Content = this.Content;
newObj.ElementName = this.ElementName;
// if (this.Parent != null)
// this.Parent.Children.Add(newObj);
if (this.Transforms != null)
{
newObj.Transforms = new SvgTransformCollection();
foreach (var transform in this.Transforms)
newObj.Transforms.Add(transform.Clone() as SvgTransform);
}
foreach (var child in this.Children)
{
newObj.Children.Add(child.DeepCopy());
}
return newObj;
}
}
internal interface ISvgElement
{
SvgElement Parent {get;}
SvgElementCollection Children { get; }
void Render(SvgRenderer renderer);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
......@@ -139,6 +140,32 @@ namespace Svg
return removed;
}
/// <summary>
/// expensive recursive search for nodes of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IEnumerable<T> FindSvgElementsOf<T>() where T : SvgElement
{
return _elements.Where(x => x is T).Select(x => x as T).Concat(_elements.SelectMany(x => x.Children.FindSvgElementsOf<T>()));
}
/// <summary>
/// expensive recursive search for first node of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T FindSvgElementOf<T>() where T : SvgElement
{
return _elements.OfType<T>().FirstOrDefault() ?? _elements.Select(x => x.Children.FindSvgElementOf<T>()).FirstOrDefault<T>(x => x != null);
}
public T GetSvgElementOf<T>() where T : SvgElement
{
return _elements.FirstOrDefault(x => x is T) as T;
}
public IEnumerator<SvgElement> GetEnumerator()
{
return this._elements.GetEnumerator();
......
......@@ -100,7 +100,6 @@ namespace Svg
if (createdElement != null)
{
createdElement.ElementName = elementName;
SetAttributes(createdElement, reader, document);
}
......
......@@ -5,6 +5,8 @@ using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using Svg.DataTypes;
using System.Linq;
namespace Svg
{
......@@ -19,12 +21,12 @@ namespace Svg
private SvgUnit _letterSpacing;
private SvgUnit _wordSpacing;
private SvgUnit _fontSize;
private SvgFontWeight _fontWeight;
private string _font;
private string _fontFamily;
private GraphicsPath _path;
private SvgTextAnchor _textAnchor = SvgTextAnchor.Start;
private static readonly SvgRenderer _stringMeasure;
/// <summary>
/// Initializes the <see cref="SvgText"/> class.
/// </summary>
......@@ -135,6 +137,18 @@ namespace Svg
set { this._fontSize = value; this.IsPathDirty = true; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-weight")]
public virtual SvgFontWeight FontWeight
{
get { return this._fontWeight; }
set { this._fontWeight = value; this.IsPathDirty = true; }
}
/// <summary>
/// Set all font information.
/// </summary>
......@@ -205,7 +219,11 @@ namespace Svg
get
{
// Make sure the path is always null if there is no text
if (_path == null || this.IsPathDirty && !string.IsNullOrEmpty(this.Text))
//if (string.IsNullOrEmpty(this.Text))
// _path = null;
//NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF?
if (_path == null || this.IsPathDirty)
{
float fontSize = this.FontSize.ToDeviceValue(this);
if (fontSize == 0.0f)
......@@ -213,32 +231,64 @@ namespace Svg
fontSize = 1.0f;
}
FontStyle fontWeight = (this.FontWeight == SvgFontWeight.bold ? FontStyle.Bold : FontStyle.Regular);
Font font = new Font(this._fontFamily, fontSize, fontWeight, GraphicsUnit.Pixel);
_path = new GraphicsPath();
_path.StartFigure();
if (!string.IsNullOrEmpty(this.Text))
DrawString(_path, this.X, this.Y, SvgUnit.Empty, SvgUnit.Empty, font, fontSize, this.Text);
foreach (var tspan in this.Children.Where(x => x is SvgTextSpan).Select(x => x as SvgTextSpan))
{
if (!string.IsNullOrEmpty(tspan.Text))
DrawString(
_path,
tspan.X == SvgUnit.Empty ? this.X: tspan.X,
tspan.Y == SvgUnit.Empty ? this.Y : tspan.Y,
tspan.DX,
tspan.DY,
font,
fontSize,
tspan.Text);
}
_path.CloseFigure();
this.IsPathDirty = false;
}
return _path;
}
}
private void DrawString(GraphicsPath path, SvgUnit x, SvgUnit y, SvgUnit dx, SvgUnit dy, Font font, float fontSize, string text)
{
PointF location = PointF.Empty;
Font font = new Font(this._fontFamily, fontSize, FontStyle.Regular, GraphicsUnit.Pixel);
SizeF stringBounds = _stringMeasure.MeasureString(this.Text, font);
SizeF stringBounds = _stringMeasure.MeasureString(text, font);
float xToDevice = x.ToDeviceValue(this) + dx.ToDeviceValue(this);
float yToDevice = y.ToDeviceValue(this, true) + dy.ToDeviceValue(this, true);
// Minus FontSize because the x/y coords mark the bottom left, not bottom top.
switch (this.TextAnchor)
{
case SvgTextAnchor.Start:
location = new PointF(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true) - stringBounds.Height);
location = new PointF(xToDevice, yToDevice - stringBounds.Height);
break;
case SvgTextAnchor.Middle:
location = new PointF(this.X.ToDeviceValue(this) - (stringBounds.Width / 2), this.Y.ToDeviceValue(this, true) - stringBounds.Height);
location = new PointF(xToDevice - (stringBounds.Width / 2), yToDevice - stringBounds.Height);
break;
case SvgTextAnchor.End:
location = new PointF(this.X.ToDeviceValue(this) - stringBounds.Width, this.Y.ToDeviceValue(this, true) - stringBounds.Height);
location = new PointF(xToDevice - stringBounds.Width, yToDevice - stringBounds.Height);
break;
}
_path = new GraphicsPath();
_path.StartFigure();
// 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) ? this.Text.Split(' ') : new string[] { this.Text };
string[] words = (this.WordSpacing.Value > 0.0f) ? text.Split(' ') : new string[] { text };
float wordSpacing = this.WordSpacing.ToDeviceValue(this);
float letterSpacing = this.LetterSpacing.ToDeviceValue(this);
float start = this.X.ToDeviceValue(this);
......@@ -251,32 +301,48 @@ namespace Svg
char[] characters = word.ToCharArray();
foreach (char currentCharacter in characters)
{
_path.AddString(currentCharacter.ToString(), new FontFamily(this._fontFamily), 0, fontSize, location, StringFormat.GenericTypographic);
location = new PointF(_path.GetBounds().Width + start + letterSpacing, location.Y);
path.AddString(currentCharacter.ToString(), new FontFamily(this._fontFamily), (int)font.Style, fontSize, location, StringFormat.GenericTypographic);
location = new PointF(path.GetBounds().Width + start + letterSpacing, location.Y);
}
}
else
{
_path.AddString(word, new FontFamily(this._fontFamily), 0, fontSize, location, StringFormat.GenericTypographic);
path.AddString(word, new FontFamily(this._fontFamily), (int)font.Style, fontSize, location, StringFormat.GenericTypographic);
}
// Move the location of the word to be written along
location = new PointF(_path.GetBounds().Width + start + wordSpacing, location.Y);
location = new PointF(path.GetBounds().Width + start + wordSpacing, location.Y);
}
}
else
{
if (!string.IsNullOrEmpty(this.Text))
if (!string.IsNullOrEmpty(text))
{
_path.AddString(this.Text, new FontFamily(this._fontFamily), 0, fontSize, location, StringFormat.GenericTypographic);
path.AddString(text, new FontFamily(this._fontFamily), (int)font.Style, fontSize, location, StringFormat.GenericTypographic);
}
}
_path.CloseFigure();
this.IsPathDirty = false;
}
return _path;
public override SvgElement DeepCopy()
{
return DeepCopy<SvgText>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgText;
newObj.TextAnchor = this.TextAnchor;
newObj.WordSpacing = this.WordSpacing;
newObj.LetterSpacing = this.LetterSpacing;
newObj.Font = this.Font;
newObj.FontFamily = this.FontFamily;
newObj.FontSize = this.FontSize;
newObj.FontWeight = this.FontWeight;
newObj.X = this.X;
newObj.Y = this.Y;
return newObj;
}
}
}
\ No newline at end of file
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
namespace Svg
{
[SvgElement("tspan")]
public class SvgTextSpan : SvgText
public class SvgTextSpan : SvgElement
{
private SvgUnit _x;
private SvgUnit _y;
private SvgUnit _dx;
private SvgUnit _dy;
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[SvgAttribute("x")]
public SvgUnit X
{
get { return this._x; }
set { this._x = value; }
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override SvgUnit X
[SvgAttribute("y")]
public SvgUnit Y
{
get { return base.X; }
set { base.X = value; }
get { return this._y; }
set { this._y = value; }
}
/// <summary>
/// Gets or sets the deltaX from the containing text.
/// </summary>
/// <value>The dX.</value>
[SvgAttribute("dx")]
public SvgUnit DX
{
get { return this._dx; }
set { this._dx = value; }
}
/// <summary>
/// Gets or sets the Y.
/// Gets or sets the deltaY from the containing text.
/// </summary>
/// <value>The Y.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override SvgUnit Y
/// <value>The dY.</value>
[SvgAttribute("dy")]
public SvgUnit DY
{
get { return base.Y; }
set { base.Y = value; }
get { return this._dy; }
set { this._dy = value; }
}
/// <summary>
/// Gets or sets the text to be rendered.
/// </summary>
public virtual string Text
{
get { return base.Content; }
set { base.Content = value; this.Content = value; }
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgTextSpan>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgTextSpan;
newObj.X = this.X;
newObj.Y = this.Y;
newObj.DX = this.DX;
newObj.DY = this.DY;
newObj.Text = this.Text;
return newObj;
}
}
}
\ No newline at end of file
......@@ -45,5 +45,12 @@ namespace Svg.Transforms
{
this.points = m;
}
public override object Clone()
{
return new SvgMatrix(this.Points);
}
}
}
\ No newline at end of file
......@@ -54,5 +54,11 @@ namespace Svg.Transforms
this.CenterX = centerX;
this.CenterY = centerY;
}
public override object Clone()
{
return new SvgRotate(this.Angle, this.CenterX, this.CenterY);
}
}
}
\ No newline at end of file
......@@ -45,5 +45,11 @@ namespace Svg.Transforms
this.scaleFactorX = x;
this.scaleFactorY = y;
}
public override object Clone()
{
return new SvgScale(this.X, this.Y);
}
}
}
......@@ -48,5 +48,11 @@ namespace Svg.Transforms
this.shearFactorX = x;
this.shearFactorY = y;
}
public override object Clone()
{
return new SvgShear(this.X, this.Y);
}
}
}
\ No newline at end of file
......@@ -35,5 +35,11 @@ namespace Svg.Transforms
AngleX = x;
AngleY = y;
}
public override object Clone()
{
return new SvgSkew(this.AngleX, this.AngleY);
}
}
}
\ No newline at end of file
......@@ -6,9 +6,11 @@ using System.Drawing.Drawing2D;
namespace Svg.Transforms
{
public abstract class SvgTransform
public abstract class SvgTransform : ICloneable
{
public abstract Matrix Matrix { get; }
public abstract string WriteToString();
public abstract object Clone();
}
}
\ No newline at end of file
......@@ -32,5 +32,13 @@ namespace Svg.Transforms
return transformMatrix;
}
public override bool Equals(object obj)
{
if (this.Count == 0 && this.Count == this.Count) //default will be an empty list
return true;
return base.Equals(obj);
}
}
}
......@@ -47,5 +47,12 @@ namespace Svg.Transforms
: this(x, 0.0f)
{
}
public override object Clone()
{
return new SvgTranslate(this.x, this.y);
}
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment