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. /// public class SvgText : SvgGraphicsElement { 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 Graphics _stringMeasure; /// /// Initializes the class. /// static SvgText() { Bitmap bitmap = new Bitmap(1, 1); _stringMeasure = Graphics.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(); } } /// /// Renders the and contents to the specified object. /// /// The object to render to. protected override void Render(Graphics graphics) { base.Render(graphics); } static private int MeasureString(Graphics graphics, 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(graphics.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); 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 { _path.AddString(this.Text, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic); } _path.CloseFigure(); this.IsPathDirty = false; } return _path; } } } }