using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
namespace Svg
{
///
/// The element defines a graphics element consisting of text.
///
[SvgElement("text")]
public class SvgText : SvgVisualElement
{
private SvgUnit _x;
private SvgUnit _y;
private SvgUnit _letterSpacing;
private SvgUnit _wordSpacing;
private SvgUnit _fontSize;
private Font _font;
private GraphicsPath _path;
private SvgTextAnchor _textAnchor = SvgTextAnchor.Start;
private static readonly SvgRenderer _stringMeasure;
///
/// Initializes the class.
///
static SvgText()
{
Bitmap bitmap = new Bitmap(1, 1);
_stringMeasure = SvgRenderer.FromImage(bitmap);
_stringMeasure.TextRenderingHint = TextRenderingHint.AntiAlias;
}
///
/// Initializes a new instance of the class.
///
public SvgText()
{
this._font = new Font(new FontFamily("Times New Roman"), 1.0f);
this._fontSize = new SvgUnit(0.0f);
}
///
/// Initializes a new instance of the class.
///
/// The text.
public SvgText(string text) : this()
{
this.Text = text;
}
///
/// Gets or sets the text to be rendered.
///
public virtual string Text
{
get { return base.Content; }
set { base.Content = value; this.IsPathDirty = true; }
}
///
/// Gets or sets the text anchor.
///
/// The text anchor.
[SvgAttribute("text-anchor")]
public virtual SvgTextAnchor TextAnchor
{
get { return this._textAnchor; }
set { this._textAnchor = value; this.IsPathDirty = true; }
}
///
/// Gets or sets the X.
///
/// The X.
[SvgAttribute("x")]
public virtual SvgUnit X
{
get { return this._x; }
set { this._x = value; this.IsPathDirty = true; }
}
///
/// Gets or sets the Y.
///
/// The Y.
[SvgAttribute("y")]
public virtual SvgUnit Y
{
get { return this._y; }
set { this._y = 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; }
}
///
/// Specifies spacing behavior between words.
///
[SvgAttribute("word-spacing")]
public virtual SvgUnit WordSpacing
{
get { return this._wordSpacing; }
set { this._wordSpacing = value; this.IsPathDirty = true; }
}
///
/// Indicates which font family is to be used to render the text.
///
[SvgAttribute("font-family")]
public virtual Font FontFamily
{
get { return this._font; }
set { this._font = value; this.IsPathDirty = true; }
}
///
/// 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._fontSize; }
set { this._fontSize = value; this.IsPathDirty = true; }
}
///
/// Set all font information.
///
[SvgAttribute("font")]
public virtual Font Font
{
get { return this._font; }
set { this._font = value; this.IsPathDirty = true; }
}
///
/// Gets or sets the fill.
///
///
/// Unlike other s, has a default fill of black rather than transparent.
///
/// The fill.
public override SvgPaintServer Fill
{
get { return (this.Attributes["Fill"] == null) ? new SvgColourServer(Color.Black) : (SvgPaintServer)this.Attributes["Fill"]; }
set { this.Attributes["Fill"] = value; }
}
///
/// Returns a that represents the current .
///
///
/// A that represents the current .
///
public override string ToString()
{
return this.Text;
}
///
/// 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.GetBounds(); }
}
static private int 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 (int)(p.GetBounds().Width + 1.0f);
}
///
/// Gets the for this element.
///
///
public override System.Drawing.Drawing2D.GraphicsPath Path
{
get
{
// Make sure the path is always null if there is no text
if (_path == null || this.IsPathDirty && !string.IsNullOrEmpty(this.Text))
{
float fontSize = this.FontSize.ToDeviceValue(this);
if (fontSize == 0.0f)
{
fontSize = 1.0f;
}
int stringWidth;
PointF location = PointF.Empty;
// 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) - fontSize);
break;
case SvgTextAnchor.Middle:
stringWidth = SvgText.MeasureString(_stringMeasure, this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - (stringWidth / 2), this.Y.ToDeviceValue(this, true) - fontSize);
break;
case SvgTextAnchor.End:
stringWidth = SvgText.MeasureString(_stringMeasure, this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - stringWidth, this.Y.ToDeviceValue(this, true) - fontSize);
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 };
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(), this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic);
location = new PointF(_path.GetBounds().Width + start + letterSpacing, location.Y);
}
}
else
{
_path.AddString(word, this._font.FontFamily, 0, 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(this.Text))
{
_path.AddString(this.Text, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic);
}
}
_path.CloseFigure();
this.IsPathDirty = false;
}
return _path;
}
}
}
}