Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "SPFontFormatter.h"
24 : #include "SPFontFace.h"
25 :
26 : namespace STAPPLER_VERSIONIZED stappler::font {
27 :
28 130 : Formatter::Formatter() { }
29 :
30 17009 : Formatter::Formatter(FontCallback &&cb, TextLayoutData<memory::StandartInterface> *d)
31 17009 : : fontCallback(move(cb)) {
32 17009 : reset(d);
33 17009 : }
34 :
35 0 : Formatter::Formatter(FontCallback &&cb, TextLayoutData<memory::PoolInterface> *d)
36 0 : : fontCallback(move(cb)) {
37 0 : reset(d);
38 0 : }
39 :
40 130 : void Formatter::setFontCallback(FontCallback &&cb) {
41 130 : fontCallback = move(cb);
42 130 : }
43 :
44 17139 : void Formatter::reset(TextLayoutData<memory::StandartInterface> *d) {
45 17139 : _output = Output(d);
46 17139 : reset();
47 17139 : }
48 :
49 0 : void Formatter::reset(TextLayoutData<memory::PoolInterface> *d) {
50 0 : _output = Output(d);
51 0 : reset();
52 0 : }
53 :
54 17139 : void Formatter::reset() {
55 17139 : b = 0;
56 17139 : c = 0;
57 :
58 17139 : defaultWidth = 0;
59 17139 : width = 0;
60 17139 : lineOffset = 0;
61 17139 : lineX = 0;
62 17139 : lineY = 0;
63 :
64 17139 : maxLineX = 0;
65 :
66 17139 : charNum = 0;
67 17139 : lineHeight = 0;
68 17139 : currentLineHeight = 0;
69 17139 : rangeLineHeight = 0;
70 :
71 17139 : lineHeightMod = 1.0f;
72 17139 : lineHeightIsAbsolute = false;
73 :
74 17139 : firstInLine = 0;
75 17139 : wordWrapPos = 0;
76 :
77 17139 : bufferedSpace = false;
78 17139 : }
79 :
80 17139 : void Formatter::finalize() {
81 17139 : if (firstInLine < charNum) {
82 16958 : pushLine(false);
83 : }
84 :
85 17139 : if (!_output.chars.empty() && _output.chars.back().charID == char16_t(0x0A)) {
86 131 : pushLine(false);
87 : }
88 :
89 17139 : auto chars = _output.chars.size();
90 17139 : if (chars > 0 && _output.ranges.size() > 0 && _output.lines.size() > 0) {
91 17089 : auto &lastRange = _output.ranges.back();
92 17089 : auto &lastLine = _output.lines.back();
93 :
94 17089 : if (lastLine.start + lastLine.count != chars) {
95 0 : lastLine.count = uint32_t(chars - lastLine.start);
96 : }
97 :
98 17089 : if (lastRange.start + lastRange.count != chars) {
99 0 : lastRange.count = uint32_t(chars - lastRange.start);
100 : }
101 : }
102 :
103 17139 : *_output.width = getWidth();
104 17139 : *_output.height = getHeight();
105 17139 : *_output.maxAdvance = getMaxLineX();
106 17139 : }
107 :
108 0 : void Formatter::setLinePositionCallback(const LinePositionCallback &func) {
109 0 : linePositionFunc = func;
110 0 : }
111 :
112 16875 : void Formatter::setWidth(uint16_t w) {
113 16875 : defaultWidth = w;
114 16875 : width = w;
115 16875 : }
116 :
117 16487 : void Formatter::setTextAlignment(TextAlign align) {
118 16487 : alignment = align;
119 16487 : }
120 :
121 130 : void Formatter::setLineHeightAbsolute(uint16_t val) {
122 130 : lineHeight = val;
123 130 : currentLineHeight = val;
124 130 : lineHeightIsAbsolute = true;
125 130 : parseFontLineHeight(rangeLineHeight);
126 130 : }
127 :
128 259 : void Formatter::setLineHeightRelative(float val) {
129 259 : lineHeightMod = val;
130 259 : lineHeightIsAbsolute = false;
131 259 : parseFontLineHeight(rangeLineHeight);
132 259 : }
133 :
134 16487 : void Formatter::setMaxWidth(uint16_t value) {
135 16487 : maxWidth = value;
136 16487 : }
137 16487 : void Formatter::setMaxLines(size_t value) {
138 16487 : maxLines = value;
139 16487 : }
140 16487 : void Formatter::setOpticalAlignment(bool value) {
141 16487 : opticalAlignment = value;
142 16487 : }
143 16487 : void Formatter::setEmplaceAllChars(bool value) {
144 16487 : emplaceAllChars = value;
145 16487 : }
146 16487 : void Formatter::setFillerChar(char16_t value) {
147 16487 : _fillerChar = value;
148 16487 : }
149 0 : void Formatter::setHyphens(HyphenMap *map) {
150 0 : _hyphens = map;
151 0 : }
152 0 : void Formatter::setRequest(ContentRequest req) {
153 0 : request = req;
154 0 : }
155 :
156 17139 : void Formatter::begin(uint16_t ind, uint16_t blockMargin) {
157 17139 : lineX = ind;
158 :
159 17139 : firstInLine = charNum;
160 17139 : wordWrapPos = charNum;
161 :
162 17139 : bufferedSpace = false;
163 17139 : c = 0;
164 17139 : b = 0;
165 :
166 17139 : if (lineY != 0) {
167 0 : lineY += blockMargin;
168 : }
169 17139 : }
170 :
171 17713 : void Formatter::parseWhiteSpace(WhiteSpace whiteSpacePolicy) {
172 17713 : switch (whiteSpacePolicy) {
173 0 : case WhiteSpace::Normal:
174 0 : preserveLineBreaks = false;
175 0 : collapseSpaces = true;
176 0 : wordWrap = true;
177 0 : break;
178 0 : case WhiteSpace::Nowrap:
179 0 : preserveLineBreaks = false;
180 0 : collapseSpaces = true;
181 0 : wordWrap = false;
182 0 : break;
183 0 : case WhiteSpace::Pre:
184 0 : preserveLineBreaks = true;
185 0 : collapseSpaces = false;
186 0 : wordWrap = false;
187 0 : break;
188 0 : case WhiteSpace::PreLine:
189 0 : preserveLineBreaks = true;
190 0 : collapseSpaces = true;
191 0 : wordWrap = true;
192 0 : break;
193 17713 : case WhiteSpace::PreWrap:
194 17713 : preserveLineBreaks = true;
195 17713 : collapseSpaces = false;
196 17713 : wordWrap = true;
197 17713 : break;
198 0 : default:
199 0 : preserveLineBreaks = false;
200 0 : collapseSpaces = true;
201 0 : wordWrap = true;
202 0 : break;
203 : };
204 17713 : }
205 :
206 72051 : void Formatter::parseFontLineHeight(uint16_t h) {
207 72051 : if (!lineHeightIsAbsolute) {
208 71791 : if (lineHeight == 0) {
209 17088 : lineHeight = h;
210 : }
211 71791 : float fontLineHeight = static_cast<uint16_t>(h * lineHeightMod);
212 71791 : if (fontLineHeight > currentLineHeight) {
213 17863 : currentLineHeight = fontLineHeight;
214 : }
215 : }
216 72051 : }
217 :
218 21374 : bool Formatter::updatePosition(uint16_t &linePos, uint16_t &height) {
219 21374 : if (linePositionFunc) {
220 0 : auto pos = linePositionFunc(linePos, height, _primaryFontSet->getSpec().density);
221 0 : lineOffset = pos.offset;
222 0 : width = std::min(pos.width, defaultWidth);
223 :
224 0 : uint16_t maxHeight = lineHeight * 16;
225 0 : uint16_t extraHeight = 0;
226 :
227 : // skip lines if not enough space
228 0 : while (width < _primaryFontSet->getFontHeight() && extraHeight < maxHeight) {
229 0 : extraHeight += lineHeight;
230 0 : linePos += lineHeight;
231 0 : pos = linePositionFunc(linePos, height, _primaryFontSet->getSpec().density);
232 0 : lineOffset = pos.offset;
233 0 : width = std::min(pos.width, defaultWidth);
234 : }
235 :
236 0 : if (extraHeight >= maxHeight) {
237 0 : return false;
238 : }
239 : }
240 21374 : return true;
241 : }
242 :
243 0 : uint16_t Formatter::getAdvance(const CharLayoutData &ch) const {
244 0 : return ch.advance;
245 : }
246 :
247 0 : uint16_t Formatter::getAdvance(uint16_t pos) const {
248 0 : if (pos < _output.chars.size()) {
249 0 : return getAdvance(_output.chars.at(pos));
250 : } else {
251 0 : return 0;
252 : }
253 : }
254 :
255 53818 : inline uint16_t Formatter::getAdvancePosition(const CharLayoutData &ch) const {
256 53818 : return ch.pos + ch.advance;
257 : }
258 :
259 53818 : inline uint16_t Formatter::getAdvancePosition(uint16_t pos) const {
260 53818 : return (pos < _output.chars.size())?getAdvancePosition(_output.chars.at(pos)):uint16_t(0);
261 : }
262 :
263 705 : inline uint16_t Formatter::getOriginPosition(const CharLayoutData &ch) const {
264 705 : return ch.pos;
265 : }
266 :
267 733 : inline uint16_t Formatter::getOriginPosition(uint16_t pos) const {
268 733 : return (pos < _output.chars.size())?getOriginPosition(_output.chars.at(pos)):uint16_t(0);
269 : }
270 :
271 107253 : bool Formatter::isSpecial(char16_t ch) const {
272 : // collapseSpaces can be disabled for manual optical alignment
273 107253 : if (!opticalAlignment || !collapseSpaces) {
274 107253 : return false;
275 : }
276 0 : return chars::CharGroup<char16_t, CharGroupId::OpticalAlignmentSpecial>::match(ch);
277 : }
278 :
279 0 : uint16_t Formatter::checkBullet(uint16_t first, uint16_t len) const {
280 : // collapseSpaces can be disabled for manual optical alignment
281 0 : if (!opticalAlignment || !collapseSpaces) {
282 0 : return 0;
283 : }
284 :
285 0 : uint16_t offset = 0;
286 0 : for (uint16_t i = first; i < first + len - 1; i++) {
287 0 : auto ch = _output.chars.at(i).charID;
288 0 : if (chars::CharGroup<char16_t, CharGroupId::OpticalAlignmentBullet>::match(ch)) {
289 0 : offset ++;
290 0 : } else if (chars::isspace(ch) && offset >= 1) {
291 0 : return offset + 1;
292 : } else {
293 0 : break;
294 : }
295 : }
296 :
297 0 : return 0;
298 : }
299 :
300 0 : void Formatter::pushLineFiller(bool replaceLastChar) {
301 0 : *_output.overflow = true;
302 0 : if (_fillerChar == 0) {
303 0 : return;
304 : }
305 :
306 0 : auto charDef = _primaryFontSet->getChar(_fillerChar, faceId);
307 0 : if (!charDef) {
308 0 : return;
309 : }
310 :
311 0 : if (replaceLastChar && !_output.chars.empty()) {
312 0 : auto &bc = _output.chars.back();
313 0 : bc.charID = _fillerChar;
314 0 : bc.advance = charDef.xAdvance;
315 : } else {
316 0 : _output.chars.emplace_back(CharLayoutData{_fillerChar, lineX, charDef.xAdvance, faceId});
317 0 : charNum ++;
318 : }
319 : }
320 :
321 852685 : bool Formatter::pushChar(char16_t ch) {
322 852685 : if (_textStyle.textTransform == TextTransform::Uppercase) {
323 3240 : ch = string::toupper(ch);
324 849445 : } else if (_textStyle.textTransform == TextTransform::Lowercase) {
325 10690 : ch = string::tolower(ch);
326 : }
327 :
328 852685 : CharShape charDef = _primaryFontSet->getChar(ch, faceId);
329 :
330 852685 : if (charDef.charID == 0) {
331 0 : if (ch == char16_t(0x00AD)) {
332 0 : charDef = _primaryFontSet->getChar('-', faceId);
333 : } else {
334 0 : log::format(log::Warn, "RichTextFormatter", "%s: Attempted to use undefined character: %d '%s'",
335 0 : _primaryFontSet->getName().data(), ch, string::toUtf8<Interface>(ch).c_str());
336 0 : return true;
337 : }
338 : }
339 :
340 852685 : if (charNum == firstInLine && lineOffset > 0) {
341 0 : lineX += lineOffset;
342 : }
343 :
344 852685 : auto posX = lineX;
345 :
346 852685 : CharLayoutData spec{ch, posX, charDef.xAdvance, faceId};
347 :
348 852685 : if (ch == static_cast<char16_t>(0x00AD)) {
349 0 : if (_textStyle.hyphens == Hyphens::Manual || _textStyle.hyphens == Hyphens::Auto) {
350 0 : wordWrapPos = charNum + 1;
351 : }
352 852685 : } else if (ch == u'-' || ch == u'+' || ch == u'*' || ch == u'/' || ch == u'\\') {
353 2722 : auto pos = charNum;
354 56994 : while(pos > firstInLine && (!chars::isspace(_output.chars.at(pos - 1).charID))) {
355 54272 : pos --;
356 : }
357 2722 : if (charNum - pos > 2) {
358 2702 : wordWrapPos = charNum + 1;
359 : }
360 2722 : auto newlineX = lineX + charDef.xAdvance;
361 2722 : if (maxWidth && lineX > maxWidth) {
362 0 : pushLineFiller();
363 0 : return false;
364 : }
365 2722 : lineX = newlineX;
366 852685 : } else if (charDef) {
367 849963 : if (charNum == firstInLine && isSpecial(ch)) {
368 0 : spec.pos -= charDef.xAdvance / 2;
369 0 : lineX += charDef.xAdvance / 2;
370 : } else {
371 849963 : auto newlineX = lineX + charDef.xAdvance;
372 849963 : if (maxWidth && lineX > maxWidth) {
373 0 : pushLineFiller(true);
374 0 : return false;
375 : }
376 849963 : lineX = newlineX;
377 : }
378 : }
379 852685 : charNum ++;
380 852685 : _output.chars.emplace_back(std::move(spec));
381 :
382 852685 : return true;
383 : }
384 :
385 106739 : bool Formatter::pushSpace(bool wrap) {
386 106739 : if (pushChar(' ')) {
387 106739 : if (wordWrap && wrap) {
388 106739 : wordWrapPos = charNum;
389 : }
390 106739 : return true;
391 : }
392 0 : return false;
393 : }
394 :
395 0 : bool Formatter::pushTab() {
396 0 : CharShape charDef = _primaryFontSet->getChar(' ', faceId);
397 :
398 0 : auto posX = lineX;
399 0 : auto tabPos = (lineX + charDef.xAdvance) / (charDef.xAdvance * 4) + 1;
400 0 : lineX = tabPos * charDef.xAdvance * 4;
401 :
402 0 : charNum ++;
403 0 : _output.chars.emplace_back(CharLayoutData{char16_t('\t'), posX, uint16_t(lineX - posX), faceId});
404 0 : if (wordWrap) {
405 0 : wordWrapPos = charNum;
406 : }
407 :
408 0 : return true;
409 : }
410 :
411 53818 : uint16_t Formatter::getLineAdvancePos(uint16_t lastPos) {
412 53818 : auto &origChar = _output.chars.at(lastPos);
413 53818 : auto ch = origChar.charID;
414 53818 : if (ch == ' ' && lastPos > firstInLine) {
415 150 : lastPos --;
416 : }
417 53818 : if (lastPos < firstInLine) {
418 0 : return 0;
419 : }
420 :
421 53818 : auto a = getAdvancePosition(lastPos);
422 53818 : auto &lastChar = _output.chars.at(lastPos);
423 53818 : ch = lastChar.charID;
424 53818 : if (isSpecial(ch)) {
425 0 : if (ch == '.' || ch == ',') {
426 0 : a -= min(a, lastChar.advance);
427 : } else {
428 0 : a -= min(a, uint16_t(lastChar.advance / 2));
429 : }
430 : }
431 53818 : return a;
432 : }
433 :
434 53949 : bool Formatter::pushLine(uint16_t first, uint16_t len, bool forceAlign) {
435 53949 : if (maxLines && _output.lines.size() + 1 == maxLines && forceAlign) {
436 0 : pushLineFiller(true);
437 0 : return false;
438 : }
439 :
440 53949 : uint16_t linePos = lineY + currentLineHeight;
441 :
442 53949 : if (len > 0) {
443 53818 : _output.lines.emplace_back(LineLayoutData{first, len, linePos, currentLineHeight});
444 53818 : uint16_t advance = getLineAdvancePos(first + len - 1);
445 53818 : uint16_t offsetLeft = (advance < (width + lineOffset))?((width + lineOffset) - advance):0;
446 53818 : if (offsetLeft > 0 && alignment == TextAlign::Right) {
447 0 : for (uint16_t i = first; i < first + len; i++) {
448 0 : _output.chars.at(i).pos += offsetLeft;
449 : }
450 53818 : } else if (offsetLeft > 0 && alignment == TextAlign::Center) {
451 3021 : offsetLeft /= 2;
452 71479 : for (uint16_t i = first; i < first + len; i++) {
453 68458 : _output.chars.at(i).pos += offsetLeft;
454 : }
455 53818 : } else if ((offsetLeft > 0 || (advance > width + lineOffset)) && alignment == TextAlign::Justify && forceAlign && len > 0) {
456 0 : int16_t joffset = (advance > width + lineOffset)?(width + lineOffset - advance):offsetLeft;
457 0 : uint16_t spacesCount = 0;
458 0 : if (first == 0) {
459 0 : auto bc = checkBullet(first, len);
460 0 : first += bc;
461 0 : len -= bc;
462 : }
463 :
464 0 : for (uint16_t i = first; i < first + len - 1; i++) {
465 0 : auto ch = _output.chars.at(i).charID;
466 0 : if (chars::isspace(ch) && ch != '\n') {
467 0 : spacesCount ++;
468 : }
469 : }
470 :
471 0 : int16_t offset = 0;
472 0 : for (uint16_t i = first; i < first + len; i++) {
473 0 : auto ch = _output.chars.at(i).charID;
474 0 : if (ch != char16_t(0xffff) && chars::isspace(ch) && ch != '\n' && spacesCount > 0) {
475 0 : offset += joffset / spacesCount;
476 0 : joffset -= joffset / spacesCount;
477 0 : spacesCount --;
478 : } else {
479 0 : _output.chars.at(i).pos += offset;
480 : }
481 : }
482 : }
483 :
484 53818 : if (advance > maxLineX) {
485 27740 : maxLineX = advance;
486 : }
487 : }
488 :
489 53949 : lineY = linePos;
490 53949 : firstInLine = charNum;
491 53949 : wordWrapPos = firstInLine;
492 53949 : bufferedSpace = false;
493 53949 : currentLineHeight = min(rangeLineHeight, lineHeight);
494 53949 : parseFontLineHeight(rangeLineHeight);
495 53949 : width = defaultWidth;
496 53949 : if (defaultWidth >= _primaryFontSet->getFontHeight()) {
497 3661 : if (!updatePosition(lineY, currentLineHeight)) {
498 0 : return false;
499 : }
500 : }
501 53949 : b = 0;
502 53949 : return true;
503 : }
504 :
505 53391 : bool Formatter::pushLine(bool forceAlign) {
506 53391 : uint16_t first = firstInLine;
507 53391 : if (firstInLine <= charNum) {
508 53391 : uint16_t len = charNum - firstInLine;
509 53391 : return pushLine(first, len, forceAlign);
510 : }
511 0 : return true;
512 : }
513 :
514 383 : void Formatter::updateLineHeight(uint16_t first, uint16_t last) {
515 383 : if (!lineHeightIsAbsolute) {
516 383 : bool found = false;
517 383 : for (RangeLayoutData &it : _output.ranges) {
518 0 : if (it.start <= first && it.start + it.count > first) {
519 0 : found = true;
520 0 : } else if (it.start > last) {
521 0 : break;
522 : }
523 0 : if (found) {
524 0 : parseFontLineHeight(it.height);
525 : }
526 : }
527 : }
528 383 : }
529 :
530 17139 : Formatter::Output::Output(TextLayoutData<memory::StandartInterface> *d)
531 17139 : : width(&d->width), height(&d->height), maxAdvance(&d->maxAdvance), overflow(&d->overflow), ranges(d->ranges), chars(d->chars), lines(d->lines) { }
532 :
533 0 : Formatter::Output::Output(TextLayoutData<memory::PoolInterface> *d)
534 0 : : width(&d->width), height(&d->height), maxAdvance(&d->maxAdvance), overflow(&d->overflow), ranges(d->ranges), chars(d->chars), lines(d->lines) { }
535 :
536 558 : bool Formatter::pushLineBreak() {
537 1116 : if (chars::CharGroup<char16_t, CharGroupId::WhiteSpace>::match(_output.chars.back().charID)) {
538 0 : return true;
539 : }
540 :
541 558 : if (firstInLine >= wordWrapPos - 1 && (maxLines != 0 && _output.lines.size() + 1 != maxLines)) {
542 0 : return true;
543 : }
544 :
545 558 : uint16_t wordStart = wordWrapPos;
546 558 : uint16_t wordEnd = charNum - 1;
547 :
548 558 : if (request == ContentRequest::Normal && (lineX - getOriginPosition(wordWrapPos) > width || wordWrapPos == 0)) {
549 383 : if (wordWrap) {
550 383 : lineX = lineOffset;
551 383 : if (!pushLine(firstInLine, wordEnd - firstInLine, true)) {
552 0 : return false;
553 : }
554 :
555 383 : firstInLine = wordEnd;
556 383 : wordWrapPos = wordEnd;
557 :
558 383 : auto &ch = _output.chars.at(wordEnd);
559 :
560 383 : ch.pos = lineX;
561 383 : lineX += ch.advance;
562 :
563 383 : updateLineHeight(wordEnd, charNum);
564 : }
565 : } else {
566 : // we can wrap the word
567 175 : auto &ch = _output.chars.at((wordWrapPos - 1));
568 175 : if (!chars::isspace(ch.charID)) {
569 125 : if (!pushLine(firstInLine, (wordWrapPos) - firstInLine, true)) {
570 0 : return false;
571 : }
572 : } else {
573 50 : if (!pushLine(firstInLine, (wordWrapPos > firstInLine)?((wordWrapPos) - firstInLine):0, true)) {
574 0 : return false;
575 : }
576 : }
577 175 : firstInLine = wordStart;
578 :
579 175 : if (wordStart < _output.chars.size()) {
580 175 : uint16_t originOffset = getOriginPosition(wordStart);
581 175 : auto &bc = _output.chars.at((wordStart));
582 175 : if (isSpecial(bc.charID)) {
583 0 : originOffset += bc.advance / 2;
584 : }
585 :
586 175 : if (originOffset > lineOffset) {
587 175 : originOffset -= lineOffset;
588 : }
589 :
590 759 : for (uint32_t i = wordStart; i <= wordEnd; i++) {
591 584 : _output.chars.at(i).pos -= originOffset;
592 : }
593 175 : lineX -= originOffset;
594 : } else {
595 0 : lineX = 0;
596 : }
597 : }
598 558 : return true;
599 : }
600 :
601 36302 : bool Formatter::pushLineBreakChar() {
602 36302 : charNum ++;
603 36302 : _output.chars.emplace_back(CharLayoutData{char16_t(0x0A), lineX, 0, 0});
604 :
605 36302 : if (!pushLine(false)) {
606 0 : return false;
607 : }
608 36302 : lineX = 0;
609 :
610 36302 : return true;
611 : }
612 :
613 17713 : bool Formatter::readChars(WideStringView &r, const Vector<uint8_t> &hyph) {
614 17713 : size_t wordPos = 0;
615 17713 : auto hIt = hyph.begin();
616 17713 : bool startWhitespace = _output.chars.empty();
617 906700 : for (c = r[0]; !r.empty(); ++_charPosition, ++wordPos, ++r, c = r[0]) {
618 888987 : if (hIt != hyph.end() && wordPos == *hIt) {
619 0 : pushChar(char16_t(0x00AD));
620 0 : ++ hIt;
621 : }
622 :
623 888987 : if (c == char16_t('\n')) {
624 36302 : if (preserveLineBreaks) {
625 36302 : if (!pushLineBreakChar()) {
626 0 : return false;
627 : }
628 0 : } else if (collapseSpaces) {
629 0 : if (!startWhitespace) {
630 0 : bufferedSpace = true;
631 : }
632 : }
633 36302 : b = 0;
634 36302 : continue;
635 : }
636 :
637 852685 : if (c == char16_t('\t') && !collapseSpaces) {
638 0 : if (request == ContentRequest::Minimize) {
639 0 : wordWrapPos = charNum;
640 0 : if (!pushLineBreak()) {
641 0 : return false;
642 : }
643 0 : } else if (!pushTab()) {
644 0 : return false;
645 : }
646 0 : continue;
647 : }
648 :
649 852685 : if (c < char16_t(0x20)) {
650 0 : if (emplaceAllChars) {
651 0 : charNum ++;
652 0 : _output.chars.emplace_back(CharLayoutData{char16_t(0xFFFF), lineX, 0, 0});
653 : }
654 0 : continue;
655 : }
656 :
657 852685 : if (c != char16_t(0x00A0) && chars::isspace(c) && collapseSpaces) {
658 0 : if (!startWhitespace) {
659 0 : bufferedSpace = true;
660 : }
661 0 : b = 0;
662 0 : continue;
663 : }
664 :
665 852685 : if (c == char16_t(0x00A0)) {
666 0 : if (!pushSpace(false)) {
667 0 : return false;
668 : }
669 0 : bufferedSpace = false;
670 0 : continue;
671 : }
672 :
673 852685 : if (bufferedSpace || (!collapseSpaces && c != 0x00A0 && chars::isspace(c))) {
674 106739 : if (request == ContentRequest::Minimize && charNum > 0) {
675 0 : wordWrapPos = charNum;
676 0 : auto b = bufferedSpace;
677 0 : if (!pushLineBreak()) {
678 0 : return false;
679 : }
680 0 : bufferedSpace = b;
681 106739 : } else if (!pushSpace()) {
682 0 : return false;
683 : }
684 106739 : if (!bufferedSpace) {
685 106739 : continue;
686 : } else {
687 0 : bufferedSpace = false;
688 : }
689 : }
690 :
691 745946 : auto kerning = _primaryFontSet->getKerningAmount(b, c, faceId);
692 745946 : lineX += kerning;
693 745946 : if (!pushChar(c)) {
694 0 : return false;
695 : }
696 745946 : startWhitespace = false;
697 :
698 745946 : switch (request) {
699 0 : case ContentRequest::Minimize:
700 0 : if (charNum > 0 && wordWrapPos == charNum && c != char16_t(0x00AD)) {
701 0 : if (!pushLineBreak()) {
702 0 : return false;
703 : }
704 : }
705 0 : break;
706 0 : case ContentRequest::Maximize:
707 0 : break;
708 745946 : case ContentRequest::Normal:
709 745946 : if (width + lineOffset > 0 && lineX > width + lineOffset) {
710 558 : lineX -= kerning;
711 558 : if (!pushLineBreak()) {
712 0 : return false;
713 : }
714 : }
715 745946 : break;
716 : }
717 :
718 745946 : if (c != char16_t(0x00AD)) {
719 745946 : b = c;
720 : }
721 : }
722 17713 : return true;
723 : }
724 :
725 0 : bool Formatter::read(const FontParameters &f, const TextParameters &s, WideStringView str, uint16_t frontOffset, uint16_t backOffset) {
726 0 : return read(f, s, str.data(), str.size(), frontOffset, backOffset);
727 : }
728 :
729 17763 : bool Formatter::read(const FontParameters &f, const TextParameters &s, const char16_t *str, size_t len, uint16_t frontOffset, uint16_t backOffset) {
730 17763 : if (!str) {
731 0 : return false;
732 : }
733 :
734 17763 : _primaryFontSet = nullptr;
735 :
736 17763 : Rc<FontFaceSet> primaryLayout;
737 17763 : Rc<FontFaceSet> secondaryLayout;
738 :
739 17763 : if (f.fontVariant == FontVariant::SmallCaps) {
740 : //secondary = _output.source->getLayout(f.getSmallCaps());
741 :
742 0 : CharVector primaryStr;
743 0 : CharVector secondaryStr;
744 0 : for (size_t i = 0; i < len; ++ i) {
745 0 : char16_t ch = str[i];
746 0 : if (s.textTransform == TextTransform::Uppercase) {
747 0 : ch = string::toupper(ch);
748 0 : } else if (s.textTransform == TextTransform::Lowercase) {
749 0 : ch = string::tolower(ch);
750 : }
751 0 : if (ch != string::toupper(ch)) {
752 0 : secondaryStr.addChar(string::toupper(ch));
753 : } else {
754 0 : primaryStr.addChar(ch);
755 : }
756 : }
757 0 : if (_fillerChar) {
758 0 : primaryStr.addChar(_fillerChar);
759 : }
760 0 : primaryStr.addChar('-');
761 0 : primaryStr.addChar(' ');
762 0 : primaryStr.addChar(char16_t(0xAD));
763 :
764 :
765 0 : primaryLayout = fontCallback(f);
766 0 : if (primaryLayout) {
767 0 : primaryLayout->addString(primaryStr);
768 : }
769 :
770 0 : secondaryLayout = fontCallback(f.getSmallCaps());
771 0 : if (secondaryLayout) {
772 0 : secondaryLayout->addString(secondaryStr);
773 : }
774 :
775 0 : if (!secondaryLayout) {
776 0 : return false;
777 : }
778 0 : } else {
779 17763 : CharVector primaryStr;
780 17763 : if (s.textTransform == TextTransform::None) {
781 16953 : primaryStr.addString(WideStringView(str, len));
782 : } else {
783 15385 : for (size_t i = 0; i < len; ++ i) {
784 14575 : char16_t ch = str[i];
785 14575 : if (s.textTransform == TextTransform::Uppercase) {
786 3240 : ch = string::toupper(ch);
787 11335 : } else if (s.textTransform == TextTransform::Lowercase) {
788 11335 : ch = string::tolower(ch);
789 : }
790 14575 : primaryStr.addChar(ch);
791 : }
792 : }
793 17763 : if (_fillerChar) {
794 16981 : primaryStr.addChar(_fillerChar);
795 : }
796 17763 : primaryStr.addChar('-');
797 17763 : primaryStr.addChar(' ');
798 17763 : primaryStr.addChar(char16_t(0xAD));
799 :
800 17763 : primaryLayout = fontCallback(f);
801 17763 : if (primaryLayout) {
802 17713 : primaryLayout->addString(primaryStr);
803 : }
804 17763 : }
805 :
806 17763 : if (!primaryLayout) {
807 50 : return false;
808 : }
809 :
810 17713 : auto h = primaryLayout->getFontHeight();
811 :
812 17713 : if (f.fontVariant == FontVariant::SmallCaps && s.textTransform != TextTransform::Uppercase) {
813 0 : size_t blockStart = 0;
814 0 : size_t blockSize = 0;
815 0 : bool caps = false;
816 0 : TextParameters capsParams = s;
817 0 : capsParams.textTransform = TextTransform::Uppercase;
818 0 : for (size_t idx = 0; idx < len; ++idx) {
819 0 : const char16_t c = (s.textTransform == TextTransform::None)?str[idx]:string::tolower(str[idx]);
820 0 : if (string::toupper(c) != c) { // char can be uppercased - use caps
821 0 : if (caps != true) {
822 0 : caps = true;
823 0 : if (blockSize > 0) {
824 0 : readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
825 0 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), h,
826 0 : primaryLayout->getMetrics(), primaryLayout},
827 0 : s, str + blockStart, blockSize, frontOffset, backOffset);
828 : }
829 0 : blockStart = idx;
830 0 : blockSize = 0;
831 : }
832 : } else {
833 0 : if (caps != false) {
834 0 : caps = false;
835 0 : if (blockSize > 0) {
836 0 : readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
837 0 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), h,
838 0 : secondaryLayout->getMetrics(), secondaryLayout},
839 0 : capsParams, str + blockStart, blockSize, frontOffset, backOffset);
840 : }
841 0 : blockStart = idx;
842 0 : blockSize = 0;
843 : }
844 : }
845 0 : ++ blockSize;
846 : }
847 0 : if (blockSize > 0) {
848 0 : if (caps) {
849 0 : return readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
850 0 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), h,
851 0 : secondaryLayout->getMetrics(), secondaryLayout},
852 0 : capsParams, str + blockStart, blockSize, frontOffset, backOffset);
853 : } else {
854 0 : return readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
855 0 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), h,
856 0 : primaryLayout->getMetrics(), primaryLayout},
857 0 : s, str + blockStart, blockSize, frontOffset, backOffset);
858 : }
859 : }
860 0 : } else {
861 17713 : return readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
862 17713 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), h,
863 17713 : primaryLayout->getMetrics(), primaryLayout},
864 17713 : s, str, len, frontOffset, backOffset);
865 : }
866 :
867 0 : return true;
868 17763 : }
869 :
870 0 : bool Formatter::read(const FontParameters &f, const TextParameters &s, uint16_t blockWidth, uint16_t blockHeight) {
871 0 : _primaryFontSet = nullptr;
872 :
873 0 : Rc<FontFaceSet> primaryLayout = fontCallback(f);
874 0 : return readWithRange(RangeLayoutData{false, false, s.textDecoration, s.verticalAlign,
875 0 : uint32_t(_output.chars.size()), 0, geom::Color4B(s.color, s.opacity), blockHeight,
876 0 : primaryLayout->getMetrics(), primaryLayout},
877 0 : s, blockWidth, blockHeight);
878 0 : }
879 :
880 17713 : bool Formatter::readWithRange(RangeLayoutData && range, const TextParameters &s,
881 : const char16_t *str, size_t len, uint16_t frontOffset, uint16_t backOffset) {
882 17713 : _primaryFontSet = range.layout;
883 17713 : rangeLineHeight = range.height;
884 :
885 17713 : _charPosition = 0;
886 17713 : if (bufferedSpace) {
887 0 : pushSpace();
888 0 : bufferedSpace = false;
889 : }
890 :
891 17713 : parseFontLineHeight(rangeLineHeight);
892 :
893 17713 : _textStyle = s;
894 17713 : parseWhiteSpace(_textStyle.whiteSpace);
895 17713 : if (!updatePosition(lineY, currentLineHeight)) {
896 0 : return false;
897 : }
898 :
899 17713 : if (!_output.chars.empty() && _output.chars.back().charID == ' ' && collapseSpaces) {
900 0 : while (len > 0 && ((chars::isspace(str[0]) && str[0] != 0x00A0) || str[0] < 0x20)) {
901 0 : len --; str ++;
902 : }
903 : }
904 :
905 17713 : b = 0;
906 :
907 17713 : lineX += frontOffset;
908 17713 : _charPosition = 0;
909 17713 : WideStringView r(str, len);
910 17713 : if (_textStyle.hyphens == Hyphens::Auto && _hyphens) {
911 0 : while (!r.empty()) {
912 : WideStringView tmp = r.readUntil<WideStringView::CharGroup<CharGroupId::Latin>,
913 0 : WideStringView::CharGroup<CharGroupId::Cyrillic>>();
914 0 : if (!tmp.empty()) {
915 0 : readChars(tmp);
916 : }
917 : tmp = r.readChars<WideStringView::CharGroup<CharGroupId::Latin>,
918 0 : WideStringView::CharGroup<CharGroupId::Cyrillic>>();
919 0 : if (!tmp.empty()) {
920 0 : readChars(tmp, _hyphens->makeWordHyphens(tmp));
921 : }
922 : }
923 : } else {
924 17713 : readChars(r);
925 : }
926 :
927 17713 : range.count = uint32_t(_output.chars.size() - range.start);
928 17713 : if (range.count > 0) {
929 17713 : _output.ranges.emplace_back(std::move(range));
930 : }
931 17713 : lineX += backOffset;
932 :
933 17713 : return true;
934 : }
935 0 : bool Formatter::readWithRange(RangeLayoutData &&range, const TextParameters &s, uint16_t blockWidth, uint16_t blockHeight) {
936 0 : _primaryFontSet = range.layout;
937 0 : rangeLineHeight = range.height;
938 :
939 0 : _charPosition = 0;
940 0 : if (bufferedSpace) {
941 0 : pushSpace();
942 0 : bufferedSpace = false;
943 : }
944 :
945 :
946 0 : _textStyle = s;
947 0 : parseWhiteSpace(_textStyle.whiteSpace);
948 :
949 0 : if (maxWidth && lineX + blockWidth > maxWidth) {
950 0 : pushLineFiller(false);
951 0 : return false;
952 : }
953 :
954 0 : if (width + lineOffset > 0) {
955 0 : if (lineX + blockWidth > width + lineOffset) {
956 0 : if (!pushLine(true)) {
957 0 : return false;
958 : }
959 0 : lineX = 0;
960 : }
961 : }
962 :
963 0 : parseFontLineHeight(rangeLineHeight);
964 0 : if (currentLineHeight < blockHeight) {
965 0 : currentLineHeight = blockHeight;
966 : }
967 :
968 0 : if (!updatePosition(lineY, currentLineHeight)) {
969 0 : return false;
970 : }
971 :
972 0 : if (charNum == firstInLine && lineOffset > 0) {
973 0 : lineX += lineOffset;
974 : }
975 :
976 0 : CharLayoutData spec{char16_t(0xFFFF), lineX, blockWidth, 0};
977 0 : lineX += spec.advance;
978 0 : charNum ++;
979 0 : _output.chars.emplace_back(std::move(spec));
980 :
981 0 : switch (request) {
982 0 : case ContentRequest::Minimize:
983 0 : wordWrapPos = charNum - 1;
984 0 : if (!pushLineBreak()) {
985 0 : return false;
986 : }
987 0 : break;
988 0 : case ContentRequest::Maximize:
989 0 : break;
990 0 : case ContentRequest::Normal:
991 0 : if (width + lineOffset > 0 && lineX > width + lineOffset) {
992 0 : if (!pushLineBreak()) {
993 0 : return false;
994 : }
995 : }
996 0 : break;
997 : }
998 :
999 :
1000 0 : range.count = uint32_t(_output.chars.size() - range.start);
1001 0 : _output.ranges.emplace_back(std::move(range));
1002 :
1003 0 : return true;
1004 : }
1005 :
1006 17139 : uint16_t Formatter::getHeight() const {
1007 17139 : return lineY;
1008 : }
1009 17139 : uint16_t Formatter::getWidth() const {
1010 17139 : return std::max(maxLineX, width);
1011 : }
1012 17139 : uint16_t Formatter::getMaxLineX() const {
1013 17139 : return maxLineX;
1014 : }
1015 0 : uint16_t Formatter::getLineHeight() const {
1016 0 : return lineHeight;
1017 : }
1018 :
1019 : }
|