VectSharp  2.2.1
A light library for C# vector graphics
Word.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 
22 namespace VectSharp.Markdown
23 {
24  internal class Word
25  {
26  public char PrecedingWhitespace { get; }
27  public int WhitespaceCount { get; }
28  public string Text { get; }
29 
30  public Font.DetailedFontMetrics Metrics { get; }
31 
32  private Word(char precedingWhitespace, int whitespaceCount, string text, Font font)
33  {
34  this.PrecedingWhitespace = precedingWhitespace;
35  this.WhitespaceCount = whitespaceCount;
36  this.Text = text;
37 
38  if (!string.IsNullOrEmpty(text))
39  {
40  this.Metrics = font.MeasureTextAdvanced(text);
41  }
42  else
43  {
44  this.Metrics = font.MeasureTextAdvanced(" ");
45  }
46  }
47 
48  private static Dictionary<FontFamily, double> SpaceWidths { get; } = new Dictionary<FontFamily, double>();
49 
50  private static IEnumerable<Word> SplitWord(Word word, Font font, double maxWidth)
51  {
52  if (!SpaceWidths.TryGetValue(font.FontFamily, out double spaceWidth))
53  {
54  spaceWidth = font.FontFamily.TrueTypeFile.Get1000EmGlyphWidth(' ') / 1000.0;
55  SpaceWidths[font.FontFamily] = spaceWidth;
56  }
57 
58  spaceWidth *= font.FontSize;
59 
60  while (!string.IsNullOrEmpty(word.Text) && word.Metrics.Width + word.Metrics.LeftSideBearing + word.Metrics.RightSideBearing + spaceWidth * word.WhitespaceCount * (word.PrecedingWhitespace == '\t' ? 4 : 1) > maxWidth)
61  {
62  int minIndex = 1;
63  int maxIndex = word.Text.Length;
64 
65  while (maxIndex - minIndex > 1)
66  {
67  int index = (minIndex + maxIndex) / 2;
68 
69  double width = font.MeasureText(word.Text.Substring(0, index)).Width;
70 
71  if (width > maxWidth)
72  {
73  maxIndex = index;
74  }
75  else if (width < maxWidth)
76  {
77  minIndex = index;
78  }
79  else
80  {
81  minIndex = index;
82  maxIndex = index;
83  }
84  }
85 
86  Word newWord = new Word(word.PrecedingWhitespace, word.WhitespaceCount, word.Text.Substring(0, minIndex), font);
87  yield return newWord;
88 
89  word = new Word('\0', 0, word.Text.Substring(minIndex), font);
90  }
91 
92  yield return word;
93  }
94 
95  public static IEnumerable<Word> GetWords(string text, Font font, double maxWidth)
96  {
97  if (!SpaceWidths.TryGetValue(font.FontFamily, out double spaceWidth))
98  {
99  spaceWidth = font.FontFamily.TrueTypeFile.Get1000EmGlyphWidth(' ') / 1000.0;
100  SpaceWidths[font.FontFamily] = spaceWidth;
101  }
102 
103  spaceWidth *= font.FontSize;
104 
105  foreach (Word w in GetWordsInternal(text, font))
106  {
107  if (w.Metrics.Width + w.Metrics.LeftSideBearing + w.Metrics.RightSideBearing + spaceWidth * w.WhitespaceCount * (w.PrecedingWhitespace == '\t' ? 4 : 1) > maxWidth)
108  {
109  foreach (Word w2 in SplitWord(w, font, maxWidth))
110  {
111  yield return w2;
112  }
113  }
114  else
115  {
116  yield return w;
117  }
118  }
119  }
120 
121  private static IEnumerable<Word> GetWordsInternal(string text, Font font)
122  {
123  StringBuilder currWord = new StringBuilder();
124 
125  char currWhitespace = '\0';
126  int whitespaceCount = 0;
127 
128  for (int i = 0; i < text.Length; i++)
129  {
130  if (char.IsWhiteSpace(text[i]))
131  {
132  if (currWord.Length > 0)
133  {
134  yield return new Word(currWhitespace, whitespaceCount, currWord.ToString(), font);
135 
136  currWord.Clear();
137  currWhitespace = text[i];
138  whitespaceCount = 1;
139  }
140  else if (currWhitespace == text[i])
141  {
142  whitespaceCount++;
143  }
144  else
145  {
146  yield return new Word(currWhitespace, whitespaceCount, null, font);
147 
148  currWhitespace = text[i];
149  whitespaceCount = 1;
150  }
151  }
152  else
153  {
154  currWord.Append(text[i]);
155  }
156  }
157 
158  if (currWord.Length > 0)
159  {
160  yield return new Word(currWhitespace, whitespaceCount, currWord.ToString(), font);
161 
162  currWord.Clear();
163  }
164  else if (whitespaceCount > 0)
165  {
166  yield return new Word(currWhitespace, whitespaceCount, null, font);
167  }
168  }
169  }
170 }
VectSharp.Markdown
Definition: HtmlTag.cs:26