19 using System.Collections.Generic;
26 public char PrecedingWhitespace {
get; }
27 public int WhitespaceCount {
get; }
28 public string Text {
get; }
30 public Font.DetailedFontMetrics Metrics {
get; }
32 private Word(
char precedingWhitespace,
int whitespaceCount,
string text, Font font)
34 this.PrecedingWhitespace = precedingWhitespace;
35 this.WhitespaceCount = whitespaceCount;
38 if (!
string.IsNullOrEmpty(text))
40 this.Metrics = font.MeasureTextAdvanced(text);
44 this.Metrics = font.MeasureTextAdvanced(
" ");
48 private static Dictionary<FontFamily, double> SpaceWidths {
get; } =
new Dictionary<FontFamily, double>();
50 private static IEnumerable<Word> SplitWord(Word word, Font font,
double maxWidth)
52 if (!SpaceWidths.TryGetValue(font.FontFamily, out
double spaceWidth))
54 spaceWidth = font.FontFamily.TrueTypeFile.Get1000EmGlyphWidth(
' ') / 1000.0;
55 SpaceWidths[font.FontFamily] = spaceWidth;
58 spaceWidth *= font.FontSize;
60 while (!
string.IsNullOrEmpty(word.Text) && word.Metrics.Width + word.Metrics.LeftSideBearing + word.Metrics.RightSideBearing + spaceWidth * word.WhitespaceCount * (word.PrecedingWhitespace ==
'\t' ? 4 : 1) > maxWidth)
63 int maxIndex = word.Text.Length;
65 while (maxIndex - minIndex > 1)
67 int index = (minIndex + maxIndex) / 2;
69 double width = font.MeasureText(word.Text.Substring(0, index)).Width;
75 else if (width < maxWidth)
86 Word newWord =
new Word(word.PrecedingWhitespace, word.WhitespaceCount, word.Text.Substring(0, minIndex), font);
89 word =
new Word(
'\0', 0, word.Text.Substring(minIndex), font);
95 public static IEnumerable<Word> GetWords(
string text, Font font,
double maxWidth)
97 if (!SpaceWidths.TryGetValue(font.FontFamily, out
double spaceWidth))
99 spaceWidth = font.FontFamily.TrueTypeFile.Get1000EmGlyphWidth(
' ') / 1000.0;
100 SpaceWidths[font.FontFamily] = spaceWidth;
103 spaceWidth *= font.FontSize;
105 foreach (Word w
in GetWordsInternal(text, font))
107 if (w.Metrics.Width + w.Metrics.LeftSideBearing + w.Metrics.RightSideBearing + spaceWidth * w.WhitespaceCount * (w.PrecedingWhitespace ==
'\t' ? 4 : 1) > maxWidth)
109 foreach (Word w2
in SplitWord(w, font, maxWidth))
121 private static IEnumerable<Word> GetWordsInternal(
string text, Font font)
123 StringBuilder currWord =
new StringBuilder();
125 char currWhitespace =
'\0';
126 int whitespaceCount = 0;
128 for (
int i = 0; i < text.Length; i++)
130 if (
char.IsWhiteSpace(text[i]))
132 if (currWord.Length > 0)
134 yield
return new Word(currWhitespace, whitespaceCount, currWord.ToString(), font);
137 currWhitespace = text[i];
140 else if (currWhitespace == text[i])
146 yield
return new Word(currWhitespace, whitespaceCount,
null, font);
148 currWhitespace = text[i];
154 currWord.Append(text[i]);
158 if (currWord.Length > 0)
160 yield
return new Word(currWhitespace, whitespaceCount, currWord.ToString(), font);
164 else if (whitespaceCount > 0)
166 yield
return new Word(currWhitespace, whitespaceCount,
null, font);