VectSharp  2.2.1
A light library for C# vector graphics
FormattedText.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Text;
21 using System.Linq;
22 using static System.Net.Mime.MediaTypeNames;
23 
24 namespace VectSharp
25 {
26  /// <summary>
27  /// Represents the position of the text.
28  /// </summary>
29  public enum Script
30  {
31  /// <summary>
32  /// The text is normal text.
33  /// </summary>
34  Normal,
35 
36  /// <summary>
37  /// The text is a superscript.
38  /// </summary>
40 
41  /// <summary>
42  /// The text is a subscript.
43  /// </summary>
44  Subscript
45  }
46 
47  /// <summary>
48  /// Represents a run of text that should be drawn with the same style.
49  /// </summary>
50  public class FormattedText
51  {
52  /// <summary>
53  /// Represents the text represented by this instance.
54  /// </summary>
55  public string Text { get; }
56 
57  /// <summary>
58  /// Represents the font that should be used to draw the text.
59  /// </summary>
60  public Font Font { get; }
61 
62  /// <summary>
63  /// Represents the position of the text.
64  /// </summary>
65  public Script Script { get; }
66 
67  /// <summary>
68  /// Represents the brush that should be used to draw the text. If this is null, the default brush is used.
69  /// </summary>
70  public Brush Brush { get; }
71 
72  /// <summary>
73  /// Creates a new <see cref="FormattedText"/> instance with the specified <paramref name="text"/>, <paramref name="font"/>, <paramref name="script"/> position and <paramref name="brush"/>.
74  /// </summary>
75  /// <param name="text">The text that will be contained in the new <see cref="FormattedText"/>.</param>
76  /// <param name="font">The font that will be used by the new <see cref="FormattedText"/>.</param>
77  /// <param name="script">The script position of the new <see cref="FormattedText"/>.</param>
78  /// <param name="brush">The brush that will be used by the new <see cref="FormattedText"/>.</param>
79  public FormattedText(string text, Font font, Script script = Script.Normal, Brush brush = null)
80  {
81  this.Text = text;
82  this.Font = font;
83  this.Script = script;
84  this.Brush = brush;
85  }
86 
87  /// <summary>
88  /// Parse the formatting information contained in a text string into a collection of <see cref="FormattedText"/> objects.
89  /// </summary>
90  /// <param name="text">
91  /// The string containing formatting information. Format information is specified using HTML-like tags:
92  /// <list type="bullet">
93  /// <item><c>&lt;b&gt;&lt;/b&gt;</c> or <c>&lt;strong&gt;&lt;/strong&gt;</c> are used for bold text;</item>
94  /// <item><c>&lt;i&gt;&lt;/i&gt;</c> or <c>&lt;em&gt;&lt;/em&gt;</c> are used for text in italics;</item>
95  /// <item><c>&lt;u&gt;&lt;/u&gt;</c></item> are used for underlined text;
96  /// <item><c>&lt;sup&gt;&lt;/sup&gt;</c> and <c>&lt;sub&gt;&lt;/sub&gt;</c> are used, respectively, for superscript and subscript text;</item>
97  /// <item><c>&lt;#COLOUR&gt;&lt;/#&gt;</c> is used to specify the colour of the text, where <c>COLOUR</c> is a CSS colour string (e.g. <c>&lt;#red&gt;</c>, <c>&lt;#0080FF&gt;</c>, or <c>&lt;#rgba(128, 80, 52, 0.5)&gt;</c>).</item>
98  /// </list></param>
99  /// <param name="normalFont">The font that will be used for text that is neither bold nor italic.</param>
100  /// <param name="boldFont">The font that will be used for text that is bold. Note that this does not necessarily have to be a bold font; this is just the font that is applied to text contained within <c>&lt;b&gt;&lt;/b&gt;</c> tags.</param>
101  /// <param name="italicFont">The font that will be used for text that is in italics. Note that this does not necessarily have to be an italic font; this is just the font that is applied to text contained within <c>&lt;i&gt;&lt;/i&gt;</c> tags.</param>
102  /// <param name="boldItalicFont">The font that will be used for text that is both in bold and in italics.</param>
103  /// <param name="defaultBrush">The default <see cref="Brush"/> that will be used for text runs that do not specify a colour. If this is <see langword="null"/>, the default <see cref="Brush"/> will be the one specified in the painting call.</param>
104  /// <returns>A lazy collection of <see cref="FormattedText"/> objects. Note that every enumeration of this collection causes the text to be parsed again; if you need to enumerate this collection more than once, you should probably convert it e.g. to a <see cref="List{T}"/>.</returns>
105  public static IEnumerable<FormattedText> Format(string text, Font normalFont, Font boldFont, Font italicFont, Font boldItalicFont, Brush defaultBrush = null)
106  {
107  StringBuilder currentRun = new StringBuilder();
108  int boldDepth = 0;
109  int italicsDepth = 0;
110  int underlineDepth = 0;
111  int superscriptDepth = 0;
112  int subscriptDepth = 0;
113  Stack<Brush> brushes = new Stack<Brush>();
114  brushes.Push(defaultBrush);
115 
116  for (int i = 0; i < text.Length; i++)
117  {
118  if (text[i] != '<')
119  {
120  currentRun.Append(text[i]);
121  }
122  else
123  {
124  Tags tag = GetTag(text, i, out int tagEnd, out Brush tagBrush);
125 
126  if (tag == Tags.None)
127  {
128  currentRun.Append(text[i]);
129  }
130  else
131  {
132  i = tagEnd;
133 
134  string txt = currentRun.ToString();
135 
136  switch (tag)
137  {
138  case Tags.BoldOpen:
139  if (!string.IsNullOrEmpty(txt))
140  {
141  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
142  }
143  currentRun.Clear();
144  boldDepth++;
145  break;
146  case Tags.BoldClose:
147  if (boldDepth > 0)
148  {
149  if (!string.IsNullOrEmpty(txt))
150  {
151  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
152  }
153  currentRun.Clear();
154  boldDepth--;
155  }
156  break;
157  case Tags.UnderlineOpen:
158  if (!string.IsNullOrEmpty(txt))
159  {
160  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
161  }
162  currentRun.Clear();
163  underlineDepth++;
164  break;
165  case Tags.UnderlineClose:
166  if (underlineDepth > 0)
167  {
168  if (!string.IsNullOrEmpty(txt))
169  {
170  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
171  }
172  currentRun.Clear();
173  underlineDepth--;
174  }
175  break;
176 
177  case Tags.ItalicsOpen:
178  if (!string.IsNullOrEmpty(txt))
179  {
180  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
181  }
182  currentRun.Clear();
183  italicsDepth++;
184  break;
185  case Tags.ItalicsClose:
186  if (italicsDepth > 0)
187  {
188  if (!string.IsNullOrEmpty(txt))
189  {
190  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
191  }
192  currentRun.Clear();
193  italicsDepth--;
194  }
195  break;
196 
197  case Tags.SupOpen:
198  if (!string.IsNullOrEmpty(txt))
199  {
200  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
201  }
202  currentRun.Clear();
203  superscriptDepth++;
204  break;
205  case Tags.SupClose:
206  if (superscriptDepth > 0)
207  {
208  if (!string.IsNullOrEmpty(txt))
209  {
210  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
211  }
212  currentRun.Clear();
213  superscriptDepth--;
214  }
215  break;
216 
217  case Tags.SubOpen:
218  if (!string.IsNullOrEmpty(txt))
219  {
220  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
221  }
222  currentRun.Clear();
223  subscriptDepth++;
224  break;
225  case Tags.SubClose:
226  if (subscriptDepth > 0)
227  {
228  if (!string.IsNullOrEmpty(txt))
229  {
230  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
231  }
232  currentRun.Clear();
233  subscriptDepth--;
234  }
235  break;
236  case Tags.ColourOpen:
237  if (!string.IsNullOrEmpty(txt))
238  {
239  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
240  }
241  currentRun.Clear();
242  brushes.Push(tagBrush);
243  break;
244  case Tags.ColourClose:
245  if (brushes.Count > 1)
246  {
247  if (!string.IsNullOrEmpty(txt))
248  {
249  yield return new FormattedText(txt, GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
250  }
251  currentRun.Clear();
252  brushes.Pop();
253  }
254  break;
255  }
256  }
257  }
258  }
259 
260  if (currentRun.Length > 0)
261  {
262  yield return new FormattedText(currentRun.ToString(), GetFont(boldDepth % 2 == 1, italicsDepth % 2 == 1, underlineDepth % 2 == 1, normalFont, boldFont, italicFont, boldItalicFont), superscriptDepth > subscriptDepth ? Script.Superscript : subscriptDepth > superscriptDepth ? Script.Subscript : Script.Normal, brushes.Peek());
263  }
264  }
265 
266  /// <summary>
267  /// Parse the formatting information contained in a text string into a collection of <see cref="FormattedText"/> objects, using fonts from a standard font family.
268  /// </summary>
269  /// <param name="text">The string containing formatting information. Format information is specified using HTML-like tags:
270  /// <list type="bullet">
271  /// <item><c>&lt;b&gt;&lt;/b&gt;</c> or <c>&lt;strong&gt;&lt;/strong&gt;</c> are used for bold text;</item>
272  /// <item><c>&lt;i&gt;&lt;/i&gt;</c> or <c>&lt;em&gt;&lt;/em&gt;</c> are used for text in italics;</item>
273  /// <item><c>&lt;u&gt;&lt;/u&gt;</c></item> are used for underlined text;
274  /// <item><c>&lt;sup&gt;&lt;/sup&gt;</c> and <c>&lt;sub&gt;&lt;/sub&gt;</c> are used, respectively, for superscript and subscript text;</item>
275  /// <item><c>&lt;#COLOUR&gt;&lt;/#&gt;</c> is used to specify the colour of the text, where <c>COLOUR</c> is a CSS colour string (e.g. <c>&lt;#red&gt;</c>, <c>&lt;#0080FF&gt;</c>, or <c>&lt;#rgba(128, 80, 52, 0.5)&gt;</c>).</item>
276  /// </list></param>
277  /// <param name="fontFamily">The font family from which the fonts will be created. If this is a regular font family, the bold, italic and bold-italic versions of the font will be used for the formatted text. Otherwise, the relevant font styles will be toggled (e.g. if the supplied font family is bold, then regular text in the formatted string will be displayed as bold, while bold text in the formatted string will be displayed as regular text).</param>
278  /// <param name="fontSize">The size of the fonts to use.</param>
279  /// <param name="defaultUnderline">Determines whether text should be underlined by default. This is toggled by <c>&lt;u&gt;&lt;/u&gt;</c> tags.</param>
280  /// <param name="defaultBrush">The default <see cref="Brush"/> that will be used for text runs that do not specify a colour. If this is <see langword="null"/>, the default <see cref="Brush"/> will be the one specified in the painting call.</param>
281  /// <returns>A lazy collection of <see cref="FormattedText"/> objects. Note that every enumeration of this collection causes the text to be parsed again; if you need to enumerate this collection more than once, you should probably convert it e.g. to a <see cref="List{T}"/>.</returns>
282  public static IEnumerable<FormattedText> Format(string text, FontFamily.StandardFontFamilies fontFamily, double fontSize, bool defaultUnderline = false, Brush defaultBrush = null)
283  {
284  Font normalFont = new Font(FontFamily.ResolveFontFamily(fontFamily), fontSize, defaultUnderline);
285 
286  Font boldFont = normalFont;
287  Font italicFont = normalFont;
288  Font boldItalicFont = normalFont;
289 
290  switch (fontFamily)
291  {
292  case FontFamily.StandardFontFamilies.Courier:
293  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBold), fontSize, defaultUnderline);
294  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierOblique), fontSize, defaultUnderline);
295  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBoldOblique), fontSize, defaultUnderline);
296  break;
297  case FontFamily.StandardFontFamilies.CourierBold:
298  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Courier), fontSize, defaultUnderline);
299  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBoldOblique), fontSize, defaultUnderline);
300  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierOblique), fontSize, defaultUnderline);
301  break;
302  case FontFamily.StandardFontFamilies.CourierOblique:
303  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBoldOblique), fontSize, defaultUnderline);
304  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Courier), fontSize, defaultUnderline);
305  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBold), fontSize, defaultUnderline);
306  break;
307  case FontFamily.StandardFontFamilies.CourierBoldOblique:
308  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierOblique), fontSize, defaultUnderline);
309  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.CourierBold), fontSize, defaultUnderline);
310  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Courier), fontSize, defaultUnderline);
311  break;
312 
313  case FontFamily.StandardFontFamilies.Helvetica:
314  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBold), fontSize, defaultUnderline);
315  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaOblique), fontSize, defaultUnderline);
316  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBoldOblique), fontSize, defaultUnderline);
317  break;
318  case FontFamily.StandardFontFamilies.HelveticaBold:
319  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Helvetica), fontSize, defaultUnderline);
320  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBoldOblique), fontSize, defaultUnderline);
321  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaOblique), fontSize, defaultUnderline);
322  break;
323  case FontFamily.StandardFontFamilies.HelveticaOblique:
324  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBoldOblique), fontSize, defaultUnderline);
325  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Helvetica), fontSize, defaultUnderline);
326  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBold), fontSize, defaultUnderline);
327  break;
328  case FontFamily.StandardFontFamilies.HelveticaBoldOblique:
329  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaOblique), fontSize, defaultUnderline);
330  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.HelveticaBold), fontSize, defaultUnderline);
331  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.Helvetica), fontSize, defaultUnderline);
332  break;
333 
334  case FontFamily.StandardFontFamilies.TimesRoman:
335  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBold), fontSize, defaultUnderline);
336  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesItalic), fontSize, defaultUnderline);
337  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBoldItalic), fontSize, defaultUnderline);
338  break;
339  case FontFamily.StandardFontFamilies.TimesBold:
340  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesRoman), fontSize, defaultUnderline);
341  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBoldItalic), fontSize, defaultUnderline);
342  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesItalic), fontSize, defaultUnderline);
343  break;
344  case FontFamily.StandardFontFamilies.TimesItalic:
345  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBoldItalic), fontSize, defaultUnderline);
346  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesRoman), fontSize, defaultUnderline);
347  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBold), fontSize, defaultUnderline);
348  break;
349  case FontFamily.StandardFontFamilies.TimesBoldItalic:
350  boldFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesItalic), fontSize, defaultUnderline);
351  italicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesBold), fontSize, defaultUnderline);
352  boldItalicFont = new Font(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesRoman), fontSize, defaultUnderline);
353  break;
354  }
355 
356  return Format(text, normalFont, boldFont, italicFont, boldItalicFont, defaultBrush);
357  }
358 
359  private static Font GetFont(bool bold, bool italic, bool underlined, Font normalFont, Font boldFont, Font italicFont, Font boldItalicFont)
360  {
361  if (!bold && !italic)
362  {
363  if (underlined)
364  {
365  return new Font(normalFont.FontFamily, normalFont.FontSize, normalFont.Underline == null);
366  }
367  else
368  {
369  return normalFont;
370  }
371  }
372  else if (bold && !italic)
373  {
374  if (underlined)
375  {
376  return new Font(boldFont.FontFamily, boldFont.FontSize, boldFont.Underline == null);
377  }
378  else
379  {
380  return boldFont;
381  }
382  }
383  else if (!bold && italic)
384  {
385  if (underlined)
386  {
387  return new Font(italicFont.FontFamily, italicFont.FontSize, italicFont.Underline == null);
388  }
389  else
390  {
391  return italicFont;
392  }
393  }
394  else
395  {
396  if (underlined)
397  {
398  return new Font(boldItalicFont.FontFamily, boldItalicFont.FontSize, boldItalicFont.Underline == null);
399  }
400  else
401  {
402  return boldItalicFont;
403  }
404  }
405  }
406 
407  private enum Tags
408  {
409  BoldOpen,
410  BoldClose,
411  ItalicsOpen,
412  ItalicsClose,
413  UnderlineOpen,
414  UnderlineClose,
415  SupOpen,
416  SupClose,
417  SubOpen,
418  SubClose,
419  ColourOpen,
420  ColourClose,
421  None
422  }
423 
424  private static Tags GetTag(string text, int start, out int tagEnd, out Brush tagBrush)
425  {
426  StringBuilder tag = new StringBuilder();
427  bool closed = false;
428 
429  int i = start;
430 
431  for (; i < text.Length; i++)
432  {
433  tag.Append(text[i]);
434 
435  if (text[i] == '>')
436  {
437  closed = true;
438  break;
439  }
440  }
441 
442  tagEnd = i;
443  tagBrush = null;
444 
445  if (!closed)
446  {
447  return Tags.None;
448  }
449  else
450  {
451  string tagString = tag.Replace(" ", "").ToString().ToLowerInvariant();
452 
453  if (tagString == "<b>" || tagString == "<strong>")
454  {
455  return Tags.BoldOpen;
456  }
457  else if (tagString == "</b>" || tagString == "</strong>")
458  {
459  return Tags.BoldClose;
460  }
461  else if (tagString == "<i>" || tagString == "<em>")
462  {
463  return Tags.ItalicsOpen;
464  }
465  else if (tagString == "</i>" || tagString == "</em>")
466  {
467  return Tags.ItalicsClose;
468  }
469  else if (tagString == "<u>")
470  {
471  return Tags.UnderlineOpen;
472  }
473  else if (tagString == "</u>")
474  {
475  return Tags.UnderlineClose;
476  }
477  else if (tagString == "<sup>")
478  {
479  return Tags.SupOpen;
480  }
481  else if (tagString == "</sup>")
482  {
483  return Tags.SupClose;
484  }
485  else if (tagString == "<sub>")
486  {
487  return Tags.SubOpen;
488  }
489  else if (tagString == "</sub>")
490  {
491  return Tags.SubClose;
492  }
493  else if (tagString.StartsWith("<#"))
494  {
495  string colour = tagString.Substring(1, tagString.Length - 2);
496 
497  Colour? col = null;
498 
499  try
500  {
501  col = Colour.FromCSSString(colour);
502  }
503  catch { }
504 
505  if (col == null)
506  {
507  colour = tagString.Substring(2, tagString.Length - 3);
508 
509  col = Colour.FromCSSString(colour);
510  }
511 
512  if (col != null)
513  {
514  tagBrush = col.Value;
515  return Tags.ColourOpen;
516  }
517  else
518  {
519  return Tags.None;
520  }
521  }
522  else if (tagString == "</#>")
523  {
524  return Tags.ColourClose;
525  }
526  else
527  {
528  return Tags.None;
529  }
530  }
531  }
532  }
533 
534  /// <summary>
535  /// Contains extension methods for collections of <see cref="FormattedText"/> objects.
536  /// </summary>
537  public static class FormattedTextExtensions
538  {
539  internal static Font.DetailedFontMetrics Measure(this IEnumerable<FormattedText> text, List<FormattedText> items, List<Font.DetailedFontMetrics> allMetrics)
540  {
541  double width = 0;
542  double advanceWidth = 0;
543 
544  double lsb = 0;
545  double rsb = 0;
546 
547  double top = 0;
548  double bottom = 0;
549 
550  bool isFirst = true;
551 
552  foreach (FormattedText txt in text)
553  {
554  items?.Add(txt);
555 
556  if (txt.Script == Script.Normal)
557  {
559  allMetrics?.Add(metrics);
560 
561  top = Math.Max(top, metrics.Top);
562  bottom = Math.Min(bottom, metrics.Bottom);
563  rsb = metrics.RightSideBearing;
564 
565  advanceWidth += metrics.AdvanceWidth;
566 
567  if (!isFirst)
568  {
569  width += metrics.Width + metrics.RightSideBearing + metrics.LeftSideBearing;
570  }
571  else
572  {
573  width += metrics.Width + metrics.RightSideBearing;
574  lsb = metrics.LeftSideBearing;
575  }
576  }
577  else
578  {
579  Font newFont = new Font(txt.Font.FontFamily, txt.Font.FontSize * 0.7);
580 
581  Font.DetailedFontMetrics metrics = newFont.MeasureTextAdvanced(txt.Text);
582  allMetrics?.Add(metrics);
583 
584  advanceWidth += metrics.AdvanceWidth;
585 
586  if (txt.Script == Script.Subscript)
587  {
588  top = Math.Max(top, metrics.Top - txt.Font.FontSize * 0.14);
589  bottom = Math.Min(bottom, metrics.Bottom - txt.Font.FontSize * 0.14);
590  }
591  else if (txt.Script == Script.Superscript)
592  {
593  top = Math.Max(top, metrics.Top + txt.Font.FontSize * 0.33);
594  bottom = Math.Min(bottom, metrics.Bottom + txt.Font.FontSize * 0.33);
595  }
596 
597  rsb = metrics.RightSideBearing;
598 
599  if (!isFirst)
600  {
601  width += metrics.Width + metrics.RightSideBearing + metrics.LeftSideBearing;
602  }
603  else
604  {
605  width += metrics.Width + metrics.RightSideBearing;
606  lsb = metrics.LeftSideBearing;
607  }
608  }
609 
610  if (isFirst)
611  {
612  isFirst = false;
613  }
614  }
615 
616  width -= rsb;
617 
618  return new Font.DetailedFontMetrics(width, top - bottom, lsb, rsb, top, bottom, advanceWidth);
619  }
620 
621  /// <summary>
622  /// Measures a collection of <see cref="FormattedText"/> objects.
623  /// </summary>
624  /// <param name="text">The collection of <see cref="FormattedText"/> objects to be measured.</param>
625  /// <returns>A <see cref="Font.DetailedFontMetrics"/> containing detailed measurements for the text obtained by composing the elements in the <see cref="FormattedText"/> collection.</returns>
626  public static Font.DetailedFontMetrics Measure(this IEnumerable<FormattedText> text)
627  {
628  return text.Measure(null, null);
629  }
630  }
631 
632 }
VectSharp.Font.Underline
FontUnderline Underline
Determines the underline style of text drawn using this font. If this is null, the text is not underl...
Definition: Font.cs:294
VectSharp.FormattedTextExtensions.Measure
static Font.DetailedFontMetrics Measure(this IEnumerable< FormattedText > text)
Measures a collection of FormattedText objects.
Definition: FormattedText.cs:626
VectSharp.Font.DetailedFontMetrics.RightSideBearing
double RightSideBearing
How much the rightmost glyph in the string overhangs the glyph end on the right. Positive for glyphs ...
Definition: Font.cs:121
VectSharp
Definition: Brush.cs:26
VectSharp.FormattedText.Text
string Text
Represents the text represented by this instance.
Definition: FormattedText.cs:55
VectSharp.Font.MeasureTextAdvanced
DetailedFontMetrics MeasureTextAdvanced(string text)
Measure all the metrics of a text string when typeset with this font.
Definition: Font.cs:358
VectSharp.Brush
Represents a brush used to fill or stroke graphics elements. This could be a solid colour,...
Definition: Brush.cs:31
VectSharp.FormattedText.Format
static IEnumerable< FormattedText > Format(string text, FontFamily.StandardFontFamilies fontFamily, double fontSize, bool defaultUnderline=false, Brush defaultBrush=null)
Parse the formatting information contained in a text string into a collection of FormattedText object...
Definition: FormattedText.cs:282
VectSharp.Font
Represents a typeface with a specific size.
Definition: Font.cs:29
VectSharp.FormattedText.FormattedText
FormattedText(string text, Font font, Script script=Script.Normal, Brush brush=null)
Creates a new FormattedText instance with the specified text , font , script position and brush .
Definition: FormattedText.cs:79
VectSharp.FontFamily.ResolveFontFamily
static FontFamily ResolveFontFamily(string fontFamily)
Create a new font family from the specified family name or true type file. If the family name or the ...
VectSharp.FontFamily
Represents a typeface.
Definition: Font.cs:421
VectSharp.Script.Superscript
@ Superscript
The text is a superscript.
VectSharp.Script.Subscript
@ Subscript
The text is a subscript.
VectSharp.FormattedText.Brush
Brush Brush
Represents the brush that should be used to draw the text. If this is null, the default brush is used...
Definition: FormattedText.cs:70
VectSharp.FormattedText.Font
Font Font
Represents the font that should be used to draw the text.
Definition: FormattedText.cs:60
VectSharp.Font.DetailedFontMetrics.AdvanceWidth
double AdvanceWidth
Advance width of the text (excluding any left- or right- side bearing).
Definition: Font.cs:136
VectSharp.FormattedText.Script
Script Script
Represents the position of the text.
Definition: FormattedText.cs:65
VectSharp.Font.DetailedFontMetrics
Represents detailed information about the metrics of a text string when drawn with a certain font.
Definition: Font.cs:102
VectSharp.Script
Script
Represents the position of the text.
Definition: FormattedText.cs:30
VectSharp.FormattedText.Format
static IEnumerable< FormattedText > Format(string text, Font normalFont, Font boldFont, Font italicFont, Font boldItalicFont, Brush defaultBrush=null)
Parse the formatting information contained in a text string into a collection of FormattedText object...
Definition: FormattedText.cs:105
VectSharp.FontFamily.StandardFontFamilies
StandardFontFamilies
The 14 standard font families.
Definition: Font.cs:500
VectSharp.Font.FontFamily
FontFamily FontFamily
Font typeface.
Definition: Font.cs:158
VectSharp.FormattedTextExtensions
Contains extension methods for collections of FormattedText objects.
Definition: FormattedText.cs:538
VectSharp.Font.FontSize
double FontSize
Font size, in graphics units.
Definition: Font.cs:153
VectSharp.Script.Normal
@ Normal
The text is normal text.
VectSharp.FormattedText
Represents a run of text that should be drawn with the same style.
Definition: FormattedText.cs:51