Parse formatted text into segments.
- Horizontal rule - Bullet list item (• + text) <indent> - Indent by 4 characters <indent=8> - Indent by 8 characters
56 {
57 std::vector<FormatSegment> segments;
58
59
60 bool boldOn = false;
61 bool italicOn = false;
62 bool underlineOn = false;
63 bool colorOn = false;
64 PColor currentColor(0, 0, 0, 255);
65 bool sizeOn = false;
66 int currentSize = 0;
67 int currentIndent = 0;
69
70 std::string currentText;
71 size_t pos = 0;
72
73 while (pos < input.length()) {
74
75 if (input[pos] == '<') {
76
77 if (!currentText.empty()) {
79 seg.
text = currentText;
80 seg.
style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
87 segments.push_back(seg);
88 currentText.clear();
89 }
90
91
92 size_t tagEnd = input.find('>', pos);
93 if (tagEnd == std::string::npos) {
94
95 currentText += input[pos];
96 pos++;
97 continue;
98 }
99
100
101 std::string tag = input.substr(pos + 1, tagEnd - pos - 1);
102 pos = tagEnd + 1;
103
104
105 bool isClosing = (tag.length() > 0 && tag[0] == '/');
106 std::string tagName = isClosing ? tag.substr(1) : tag;
107
108
109
110 std::string param;
111 size_t spacePos = tagName.find(' ');
112 size_t eqPos = tagName.find('=');
113
114
115
116 if (spacePos != std::string::npos && (eqPos == std::string::npos || spacePos < eqPos)) {
117
118 tagName = tagName.substr(0, spacePos);
119 } else if (eqPos != std::string::npos) {
120
121 param = tagName.substr(eqPos + 1);
122 tagName = tagName.substr(0, eqPos);
123 }
124
125 tagName = toLower(tagName);
126
127
128 if (tagName == "br" || tagName == "br/") {
129
130 currentText += '\n';
131
132
133 if (!currentText.empty()) {
134 FormatSegment seg;
135 seg.
text = currentText;
136 seg.style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
137 seg.hasColorOverride = colorOn;
138 seg.colorOverride = currentColor;
139 seg.hasSizeOverride = sizeOn;
140 seg.sizeOverride = currentSize;
141 seg.indentPixels = currentIndent;
142 seg.justification = currentJustification;
143 segments.push_back(seg);
144 currentText.clear();
145 }
146
147
148 currentIndent = 0;
150 } else if (tagName == "b") {
151 boldOn = !isClosing;
152 } else if (tagName == "i") {
153 italicOn = !isClosing;
154 } else if (tagName == "u") {
155 underlineOn = !isClosing;
156 } else if (tagName == "c") {
157 if (isClosing) {
158 colorOn = false;
159 } else if (!param.empty()) {
161 colorOn = true;
162 }
163 }
164 } else if (tagName == "size") {
165
166
167
168 if (isClosing) {
169 sizeOn = false;
170 } else if (!param.empty()) {
171 try {
172 int size = std::stoi(param);
173 if (size > 0 && size < 1000) {
174 sizeOn = true;
175 currentSize = size;
176 }
177 } catch (...) {
178
179 }
180 }
181 } else if (tagName == "h1" || tagName == "h2" || tagName == "h3" ||
182 tagName == "h4" || tagName == "h5" || tagName == "h6") {
183
184
185 if (!isClosing) {
186 boldOn = true;
187 sizeOn = true;
188
189
190 int level = tagName[1] - '0';
191 if (level == 1) currentSize = 230;
192 else if (level == 2) currentSize = 200;
193 else if (level == 3) currentSize = 170;
194 else if (level == 4) currentSize = 140;
195 else if (level == 5) currentSize = 130;
196 else if (level == 6) currentSize = 115;
197
198
199 if (!param.empty()) {
200 std::string justifyParam = toLower(param);
201 if (justifyParam ==
"left") currentJustification =
JUSTIFY_LEFT;
202 else if (justifyParam ==
"center") currentJustification =
JUSTIFY_CENTER;
203 else if (justifyParam ==
"right") currentJustification =
JUSTIFY_RIGHT;
204 }
205 } else {
206
207 boldOn = false;
208 sizeOn = false;
210
211
212 currentText += '\n';
213 }
214 } else if (tagName == "indent") {
215
216
217 int indentChars = 4;
218 if (!param.empty()) {
219 try {
220 indentChars = std::stoi(param);
221 } catch (...) {
222
223 }
224 }
225 currentIndent = indentChars * charWidth;
226 } else if (tagName == "p") {
227
228
229
230
231 if (isClosing) {
232
233 sizeOn = false;
235 } else {
236
237
238 if (!param.empty() && tag.find("align=") == std::string::npos && tag.find("size=") == std::string::npos) {
239
240 std::string justifyParam = toLower(param);
241 if (justifyParam ==
"left") currentJustification =
JUSTIFY_LEFT;
242 else if (justifyParam ==
"center") currentJustification =
JUSTIFY_CENTER;
243 else if (justifyParam ==
"right") currentJustification =
JUSTIFY_RIGHT;
244 } else {
245
246
247 size_t alignPos = tag.find("align=");
248 if (alignPos != std::string::npos) {
249 size_t alignStart = alignPos + 6;
250 size_t alignEnd = tag.find_first_of(" >", alignStart);
251 if (alignEnd == std::string::npos) alignEnd = tag.length();
252 std::string alignValue = toLower(tag.substr(alignStart, alignEnd - alignStart));
253
254 if (alignValue ==
"left") currentJustification =
JUSTIFY_LEFT;
255 else if (alignValue ==
"center") currentJustification =
JUSTIFY_CENTER;
256 else if (alignValue ==
"right") currentJustification =
JUSTIFY_RIGHT;
257 }
258
259 size_t sizePos = tag.find("size=");
260 if (sizePos != std::string::npos) {
261 size_t sizeStart = sizePos + 5;
262 size_t sizeEnd = tag.find_first_of(" >", sizeStart);
263 if (sizeEnd == std::string::npos) sizeEnd = tag.length();
264 std::string sizeValue = tag.substr(sizeStart, sizeEnd - sizeStart);
265
266 try {
267 int size = std::stoi(sizeValue);
268 if (size > 0 && size < 1000) {
269 sizeOn = true;
270 currentSize = size;
271 }
272 } catch (...) {
273
274 }
275 }
276 }
277 }
278 } else if (tagName == "hr") {
279
280 FormatSegment hrSeg;
281 hrSeg.isHorizontalRule = true;
282 hrSeg.text = "\n";
283 hrSeg.indentPixels = currentIndent;
284 segments.push_back(hrSeg);
285
286 currentIndent = 0;
287 } else if (tagName == "li") {
288
289
290
291 if (!currentText.empty()) {
292 FormatSegment seg;
293 seg.text = currentText;
294 seg.style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
295 seg.hasColorOverride = colorOn;
296 seg.colorOverride = currentColor;
297 seg.hasSizeOverride = sizeOn;
298 seg.sizeOverride = currentSize;
299 seg.indentPixels = currentIndent;
300 seg.justification = currentJustification;
301 segments.push_back(seg);
302 currentText.clear();
303 }
304
305
306 if (!segments.empty()) {
307 FormatSegment& lastSeg = segments.back();
308 if (!lastSeg.text.empty() && lastSeg.text.back() != '\n') {
309 lastSeg.text += '\n';
310 }
311 currentIndent = 0;
312 }
313
314
315 currentIndent += 2 * charWidth;
316
317 FormatSegment liSeg;
318 liSeg.isBulletItem = true;
319 liSeg.text = "• ";
320 liSeg.style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
321 liSeg.hasColorOverride = colorOn;
322 liSeg.colorOverride = currentColor;
323 liSeg.hasSizeOverride = sizeOn;
324 liSeg.sizeOverride = currentSize;
325 liSeg.indentPixels = currentIndent;
326 liSeg.justification = currentJustification;
327 segments.push_back(liSeg);
328
329
330 currentIndent += 2 * charWidth;
331 } else {
332
333 currentText += '<';
334 currentText += (isClosing ? "/" : "");
335 currentText += tagName;
336 if (!param.empty()) {
337 currentText += '=';
338 currentText += param;
339 }
340 currentText += '>';
341 }
342 } else {
343
344 char ch = input[pos];
345 currentText += ch;
346
347
348 if (ch == '\n') {
349
350 if (!currentText.empty()) {
351 FormatSegment seg;
352 seg.text = currentText;
353 seg.style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
354 seg.hasColorOverride = colorOn;
355 seg.colorOverride = currentColor;
356 seg.hasSizeOverride = sizeOn;
357 seg.sizeOverride = currentSize;
358 seg.indentPixels = currentIndent;
359 seg.justification = currentJustification;
360 segments.push_back(seg);
361 currentText.clear();
362 }
363
364 currentIndent = 0;
366 }
367
368 pos++;
369 }
370 }
371
372
373 if (!currentText.empty()) {
374 FormatSegment seg;
375 seg.text = currentText;
376 seg.style = (boldOn ? 1 : 0) + (italicOn ? 2 : 0) + (underlineOn ? 4 : 0);
377 seg.hasColorOverride = colorOn;
378 seg.colorOverride = currentColor;
379 seg.hasSizeOverride = sizeOn;
380 seg.sizeOverride = currentSize;
381 seg.indentPixels = currentIndent;
382 seg.justification = currentJustification;
383 segments.push_back(seg);
384 }
385
386
387 if (segments.empty()) {
388 segments.push_back(FormatSegment());
389 }
390
391 return segments;
392}