19 using System.Collections.Generic;
22 using static System.Net.Mime.MediaTypeNames;
55 public string Text {
get; }
105 public static IEnumerable<FormattedText>
Format(
string text,
Font normalFont,
Font boldFont,
Font italicFont,
Font boldItalicFont,
Brush defaultBrush =
null)
107 StringBuilder currentRun =
new StringBuilder();
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);
116 for (
int i = 0; i < text.Length; i++)
120 currentRun.Append(text[i]);
124 Tags tag = GetTag(text, i, out
int tagEnd, out
Brush tagBrush);
126 if (tag == Tags.None)
128 currentRun.Append(text[i]);
134 string txt = currentRun.ToString();
139 if (!
string.IsNullOrEmpty(txt))
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());
149 if (!
string.IsNullOrEmpty(txt))
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());
157 case Tags.UnderlineOpen:
158 if (!
string.IsNullOrEmpty(txt))
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());
165 case Tags.UnderlineClose:
166 if (underlineDepth > 0)
168 if (!
string.IsNullOrEmpty(txt))
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());
177 case Tags.ItalicsOpen:
178 if (!
string.IsNullOrEmpty(txt))
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());
185 case Tags.ItalicsClose:
186 if (italicsDepth > 0)
188 if (!
string.IsNullOrEmpty(txt))
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());
198 if (!
string.IsNullOrEmpty(txt))
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());
206 if (superscriptDepth > 0)
208 if (!
string.IsNullOrEmpty(txt))
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());
218 if (!
string.IsNullOrEmpty(txt))
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());
226 if (subscriptDepth > 0)
228 if (!
string.IsNullOrEmpty(txt))
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());
236 case Tags.ColourOpen:
237 if (!
string.IsNullOrEmpty(txt))
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());
242 brushes.Push(tagBrush);
244 case Tags.ColourClose:
245 if (brushes.Count > 1)
247 if (!
string.IsNullOrEmpty(txt))
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());
260 if (currentRun.Length > 0)
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());
286 Font boldFont = normalFont;
287 Font italicFont = normalFont;
288 Font boldItalicFont = normalFont;
356 return Format(text, normalFont, boldFont, italicFont, boldItalicFont, defaultBrush);
359 private static Font GetFont(
bool bold,
bool italic,
bool underlined,
Font normalFont,
Font boldFont,
Font italicFont,
Font boldItalicFont)
361 if (!bold && !italic)
372 else if (bold && !italic)
383 else if (!bold && italic)
402 return boldItalicFont;
424 private static Tags GetTag(
string text,
int start, out
int tagEnd, out
Brush tagBrush)
426 StringBuilder tag =
new StringBuilder();
431 for (; i < text.Length; i++)
451 string tagString = tag.Replace(
" ",
"").ToString().ToLowerInvariant();
453 if (tagString ==
"<b>" || tagString ==
"<strong>")
455 return Tags.BoldOpen;
457 else if (tagString ==
"</b>" || tagString ==
"</strong>")
459 return Tags.BoldClose;
461 else if (tagString ==
"<i>" || tagString ==
"<em>")
463 return Tags.ItalicsOpen;
465 else if (tagString ==
"</i>" || tagString ==
"</em>")
467 return Tags.ItalicsClose;
469 else if (tagString ==
"<u>")
471 return Tags.UnderlineOpen;
473 else if (tagString ==
"</u>")
475 return Tags.UnderlineClose;
477 else if (tagString ==
"<sup>")
481 else if (tagString ==
"</sup>")
483 return Tags.SupClose;
485 else if (tagString ==
"<sub>")
489 else if (tagString ==
"</sub>")
491 return Tags.SubClose;
493 else if (tagString.StartsWith(
"<#"))
495 string colour = tagString.Substring(1, tagString.Length - 2);
501 col = Colour.FromCSSString(colour);
507 colour = tagString.Substring(2, tagString.Length - 3);
509 col = Colour.FromCSSString(colour);
514 tagBrush = col.Value;
515 return Tags.ColourOpen;
522 else if (tagString ==
"</#>")
524 return Tags.ColourClose;
539 internal static Font.
DetailedFontMetrics Measure(
this IEnumerable<FormattedText> text, List<FormattedText> items, List<Font.DetailedFontMetrics> allMetrics)
542 double advanceWidth = 0;
559 allMetrics?.Add(metrics);
561 top = Math.Max(top, metrics.Top);
562 bottom = Math.Min(bottom, metrics.Bottom);
565 advanceWidth += metrics.AdvanceWidth;
569 width += metrics.Width + metrics.RightSideBearing + metrics.LeftSideBearing;
573 width += metrics.Width + metrics.RightSideBearing;
574 lsb = metrics.LeftSideBearing;
582 allMetrics?.Add(metrics);
588 top = Math.Max(top, metrics.Top - txt.
Font.
FontSize * 0.14);
589 bottom = Math.Min(bottom, metrics.Bottom - txt.
Font.
FontSize * 0.14);
593 top = Math.Max(top, metrics.Top + txt.
Font.
FontSize * 0.33);
594 bottom = Math.Min(bottom, metrics.Bottom + txt.
Font.
FontSize * 0.33);
597 rsb = metrics.RightSideBearing;
601 width += metrics.Width + metrics.RightSideBearing + metrics.LeftSideBearing;
605 width += metrics.Width + metrics.RightSideBearing;
606 lsb = metrics.LeftSideBearing;
628 return text.Measure(
null,
null);