LCOV - code coverage report
Current view: top level - core/font - SPFontFormatter.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 330 663 49.8 %
Date: 2024-05-12 00:16:13 Functions: 39 54 72.2 %

          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             : }

Generated by: LCOV version 1.14