1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC. 2cb93a386Sopenharmony_ci#include "modules/skparagraph/src/ParagraphImpl.h" 3cb93a386Sopenharmony_ci#include "modules/skparagraph/src/TextWrapper.h" 4cb93a386Sopenharmony_ci 5cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 6cb93a386Sopenharmony_ci#include "log.h" 7cb93a386Sopenharmony_ci#include "modules/skparagraph/src/TextTabAlign.h" 8cb93a386Sopenharmony_ci#include "TextParameter.h" 9cb93a386Sopenharmony_ci#endif 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_cinamespace skia { 12cb93a386Sopenharmony_cinamespace textlayout { 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_cinamespace { 15cb93a386Sopenharmony_ciconst size_t BREAK_NUM_TWO = 2; 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_cistruct LineBreakerWithLittleRounding { 18cb93a386Sopenharmony_ci LineBreakerWithLittleRounding(SkScalar maxWidth, bool applyRoundingHack) 19cb93a386Sopenharmony_ci : fLower(maxWidth - 0.25f) 20cb93a386Sopenharmony_ci , fMaxWidth(maxWidth) 21cb93a386Sopenharmony_ci , fUpper(maxWidth + 0.25f) 22cb93a386Sopenharmony_ci , fApplyRoundingHack(applyRoundingHack) {} 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci bool breakLine(SkScalar width) const { 25cb93a386Sopenharmony_ci if (width < fLower) { 26cb93a386Sopenharmony_ci return false; 27cb93a386Sopenharmony_ci } else if (width > fUpper) { 28cb93a386Sopenharmony_ci return true; 29cb93a386Sopenharmony_ci } 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_ci auto val = std::fabs(width); 32cb93a386Sopenharmony_ci SkScalar roundedWidth; 33cb93a386Sopenharmony_ci if (fApplyRoundingHack) { 34cb93a386Sopenharmony_ci if (val < 10000) { 35cb93a386Sopenharmony_ci roundedWidth = SkScalarRoundToScalar(width * 100) * (1.0f/100); 36cb93a386Sopenharmony_ci } else if (val < 100000) { 37cb93a386Sopenharmony_ci roundedWidth = SkScalarRoundToScalar(width * 10) * (1.0f/10); 38cb93a386Sopenharmony_ci } else { 39cb93a386Sopenharmony_ci roundedWidth = SkScalarFloorToScalar(width); 40cb93a386Sopenharmony_ci } 41cb93a386Sopenharmony_ci } else { 42cb93a386Sopenharmony_ci if (val < 10000) { 43cb93a386Sopenharmony_ci roundedWidth = SkScalarFloorToScalar(width * 100) * (1.0f/100); 44cb93a386Sopenharmony_ci } else if (val < 100000) { 45cb93a386Sopenharmony_ci roundedWidth = SkScalarFloorToScalar(width * 10) * (1.0f/10); 46cb93a386Sopenharmony_ci } else { 47cb93a386Sopenharmony_ci roundedWidth = SkScalarFloorToScalar(width); 48cb93a386Sopenharmony_ci } 49cb93a386Sopenharmony_ci } 50cb93a386Sopenharmony_ci return roundedWidth > fMaxWidth; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci const SkScalar fLower, fMaxWidth, fUpper; 54cb93a386Sopenharmony_ci const bool fApplyRoundingHack; 55cb93a386Sopenharmony_ci}; 56cb93a386Sopenharmony_ci} // namespace 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 59cb93a386Sopenharmony_ciSkScalar TextWrapper::calculateFakeSpacing(Cluster* cluster, bool autoSpacingEnable) 60cb93a386Sopenharmony_ci{ 61cb93a386Sopenharmony_ci if (!autoSpacingEnable || cluster == fEndLine.endCluster()) { 62cb93a386Sopenharmony_ci return 0; 63cb93a386Sopenharmony_ci } 64cb93a386Sopenharmony_ci if ((cluster - 1)->isWhitespaceBreak() || cluster->isWhitespaceBreak()) { 65cb93a386Sopenharmony_ci return 0; 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci if ((cluster - 1)->isHardBreak() || cluster->isHardBreak()) { 68cb93a386Sopenharmony_ci return 0; 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci if ((cluster - 1)->isCopyright() || cluster->isCopyright()) { 71cb93a386Sopenharmony_ci return (cluster - 1)->getFontSize() / AUTO_SPACING_WIDTH_RATIO; 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci if ((cluster->isCJK() && (cluster - 1)->isWestern()) || (cluster->isWestern() && (cluster - 1)->isCJK())) { 74cb93a386Sopenharmony_ci return (cluster - 1)->getFontSize() / AUTO_SPACING_WIDTH_RATIO; 75cb93a386Sopenharmony_ci } 76cb93a386Sopenharmony_ci return 0; 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci// Since we allow cluster clipping when they don't fit 80cb93a386Sopenharmony_ci// we have to work with stretches - parts of clusters 81cb93a386Sopenharmony_civoid TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, 82cb93a386Sopenharmony_ci WordBreakType wordBreakType, bool autoSpacingEnable) { 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci reset(); 85cb93a386Sopenharmony_ci fEndLine.metrics().clean(); 86cb93a386Sopenharmony_ci fWords.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 87cb93a386Sopenharmony_ci fClusters.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 88cb93a386Sopenharmony_ci fClip.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci bool isFirstWord = true; 91cb93a386Sopenharmony_ci TextTabAlign textTabAlign(endOfClusters->getOwner()->paragraphStyle().getTextTab()); 92cb93a386Sopenharmony_ci textTabAlign.init(maxWidth, endOfClusters); 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci LineBreakerWithLittleRounding breaker(maxWidth, applyRoundingHack); 95cb93a386Sopenharmony_ci Cluster* nextNonBreakingSpace = nullptr; 96cb93a386Sopenharmony_ci SkScalar totalFakeSpacing = 0.0; 97cb93a386Sopenharmony_ci for (auto cluster = fEndLine.endCluster(); cluster < endOfClusters; ++cluster) { 98cb93a386Sopenharmony_ci auto fakeSpacing = calculateFakeSpacing(cluster, autoSpacingEnable); 99cb93a386Sopenharmony_ci totalFakeSpacing += fakeSpacing; 100cb93a386Sopenharmony_ci if (cluster->isHardBreak()) { 101cb93a386Sopenharmony_ci if (cluster != fEndLine.endCluster()) { 102cb93a386Sopenharmony_ci isFirstWord = false; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci } else if ( 105cb93a386Sopenharmony_ci // TODO: Trying to deal with flutter rounding problem. Must be removed... 106cb93a386Sopenharmony_ci SkScalar width = fWords.width() + fClusters.width() + cluster->width() + totalFakeSpacing; 107cb93a386Sopenharmony_ci (!isFirstWord || wordBreakType != WordBreakType::NORMAL) && 108cb93a386Sopenharmony_ci breaker.breakLine(width)) { 109cb93a386Sopenharmony_ci if (cluster->isWhitespaceBreak()) { 110cb93a386Sopenharmony_ci // It's the end of the word 111cb93a386Sopenharmony_ci isFirstWord = false; 112cb93a386Sopenharmony_ci fClusters.extend(cluster); 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_ci bool tabAlignRet = false; 115cb93a386Sopenharmony_ci if (cluster->isTabulation()) { 116cb93a386Sopenharmony_ci tabAlignRet = textTabAlign.processTab(fWords, fClusters, cluster, totalFakeSpacing); 117cb93a386Sopenharmony_ci } else { 118cb93a386Sopenharmony_ci tabAlignRet = textTabAlign.processEndofWord(fWords, fClusters, cluster, totalFakeSpacing); 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci if (tabAlignRet) { 121cb93a386Sopenharmony_ci break; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth()); 124cb93a386Sopenharmony_ci fWords.extend(fClusters); 125cb93a386Sopenharmony_ci continue; 126cb93a386Sopenharmony_ci } else if (cluster->run().isPlaceholder()) { 127cb93a386Sopenharmony_ci isFirstWord = false; 128cb93a386Sopenharmony_ci if (!fClusters.empty()) { 129cb93a386Sopenharmony_ci // Placeholder ends the previous word 130cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth()); 131cb93a386Sopenharmony_ci fWords.extend(fClusters); 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci if (cluster->width() > maxWidth && fWords.empty()) { 135cb93a386Sopenharmony_ci // Placeholder is the only text and it's longer than the line; 136cb93a386Sopenharmony_ci // it does not count in fMinIntrinsicWidth 137cb93a386Sopenharmony_ci fClusters.extend(cluster); 138cb93a386Sopenharmony_ci fTooLongCluster = true; 139cb93a386Sopenharmony_ci fTooLongWord = true; 140cb93a386Sopenharmony_ci } else { 141cb93a386Sopenharmony_ci // Placeholder does not fit the line; it will be considered again on the next line 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci break; 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ci textTabAlign.processEndofLine(fWords, fClusters, cluster, totalFakeSpacing); 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci // Walk further to see if there is a too long word, cluster or glyph 149cb93a386Sopenharmony_ci SkScalar nextWordLength = fClusters.width(); 150cb93a386Sopenharmony_ci SkScalar nextShortWordLength = nextWordLength; 151cb93a386Sopenharmony_ci for (auto further = cluster; further != endOfClusters; ++further) { 152cb93a386Sopenharmony_ci if (further->isSoftBreak() || further->isHardBreak() || further->isWhitespaceBreak()) { 153cb93a386Sopenharmony_ci break; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci if (further->run().isPlaceholder()) { 156cb93a386Sopenharmony_ci // Placeholder ends the word 157cb93a386Sopenharmony_ci break; 158cb93a386Sopenharmony_ci } 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci if (nextWordLength > 0 && nextWordLength <= maxWidth && further->isIntraWordBreak()) { 161cb93a386Sopenharmony_ci // The cluster is spaces but not the end of the word in a normal sense 162cb93a386Sopenharmony_ci nextNonBreakingSpace = further; 163cb93a386Sopenharmony_ci nextShortWordLength = nextWordLength; 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci if (maxWidth == 0) { 167cb93a386Sopenharmony_ci // This is a tricky flutter case: layout(width:0) places 1 cluster on each line 168cb93a386Sopenharmony_ci nextWordLength = std::max(nextWordLength, further->width()); 169cb93a386Sopenharmony_ci } else { 170cb93a386Sopenharmony_ci nextWordLength += further->width(); 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci if (nextWordLength > maxWidth) { 174cb93a386Sopenharmony_ci if (nextNonBreakingSpace != nullptr) { 175cb93a386Sopenharmony_ci // We only get here if the non-breaking space improves our situation 176cb93a386Sopenharmony_ci // (allows us to break the text to fit the word) 177cb93a386Sopenharmony_ci if (SkScalar shortLength = fWords.width() + nextShortWordLength; 178cb93a386Sopenharmony_ci !breaker.breakLine(shortLength)) { 179cb93a386Sopenharmony_ci // We can add the short word to the existing line 180cb93a386Sopenharmony_ci fClusters = TextStretch(fClusters.startCluster(), nextNonBreakingSpace, fClusters.metrics().getForceStrut()); 181cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextShortWordLength); 182cb93a386Sopenharmony_ci fWords.extend(fClusters); 183cb93a386Sopenharmony_ci } else { 184cb93a386Sopenharmony_ci // We can place the short word on the next line 185cb93a386Sopenharmony_ci fClusters.clean(); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci // Either way we are not in "word is too long" situation anymore 188cb93a386Sopenharmony_ci break; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci // If the word is too long we can break it right now and hope it's enough 191cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextWordLength); 192cb93a386Sopenharmony_ci if (fClusters.endPos() - fClusters.startPos() > 1 || 193cb93a386Sopenharmony_ci fWords.empty()) { 194cb93a386Sopenharmony_ci fTooLongWord = true; 195cb93a386Sopenharmony_ci } else { 196cb93a386Sopenharmony_ci // Even if the word is too long there is a very little space on this line. 197cb93a386Sopenharmony_ci // let's deal with it on the next line. 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci if (fWords.empty() && breaker.breakLine(cluster->width())) { 202cb93a386Sopenharmony_ci fClusters.extend(cluster); 203cb93a386Sopenharmony_ci fTooLongCluster = true; 204cb93a386Sopenharmony_ci fTooLongWord = true; 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci break; 207cb93a386Sopenharmony_ci } 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ci if (cluster->isSoftBreak() || cluster->isWhitespaceBreak()) { 210cb93a386Sopenharmony_ci isFirstWord = false; 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci if (cluster->run().isPlaceholder()) { 214cb93a386Sopenharmony_ci if (!fClusters.empty()) { 215cb93a386Sopenharmony_ci // Placeholder ends the previous word (placeholders are ignored in trimming) 216cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth()); 217cb93a386Sopenharmony_ci fWords.extend(fClusters); 218cb93a386Sopenharmony_ci } 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci // Placeholder is separate word and its width now is counted in minIntrinsicWidth 221cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width()); 222cb93a386Sopenharmony_ci fWords.extend(cluster); 223cb93a386Sopenharmony_ci } else { 224cb93a386Sopenharmony_ci if (cluster->isTabulation()) { 225cb93a386Sopenharmony_ci if (textTabAlign.processTab(fWords, fClusters, cluster, totalFakeSpacing)) { 226cb93a386Sopenharmony_ci break; 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci fClusters.extend(cluster); 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth()); 231cb93a386Sopenharmony_ci fWords.extend(fClusters); 232cb93a386Sopenharmony_ci } else { 233cb93a386Sopenharmony_ci fClusters.extend(cluster); 234cb93a386Sopenharmony_ci if (fClusters.endOfWord()) { // Keep adding clusters/words 235cb93a386Sopenharmony_ci if (textTabAlign.processEndofWord(fWords, fClusters, cluster, totalFakeSpacing)) { 236cb93a386Sopenharmony_ci if (wordBreakType == WordBreakType::BREAK_ALL) { 237cb93a386Sopenharmony_ci fClusters.trim(cluster); 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci break; 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth()); 243cb93a386Sopenharmony_ci fWords.extend(fClusters); 244cb93a386Sopenharmony_ci } else { 245cb93a386Sopenharmony_ci if (textTabAlign.processCluster(fWords, fClusters, cluster, totalFakeSpacing)) { 246cb93a386Sopenharmony_ci fClusters.trim(cluster); 247cb93a386Sopenharmony_ci break; 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci if ((fHardLineBreak = cluster->isHardBreak())) { 254cb93a386Sopenharmony_ci // Stop at the hard line break 255cb93a386Sopenharmony_ci break; 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci} 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_civoid TextWrapper::moveForward(bool hasEllipsis, bool breakAll) { 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci // We normally break lines by words. 263cb93a386Sopenharmony_ci // The only way we may go to clusters is if the word is too long or 264cb93a386Sopenharmony_ci // it's the first word and it has an ellipsis attached to it. 265cb93a386Sopenharmony_ci // If nothing fits we show the clipping. 266cb93a386Sopenharmony_ci fTooLongWord = breakAll; 267cb93a386Sopenharmony_ci if (!fWords.empty()) { 268cb93a386Sopenharmony_ci fEndLine.extend(fWords); 269cb93a386Sopenharmony_ci#ifdef SK_IGNORE_SKPARAGRAPH_ELLIPSIS_FIX 270cb93a386Sopenharmony_ci if (!fTooLongWord || hasEllipsis) { // Ellipsis added to a word 271cb93a386Sopenharmony_ci#else 272cb93a386Sopenharmony_ci if (!fTooLongWord && !hasEllipsis) { // Ellipsis added to a grapheme 273cb93a386Sopenharmony_ci#endif 274cb93a386Sopenharmony_ci return; 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci } 277cb93a386Sopenharmony_ci if (!fClusters.empty()) { 278cb93a386Sopenharmony_ci fEndLine.extend(fClusters); 279cb93a386Sopenharmony_ci if (!fTooLongCluster) { 280cb93a386Sopenharmony_ci return; 281cb93a386Sopenharmony_ci } 282cb93a386Sopenharmony_ci } 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci if (!fClip.empty()) { 285cb93a386Sopenharmony_ci // Flutter: forget the clipped cluster but keep the metrics 286cb93a386Sopenharmony_ci fEndLine.metrics().add(fClip.metrics()); 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci} 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci// Special case for start/end cluster since they can be clipped 291cb93a386Sopenharmony_civoid TextWrapper::trimEndSpaces(TextAlign align) { 292cb93a386Sopenharmony_ci // Remember the breaking position 293cb93a386Sopenharmony_ci fEndLine.saveBreak(); 294cb93a386Sopenharmony_ci // Skip all space cluster at the end 295cb93a386Sopenharmony_ci for (auto cluster = fEndLine.endCluster(); 296cb93a386Sopenharmony_ci cluster >= fEndLine.startCluster() && cluster->isWhitespaceBreak(); 297cb93a386Sopenharmony_ci --cluster) { 298cb93a386Sopenharmony_ci fEndLine.trim(cluster); 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci fEndLine.trim(); 301cb93a386Sopenharmony_ci} 302cb93a386Sopenharmony_ci 303cb93a386Sopenharmony_ciSkScalar TextWrapper::getClustersTrimmedWidth() { 304cb93a386Sopenharmony_ci // Move the end of the line to the left 305cb93a386Sopenharmony_ci SkScalar width = 0; 306cb93a386Sopenharmony_ci bool trailingSpaces = true; 307cb93a386Sopenharmony_ci for (auto cluster = fClusters.endCluster(); cluster >= fClusters.startCluster(); --cluster) { 308cb93a386Sopenharmony_ci if (cluster->run().isPlaceholder()) { 309cb93a386Sopenharmony_ci continue; 310cb93a386Sopenharmony_ci } 311cb93a386Sopenharmony_ci if (trailingSpaces) { 312cb93a386Sopenharmony_ci if (!cluster->isWhitespaceBreak()) { 313cb93a386Sopenharmony_ci width += cluster->trimmedWidth(cluster->endPos()); 314cb93a386Sopenharmony_ci trailingSpaces = false; 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci continue; 317cb93a386Sopenharmony_ci } 318cb93a386Sopenharmony_ci width += cluster->width(); 319cb93a386Sopenharmony_ci } 320cb93a386Sopenharmony_ci return width; 321cb93a386Sopenharmony_ci} 322cb93a386Sopenharmony_ci 323cb93a386Sopenharmony_ci// Trim the beginning spaces in case of soft line break 324cb93a386Sopenharmony_cistd::tuple<Cluster*, size_t, SkScalar> TextWrapper::trimStartSpaces(Cluster* endOfClusters) { 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_ci if (fHardLineBreak) { 327cb93a386Sopenharmony_ci // End of line is always end of cluster, but need to skip \n 328cb93a386Sopenharmony_ci auto width = fEndLine.width(); 329cb93a386Sopenharmony_ci auto cluster = fEndLine.endCluster() + 1; 330cb93a386Sopenharmony_ci while (cluster < fEndLine.breakCluster() && cluster->isWhitespaceBreak()) { 331cb93a386Sopenharmony_ci width += cluster->width(); 332cb93a386Sopenharmony_ci ++cluster; 333cb93a386Sopenharmony_ci } 334cb93a386Sopenharmony_ci return std::make_tuple(fEndLine.breakCluster() + 1, 0, width); 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci 337cb93a386Sopenharmony_ci // breakCluster points to the end of the line; 338cb93a386Sopenharmony_ci // It's a soft line break so we need to move lineStart forward skipping all the spaces 339cb93a386Sopenharmony_ci auto width = fEndLine.widthWithGhostSpaces(); 340cb93a386Sopenharmony_ci auto cluster = fEndLine.breakCluster() + 1; 341cb93a386Sopenharmony_ci while (cluster < endOfClusters && cluster->isWhitespaceBreak() && !cluster->isTabulation()) { 342cb93a386Sopenharmony_ci width += cluster->width(); 343cb93a386Sopenharmony_ci ++cluster; 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci if (fEndLine.breakCluster()->isWhitespaceBreak() && fEndLine.breakCluster() < endOfClusters) { 347cb93a386Sopenharmony_ci // In case of a soft line break by the whitespace 348cb93a386Sopenharmony_ci // fBreak should point to the beginning of the next line 349cb93a386Sopenharmony_ci // (it only matters when there are trailing spaces) 350cb93a386Sopenharmony_ci fEndLine.shiftBreak(); 351cb93a386Sopenharmony_ci } 352cb93a386Sopenharmony_ci 353cb93a386Sopenharmony_ci return std::make_tuple(cluster, 0, width); 354cb93a386Sopenharmony_ci} 355cb93a386Sopenharmony_ci 356cb93a386Sopenharmony_ci// calculate heuristics for different variants and select the least bad 357cb93a386Sopenharmony_ci 358cb93a386Sopenharmony_ci// calculate the total space required 359cb93a386Sopenharmony_ci// define the goal for line numbers (max vs space required). 360cb93a386Sopenharmony_ci// If text could fit, it has substantially larger score compared to nicer wrap with overflow 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ci// iterate: select nontrivial candidates with some maximum offset and set the penalty / benefit of variants 363cb93a386Sopenharmony_ci// goals: 0) fit maximum amount of text 364cb93a386Sopenharmony_ci// 1) fill lines 365cb93a386Sopenharmony_ci// 2) make line lengths even 366cb93a386Sopenharmony_ci// 2.5) define a cost for hyphenation - not done 367cb93a386Sopenharmony_ci// 3) try to make it fast 368cb93a386Sopenharmony_ci 369cb93a386Sopenharmony_ciconstexpr int64_t MINIMUM_FILL_RATIO = 75; 370cb93a386Sopenharmony_ciconstexpr int64_t MINIMUM_FILL_RATIO_SQUARED = MINIMUM_FILL_RATIO * MINIMUM_FILL_RATIO; 371cb93a386Sopenharmony_ciconstexpr int64_t GOOD_ENOUGH_LINE_SCORE = 95 * 95; 372cb93a386Sopenharmony_ciconstexpr int64_t UNDERFLOW_SCORE = 100; 373cb93a386Sopenharmony_ciconstexpr float BALANCED_LAST_LINE_MULTIPLIER = 1.4f; 374cb93a386Sopenharmony_ciconstexpr int64_t BEST_LOCAL_SCORE = -1000000; 375cb93a386Sopenharmony_ciconstexpr float WIDTH_TOLERANCE = 5.f; 376cb93a386Sopenharmony_ciconstexpr int64_t PARAM_2 = 2; 377cb93a386Sopenharmony_ciconstexpr int64_t PARAM_10000 = 10000; 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci// mkay, this makes an assumption that we do the scoring runs in a single thread and holds the variables during 380cb93a386Sopenharmony_ci// recursion 381cb93a386Sopenharmony_cistruct TextWrapScorer { 382cb93a386Sopenharmony_ci TextWrapScorer(SkScalar maxWidth, ParagraphImpl& parent, size_t maxLines) 383cb93a386Sopenharmony_ci : maxWidth_(maxWidth), currentTarget_(maxWidth), maxLines_(maxLines), parent_(parent) 384cb93a386Sopenharmony_ci { 385cb93a386Sopenharmony_ci if (parent_.getLineBreakStrategy() == LineBreakStrategy::BALANCED) { 386cb93a386Sopenharmony_ci // calculate cumulative length & target width before breakes 387cb93a386Sopenharmony_ci for (auto& cluster : parent.clusters()) { 388cb93a386Sopenharmony_ci auto len = cluster.width(); 389cb93a386Sopenharmony_ci cumulativeLen_ += len; 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci int64_t targetLines = 1 + cumulativeLen_ / maxWidth_; 392cb93a386Sopenharmony_ci currentTarget_ = cumulativeLen_ / targetLines; 393cb93a386Sopenharmony_ci } 394cb93a386Sopenharmony_ci 395cb93a386Sopenharmony_ci // we trust that clusters are sorted on parent 396cb93a386Sopenharmony_ci bool prevWasWhitespace = false; 397cb93a386Sopenharmony_ci SkScalar currentWidth = 0; 398cb93a386Sopenharmony_ci SkScalar cumulativeLen_ = 0; 399cb93a386Sopenharmony_ci for (size_t ix = 0; ix < parent.clusters().size(); ix++) { 400cb93a386Sopenharmony_ci auto& cluster = parent.clusters()[ix]; 401cb93a386Sopenharmony_ci auto len = cluster.width(); 402cb93a386Sopenharmony_ci cumulativeLen_ += len; 403cb93a386Sopenharmony_ci currentWidth += len; 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci if (cluster.isWhitespaceBreak()) { 406cb93a386Sopenharmony_ci breaks_.emplace_back(cumulativeLen_, Break::BreakType::BREAKTYPE_WHITE_SPACE, prevWasWhitespace); 407cb93a386Sopenharmony_ci prevWasWhitespace = true; 408cb93a386Sopenharmony_ci currentWidth = 0; 409cb93a386Sopenharmony_ci } else if (cluster.isHardBreak()) { 410cb93a386Sopenharmony_ci breaks_.emplace_back(cumulativeLen_, Break::BreakType::BREAKTYPE_HARD, false); 411cb93a386Sopenharmony_ci prevWasWhitespace = true; 412cb93a386Sopenharmony_ci currentWidth = 0; 413cb93a386Sopenharmony_ci } else if (cluster.isIntraWordBreak()) { 414cb93a386Sopenharmony_ci breaks_.emplace_back(cumulativeLen_, Break::BreakType::BREAKTYPE_INTRA, false); 415cb93a386Sopenharmony_ci prevWasWhitespace = true; 416cb93a386Sopenharmony_ci currentWidth = 0; 417cb93a386Sopenharmony_ci } else if (currentWidth > currentTarget_) { 418cb93a386Sopenharmony_ci cumulativeLen_ -= cluster.width(); 419cb93a386Sopenharmony_ci ix--; 420cb93a386Sopenharmony_ci breaks_.emplace_back(cumulativeLen_, Break::BreakType::BREAKTYPE_FORCED, false); 421cb93a386Sopenharmony_ci prevWasWhitespace = false; 422cb93a386Sopenharmony_ci currentWidth = 0; 423cb93a386Sopenharmony_ci } else { 424cb93a386Sopenharmony_ci prevWasWhitespace = false; 425cb93a386Sopenharmony_ci } 426cb93a386Sopenharmony_ci } 427cb93a386Sopenharmony_ci } 428cb93a386Sopenharmony_ci 429cb93a386Sopenharmony_ci struct RecursiveParam { 430cb93a386Sopenharmony_ci int64_t targetLines; 431cb93a386Sopenharmony_ci size_t maxLines; 432cb93a386Sopenharmony_ci size_t lineNumber; 433cb93a386Sopenharmony_ci SkScalar begin; 434cb93a386Sopenharmony_ci SkScalar remainingTextWidth; 435cb93a386Sopenharmony_ci SkScalar currentMax; 436cb93a386Sopenharmony_ci size_t breakPos; 437cb93a386Sopenharmony_ci }; 438cb93a386Sopenharmony_ci 439cb93a386Sopenharmony_ci void Run() { 440cb93a386Sopenharmony_ci int64_t targetLines = 1 + cumulativeLen_ / maxWidth_; 441cb93a386Sopenharmony_ci 442cb93a386Sopenharmony_ci if (parent_.getLineBreakStrategy() == LineBreakStrategy::BALANCED) { 443cb93a386Sopenharmony_ci currentTarget_ = cumulativeLen_ / targetLines; 444cb93a386Sopenharmony_ci } 445cb93a386Sopenharmony_ci 446cb93a386Sopenharmony_ci if (targetLines < PARAM_2) { 447cb93a386Sopenharmony_ci // need to have at least two lines for algo to do anything useful 448cb93a386Sopenharmony_ci return; 449cb93a386Sopenharmony_ci } 450cb93a386Sopenharmony_ci CalculateRecursive(RecursiveParam{ 451cb93a386Sopenharmony_ci targetLines, maxLines_, 0, 0.f, cumulativeLen_, 452cb93a386Sopenharmony_ci }); 453cb93a386Sopenharmony_ci LOGD("cache_: %{public}zu", cache_.size()); 454cb93a386Sopenharmony_ci } 455cb93a386Sopenharmony_ci 456cb93a386Sopenharmony_ci int64_t CalculateRecursive(RecursiveParam param) 457cb93a386Sopenharmony_ci { 458cb93a386Sopenharmony_ci if (param.maxLines == 0 || param.remainingTextWidth <= 1.f) { 459cb93a386Sopenharmony_ci return BEST_LOCAL_SCORE; 460cb93a386Sopenharmony_ci } 461cb93a386Sopenharmony_ci 462cb93a386Sopenharmony_ci // This should come precalculated 463cb93a386Sopenharmony_ci param.currentMax = maxWidth_ - parent_.detectIndents(param.lineNumber); 464cb93a386Sopenharmony_ci if (nearlyZero(param.currentMax)) { 465cb93a386Sopenharmony_ci return BEST_LOCAL_SCORE; 466cb93a386Sopenharmony_ci } 467cb93a386Sopenharmony_ci 468cb93a386Sopenharmony_ci // trim possible spaces at the beginning of line 469cb93a386Sopenharmony_ci while ((param.lineNumber > 0) && (lastBreakPos_ + 1 < breaks_.size()) && 470cb93a386Sopenharmony_ci (breaks_[lastBreakPos_ + 1].subsequentWhitespace)) { 471cb93a386Sopenharmony_ci param.remainingTextWidth += (param.begin - breaks_[++lastBreakPos_].width); 472cb93a386Sopenharmony_ci param.begin = breaks_[lastBreakPos_].width; 473cb93a386Sopenharmony_ci } 474cb93a386Sopenharmony_ci 475cb93a386Sopenharmony_ci if (lastBreakPos_ < breaks_.size() && breaks_[lastBreakPos_].type == Break::BreakType::BREAKTYPE_FORCED) { 476cb93a386Sopenharmony_ci lastBreakPos_++; 477cb93a386Sopenharmony_ci } 478cb93a386Sopenharmony_ci param.breakPos = lastBreakPos_; 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_ci while (param.breakPos < breaks_.size() && breaks_[param.breakPos].width < (param.begin + param.currentMax)) { 481cb93a386Sopenharmony_ci param.breakPos++; 482cb93a386Sopenharmony_ci } 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_ci if (param.breakPos == lastBreakPos_ && param.remainingTextWidth > param.currentMax) { 485cb93a386Sopenharmony_ci // If we were unable to find a break that matches the criteria, insert new one 486cb93a386Sopenharmony_ci // This may happen if there is a long word and per line indent for this particular line 487cb93a386Sopenharmony_ci breaks_.insert(breaks_.cbegin() + param.breakPos + 1, Break(param.begin + param.currentMax, 488cb93a386Sopenharmony_ci Break::BreakType::BREAKTYPE_FORCED, false)); 489cb93a386Sopenharmony_ci param.breakPos += BREAK_NUM_TWO; 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci 492cb93a386Sopenharmony_ci LOGD("Line %{public}lu about to loop %{public}f, %{public}lu, %{public}lu, max: %{public}f", 493cb93a386Sopenharmony_ci static_cast<unsigned long>(param.lineNumber), param.begin, static_cast<unsigned long>(param.breakPos), 494cb93a386Sopenharmony_ci static_cast<unsigned long>(lastBreakPos_), maxWidth_); 495cb93a386Sopenharmony_ci 496cb93a386Sopenharmony_ci return FindOptimalSolutionForCurrentLine(param); 497cb93a386Sopenharmony_ci } 498cb93a386Sopenharmony_ci 499cb93a386Sopenharmony_ci std::vector<SkScalar>& GetResult() 500cb93a386Sopenharmony_ci { 501cb93a386Sopenharmony_ci return current_; 502cb93a386Sopenharmony_ci } 503cb93a386Sopenharmony_ci 504cb93a386Sopenharmony_ci int64_t FindOptimalSolutionForCurrentLine(RecursiveParam& param) 505cb93a386Sopenharmony_ci { 506cb93a386Sopenharmony_ci // have this in reversed order to avoid extra insertions 507cb93a386Sopenharmony_ci std::vector<SkScalar> currentBest; 508cb93a386Sopenharmony_ci bool looped = false; 509cb93a386Sopenharmony_ci int64_t score = 0; 510cb93a386Sopenharmony_ci int64_t overallScore = score; 511cb93a386Sopenharmony_ci int64_t bestLocalScore = BEST_LOCAL_SCORE; 512cb93a386Sopenharmony_ci do { 513cb93a386Sopenharmony_ci // until the given threshold is crossed (minimum line fill rate) 514cb93a386Sopenharmony_ci // re-break this line, if a result is different, calculate score 515cb93a386Sopenharmony_ci SkScalar newWidth = param.currentMax; 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci if (param.breakPos > 0 && param.begin < breaks_[param.breakPos - 1].width) { 518cb93a386Sopenharmony_ci newWidth = std::min(breaks_[--param.breakPos].width - param.begin, param.currentMax); 519cb93a386Sopenharmony_ci } 520cb93a386Sopenharmony_ci 521cb93a386Sopenharmony_ci if (looped && ((lastBreakPos_ == param.breakPos) || 522cb93a386Sopenharmony_ci (newWidth/param.currentMax*UNDERFLOW_SCORE < MINIMUM_FILL_RATIO))) { 523cb93a386Sopenharmony_ci LOGD("line %{public}lu breaking %{public}f, %{public}lu, %{public}f/%{public}f", 524cb93a386Sopenharmony_ci static_cast<unsigned long>(param.lineNumber), param.begin, 525cb93a386Sopenharmony_ci static_cast<unsigned long>(param.breakPos), newWidth, maxWidth_); 526cb93a386Sopenharmony_ci break; 527cb93a386Sopenharmony_ci } 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci lastBreakPos_ = param.breakPos; 530cb93a386Sopenharmony_ci 531cb93a386Sopenharmony_ci SkScalar currentWidth = std::min(newWidth, param.remainingTextWidth); 532cb93a386Sopenharmony_ci Index index { param.lineNumber, param.begin, currentWidth }; 533cb93a386Sopenharmony_ci 534cb93a386Sopenharmony_ci // check cache 535cb93a386Sopenharmony_ci const auto& ite = cache_.find(index); 536cb93a386Sopenharmony_ci if (ite != cache_.cend()) { 537cb93a386Sopenharmony_ci cacheHits_++; 538cb93a386Sopenharmony_ci current_ = ite->second.widths; 539cb93a386Sopenharmony_ci overallScore = ite->second.score; 540cb93a386Sopenharmony_ci UpdateSolution(bestLocalScore, overallScore, currentBest); 541cb93a386Sopenharmony_ci looped = true; 542cb93a386Sopenharmony_ci continue; 543cb93a386Sopenharmony_ci } 544cb93a386Sopenharmony_ci SkScalar scoref = std::min(1.f, abs(currentTarget_ - currentWidth) / currentTarget_); 545cb93a386Sopenharmony_ci score = int64_t((1.f - scoref) * UNDERFLOW_SCORE); 546cb93a386Sopenharmony_ci score *= score; 547cb93a386Sopenharmony_ci 548cb93a386Sopenharmony_ci current_.clear(); 549cb93a386Sopenharmony_ci overallScore = score; 550cb93a386Sopenharmony_ci 551cb93a386Sopenharmony_ci // Handle last line 552cb93a386Sopenharmony_ci if (!HandleLastLine(param, overallScore, currentWidth, score)) { 553cb93a386Sopenharmony_ci break; 554cb93a386Sopenharmony_ci } 555cb93a386Sopenharmony_ci 556cb93a386Sopenharmony_ci // we have exceeded target number of lines, add some penalty 557cb93a386Sopenharmony_ci if (param.targetLines < 0) { 558cb93a386Sopenharmony_ci overallScore += param.targetLines * PARAM_10000; // MINIMUM_FILL_RATIO; 559cb93a386Sopenharmony_ci } 560cb93a386Sopenharmony_ci 561cb93a386Sopenharmony_ci // We always hold the best possible score of children at this point 562cb93a386Sopenharmony_ci current_.push_back(currentWidth); 563cb93a386Sopenharmony_ci cache_[index] = { overallScore, current_ }; 564cb93a386Sopenharmony_ci 565cb93a386Sopenharmony_ci UpdateSolution(bestLocalScore, overallScore, currentBest); 566cb93a386Sopenharmony_ci looped = true; 567cb93a386Sopenharmony_ci } while (score > MINIMUM_FILL_RATIO_SQUARED && 568cb93a386Sopenharmony_ci !(param.lineNumber == 0 && bestLocalScore > param.targetLines * GOOD_ENOUGH_LINE_SCORE)); 569cb93a386Sopenharmony_ci current_ = currentBest; 570cb93a386Sopenharmony_ci return bestLocalScore; 571cb93a386Sopenharmony_ci } 572cb93a386Sopenharmony_ci 573cb93a386Sopenharmony_ci bool HandleLastLine(RecursiveParam& param, int64_t& overallScore, SkScalar& currentWidth, int64_t&score) 574cb93a386Sopenharmony_ci { 575cb93a386Sopenharmony_ci // Handle last line 576cb93a386Sopenharmony_ci if (abs(currentWidth - param.remainingTextWidth) < 1.f) { 577cb93a386Sopenharmony_ci // this is last line, with high-quality wrapping, relax the score a bit 578cb93a386Sopenharmony_ci if (parent_.getLineBreakStrategy() == LineBreakStrategy::HIGH_QUALITY) { 579cb93a386Sopenharmony_ci overallScore = std::max(MINIMUM_FILL_RATIO, overallScore); 580cb93a386Sopenharmony_ci } else { 581cb93a386Sopenharmony_ci overallScore *= BALANCED_LAST_LINE_MULTIPLIER; 582cb93a386Sopenharmony_ci } 583cb93a386Sopenharmony_ci 584cb93a386Sopenharmony_ci // let's break the loop, under no same condition / fill-rate added rows can result to a better 585cb93a386Sopenharmony_ci // score. 586cb93a386Sopenharmony_ci currentWidth = param.currentMax; 587cb93a386Sopenharmony_ci score = MINIMUM_FILL_RATIO_SQUARED - 1; 588cb93a386Sopenharmony_ci LOGD("last line %{public}lu reached", static_cast<unsigned long>(param.lineNumber)); 589cb93a386Sopenharmony_ci return true; 590cb93a386Sopenharmony_ci } 591cb93a386Sopenharmony_ci if (((param.remainingTextWidth - currentWidth) / maxWidth_) < param.maxLines) { 592cb93a386Sopenharmony_ci // recursively calculate best score for children 593cb93a386Sopenharmony_ci overallScore += CalculateRecursive(RecursiveParam{ 594cb93a386Sopenharmony_ci param.targetLines - 1, 595cb93a386Sopenharmony_ci param.maxLines - param.lineNumber, 596cb93a386Sopenharmony_ci param.lineNumber + 1, 597cb93a386Sopenharmony_ci param.begin + currentWidth, 598cb93a386Sopenharmony_ci param.remainingTextWidth - currentWidth 599cb93a386Sopenharmony_ci }); 600cb93a386Sopenharmony_ci lastBreakPos_ = param.breakPos; // restore our ix 601cb93a386Sopenharmony_ci return true; 602cb93a386Sopenharmony_ci } 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_ci // the text is not going to fit anyway (anymore), no need to push it 605cb93a386Sopenharmony_ci return false; 606cb93a386Sopenharmony_ci } 607cb93a386Sopenharmony_ci 608cb93a386Sopenharmony_ci void UpdateSolution(int64_t& bestLocalScore, const int64_t overallScore, std::vector<SkScalar>& currentBest) 609cb93a386Sopenharmony_ci { 610cb93a386Sopenharmony_ci if (overallScore > bestLocalScore) { 611cb93a386Sopenharmony_ci bestLocalScore = overallScore; 612cb93a386Sopenharmony_ci currentBest = current_; 613cb93a386Sopenharmony_ci } 614cb93a386Sopenharmony_ci } 615cb93a386Sopenharmony_ci 616cb93a386Sopenharmony_ciprivate: 617cb93a386Sopenharmony_ci struct Index { 618cb93a386Sopenharmony_ci size_t lineNumber { 0 }; 619cb93a386Sopenharmony_ci SkScalar begin { 0 }; 620cb93a386Sopenharmony_ci SkScalar width { 0 }; 621cb93a386Sopenharmony_ci bool operator==(const Index& other) const 622cb93a386Sopenharmony_ci { 623cb93a386Sopenharmony_ci return (lineNumber == other.lineNumber && fabs(begin - other.begin) < WIDTH_TOLERANCE && 624cb93a386Sopenharmony_ci fabs(width - other.width) < WIDTH_TOLERANCE); 625cb93a386Sopenharmony_ci } 626cb93a386Sopenharmony_ci bool operator<(const Index& other) const 627cb93a386Sopenharmony_ci { 628cb93a386Sopenharmony_ci return lineNumber < other.lineNumber || 629cb93a386Sopenharmony_ci (lineNumber == other.lineNumber && other.begin - begin > WIDTH_TOLERANCE) || 630cb93a386Sopenharmony_ci (lineNumber == other.lineNumber && fabs(begin - other.begin) < WIDTH_TOLERANCE && 631cb93a386Sopenharmony_ci other.width - width > WIDTH_TOLERANCE); 632cb93a386Sopenharmony_ci } 633cb93a386Sopenharmony_ci }; 634cb93a386Sopenharmony_ci 635cb93a386Sopenharmony_ci struct Score { 636cb93a386Sopenharmony_ci int64_t score { 0 }; 637cb93a386Sopenharmony_ci // in reversed order 638cb93a386Sopenharmony_ci std::vector<SkScalar> widths; 639cb93a386Sopenharmony_ci }; 640cb93a386Sopenharmony_ci 641cb93a386Sopenharmony_ci // to be seen if unordered map would be better fit 642cb93a386Sopenharmony_ci std::map<Index, Score> cache_; 643cb93a386Sopenharmony_ci 644cb93a386Sopenharmony_ci SkScalar maxWidth_ { 0 }; 645cb93a386Sopenharmony_ci SkScalar currentTarget_ { 0 }; 646cb93a386Sopenharmony_ci SkScalar cumulativeLen_ { 0 }; 647cb93a386Sopenharmony_ci size_t maxLines_ { 0 }; 648cb93a386Sopenharmony_ci ParagraphImpl& parent_; 649cb93a386Sopenharmony_ci std::vector<SkScalar> current_; 650cb93a386Sopenharmony_ci 651cb93a386Sopenharmony_ci struct Break { 652cb93a386Sopenharmony_ci enum class BreakType { 653cb93a386Sopenharmony_ci BREAKTYPE_NONE, 654cb93a386Sopenharmony_ci BREAKTYPE_HARD, 655cb93a386Sopenharmony_ci BREAKTYPE_WHITE_SPACE, 656cb93a386Sopenharmony_ci BREAKTYPE_INTRA, 657cb93a386Sopenharmony_ci BREAKTYPE_FORCED 658cb93a386Sopenharmony_ci }; 659cb93a386Sopenharmony_ci Break(SkScalar w, BreakType t, bool ssws) : width(w), type(t), subsequentWhitespace(ssws) {} 660cb93a386Sopenharmony_ci 661cb93a386Sopenharmony_ci SkScalar width { 0.f }; 662cb93a386Sopenharmony_ci BreakType type { BreakType::BREAKTYPE_NONE }; 663cb93a386Sopenharmony_ci bool subsequentWhitespace { false }; 664cb93a386Sopenharmony_ci }; 665cb93a386Sopenharmony_ci 666cb93a386Sopenharmony_ci std::vector<Break> breaks_; 667cb93a386Sopenharmony_ci size_t lastBreakPos_ { 0 }; 668cb93a386Sopenharmony_ci 669cb93a386Sopenharmony_ci uint64_t cacheHits_ { 0 }; 670cb93a386Sopenharmony_ci}; 671cb93a386Sopenharmony_ci 672cb93a386Sopenharmony_ciuint64_t TextWrapper::CalculateBestScore(std::vector<SkScalar>& widthOut, SkScalar maxWidth, 673cb93a386Sopenharmony_ci ParagraphImpl* parent, size_t maxLines) { 674cb93a386Sopenharmony_ci if (maxLines == 0 || !parent || nearlyZero(maxWidth)) { 675cb93a386Sopenharmony_ci return -1; 676cb93a386Sopenharmony_ci } 677cb93a386Sopenharmony_ci 678cb93a386Sopenharmony_ci TextWrapScorer* scorer = new TextWrapScorer(maxWidth, *parent, maxLines); 679cb93a386Sopenharmony_ci scorer->Run(); 680cb93a386Sopenharmony_ci while (scorer && scorer->GetResult().size()) { 681cb93a386Sopenharmony_ci auto width = scorer->GetResult().back(); 682cb93a386Sopenharmony_ci widthOut.push_back(width); 683cb93a386Sopenharmony_ci LOGD("width %{public}f", width); 684cb93a386Sopenharmony_ci scorer->GetResult().pop_back(); 685cb93a386Sopenharmony_ci } 686cb93a386Sopenharmony_ci 687cb93a386Sopenharmony_ci delete scorer; 688cb93a386Sopenharmony_ci return 0; 689cb93a386Sopenharmony_ci} 690cb93a386Sopenharmony_ci 691cb93a386Sopenharmony_civoid TextWrapper::updateMetricsWithPlaceholder(std::vector<Run*>& runs, bool iterateByCluster) { 692cb93a386Sopenharmony_ci if (!iterateByCluster) { 693cb93a386Sopenharmony_ci Run* lastRun = nullptr; 694cb93a386Sopenharmony_ci for (auto& run : runs) { 695cb93a386Sopenharmony_ci if (run == lastRun) { 696cb93a386Sopenharmony_ci continue; 697cb93a386Sopenharmony_ci } 698cb93a386Sopenharmony_ci lastRun = run; 699cb93a386Sopenharmony_ci if (lastRun != nullptr && lastRun->placeholderStyle() != nullptr) { 700cb93a386Sopenharmony_ci SkASSERT(lastRun->size() == 1); 701cb93a386Sopenharmony_ci // Update the placeholder metrics so we can get the placeholder positions later 702cb93a386Sopenharmony_ci // and the line metrics (to make sure the placeholder fits) 703cb93a386Sopenharmony_ci lastRun->updateMetrics(&fEndLine.metrics()); 704cb93a386Sopenharmony_ci } 705cb93a386Sopenharmony_ci } 706cb93a386Sopenharmony_ci return; 707cb93a386Sopenharmony_ci } 708cb93a386Sopenharmony_ci runs.clear(); 709cb93a386Sopenharmony_ci // Deal with placeholder clusters == runs[@size==1] 710cb93a386Sopenharmony_ci Run* lastRun = nullptr; 711cb93a386Sopenharmony_ci for (auto cluster = fEndLine.startCluster(); cluster <= fEndLine.endCluster(); ++cluster) { 712cb93a386Sopenharmony_ci auto r = cluster->runOrNull(); 713cb93a386Sopenharmony_ci if (r == lastRun) { 714cb93a386Sopenharmony_ci continue; 715cb93a386Sopenharmony_ci } 716cb93a386Sopenharmony_ci lastRun = r; 717cb93a386Sopenharmony_ci if (lastRun != nullptr && lastRun->placeholderStyle() != nullptr) { 718cb93a386Sopenharmony_ci SkASSERT(lastRun->size() == 1); 719cb93a386Sopenharmony_ci // Update the placeholder metrics so we can get the placeholder positions later 720cb93a386Sopenharmony_ci // and the line metrics (to make sure the placeholder fits) 721cb93a386Sopenharmony_ci lastRun->updateMetrics(&fEndLine.metrics()); 722cb93a386Sopenharmony_ci runs.emplace_back(lastRun); 723cb93a386Sopenharmony_ci } 724cb93a386Sopenharmony_ci } 725cb93a386Sopenharmony_ci} 726cb93a386Sopenharmony_ci 727cb93a386Sopenharmony_ci// TODO: refactor the code for line ending (with/without ellipsis) 728cb93a386Sopenharmony_civoid TextWrapper::breakTextIntoLines(ParagraphImpl* parent, 729cb93a386Sopenharmony_ci SkScalar maxWidth, 730cb93a386Sopenharmony_ci const AddLineToParagraph& addLine) { 731cb93a386Sopenharmony_ci fHeight = 0; 732cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::numeric_limits<SkScalar>::min(); 733cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::numeric_limits<SkScalar>::min(); 734cb93a386Sopenharmony_ci 735cb93a386Sopenharmony_ci auto span = parent->clusters(); 736cb93a386Sopenharmony_ci if (span.empty()) { 737cb93a386Sopenharmony_ci return; 738cb93a386Sopenharmony_ci } 739cb93a386Sopenharmony_ci auto maxLines = parent->paragraphStyle().getMaxLines(); 740cb93a386Sopenharmony_ci auto align = parent->paragraphStyle().effective_align(); 741cb93a386Sopenharmony_ci auto unlimitedLines = maxLines == std::numeric_limits<size_t>::max(); 742cb93a386Sopenharmony_ci auto endlessLine = !SkScalarIsFinite(maxWidth); 743cb93a386Sopenharmony_ci auto hasEllipsis = parent->paragraphStyle().ellipsized(); 744cb93a386Sopenharmony_ci 745cb93a386Sopenharmony_ci auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; 746cb93a386Sopenharmony_ci auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; 747cb93a386Sopenharmony_ci bool firstLine = true; // We only interested in fist line if we have to disable the first ascent 748cb93a386Sopenharmony_ci 749cb93a386Sopenharmony_ci bool autoSpacingEnableFlag = TextParameter::GetAutoSpacingEnable(); 750cb93a386Sopenharmony_ci // Resolve balanced line widths 751cb93a386Sopenharmony_ci std::vector<SkScalar> balancedWidths; 752cb93a386Sopenharmony_ci 753cb93a386Sopenharmony_ci // if word breaking strategy is nontrivial (balanced / optimal), AND word break mode is not BREAK_ALL 754cb93a386Sopenharmony_ci if (parent->getWordBreakType() != WordBreakType::BREAK_ALL && 755cb93a386Sopenharmony_ci parent->getLineBreakStrategy() != LineBreakStrategy::GREEDY) { 756cb93a386Sopenharmony_ci if (CalculateBestScore(balancedWidths, maxWidth, parent, maxLines) < 0) { 757cb93a386Sopenharmony_ci // if the line breaking strategy returns a negative score, the algorithm could not fit or break the text 758cb93a386Sopenharmony_ci // fall back to default, greedy algorithm 759cb93a386Sopenharmony_ci balancedWidths.clear(); 760cb93a386Sopenharmony_ci } 761cb93a386Sopenharmony_ci LOGD("Got %{public}lu", static_cast<unsigned long>(balancedWidths.size())); 762cb93a386Sopenharmony_ci } 763cb93a386Sopenharmony_ci 764cb93a386Sopenharmony_ci SkScalar softLineMaxIntrinsicWidth = 0; 765cb93a386Sopenharmony_ci fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight()); 766cb93a386Sopenharmony_ci auto end = span.end() - 1; 767cb93a386Sopenharmony_ci auto start = span.begin(); 768cb93a386Sopenharmony_ci InternalLineMetrics maxRunMetrics; 769cb93a386Sopenharmony_ci bool needEllipsis = false; 770cb93a386Sopenharmony_ci SkScalar newWidth = maxWidth; 771cb93a386Sopenharmony_ci SkScalar noIndentWidth = maxWidth; 772cb93a386Sopenharmony_ci while (fEndLine.endCluster() != end) { 773cb93a386Sopenharmony_ci noIndentWidth = maxWidth - parent->detectIndents(fLineNumber - 1); 774cb93a386Sopenharmony_ci if (maxLines == 1 && parent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD) { 775cb93a386Sopenharmony_ci newWidth = FLT_MAX; 776cb93a386Sopenharmony_ci } else if (!balancedWidths.empty() && fLineNumber - 1 < balancedWidths.size()) { 777cb93a386Sopenharmony_ci newWidth = balancedWidths[fLineNumber - 1]; 778cb93a386Sopenharmony_ci } else { 779cb93a386Sopenharmony_ci newWidth = maxWidth - parent->detectIndents(fLineNumber - 1); 780cb93a386Sopenharmony_ci } 781cb93a386Sopenharmony_ci this->lookAhead(newWidth, end, parent->getApplyRoundingHack(), parent->getWordBreakType(), 782cb93a386Sopenharmony_ci autoSpacingEnableFlag); 783cb93a386Sopenharmony_ci 784cb93a386Sopenharmony_ci auto lastLine = (hasEllipsis && unlimitedLines) || fLineNumber >= maxLines; 785cb93a386Sopenharmony_ci needEllipsis = hasEllipsis && !endlessLine && lastLine; 786cb93a386Sopenharmony_ci 787cb93a386Sopenharmony_ci this->moveForward(needEllipsis, parent->getWordBreakType() == WordBreakType::BREAK_ALL); 788cb93a386Sopenharmony_ci if (fEndLine.endCluster() >= fEndLine.startCluster() || maxLines > 1) { 789cb93a386Sopenharmony_ci needEllipsis &= fEndLine.endCluster() < end - 1; // Only if we have some text to ellipsize 790cb93a386Sopenharmony_ci } 791cb93a386Sopenharmony_ci 792cb93a386Sopenharmony_ci // Do not trim end spaces on the naturally last line of the left aligned text 793cb93a386Sopenharmony_ci this->trimEndSpaces(align); 794cb93a386Sopenharmony_ci 795cb93a386Sopenharmony_ci // For soft line breaks add to the line all the spaces next to it 796cb93a386Sopenharmony_ci Cluster* startLine; 797cb93a386Sopenharmony_ci size_t pos; 798cb93a386Sopenharmony_ci SkScalar widthWithSpaces; 799cb93a386Sopenharmony_ci std::tie(startLine, pos, widthWithSpaces) = this->trimStartSpaces(end); 800cb93a386Sopenharmony_ci 801cb93a386Sopenharmony_ci if (needEllipsis && !fHardLineBreak) { 802cb93a386Sopenharmony_ci // This is what we need to do to preserve a space before the ellipsis 803cb93a386Sopenharmony_ci fEndLine.restoreBreak(); 804cb93a386Sopenharmony_ci widthWithSpaces = fEndLine.widthWithGhostSpaces(); 805cb93a386Sopenharmony_ci } 806cb93a386Sopenharmony_ci 807cb93a386Sopenharmony_ci // If the line is empty with the hard line break, let's take the paragraph font (flutter???) 808cb93a386Sopenharmony_ci if (fEndLine.metrics().isClean()) { 809cb93a386Sopenharmony_ci fEndLine.setMetrics(parent->getEmptyMetrics()); 810cb93a386Sopenharmony_ci } 811cb93a386Sopenharmony_ci 812cb93a386Sopenharmony_ci std::vector<Run*> runs; 813cb93a386Sopenharmony_ci updateMetricsWithPlaceholder(runs, true); 814cb93a386Sopenharmony_ci // update again for some case 815cb93a386Sopenharmony_ci // such as : 816cb93a386Sopenharmony_ci // placeholderA(width: 100, height: 100, align: bottom) placeholderB(width: 200, height: 200, align: top) 817cb93a386Sopenharmony_ci // without second update: the placeholderA bottom will be set 0, and placeholderB bottom will be set 100 818cb93a386Sopenharmony_ci // so the fEndline bottom will be set 100, is not equal placeholderA bottom 819cb93a386Sopenharmony_ci updateMetricsWithPlaceholder(runs, false); 820cb93a386Sopenharmony_ci // Before we update the line metrics with struts, 821cb93a386Sopenharmony_ci // let's save it for GetRectsForRange(RectHeightStyle::kMax) 822cb93a386Sopenharmony_ci maxRunMetrics = fEndLine.metrics(); 823cb93a386Sopenharmony_ci maxRunMetrics.fForceStrut = false; 824cb93a386Sopenharmony_ci 825cb93a386Sopenharmony_ci // TODO: keep start/end/break info for text and runs but in a better way that below 826cb93a386Sopenharmony_ci TextRange textExcludingSpaces(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end); 827cb93a386Sopenharmony_ci TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.breakCluster()->textRange().start); 828cb93a386Sopenharmony_ci TextRange textIncludingNewlines(fEndLine.startCluster()->textRange().start, startLine->textRange().start); 829cb93a386Sopenharmony_ci if (startLine == end) { 830cb93a386Sopenharmony_ci textIncludingNewlines.end = parent->text().size(); 831cb93a386Sopenharmony_ci text.end = parent->text().size(); 832cb93a386Sopenharmony_ci } 833cb93a386Sopenharmony_ci ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1); 834cb93a386Sopenharmony_ci ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start); 835cb93a386Sopenharmony_ci 836cb93a386Sopenharmony_ci if (disableFirstAscent && firstLine) { 837cb93a386Sopenharmony_ci fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; 838cb93a386Sopenharmony_ci } 839cb93a386Sopenharmony_ci if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak))) { 840cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 841cb93a386Sopenharmony_ci } 842cb93a386Sopenharmony_ci 843cb93a386Sopenharmony_ci if (parent->strutEnabled()) { 844cb93a386Sopenharmony_ci // Make sure font metrics are not less than the strut 845cb93a386Sopenharmony_ci parent->strutMetrics().updateLineMetrics(fEndLine.metrics()); 846cb93a386Sopenharmony_ci } 847cb93a386Sopenharmony_ci 848cb93a386Sopenharmony_ci SkScalar lineHeight = fEndLine.metrics().height(); 849cb93a386Sopenharmony_ci firstLine = false; 850cb93a386Sopenharmony_ci 851cb93a386Sopenharmony_ci if (fEndLine.empty()) { 852cb93a386Sopenharmony_ci // Correct text and clusters (make it empty for an empty line) 853cb93a386Sopenharmony_ci textExcludingSpaces.end = textExcludingSpaces.start; 854cb93a386Sopenharmony_ci clusters.end = clusters.start; 855cb93a386Sopenharmony_ci } 856cb93a386Sopenharmony_ci 857cb93a386Sopenharmony_ci // In case of a force wrapping we don't have a break cluster and have to use the end cluster 858cb93a386Sopenharmony_ci text.end = std::max(text.end, textExcludingSpaces.end); 859cb93a386Sopenharmony_ci 860cb93a386Sopenharmony_ci if (parent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD && hasEllipsis) { 861cb93a386Sopenharmony_ci needEllipsis = maxLines <= 1; 862cb93a386Sopenharmony_ci if (needEllipsis) { 863cb93a386Sopenharmony_ci fHardLineBreak = false; 864cb93a386Sopenharmony_ci } 865cb93a386Sopenharmony_ci } 866cb93a386Sopenharmony_ci 867cb93a386Sopenharmony_ci SkScalar offsetX = parent->detectIndents(fLineNumber - 1); 868cb93a386Sopenharmony_ci addLine(textExcludingSpaces, 869cb93a386Sopenharmony_ci text, 870cb93a386Sopenharmony_ci textIncludingNewlines, clusters, clustersWithGhosts, widthWithSpaces, 871cb93a386Sopenharmony_ci fEndLine.startPos(), 872cb93a386Sopenharmony_ci fEndLine.endPos(), 873cb93a386Sopenharmony_ci SkVector::Make(offsetX, fHeight), 874cb93a386Sopenharmony_ci SkVector::Make(fEndLine.width(), lineHeight), 875cb93a386Sopenharmony_ci fEndLine.metrics(), 876cb93a386Sopenharmony_ci needEllipsis, 877cb93a386Sopenharmony_ci offsetX, 878cb93a386Sopenharmony_ci noIndentWidth); 879cb93a386Sopenharmony_ci 880cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += widthWithSpaces; 881cb93a386Sopenharmony_ci 882cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 883cb93a386Sopenharmony_ci if (fHardLineBreak) { 884cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth = 0; 885cb93a386Sopenharmony_ci } 886cb93a386Sopenharmony_ci // Start a new line 887cb93a386Sopenharmony_ci fHeight += lineHeight; 888cb93a386Sopenharmony_ci if (!fHardLineBreak || startLine != end) { 889cb93a386Sopenharmony_ci fEndLine.clean(); 890cb93a386Sopenharmony_ci } 891cb93a386Sopenharmony_ci fEndLine.startFrom(startLine, pos); 892cb93a386Sopenharmony_ci parent->fMaxWidthWithTrailingSpaces = std::max(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces); 893cb93a386Sopenharmony_ci 894cb93a386Sopenharmony_ci if (hasEllipsis && unlimitedLines) { 895cb93a386Sopenharmony_ci // There is one case when we need an ellipsis on a separate line 896cb93a386Sopenharmony_ci // after a line break when width is infinite 897cb93a386Sopenharmony_ci if (!fHardLineBreak) { 898cb93a386Sopenharmony_ci break; 899cb93a386Sopenharmony_ci } 900cb93a386Sopenharmony_ci } else if (lastLine) { 901cb93a386Sopenharmony_ci // There is nothing more to draw 902cb93a386Sopenharmony_ci fHardLineBreak = false; 903cb93a386Sopenharmony_ci break; 904cb93a386Sopenharmony_ci } 905cb93a386Sopenharmony_ci 906cb93a386Sopenharmony_ci ++fLineNumber; 907cb93a386Sopenharmony_ci } 908cb93a386Sopenharmony_ci 909cb93a386Sopenharmony_ci // We finished formatting the text but we need to scan the rest for some numbers 910cb93a386Sopenharmony_ci // TODO: make it a case of a normal flow 911cb93a386Sopenharmony_ci if (fEndLine.endCluster() != nullptr) { 912cb93a386Sopenharmony_ci auto lastWordLength = 0.0f; 913cb93a386Sopenharmony_ci auto cluster = fEndLine.endCluster(); 914cb93a386Sopenharmony_ci while (cluster != end || cluster->endPos() < end->endPos()) { 915cb93a386Sopenharmony_ci fExceededMaxLines = true; 916cb93a386Sopenharmony_ci if (cluster->isHardBreak()) { 917cb93a386Sopenharmony_ci // Hard line break ends the word and the line 918cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 919cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth = 0; 920cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 921cb93a386Sopenharmony_ci lastWordLength = 0; 922cb93a386Sopenharmony_ci } else if (cluster->isWhitespaceBreak()) { 923cb93a386Sopenharmony_ci // Whitespaces end the word 924cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 925cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 926cb93a386Sopenharmony_ci lastWordLength = 0; 927cb93a386Sopenharmony_ci } else if (cluster->run().isPlaceholder()) { 928cb93a386Sopenharmony_ci // Placeholder ends the previous word and creates a separate one 929cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 930cb93a386Sopenharmony_ci // Placeholder width now counts in fMinIntrinsicWidth 931cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 932cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width()); 933cb93a386Sopenharmony_ci lastWordLength = 0; 934cb93a386Sopenharmony_ci } else { 935cb93a386Sopenharmony_ci // Nothing out of ordinary - just add this cluster to the word and to the line 936cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 937cb93a386Sopenharmony_ci lastWordLength += cluster->width(); 938cb93a386Sopenharmony_ci } 939cb93a386Sopenharmony_ci ++cluster; 940cb93a386Sopenharmony_ci } 941cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 942cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 943cb93a386Sopenharmony_ci 944cb93a386Sopenharmony_ci if (parent->lines().empty()) { 945cb93a386Sopenharmony_ci // In case we could not place even a single cluster on the line 946cb93a386Sopenharmony_ci if (disableFirstAscent) { 947cb93a386Sopenharmony_ci fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; 948cb93a386Sopenharmony_ci } 949cb93a386Sopenharmony_ci if (disableLastDescent && !fHardLineBreak) { 950cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 951cb93a386Sopenharmony_ci } 952cb93a386Sopenharmony_ci fHeight = std::max(fHeight, fEndLine.metrics().height()); 953cb93a386Sopenharmony_ci } 954cb93a386Sopenharmony_ci } 955cb93a386Sopenharmony_ci 956cb93a386Sopenharmony_ci if (fHardLineBreak) { 957cb93a386Sopenharmony_ci if (disableLastDescent) { 958cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 959cb93a386Sopenharmony_ci } 960cb93a386Sopenharmony_ci 961cb93a386Sopenharmony_ci // Last character is a line break 962cb93a386Sopenharmony_ci if (parent->strutEnabled()) { 963cb93a386Sopenharmony_ci // Make sure font metrics are not less than the strut 964cb93a386Sopenharmony_ci parent->strutMetrics().updateLineMetrics(fEndLine.metrics()); 965cb93a386Sopenharmony_ci } 966cb93a386Sopenharmony_ci 967cb93a386Sopenharmony_ci ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.endCluster() - start); 968cb93a386Sopenharmony_ci addLine(fEndLine.breakCluster()->textRange(), 969cb93a386Sopenharmony_ci fEndLine.breakCluster()->textRange(), 970cb93a386Sopenharmony_ci fEndLine.endCluster()->textRange(), 971cb93a386Sopenharmony_ci clusters, 972cb93a386Sopenharmony_ci clusters, 973cb93a386Sopenharmony_ci 0, 974cb93a386Sopenharmony_ci 0, 975cb93a386Sopenharmony_ci 0, 976cb93a386Sopenharmony_ci SkVector::Make(0, fHeight), 977cb93a386Sopenharmony_ci SkVector::Make(0, fEndLine.metrics().height()), 978cb93a386Sopenharmony_ci fEndLine.metrics(), 979cb93a386Sopenharmony_ci needEllipsis, 980cb93a386Sopenharmony_ci parent->detectIndents(fLineNumber - 1), 981cb93a386Sopenharmony_ci noIndentWidth); 982cb93a386Sopenharmony_ci fHeight += fEndLine.metrics().height(); 983cb93a386Sopenharmony_ci parent->lines().back().setMaxRunMetrics(maxRunMetrics); 984cb93a386Sopenharmony_ci } 985cb93a386Sopenharmony_ci 986cb93a386Sopenharmony_ci if (parent->lines().empty()) { 987cb93a386Sopenharmony_ci return; 988cb93a386Sopenharmony_ci } 989cb93a386Sopenharmony_ci // Correct line metric styles for the first and for the last lines if needed 990cb93a386Sopenharmony_ci if (disableFirstAscent) { 991cb93a386Sopenharmony_ci parent->lines().front().setAscentStyle(LineMetricStyle::Typographic); 992cb93a386Sopenharmony_ci } 993cb93a386Sopenharmony_ci if (disableLastDescent) { 994cb93a386Sopenharmony_ci parent->lines().back().setDescentStyle(LineMetricStyle::Typographic); 995cb93a386Sopenharmony_ci } 996cb93a386Sopenharmony_ci} 997cb93a386Sopenharmony_ci#else 998cb93a386Sopenharmony_ci// Since we allow cluster clipping when they don't fit 999cb93a386Sopenharmony_ci// we have to work with stretches - parts of clusters 1000cb93a386Sopenharmony_civoid TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack) { 1001cb93a386Sopenharmony_ci reset(); 1002cb93a386Sopenharmony_ci fEndLine.metrics().clean(); 1003cb93a386Sopenharmony_ci fWords.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 1004cb93a386Sopenharmony_ci fClusters.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 1005cb93a386Sopenharmony_ci fClip.startFrom(fEndLine.startCluster(), fEndLine.startPos()); 1006cb93a386Sopenharmony_ci LineBreakerWithLittleRounding breaker(maxWidth, applyRoundingHack); 1007cb93a386Sopenharmony_ci Cluster* nextNonBreakingSpace = nullptr; 1008cb93a386Sopenharmony_ci for (auto cluster = fEndLine.endCluster(); cluster < endOfClusters; ++cluster) { 1009cb93a386Sopenharmony_ci if (cluster->isHardBreak()) { 1010cb93a386Sopenharmony_ci } else if ( 1011cb93a386Sopenharmony_ci SkScalar width = fWords.width() + fClusters.width() + cluster->width(); 1012cb93a386Sopenharmony_ci breaker.breakLine(width)) { 1013cb93a386Sopenharmony_ci if (cluster->isWhitespaceBreak()) { 1014cb93a386Sopenharmony_ci // It's the end of the word 1015cb93a386Sopenharmony_ci fClusters.extend(cluster); 1016cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth()); 1017cb93a386Sopenharmony_ci fWords.extend(fClusters); 1018cb93a386Sopenharmony_ci continue; 1019cb93a386Sopenharmony_ci } else if (cluster->run().isPlaceholder()) { 1020cb93a386Sopenharmony_ci if (!fClusters.empty()) { 1021cb93a386Sopenharmony_ci // Placeholder ends the previous word 1022cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth()); 1023cb93a386Sopenharmony_ci fWords.extend(fClusters); 1024cb93a386Sopenharmony_ci } 1025cb93a386Sopenharmony_ci if (cluster->width() > maxWidth && fWords.empty()) { 1026cb93a386Sopenharmony_ci // Placeholder is the only text and it's longer than the line; 1027cb93a386Sopenharmony_ci // it does not count in fMinIntrinsicWidth 1028cb93a386Sopenharmony_ci fClusters.extend(cluster); 1029cb93a386Sopenharmony_ci fTooLongCluster = true; 1030cb93a386Sopenharmony_ci fTooLongWord = true; 1031cb93a386Sopenharmony_ci } else { 1032cb93a386Sopenharmony_ci // Placeholder does not fit the line; it will be considered again on the next line 1033cb93a386Sopenharmony_ci } 1034cb93a386Sopenharmony_ci break; 1035cb93a386Sopenharmony_ci } 1036cb93a386Sopenharmony_ci // Walk further to see if there is a too long word, cluster or glyph 1037cb93a386Sopenharmony_ci SkScalar nextWordLength = fClusters.width(); 1038cb93a386Sopenharmony_ci SkScalar nextShortWordLength = nextWordLength; 1039cb93a386Sopenharmony_ci for (auto further = cluster; further != endOfClusters; ++further) { 1040cb93a386Sopenharmony_ci if (further->isSoftBreak() || further->isHardBreak() || further->isWhitespaceBreak()) { 1041cb93a386Sopenharmony_ci break; 1042cb93a386Sopenharmony_ci } 1043cb93a386Sopenharmony_ci if (further->run().isPlaceholder()) { 1044cb93a386Sopenharmony_ci // Placeholder ends the word 1045cb93a386Sopenharmony_ci break; 1046cb93a386Sopenharmony_ci } 1047cb93a386Sopenharmony_ci if (nextWordLength > 0 && nextWordLength <= maxWidth && further->isIntraWordBreak()) { 1048cb93a386Sopenharmony_ci // The cluster is spaces but not the end of the word in a normal sense 1049cb93a386Sopenharmony_ci nextNonBreakingSpace = further; 1050cb93a386Sopenharmony_ci nextShortWordLength = nextWordLength; 1051cb93a386Sopenharmony_ci } 1052cb93a386Sopenharmony_ci if (maxWidth == 0) { 1053cb93a386Sopenharmony_ci // This is a tricky flutter case: layout(width:0) places 1 cluster on each line 1054cb93a386Sopenharmony_ci nextWordLength = std::max(nextWordLength, further->width()); 1055cb93a386Sopenharmony_ci } else { 1056cb93a386Sopenharmony_ci nextWordLength += further->width(); 1057cb93a386Sopenharmony_ci } 1058cb93a386Sopenharmony_ci } 1059cb93a386Sopenharmony_ci if (nextWordLength > maxWidth) { 1060cb93a386Sopenharmony_ci if (nextNonBreakingSpace != nullptr) { 1061cb93a386Sopenharmony_ci // We only get here if the non-breaking space improves our situation 1062cb93a386Sopenharmony_ci // (allows us to break the text to fit the word) 1063cb93a386Sopenharmony_ci if (SkScalar shortLength = fWords.width() + nextShortWordLength; 1064cb93a386Sopenharmony_ci !breaker.breakLine(shortLength)) { 1065cb93a386Sopenharmony_ci // We can add the short word to the existing line 1066cb93a386Sopenharmony_ci fClusters = TextStretch(fClusters.startCluster(), nextNonBreakingSpace, 1067cb93a386Sopenharmony_ci fClusters.metrics().getForceStrut()); 1068cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextShortWordLength); 1069cb93a386Sopenharmony_ci fWords.extend(fClusters); 1070cb93a386Sopenharmony_ci } else { 1071cb93a386Sopenharmony_ci // We can place the short word on the next line 1072cb93a386Sopenharmony_ci fClusters.clean(); 1073cb93a386Sopenharmony_ci } 1074cb93a386Sopenharmony_ci // Either way we are not in "word is too long" situation anymore 1075cb93a386Sopenharmony_ci break; 1076cb93a386Sopenharmony_ci } 1077cb93a386Sopenharmony_ci // If the word is too long we can break it right now and hope it's enough 1078cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextWordLength); 1079cb93a386Sopenharmony_ci if (fClusters.endPos() - fClusters.startPos() > 1 || 1080cb93a386Sopenharmony_ci fWords.empty()) { 1081cb93a386Sopenharmony_ci fTooLongWord = true; 1082cb93a386Sopenharmony_ci } else { 1083cb93a386Sopenharmony_ci // Even if the word is too long there is a very little space on this line. 1084cb93a386Sopenharmony_ci // let's deal with it on the next line. 1085cb93a386Sopenharmony_ci } 1086cb93a386Sopenharmony_ci } 1087cb93a386Sopenharmony_ci if (cluster->width() > maxWidth) { 1088cb93a386Sopenharmony_ci fClusters.extend(cluster); 1089cb93a386Sopenharmony_ci fTooLongCluster = true; 1090cb93a386Sopenharmony_ci fTooLongWord = true; 1091cb93a386Sopenharmony_ci } 1092cb93a386Sopenharmony_ci break; 1093cb93a386Sopenharmony_ci } 1094cb93a386Sopenharmony_ci if (cluster->run().isPlaceholder()) { 1095cb93a386Sopenharmony_ci if (!fClusters.empty()) { 1096cb93a386Sopenharmony_ci // Placeholder ends the previous word (placeholders are ignored in trimming) 1097cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth()); 1098cb93a386Sopenharmony_ci fWords.extend(fClusters); 1099cb93a386Sopenharmony_ci } 1100cb93a386Sopenharmony_ci // Placeholder is separate word and its width now is counted in minIntrinsicWidth 1101cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width()); 1102cb93a386Sopenharmony_ci fWords.extend(cluster); 1103cb93a386Sopenharmony_ci } else { 1104cb93a386Sopenharmony_ci fClusters.extend(cluster); 1105cb93a386Sopenharmony_ci // Keep adding clusters/words 1106cb93a386Sopenharmony_ci if (fClusters.endOfWord()) { 1107cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth()); 1108cb93a386Sopenharmony_ci fWords.extend(fClusters); 1109cb93a386Sopenharmony_ci } 1110cb93a386Sopenharmony_ci } 1111cb93a386Sopenharmony_ci if ((fHardLineBreak = cluster->isHardBreak())) { 1112cb93a386Sopenharmony_ci // Stop at the hard line break 1113cb93a386Sopenharmony_ci break; 1114cb93a386Sopenharmony_ci } 1115cb93a386Sopenharmony_ci } 1116cb93a386Sopenharmony_ci} 1117cb93a386Sopenharmony_civoid TextWrapper::moveForward(bool hasEllipsis) { 1118cb93a386Sopenharmony_ci // We normally break lines by words. 1119cb93a386Sopenharmony_ci // The only way we may go to clusters is if the word is too long or 1120cb93a386Sopenharmony_ci // it's the first word and it has an ellipsis attached to it. 1121cb93a386Sopenharmony_ci // If nothing fits we show the clipping. 1122cb93a386Sopenharmony_ci if (!fWords.empty()) { 1123cb93a386Sopenharmony_ci fEndLine.extend(fWords); 1124cb93a386Sopenharmony_ci#ifdef SK_IGNORE_SKPARAGRAPH_ELLIPSIS_FIX 1125cb93a386Sopenharmony_ci if (!fTooLongWord || hasEllipsis) { // Ellipsis added to a word 1126cb93a386Sopenharmony_ci#else 1127cb93a386Sopenharmony_ci if (!fTooLongWord && !hasEllipsis) { // Ellipsis added to a grapheme 1128cb93a386Sopenharmony_ci#endif 1129cb93a386Sopenharmony_ci return; 1130cb93a386Sopenharmony_ci } 1131cb93a386Sopenharmony_ci } 1132cb93a386Sopenharmony_ci if (!fClusters.empty()) { 1133cb93a386Sopenharmony_ci fEndLine.extend(fClusters); 1134cb93a386Sopenharmony_ci if (!fTooLongCluster) { 1135cb93a386Sopenharmony_ci return; 1136cb93a386Sopenharmony_ci } 1137cb93a386Sopenharmony_ci } 1138cb93a386Sopenharmony_ci if (!fClip.empty()) { 1139cb93a386Sopenharmony_ci // Flutter: forget the clipped cluster but keep the metrics 1140cb93a386Sopenharmony_ci fEndLine.metrics().add(fClip.metrics()); 1141cb93a386Sopenharmony_ci } 1142cb93a386Sopenharmony_ci} 1143cb93a386Sopenharmony_ci// Special case for start/end cluster since they can be clipped 1144cb93a386Sopenharmony_civoid TextWrapper::trimEndSpaces(TextAlign align) { 1145cb93a386Sopenharmony_ci // Remember the breaking position 1146cb93a386Sopenharmony_ci fEndLine.saveBreak(); 1147cb93a386Sopenharmony_ci // Skip all space cluster at the end 1148cb93a386Sopenharmony_ci for (auto cluster = fEndLine.endCluster(); 1149cb93a386Sopenharmony_ci cluster >= fEndLine.startCluster() && cluster->isWhitespaceBreak(); 1150cb93a386Sopenharmony_ci --cluster) { 1151cb93a386Sopenharmony_ci fEndLine.trim(cluster); 1152cb93a386Sopenharmony_ci } 1153cb93a386Sopenharmony_ci fEndLine.trim(); 1154cb93a386Sopenharmony_ci} 1155cb93a386Sopenharmony_ciSkScalar TextWrapper::getClustersTrimmedWidth() { 1156cb93a386Sopenharmony_ci // Move the end of the line to the left 1157cb93a386Sopenharmony_ci SkScalar width = 0; 1158cb93a386Sopenharmony_ci bool trailingSpaces = true; 1159cb93a386Sopenharmony_ci for (auto cluster = fClusters.endCluster(); cluster >= fClusters.startCluster(); --cluster) { 1160cb93a386Sopenharmony_ci if (cluster->run().isPlaceholder()) { 1161cb93a386Sopenharmony_ci continue; 1162cb93a386Sopenharmony_ci } 1163cb93a386Sopenharmony_ci if (trailingSpaces) { 1164cb93a386Sopenharmony_ci if (!cluster->isWhitespaceBreak()) { 1165cb93a386Sopenharmony_ci width += cluster->trimmedWidth(cluster->endPos()); 1166cb93a386Sopenharmony_ci trailingSpaces = false; 1167cb93a386Sopenharmony_ci } 1168cb93a386Sopenharmony_ci continue; 1169cb93a386Sopenharmony_ci } 1170cb93a386Sopenharmony_ci width += cluster->width(); 1171cb93a386Sopenharmony_ci } 1172cb93a386Sopenharmony_ci return width; 1173cb93a386Sopenharmony_ci} 1174cb93a386Sopenharmony_ci// Trim the beginning spaces in case of soft line break 1175cb93a386Sopenharmony_cistd::tuple<Cluster*, size_t, SkScalar> TextWrapper::trimStartSpaces(Cluster* endOfClusters) { 1176cb93a386Sopenharmony_ci if (fHardLineBreak) { 1177cb93a386Sopenharmony_ci // End of line is always end of cluster, but need to skip \n 1178cb93a386Sopenharmony_ci auto width = fEndLine.width(); 1179cb93a386Sopenharmony_ci auto cluster = fEndLine.endCluster() + 1; 1180cb93a386Sopenharmony_ci while (cluster < fEndLine.breakCluster() && cluster->isWhitespaceBreak()) { 1181cb93a386Sopenharmony_ci width += cluster->width(); 1182cb93a386Sopenharmony_ci ++cluster; 1183cb93a386Sopenharmony_ci } 1184cb93a386Sopenharmony_ci return std::make_tuple(fEndLine.breakCluster() + 1, 0, width); 1185cb93a386Sopenharmony_ci } 1186cb93a386Sopenharmony_ci // breakCluster points to the end of the line; 1187cb93a386Sopenharmony_ci // It's a soft line break so we need to move lineStart forward skipping all the spaces 1188cb93a386Sopenharmony_ci auto width = fEndLine.widthWithGhostSpaces(); 1189cb93a386Sopenharmony_ci auto cluster = fEndLine.breakCluster() + 1; 1190cb93a386Sopenharmony_ci while (cluster < endOfClusters && cluster->isWhitespaceBreak()) { 1191cb93a386Sopenharmony_ci width += cluster->width(); 1192cb93a386Sopenharmony_ci ++cluster; 1193cb93a386Sopenharmony_ci } 1194cb93a386Sopenharmony_ci if (fEndLine.breakCluster()->isWhitespaceBreak() && fEndLine.breakCluster() < endOfClusters) { 1195cb93a386Sopenharmony_ci // In case of a soft line break by the whitespace 1196cb93a386Sopenharmony_ci // fBreak should point to the beginning of the next line 1197cb93a386Sopenharmony_ci // (it only matters when there are trailing spaces) 1198cb93a386Sopenharmony_ci fEndLine.shiftBreak(); 1199cb93a386Sopenharmony_ci } 1200cb93a386Sopenharmony_ci return std::make_tuple(cluster, 0, width); 1201cb93a386Sopenharmony_ci} 1202cb93a386Sopenharmony_civoid TextWrapper::breakTextIntoLines(ParagraphImpl* parent, 1203cb93a386Sopenharmony_ci SkScalar maxWidth, 1204cb93a386Sopenharmony_ci const AddLineToParagraph& addLine) { 1205cb93a386Sopenharmony_ci fHeight = 0; 1206cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::numeric_limits<SkScalar>::min(); 1207cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::numeric_limits<SkScalar>::min(); 1208cb93a386Sopenharmony_ci auto span = parent->clusters(); 1209cb93a386Sopenharmony_ci if (span.empty()) { 1210cb93a386Sopenharmony_ci return; 1211cb93a386Sopenharmony_ci } 1212cb93a386Sopenharmony_ci auto maxLines = parent->paragraphStyle().getMaxLines(); 1213cb93a386Sopenharmony_ci auto align = parent->paragraphStyle().effective_align(); 1214cb93a386Sopenharmony_ci auto unlimitedLines = maxLines == std::numeric_limits<size_t>::max(); 1215cb93a386Sopenharmony_ci auto endlessLine = !SkScalarIsFinite(maxWidth); 1216cb93a386Sopenharmony_ci auto hasEllipsis = parent->paragraphStyle().ellipsized(); 1217cb93a386Sopenharmony_ci auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; 1218cb93a386Sopenharmony_ci auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; 1219cb93a386Sopenharmony_ci bool firstLine = true; // We only interested in fist line if we have to disable the first ascent 1220cb93a386Sopenharmony_ci SkScalar softLineMaxIntrinsicWidth = 0; 1221cb93a386Sopenharmony_ci fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight()); 1222cb93a386Sopenharmony_ci auto end = span.end() - 1; 1223cb93a386Sopenharmony_ci auto start = span.begin(); 1224cb93a386Sopenharmony_ci InternalLineMetrics maxRunMetrics; 1225cb93a386Sopenharmony_ci bool needEllipsis = false; 1226cb93a386Sopenharmony_ci while (fEndLine.endCluster() != end) { 1227cb93a386Sopenharmony_ci this->lookAhead(maxWidth, end, parent->getApplyRoundingHack()); 1228cb93a386Sopenharmony_ci auto lastLine = (hasEllipsis && unlimitedLines) || fLineNumber >= maxLines; 1229cb93a386Sopenharmony_ci needEllipsis = hasEllipsis && !endlessLine && lastLine; 1230cb93a386Sopenharmony_ci this->moveForward(needEllipsis); 1231cb93a386Sopenharmony_ci needEllipsis &= fEndLine.endCluster() < end - 1; // Only if we have some text to ellipsize 1232cb93a386Sopenharmony_ci // Do not trim end spaces on the naturally last line of the left aligned text 1233cb93a386Sopenharmony_ci this->trimEndSpaces(align); 1234cb93a386Sopenharmony_ci // For soft line breaks add to the line all the spaces next to it 1235cb93a386Sopenharmony_ci Cluster* startLine; 1236cb93a386Sopenharmony_ci size_t pos; 1237cb93a386Sopenharmony_ci SkScalar widthWithSpaces; 1238cb93a386Sopenharmony_ci std::tie(startLine, pos, widthWithSpaces) = this->trimStartSpaces(end); 1239cb93a386Sopenharmony_ci if (needEllipsis && !fHardLineBreak) { 1240cb93a386Sopenharmony_ci // This is what we need to do to preserve a space before the ellipsis 1241cb93a386Sopenharmony_ci fEndLine.restoreBreak(); 1242cb93a386Sopenharmony_ci widthWithSpaces = fEndLine.widthWithGhostSpaces(); 1243cb93a386Sopenharmony_ci } 1244cb93a386Sopenharmony_ci // If the line is empty with the hard line break, let's take the paragraph font (flutter???) 1245cb93a386Sopenharmony_ci if (fEndLine.metrics().isClean()) { 1246cb93a386Sopenharmony_ci fEndLine.setMetrics(parent->getEmptyMetrics()); 1247cb93a386Sopenharmony_ci } 1248cb93a386Sopenharmony_ci // Deal with placeholder clusters == runs[@size==1] 1249cb93a386Sopenharmony_ci Run* lastRun = nullptr; 1250cb93a386Sopenharmony_ci for (auto cluster = fEndLine.startCluster(); cluster <= fEndLine.endCluster(); ++cluster) { 1251cb93a386Sopenharmony_ci auto r = cluster->runOrNull(); 1252cb93a386Sopenharmony_ci if (r == lastRun) { 1253cb93a386Sopenharmony_ci continue; 1254cb93a386Sopenharmony_ci } 1255cb93a386Sopenharmony_ci lastRun = r; 1256cb93a386Sopenharmony_ci if (lastRun->placeholderStyle() != nullptr) { 1257cb93a386Sopenharmony_ci SkASSERT(lastRun->size() == 1); 1258cb93a386Sopenharmony_ci // Update the placeholder metrics so we can get the placeholder positions later 1259cb93a386Sopenharmony_ci // and the line metrics (to make sure the placeholder fits) 1260cb93a386Sopenharmony_ci lastRun->updateMetrics(&fEndLine.metrics()); 1261cb93a386Sopenharmony_ci } 1262cb93a386Sopenharmony_ci } 1263cb93a386Sopenharmony_ci // Before we update the line metrics with struts, 1264cb93a386Sopenharmony_ci // let's save it for GetRectsForRange(RectHeightStyle::kMax) 1265cb93a386Sopenharmony_ci maxRunMetrics = fEndLine.metrics(); 1266cb93a386Sopenharmony_ci maxRunMetrics.fForceStrut = false; 1267cb93a386Sopenharmony_ci TextRange textExcludingSpaces(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end); 1268cb93a386Sopenharmony_ci TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.breakCluster()->textRange().start); 1269cb93a386Sopenharmony_ci TextRange textIncludingNewlines(fEndLine.startCluster()->textRange().start, startLine->textRange().start); 1270cb93a386Sopenharmony_ci if (startLine == end) { 1271cb93a386Sopenharmony_ci textIncludingNewlines.end = parent->text().size(); 1272cb93a386Sopenharmony_ci text.end = parent->text().size(); 1273cb93a386Sopenharmony_ci } 1274cb93a386Sopenharmony_ci ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1); 1275cb93a386Sopenharmony_ci ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start); 1276cb93a386Sopenharmony_ci if (disableFirstAscent && firstLine) { 1277cb93a386Sopenharmony_ci fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; 1278cb93a386Sopenharmony_ci } 1279cb93a386Sopenharmony_ci if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak ))) { 1280cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 1281cb93a386Sopenharmony_ci } 1282cb93a386Sopenharmony_ci if (parent->strutEnabled()) { 1283cb93a386Sopenharmony_ci // Make sure font metrics are not less than the strut 1284cb93a386Sopenharmony_ci parent->strutMetrics().updateLineMetrics(fEndLine.metrics()); 1285cb93a386Sopenharmony_ci } 1286cb93a386Sopenharmony_ci SkScalar lineHeight = fEndLine.metrics().height(); 1287cb93a386Sopenharmony_ci firstLine = false; 1288cb93a386Sopenharmony_ci if (fEndLine.empty()) { 1289cb93a386Sopenharmony_ci // Correct text and clusters (make it empty for an empty line) 1290cb93a386Sopenharmony_ci textExcludingSpaces.end = textExcludingSpaces.start; 1291cb93a386Sopenharmony_ci clusters.end = clusters.start; 1292cb93a386Sopenharmony_ci } 1293cb93a386Sopenharmony_ci // In case of a force wrapping we don't have a break cluster and have to use the end cluster 1294cb93a386Sopenharmony_ci text.end = std::max(text.end, textExcludingSpaces.end); 1295cb93a386Sopenharmony_ci addLine(textExcludingSpaces, 1296cb93a386Sopenharmony_ci text, 1297cb93a386Sopenharmony_ci textIncludingNewlines, clusters, clustersWithGhosts, widthWithSpaces, 1298cb93a386Sopenharmony_ci fEndLine.startPos(), 1299cb93a386Sopenharmony_ci fEndLine.endPos(), 1300cb93a386Sopenharmony_ci SkVector::Make(0, fHeight), 1301cb93a386Sopenharmony_ci SkVector::Make(fEndLine.width(), lineHeight), 1302cb93a386Sopenharmony_ci fEndLine.metrics(), 1303cb93a386Sopenharmony_ci needEllipsis && !fHardLineBreak); 1304cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += widthWithSpaces; 1305cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 1306cb93a386Sopenharmony_ci if (fHardLineBreak) { 1307cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth = 0; 1308cb93a386Sopenharmony_ci } 1309cb93a386Sopenharmony_ci // Start a new line 1310cb93a386Sopenharmony_ci fHeight += lineHeight; 1311cb93a386Sopenharmony_ci if (!fHardLineBreak || startLine != end) { 1312cb93a386Sopenharmony_ci fEndLine.clean(); 1313cb93a386Sopenharmony_ci } 1314cb93a386Sopenharmony_ci fEndLine.startFrom(startLine, pos); 1315cb93a386Sopenharmony_ci parent->fMaxWidthWithTrailingSpaces = std::max(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces); 1316cb93a386Sopenharmony_ci if (hasEllipsis && unlimitedLines) { 1317cb93a386Sopenharmony_ci // There is one case when we need an ellipsis on a separate line 1318cb93a386Sopenharmony_ci // after a line break when width is infinite 1319cb93a386Sopenharmony_ci if (!fHardLineBreak) { 1320cb93a386Sopenharmony_ci break; 1321cb93a386Sopenharmony_ci } 1322cb93a386Sopenharmony_ci } else if (lastLine) { 1323cb93a386Sopenharmony_ci // There is nothing more to draw 1324cb93a386Sopenharmony_ci fHardLineBreak = false; 1325cb93a386Sopenharmony_ci break; 1326cb93a386Sopenharmony_ci } 1327cb93a386Sopenharmony_ci ++fLineNumber; 1328cb93a386Sopenharmony_ci } 1329cb93a386Sopenharmony_ci // We finished formatting the text but we need to scan the rest for some numbers 1330cb93a386Sopenharmony_ci if (fEndLine.endCluster() != nullptr) { 1331cb93a386Sopenharmony_ci auto lastWordLength = 0.0f; 1332cb93a386Sopenharmony_ci auto cluster = fEndLine.endCluster(); 1333cb93a386Sopenharmony_ci while (cluster != end || cluster->endPos() < end->endPos()) { 1334cb93a386Sopenharmony_ci fExceededMaxLines = true; 1335cb93a386Sopenharmony_ci if (cluster->isHardBreak()) { 1336cb93a386Sopenharmony_ci // Hard line break ends the word and the line 1337cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 1338cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth = 0; 1339cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 1340cb93a386Sopenharmony_ci lastWordLength = 0; 1341cb93a386Sopenharmony_ci } else if (cluster->isWhitespaceBreak()) { 1342cb93a386Sopenharmony_ci // Whitespaces end the word 1343cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 1344cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 1345cb93a386Sopenharmony_ci lastWordLength = 0; 1346cb93a386Sopenharmony_ci } else if (cluster->run().isPlaceholder()) { 1347cb93a386Sopenharmony_ci // Placeholder ends the previous word and creates a separate one 1348cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 1349cb93a386Sopenharmony_ci // Placeholder width now counts in fMinIntrinsicWidth 1350cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 1351cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width()); 1352cb93a386Sopenharmony_ci lastWordLength = 0; 1353cb93a386Sopenharmony_ci } else { 1354cb93a386Sopenharmony_ci // Nothing out of ordinary - just add this cluster to the word and to the line 1355cb93a386Sopenharmony_ci softLineMaxIntrinsicWidth += cluster->width(); 1356cb93a386Sopenharmony_ci lastWordLength += cluster->width(); 1357cb93a386Sopenharmony_ci } 1358cb93a386Sopenharmony_ci ++cluster; 1359cb93a386Sopenharmony_ci } 1360cb93a386Sopenharmony_ci fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength); 1361cb93a386Sopenharmony_ci fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth); 1362cb93a386Sopenharmony_ci if (parent->lines().empty()) { 1363cb93a386Sopenharmony_ci // In case we could not place even a single cluster on the line 1364cb93a386Sopenharmony_ci if (disableFirstAscent) { 1365cb93a386Sopenharmony_ci fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; 1366cb93a386Sopenharmony_ci } 1367cb93a386Sopenharmony_ci if (disableLastDescent && !fHardLineBreak) { 1368cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 1369cb93a386Sopenharmony_ci } 1370cb93a386Sopenharmony_ci fHeight = std::max(fHeight, fEndLine.metrics().height()); 1371cb93a386Sopenharmony_ci } 1372cb93a386Sopenharmony_ci } 1373cb93a386Sopenharmony_ci if (fHardLineBreak) { 1374cb93a386Sopenharmony_ci if (disableLastDescent) { 1375cb93a386Sopenharmony_ci fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; 1376cb93a386Sopenharmony_ci } 1377cb93a386Sopenharmony_ci // Last character is a line break 1378cb93a386Sopenharmony_ci if (parent->strutEnabled()) { 1379cb93a386Sopenharmony_ci // Make sure font metrics are not less than the strut 1380cb93a386Sopenharmony_ci parent->strutMetrics().updateLineMetrics(fEndLine.metrics()); 1381cb93a386Sopenharmony_ci } 1382cb93a386Sopenharmony_ci ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.endCluster() - start); 1383cb93a386Sopenharmony_ci addLine(fEndLine.breakCluster()->textRange(), 1384cb93a386Sopenharmony_ci fEndLine.breakCluster()->textRange(), 1385cb93a386Sopenharmony_ci fEndLine.endCluster()->textRange(), 1386cb93a386Sopenharmony_ci clusters, 1387cb93a386Sopenharmony_ci clusters, 1388cb93a386Sopenharmony_ci 0, 1389cb93a386Sopenharmony_ci 0, 1390cb93a386Sopenharmony_ci 0, 1391cb93a386Sopenharmony_ci SkVector::Make(0, fHeight), 1392cb93a386Sopenharmony_ci SkVector::Make(0, fEndLine.metrics().height()), 1393cb93a386Sopenharmony_ci fEndLine.metrics(), 1394cb93a386Sopenharmony_ci needEllipsis); 1395cb93a386Sopenharmony_ci fHeight += fEndLine.metrics().height(); 1396cb93a386Sopenharmony_ci parent->lines().back().setMaxRunMetrics(maxRunMetrics); 1397cb93a386Sopenharmony_ci } 1398cb93a386Sopenharmony_ci if (parent->lines().empty()) { 1399cb93a386Sopenharmony_ci return; 1400cb93a386Sopenharmony_ci } 1401cb93a386Sopenharmony_ci // Correct line metric styles for the first and for the last lines if needed 1402cb93a386Sopenharmony_ci if (disableFirstAscent) { 1403cb93a386Sopenharmony_ci parent->lines().front().setAscentStyle(LineMetricStyle::Typographic); 1404cb93a386Sopenharmony_ci } 1405cb93a386Sopenharmony_ci if (disableLastDescent) { 1406cb93a386Sopenharmony_ci parent->lines().back().setDescentStyle(LineMetricStyle::Typographic); 1407cb93a386Sopenharmony_ci } 1408cb93a386Sopenharmony_ci} 1409cb93a386Sopenharmony_ci#endif 1410cb93a386Sopenharmony_ci} // namespace textlayout 1411cb93a386Sopenharmony_ci} // namespace skia 1412