Commit 4b1ff3d4 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #89 from erdomke/master

Rendering Improvements and .Net 3.5 Support
parents ba3bc5fc ce8f2a64
......@@ -97,6 +97,7 @@ namespace Svg
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
{
LoadStops();
var origin = CalculateOrigin(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin);
......
......@@ -8,13 +8,21 @@ namespace Svg.Pathing
{
public override void AddToPath(System.Drawing.Drawing2D.GraphicsPath graphicsPath)
{
// Important for custom line caps. Force the path the close with an explicit line, not just an implicit close of the figure.
if (graphicsPath.PointCount > 0 && !graphicsPath.PathPoints[0].Equals(graphicsPath.PathPoints[graphicsPath.PathPoints.Length - 1]))
{
int i = graphicsPath.PathTypes.Length - 1;
while (i >= 0 && graphicsPath.PathTypes[i] > 0) i--;
if (i < 0) i = 0;
graphicsPath.AddLine(graphicsPath.PathPoints[graphicsPath.PathPoints.Length - 1], graphicsPath.PathPoints[i]);
}
graphicsPath.CloseFigure();
}
public override string ToString()
{
return "z";
}
{
return "z";
}
}
}
}
\ No newline at end of file
......@@ -16,7 +16,7 @@
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<SccProjectName>
</SccProjectName>
......@@ -100,6 +100,8 @@
<Compile Include="Clipping and Masking\SvgClipPath.cs" />
<Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\SvgAspectRatioConverter.cs" />
<Compile Include="DataTypes\SvgFontStyle.cs" />
<Compile Include="DataTypes\SvgFontVariant.cs" />
<Compile Include="DataTypes\SvgMarkerUnits.cs" />
<Compile Include="DataTypes\SvgOrient.cs" />
<Compile Include="DataTypes\ISvgViewPort.cs" />
......@@ -116,7 +118,9 @@
<Compile Include="Document Structure\SvgTitle.cs" />
<Compile Include="Document Structure\SvgDocumentMetadata.cs" />
<Compile Include="Extensibility\SvgForeignObject.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Painting\ISvgBoundable.cs" />
<Compile Include="Painting\SvgDeferredPaintServer.cs" />
<Compile Include="Painting\SvgMarker.cs" />
<Compile Include="Document Structure\SvgDefinitionList.cs" />
<Compile Include="Document Structure\SvgDescription.cs" />
......@@ -134,6 +138,7 @@
<Compile Include="Filter Effects\feGaussianBlur\SvgGaussianBlur.cs" />
<Compile Include="Filter Effects\feMerge\SvgMerge.cs" />
<Compile Include="Painting\EnumConverters.cs" />
<Compile Include="SvgContentNode.cs" />
<Compile Include="SvgDefinitionDefaults.cs" />
<Compile Include="NonSvgElement.cs" />
<Compile Include="SvgUnknownElement.cs" />
......@@ -181,6 +186,7 @@
<Compile Include="DataTypes\SvgUnitConverter.cs" />
<Compile Include="SvgTextReader.cs" />
<Compile Include="Text\SvgText.cs" />
<Compile Include="Text\SvgTextBase.cs" />
<Compile Include="Text\SvgTextAnchor.cs" />
<Compile Include="Text\SvgTextSpan.cs" />
<Compile Include="Transforms\ISvgTransformable.cs" />
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public class SvgContentNode : ISvgNode
{
public string Content { get; set; }
}
}
......@@ -8,6 +8,7 @@ using System.Drawing.Text;
using System.IO;
using System.Text;
using System.Xml;
using System.Linq;
namespace Svg
{
......@@ -184,7 +185,6 @@ namespace Svg
using (var reader = new SvgTextReader(stream, entities))
{
var elementStack = new Stack<SvgElement>();
var value = new StringBuilder();
bool elementEmpty;
SvgElement element = null;
SvgElement parent;
......@@ -218,7 +218,10 @@ namespace Svg
{
parent = elementStack.Peek();
if (parent != null && element != null)
{
parent.Children.Add(element);
parent.Nodes.Add(element);
}
}
// Push element into stack
......@@ -236,21 +239,24 @@ namespace Svg
// Pop the element out of the stack
element = elementStack.Pop();
if (value.Length > 0 && element != null)
if (element.Nodes.OfType<SvgContentNode>().Any())
{
element.Content = value.ToString();
// Reset content value for new element
value.Clear();
element.Content = (from e in element.Nodes select e.Content).Aggregate((p, c) => p + c);
}
else
{
element.Nodes.Clear(); // No sense wasting the space where it isn't needed
}
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
value.Append(reader.Value);
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
value.Append(reader.Value);
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
}
}
......@@ -277,8 +283,10 @@ namespace Svg
throw new ArgumentNullException("document");
}
Stream stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml));
return Open<SvgDocument>(stream, null);
using (var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml)))
{
return Open<SvgDocument>(stream, null);
}
}
public static Bitmap OpenAsBitmap(string path)
......@@ -393,4 +401,4 @@ namespace Svg
}
}
}
}
}
\ No newline at end of file
......@@ -15,7 +15,7 @@ namespace Svg
/// <summary>
/// The base class of which all SVG elements are derived from.
/// </summary>
public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable
public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
{
//optimization
protected class PropertyAttributeTuple
......@@ -42,6 +42,7 @@ namespace Svg
private static readonly object _loadEventKey = new object();
private Matrix _graphicsMatrix;
private SvgCustomAttributeCollection _customAttributes;
private List<ISvgNode> _nodes = new List<ISvgNode>();
/// <summary>
/// Gets the name of the element.
......@@ -117,6 +118,20 @@ namespace Svg
get { return this._children; }
}
public IList<ISvgNode> Nodes
{
get { return this._nodes; }
}
public IEnumerable<SvgElement> Descendants()
{
return this.AsEnumerable().Descendants();
}
private IEnumerable<SvgElement> AsEnumerable()
{
yield return this;
}
/// <summary>
/// Gets a value to determine whether the element has children.
/// </summary>
......@@ -134,6 +149,32 @@ namespace Svg
get { return this._parent; }
}
public IEnumerable<SvgElement> Parents
{
get
{
var curr = this;
while (curr.Parent != null)
{
curr = curr.Parent;
yield return curr;
}
}
}
public IEnumerable<SvgElement> ParentsAndSelf
{
get
{
var curr = this;
yield return curr;
while (curr.Parent != null)
{
curr = curr.Parent;
yield return curr;
}
}
}
/// <summary>
/// Gets the owner <see cref="SvgDocument"/>.
/// </summary>
......@@ -486,7 +527,7 @@ namespace Svg
foreach (var attr in _svgEventAttributes)
{
var evt = attr.Event.GetValue(this);
//if someone has registered publish the attribute
if (evt != null && !string.IsNullOrEmpty(this.ID))
{
......@@ -602,8 +643,8 @@ namespace Svg
childPath = (GraphicsPath)childPath.Clone();
if(child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix());
path.AddPath(childPath, false);
if (childPath.PointCount > 0) path.AddPath(childPath, false);
}
}
}
......@@ -670,7 +711,7 @@ namespace Svg
newObj.ID = this.ID;
newObj.Content = this.Content;
newObj.ElementName = this.ElementName;
// if (this.Parent != null)
// this.Parent.Children.Add(newObj);
......@@ -683,11 +724,11 @@ namespace Svg
{
newObj.Children.Add(child.DeepCopy());
}
foreach (var attr in this._svgEventAttributes)
{
var evt = attr.Event.GetValue(this);
//if someone has registered also register here
if (evt != null)
{
......@@ -709,7 +750,7 @@ namespace Svg
(newObj as SvgText).Change += delegate { };
}
}
if(this._customAttributes.Count > 0)
{
foreach (var element in _customAttributes)
......@@ -717,15 +758,15 @@ namespace Svg
newObj.CustomAttributes.Add(element.Key, element.Value);
}
}
return newObj;
}
/// <summary>
/// Fired when an Atrribute of this Element has changed
/// </summary>
public event EventHandler<AttributeEventArgs> AttributeChanged;
protected void OnAttributeChanged(AttributeEventArgs args)
{
var handler = AttributeChanged;
......@@ -734,12 +775,12 @@ namespace Svg
handler(this, args);
}
}
/// <summary>
/// Fired when an Atrribute of this Element has changed
/// </summary>
public event EventHandler<ContentEventArgs> ContentChanged;
protected void OnContentChanged(ContentEventArgs args)
{
var handler = ContentChanged;
......@@ -763,6 +804,7 @@ namespace Svg
onmouseout = "<anything>"
*/
#if Net4
/// <summary>
/// Use this method to provide your implementation ISvgEventCaller which can register Actions
/// and call them if one of the events occurs. Make sure, that your SvgElement has a unique ID.
......@@ -804,6 +846,7 @@ namespace Svg
caller.UnregisterAction(rpcID + "onmouseout");
}
}
#endif
[SvgAttribute("onclick")]
public event EventHandler<MouseArg> Click;
......@@ -826,12 +869,14 @@ namespace Svg
[SvgAttribute("onmouseout")]
public event EventHandler<MouseArg> MouseOut;
#if Net4
protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
{
return (x, y, button, clickCount, altKey, shiftKey, ctrlKey, sessionID) =>
eventRaiser(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID });
}
#endif
//click
protected void RaiseMouseClick(object sender, MouseArg e)
{
......@@ -908,7 +953,6 @@ namespace Svg
}
#endregion graphical EVENTS
}
public class SVGArg : EventArgs
......@@ -943,6 +987,7 @@ namespace Svg
public SvgElement BeforeSibling;
}
#if Net4
//deriving class registers event actions and calls the actions if the event occurs
public interface ISvgEventCaller
{
......@@ -957,6 +1002,7 @@ namespace Svg
void RegisterAction<T1, T2, T3, T4, T5, T6, T7, T8>(string rpcID, Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
void UnregisterAction(string rpcID);
}
#endif
/// <summary>
/// Represents the state of the mouse at the moment the event occured.
......@@ -1020,10 +1066,16 @@ namespace Svg
public bool CtrlKey;
}
public interface ISvgNode
{
string Content { get; }
}
internal interface ISvgElement
{
SvgElement Parent {get;}
SvgElementCollection Children { get; }
IList<ISvgNode> Nodes { get; }
void Render(SvgRenderer renderer);
}
......
......@@ -157,6 +157,7 @@ namespace Svg
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);
......
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using Svg.DataTypes;
using System.Linq;
namespace Svg
{
......@@ -15,490 +12,41 @@ namespace Svg
/// The <see cref="SvgText"/> element defines a graphics element consisting of text.
/// </summary>
[SvgElement("text")]
public class SvgText : SvgVisualElement
public class SvgText : SvgTextBase
{
private SvgUnit _x;
private SvgUnit _y;
private SvgUnit _dy;
private SvgUnit _dx;
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;
private const string DefaultFontFamily = "Times New Roman";
/// <summary>
/// Initializes the <see cref="SvgText"/> class.
/// </summary>
static SvgText()
{
Bitmap bitmap = new Bitmap(1, 1);
_stringMeasure = SvgRenderer.FromImage(bitmap);
_stringMeasure.TextRenderingHint = TextRenderingHint.AntiAlias;
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgText"/> class.
/// </summary>
public SvgText()
{
this._fontFamily = DefaultFontFamily;
this._fontSize = new SvgUnit(0.0f);
this._dy = new SvgUnit(0.0f);
this._dx = new SvgUnit(0.0f);
}
public SvgText() : base() { }
/// <summary>
/// Initializes a new instance of the <see cref="SvgText"/> class.
/// </summary>
/// <param name="text">The text.</param>
public SvgText(string text) : this()
public SvgText(string text)
: this()
{
this.Text = text;
}
/// <summary>
/// Gets or sets the text to be rendered.
/// </summary>
public virtual string Text
{
get { return base.Content; }
set { base.Content = value; this.IsPathDirty = true; this.Content = value; }
}
/// <summary>
/// Gets or sets the text anchor.
/// </summary>
/// <value>The text anchor.</value>
[SvgAttribute("text-anchor")]
public virtual SvgTextAnchor TextAnchor
{
get { return this._textAnchor; }
set { this._textAnchor = value; this.IsPathDirty = true; }
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[SvgAttribute("x")]
public virtual SvgUnit X
{
get { return this._x; }
set
{
if(_x != value)
{
this._x = value;
this.IsPathDirty = true;
OnAttributeChanged(new AttributeEventArgs{ Attribute = "x", Value = value });
}
}
}
/// <summary>
/// Gets or sets the dX.
/// </summary>
/// <value>The dX.</value>
[SvgAttribute("dx")]
public virtual SvgUnit Dx
{
get { return this._dx; }
set
{
if (_dx != value)
{
this._dx = value;
this.IsPathDirty = true;
OnAttributeChanged(new AttributeEventArgs { Attribute = "dx", Value = value });
}
}
}
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>The Y.</value>
[SvgAttribute("y")]
public virtual SvgUnit Y
{
get { return this._y; }
set
{
if(_y != value)
{
this._y = value;
this.IsPathDirty = true;
OnAttributeChanged(new AttributeEventArgs{ Attribute = "y", Value = value });
}
}
}
/// <summary>
/// Gets or sets the dY.
/// </summary>
/// <value>The dY.</value>
[SvgAttribute("dy")]
public virtual SvgUnit Dy
{
get { return this._dy; }
set
{
if (_dy != value)
{
this._dy = value;
this.IsPathDirty = true;
OnAttributeChanged(new AttributeEventArgs { Attribute = "dy", Value = value });
}
}
}
/// <summary>
/// Specifies spacing behavior between text characters.
/// </summary>
[SvgAttribute("letter-spacing")]
public virtual SvgUnit LetterSpacing
{
get { return this._letterSpacing; }
set { this._letterSpacing = value; this.IsPathDirty = true; }
}
/// <summary>
/// Specifies spacing behavior between words.
/// </summary>
[SvgAttribute("word-spacing")]
public virtual SvgUnit WordSpacing
public override SvgElement DeepCopy()
{
get { return this._wordSpacing; }
set { this._wordSpacing = value; this.IsPathDirty = true; }
return DeepCopy<SvgText>();
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[SvgAttribute("font-family")]
public virtual string FontFamily
public override SvgElement DeepCopy<T>()
{
get { return this._fontFamily; }
set
{
this._fontFamily = ValidateFontFamily(value);
this.IsPathDirty = true;
}
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;
}
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[SvgAttribute("font-size")]
public virtual SvgUnit FontSize
{
get { return this._fontSize; }
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>
[SvgAttribute("font")]
public virtual string Font
{
get { return this._font; }
set
{
var parts = value.Split(',');
foreach (var part in parts)
{
//This deals with setting font size. Looks for either <number>px or <number>pt style="font: bold 16px/normal 'trebuchet ms', verdana, sans-serif;"
Regex rx = new Regex(@"(\d+)+(?=pt|px)");
var res = rx.Match(part);
if (res.Success)
{
int fontSize = 10;
int.TryParse(res.Value, out fontSize);
this.FontSize = new SvgUnit((float)fontSize);
}
//this assumes "bold" has spaces around it. e.g.: style="font: bold 16px/normal
rx = new Regex(@"\sbold\s");
res = rx.Match(part);
if (res.Success)
{
this.FontWeight = SvgFontWeight.bold;
}
}
var font = ValidateFontFamily(value);
this._fontFamily = font;
this._font = font; //not sure this is used?
this.IsPathDirty = true;
}
}
/// <summary>
/// Gets or sets the fill.
/// </summary>
/// <remarks>
/// <para>Unlike other <see cref="SvgGraphicsElement"/>s, <see cref="SvgText"/> has a default fill of black rather than transparent.</para>
/// </remarks>
/// <value>The fill.</value>
public override SvgPaintServer Fill
{
get { return (this.Attributes["fill"] == null) ? new SvgColourServer(Color.Black) : (SvgPaintServer)this.Attributes["fill"]; }
set { this.Attributes["fill"] = value; }
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public override string ToString()
{
return this.Text;
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
/// <value></value>
protected override bool RequiresSmoothRendering
{
get { return true; }
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
{
get { return this.Path.GetBounds(); }
}
static private RectangleF MeasureString(SvgRenderer renderer, string text, Font font)
{
GraphicsPath p = new GraphicsPath();
p.AddString(text, font.FontFamily, 0, font.Size, new PointF(0.0f, 0.0f), StringFormat.GenericTypographic);
p.Transform(renderer.Transform);
return p.GetBounds();
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path
{
get
{
// 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.IsNullOrWhiteSpace(this.Text) && 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...
if (_path == null || this.IsPathDirty)
{
float fontSize = this.FontSize.ToDeviceValue(this);
if (fontSize == 0.0f)
{
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, this.Dx, this.Dy, 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;
}
protected set
{
_path = value;
}
}
private static string ValidateFontFamily(string fontFamilyList)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var fontParts = fontFamilyList.Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ','\'' }));
var families = System.Drawing.FontFamily.Families;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach (var f in fontParts.Where(f => families.Any(family => family.Name.ToLower() == f.ToLower())))
{
return f;
}
// No valid font family found from the list requested.
return DefaultFontFamily;
}
private void DrawString(GraphicsPath path, SvgUnit x, SvgUnit y, SvgUnit dx, SvgUnit dy, Font font, float fontSize, string text)
{
PointF location = PointF.Empty;
SizeF stringBounds;
lock (_stringMeasure)
{
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(xToDevice, yToDevice - stringBounds.Height);
break;
case SvgTextAnchor.Middle:
location = new PointF(xToDevice - (stringBounds.Width / 2), yToDevice - stringBounds.Height);
break;
case SvgTextAnchor.End:
location = new PointF(xToDevice - stringBounds.Width, yToDevice - stringBounds.Height);
break;
}
// 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(this);
float letterSpacing = this.LetterSpacing.ToDeviceValue(this);
float start = this.X.ToDeviceValue(this);
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(), 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), (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);
}
}
else
{
if (!string.IsNullOrEmpty(text))
{
path.AddString(text, new FontFamily(this._fontFamily), (int)font.Style, fontSize, location, StringFormat.GenericTypographic);
}
}
}
[SvgAttribute("onchange")]
public event EventHandler<StringArg> Change;
//change
protected void OnChange(string newString, string sessionID)
{
RaiseChange(this, new StringArg {s = newString, SessionID = sessionID});
}
protected void RaiseChange(object sender, StringArg s)
{
var handler = Change;
if (handler != null)
{
handler(sender, s);
}
}
public override void RegisterEvents(ISvgEventCaller caller)
{
//register basic events
base.RegisterEvents(caller);
//add change event for text
caller.RegisterAction<string, string>(this.ID + "/onchange", OnChange);
}
public override void UnregisterEvents(ISvgEventCaller caller)
{
//unregister base events
base.UnregisterEvents(caller);
//unregister change event
caller.UnregisterAction(this.ID + "/onchange");
}
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
This diff is collapsed.
......@@ -8,86 +8,24 @@ using System.Text;
namespace Svg
{
[SvgElement("tspan")]
public class SvgTextSpan : SvgElement
public class SvgTextSpan : SvgTextBase
{
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
public override SvgElement DeepCopy()
{
get { return this._x; }
set { this._x = value; }
}
return DeepCopy<SvgTextSpan>();
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[SvgAttribute("y")]
public SvgUnit Y
public override SvgElement DeepCopy<T>()
{
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 deltaY from the containing text.
/// </summary>
/// <value>The dY.</value>
[SvgAttribute("dy")]
public SvgUnit DY
{
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;
}
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;
}
}
......
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