1 // Copyright 2019 Google LLC. 2 #ifndef TextWrapper_DEFINED 3 #define TextWrapper_DEFINED 4 5 #include <string> 6 #ifdef ENABLE_TEXT_ENHANCE 7 #include "include/ParagraphStyle.h" 8 #include "src/Run.h" 9 #endif 10 #include "include/core/SkSpan.h" 11 #include "modules/skparagraph/src/TextLine.h" 12 13 #ifdef ENABLE_TEXT_ENHANCE 14 #include <list> 15 #include <vector> 16 17 #include "include/TextStyle.h" 18 #include "modules/skparagraph/src/ParagraphImpl.h" 19 #include "SkScalar.h" 20 #endif 21 22 namespace skia { 23 namespace textlayout { 24 25 #ifdef ENABLE_TEXT_ENHANCE 26 const size_t STRATEGY_START_POS{2}; 27 const size_t MIN_COST_POS{2}; 28 const size_t MAX_LINES_LIMIT{1000000000}; 29 #endif 30 31 class ParagraphImpl; 32 #ifdef ENABLE_TEXT_ENHANCE 33 class TextTabAlign; 34 #endif 35 36 class TextWrapper { 37 class ClusterPos { 38 public: ClusterPos()39 ClusterPos() : fCluster(nullptr), fPos(0) {} ClusterPos(Cluster * cluster,size_t pos)40 ClusterPos(Cluster* cluster, size_t pos) : fCluster(cluster), fPos(pos) {} cluster()41 inline Cluster* cluster() const { return fCluster; } position()42 inline size_t position() const { return fPos; } setPosition(size_t pos)43 inline void setPosition(size_t pos) { fPos = pos; } clean()44 void clean() { 45 fCluster = nullptr; 46 fPos = 0; 47 } move(bool up)48 void move(bool up) { 49 fCluster += up ? 1 : -1; 50 fPos = up ? 0 : fCluster->endPos(); 51 } 52 #ifdef ENABLE_TEXT_ENHANCE setCluster(Cluster * cluster)53 void setCluster(Cluster* cluster) { fCluster = cluster; } 54 #endif 55 56 private: 57 Cluster* fCluster; 58 size_t fPos; 59 }; 60 class TextStretch { 61 public: TextStretch()62 TextStretch() : fStart(), fEnd(), fWidth(0), fWidthWithGhostSpaces(0) {} TextStretch(Cluster * s,Cluster * e,bool forceStrut)63 TextStretch(Cluster* s, Cluster* e, bool forceStrut) 64 : fStart(s, 0), fEnd(e, e->endPos()), fMetrics(forceStrut), fWidth(0), fWidthWithGhostSpaces(0) { 65 for (auto c = s; c <= e; ++c) { 66 if (auto r = c->runOrNull()) { 67 fMetrics.add(r); 68 } 69 if (c < e) { 70 fWidth += c->width(); 71 } 72 } 73 fWidthWithGhostSpaces = fWidth; 74 } 75 76 #ifdef ENABLE_TEXT_ENHANCE TextStretch(Cluster * c,bool forceStrut)77 TextStretch(Cluster* c, bool forceStrut) 78 : fStart(c, 0), fEnd(c, c->endPos()), fMetrics(forceStrut), fWidth(0), fWidthWithGhostSpaces(0) { 79 if (auto r = c->runOrNull()) { 80 fMetrics.add(r); 81 } 82 fWidth = c->width(); 83 fWidthWithGhostSpaces = fWidth; 84 } 85 split()86 std::vector<TextStretch> split() { 87 ParagraphImpl* owner = fStart.cluster()->getOwner(); 88 Cluster* cluster = fStart.cluster(); 89 std::vector<TextStretch> result{}; 90 while(cluster <= fEnd.cluster()) { 91 auto endIndex = (cluster)->textRange().end; 92 Cluster* endCluster = &owner->cluster( 93 std::min(owner->fClustersIndexFromCodeUnit[endIndex], owner->clusters().size() - 1)); 94 TextStretch singleClusterStretch = TextStretch(cluster, metrics().getForceStrut()); 95 result.push_back(singleClusterStretch); 96 cluster = endCluster; 97 } 98 return result; 99 } 100 setStartCluster(Cluster * cluster)101 void setStartCluster(Cluster* cluster) { fStart.setCluster(cluster); } 102 #endif 103 width()104 inline SkScalar width() const { return fWidth; } widthWithGhostSpaces()105 SkScalar widthWithGhostSpaces() const { return fWidthWithGhostSpaces; } startCluster()106 inline Cluster* startCluster() const { return fStart.cluster(); } endCluster()107 inline Cluster* endCluster() const { return fEnd.cluster(); } breakCluster()108 inline Cluster* breakCluster() const { return fBreak.cluster(); } metrics()109 inline InternalLineMetrics& metrics() { return fMetrics; } startPos()110 inline size_t startPos() const { return fStart.position(); } endPos()111 inline size_t endPos() const { return fEnd.position(); } endOfCluster()112 bool endOfCluster() { return fEnd.position() == fEnd.cluster()->endPos(); } endOfWord()113 bool endOfWord() { 114 return endOfCluster() && 115 (fEnd.cluster()->isHardBreak() || fEnd.cluster()->isSoftBreak()); 116 } 117 extend(TextStretch & stretch)118 void extend(TextStretch& stretch) { 119 fMetrics.add(stretch.fMetrics); 120 fEnd = stretch.fEnd; 121 fWidth += stretch.fWidth; 122 stretch.clean(); 123 } 124 empty()125 bool empty() { return fStart.cluster() == fEnd.cluster() && 126 fStart.position() == fEnd.position(); } 127 setMetrics(const InternalLineMetrics & metrics)128 void setMetrics(const InternalLineMetrics& metrics) { fMetrics = metrics; } 129 extend(Cluster * cluster)130 void extend(Cluster* cluster) { 131 if (fStart.cluster() == nullptr) { 132 fStart = ClusterPos(cluster, cluster->startPos()); 133 } 134 fEnd = ClusterPos(cluster, cluster->endPos()); 135 // TODO: Make sure all the checks are correct and there are no unnecessary checks 136 auto& r = cluster->run(); 137 if (!cluster->isHardBreak() && !r.isPlaceholder()) { 138 // We ignore metrics for \n as the Flutter does 139 fMetrics.add(&r); 140 } 141 fWidth += cluster->width(); 142 } 143 extend(Cluster * cluster,size_t pos)144 void extend(Cluster* cluster, size_t pos) { 145 fEnd = ClusterPos(cluster, pos); 146 if (auto r = cluster->runOrNull()) { 147 fMetrics.add(r); 148 } 149 } 150 startFrom(Cluster * cluster,size_t pos)151 void startFrom(Cluster* cluster, size_t pos) { 152 fStart = ClusterPos(cluster, pos); 153 fEnd = ClusterPos(cluster, pos); 154 if (auto r = cluster->runOrNull()) { 155 // In case of placeholder we should ignore the default text style - 156 // we will pick up the correct one from the placeholder 157 if (!r->isPlaceholder()) { 158 fMetrics.add(r); 159 } 160 } 161 fWidth = 0; 162 } 163 saveBreak()164 void saveBreak() { 165 fWidthWithGhostSpaces = fWidth; 166 fBreak = fEnd; 167 } 168 restoreBreak()169 void restoreBreak() { 170 fWidth = fWidthWithGhostSpaces; 171 fEnd = fBreak; 172 } 173 shiftBreak()174 void shiftBreak() { 175 fBreak.move(true); 176 } 177 trim()178 void trim() { 179 180 if (fEnd.cluster() != nullptr && 181 fEnd.cluster()->owner() != nullptr && 182 fEnd.cluster()->runOrNull() != nullptr && 183 fEnd.cluster()->run().placeholderStyle() == nullptr && 184 fWidth > 0) { 185 fWidth -= (fEnd.cluster()->width() - fEnd.cluster()->trimmedWidth(fEnd.position())); 186 } 187 } 188 trim(Cluster * cluster)189 void trim(Cluster* cluster) { 190 SkASSERT(fEnd.cluster() == cluster); 191 if (fEnd.cluster() > fStart.cluster()) { 192 fEnd.move(false); 193 fWidth -= cluster->width(); 194 } else { 195 fEnd.setPosition(fStart.position()); 196 fWidth = 0; 197 } 198 } 199 clean()200 void clean() { 201 fStart.clean(); 202 fEnd.clean(); 203 fWidth = 0; 204 fMetrics.clean(); 205 } 206 207 #ifdef ENABLE_TEXT_ENHANCE shiftWidth(SkScalar width)208 void shiftWidth(SkScalar width) { 209 fWidth += width; 210 } 211 #endif 212 private: 213 ClusterPos fStart; 214 ClusterPos fEnd; 215 ClusterPos fBreak; 216 InternalLineMetrics fMetrics; 217 SkScalar fWidth; 218 SkScalar fWidthWithGhostSpaces; 219 }; 220 221 public: TextWrapper()222 TextWrapper() { 223 fLineNumber = 1; 224 fHardLineBreak = false; 225 fExceededMaxLines = false; 226 } 227 228 using AddLineToParagraph = std::function<void(TextRange textExcludingSpaces, 229 TextRange text, 230 TextRange textIncludingNewlines, 231 ClusterRange clusters, 232 ClusterRange clustersWithGhosts, 233 SkScalar AddLineToParagraph, 234 size_t startClip, 235 size_t endClip, 236 SkVector offset, 237 SkVector advance, 238 InternalLineMetrics metrics, 239 #ifdef ENABLE_TEXT_ENHANCE 240 bool addEllipsis, 241 SkScalar lineIndent, 242 SkScalar noIndentWidth)>; 243 #else 244 bool addEllipsis)>; 245 #endif 246 void breakTextIntoLines(ParagraphImpl* parent, 247 SkScalar maxWidth, 248 const AddLineToParagraph& addLine); 249 #ifdef ENABLE_TEXT_ENHANCE 250 void updateMetricsWithPlaceholder(std::vector<Run*>& runs, bool iterateByCluster); brokeLineWithHyphen()251 bool brokeLineWithHyphen() const { return fBrokeLineWithHyphen; } 252 #endif height()253 SkScalar height() const { return fHeight; } minIntrinsicWidth()254 SkScalar minIntrinsicWidth() const { return fMinIntrinsicWidth; } maxIntrinsicWidth()255 SkScalar maxIntrinsicWidth() const { return fMaxIntrinsicWidth; } exceededMaxLines()256 bool exceededMaxLines() const { return fExceededMaxLines; } 257 258 private: 259 #ifdef ENABLE_TEXT_ENHANCE 260 struct FormattingContext { 261 bool unlimitedLines{false}; 262 bool endlessLine{false}; 263 bool hasEllipsis{false}; 264 bool disableFirstAscent{false}; 265 bool disableLastDescent{false}; 266 size_t maxLines{0}; 267 TextAlign align{TextAlign::kLeft}; 268 }; 269 270 struct LineTextRanges { 271 TextRange textExcludingSpaces; 272 TextRange text; 273 TextRange textIncludingNewlines; 274 ClusterRange clusters; 275 ClusterRange clustersWithGhosts; 276 }; 277 278 friend TextTabAlign; 279 #endif 280 TextStretch fWords; 281 TextStretch fClusters; 282 TextStretch fClip; 283 TextStretch fEndLine; 284 size_t fLineNumber; 285 bool fTooLongWord; 286 bool fTooLongCluster; 287 288 bool fHardLineBreak; 289 bool fExceededMaxLines; 290 291 #ifdef ENABLE_TEXT_ENHANCE 292 SkScalar fHeight{0}; 293 SkScalar fMinIntrinsicWidth{std::numeric_limits<SkScalar>::min()}; 294 SkScalar fMaxIntrinsicWidth{std::numeric_limits<SkScalar>::min()}; 295 bool fBrokeLineWithHyphen{false}; 296 std::vector<TextStretch> fWordStretches; 297 std::vector<TextStretch> fLineStretches; 298 std::vector<SkScalar> fWordWidthGroups; 299 std::vector<std::vector<TextStretch>> fWordStretchesBatch; 300 std::vector<std::vector<SkScalar>> fWordWidthGroupsBatch; 301 #else 302 SkScalar fHeight; 303 SkScalar fMinIntrinsicWidth; 304 SkScalar fMaxIntrinsicWidth; 305 #endif 306 reset()307 void reset() { 308 fWords.clean(); 309 fClusters.clean(); 310 fClip.clean(); 311 fTooLongCluster = false; 312 fTooLongWord = false; 313 fHardLineBreak = false; 314 #ifdef ENABLE_TEXT_ENHANCE 315 fBrokeLineWithHyphen = false; 316 fWordStretches.clear(); 317 fLineStretches.clear(); 318 fStart = nullptr; 319 fEnd = nullptr; 320 #endif 321 } 322 323 #ifdef ENABLE_TEXT_ENHANCE 324 void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, WordBreakType wordBreakType, 325 bool needEllipsis); 326 void moveForward(bool hasEllipsis, bool breakAll); // breakAll = true, break occurs after each character 327 bool lookAheadByHyphen(Cluster* endOfClusters, SkScalar widthBeforeCluster, SkScalar maxWidth); 328 uint64_t CalculateBestScore(std::vector<SkScalar>& widthOut, 329 SkScalar maxWidth, ParagraphImpl* parent, size_t maxLines); 330 static size_t tryBreakWord(Cluster* startCluster, 331 Cluster* endOfClusters, 332 SkScalar widthBeforeCluster, 333 SkScalar maxWidth); 334 335 static void matchHyphenResult(const std::vector<uint8_t>& result, ParagraphImpl* owner, size_t& pos, 336 SkScalar maxWidth, SkScalar length); 337 static std::vector<uint8_t> findBreakPositions(Cluster* startCluster, 338 Cluster* endOfClusters, 339 SkScalar widthBeforeCluster, 340 SkScalar maxWidth); initParent(ParagraphImpl * parent)341 void initParent(ParagraphImpl* parent) { fParent = parent; } 342 void pushToWordStretches(); 343 void pushToWordStretchesBatch(); 344 void layoutLinesBalanced( 345 ParagraphImpl* parent, SkScalar maxWidth, const AddLineToParagraph& addLine); 346 void layoutLinesSimple( 347 ParagraphImpl* parent, SkScalar maxWidth, const AddLineToParagraph& addLine); 348 std::vector<SkScalar> generateWordsWidthInfo(const std::vector<TextStretch>& wordStretches); 349 std::vector<std::pair<size_t, size_t>> generateLinesGroupInfo( 350 const std::vector<float>& clustersWidth, SkScalar maxWidth); 351 void generateWordStretches(const SkSpan<Cluster>& span, WordBreakType wordBreakType); 352 void generateLineStretches(const std::vector<std::pair<size_t, size_t>>& linesGroupInfo, 353 std::vector<TextStretch>& wordStretches); 354 void preProcessingForLineStretches(); 355 void extendCommonCluster(Cluster* cluster, TextTabAlign& textTabAlign, 356 SkScalar& totalFakeSpacing, WordBreakType wordBreakType); 357 SkScalar getTextStretchTrimmedEndSpaceWidth(const TextStretch& stretch); 358 void formalizedClusters(std::vector<TextStretch>& clusters, SkScalar limitWidth); 359 void generateTextLines(SkScalar maxWidth, 360 const AddLineToParagraph& addLine, 361 const SkSpan<Cluster>& span); 362 void initializeFormattingState(SkScalar maxWidth, const SkSpan<Cluster>& span); 363 void processLineStretches(SkScalar maxWidth, const AddLineToParagraph& addLine); 364 void finalizeTextLayout(const AddLineToParagraph& addLine); 365 void prepareLineForFormatting(TextStretch& line); 366 void formatCurrentLine(const AddLineToParagraph& addLine); 367 bool determineIfEllipsisNeeded(); 368 void trimLineSpaces(); 369 void handleSpecialCases(bool needEllipsis); 370 void updateLineMetrics(); 371 void updatePlaceholderMetrics(); 372 void adjustLineMetricsForFirstLastLine(); 373 void applyStrutMetrics(); 374 LineTextRanges calculateLineTextRanges(); 375 SkScalar calculateLineHeight(); 376 void addFormattedLineToParagraph(const AddLineToParagraph& addLine, bool needEllipsis); 377 void updateIntrinsicWidths(); 378 bool shouldBreakFormattingLoop(); 379 bool isLastLine() const; 380 void advanceToNextLine(); 381 void prepareForNextLine(); 382 void processRemainingClusters(); 383 void handleHardBreak(float& lastWordLength); 384 void handleWhitespaceBreak(Cluster* cluster, float& lastWordLength); 385 void handlePlaceholder(Cluster* cluster, float& lastWordLength); 386 void handleRegularCluster(Cluster* cluster, float& lastWordLength); 387 void adjustMetricsForEmptyParagraph(); 388 void addFinalLineBreakIfNeeded(const AddLineToParagraph& addLine); 389 void adjustFirstLastLineMetrics(); 390 #else 391 void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack); 392 void moveForward(bool hasEllipsis); 393 #endif 394 void trimEndSpaces(TextAlign align); 395 std::tuple<Cluster*, size_t, SkScalar> trimStartSpaces(Cluster* endOfClusters); 396 SkScalar getClustersTrimmedWidth(); 397 398 #ifdef ENABLE_TEXT_ENHANCE 399 ParagraphImpl* fParent{nullptr}; 400 FormattingContext fFormattingContext; 401 InternalLineMetrics fMaxRunMetrics; 402 SkScalar fSoftLineMaxIntrinsicWidth{0.0f}; 403 SkScalar fCurrentLineWidthWithSpaces{0.0f}; 404 SkScalar fNoIndentWidth{0.0f}; 405 bool fFirstLine{false}; 406 Cluster* fCurrentStartLine{nullptr}; 407 size_t fCurrentStartPos{0}; 408 Cluster* fStart{nullptr}; 409 Cluster* fEnd{nullptr}; 410 #endif 411 }; 412 } // namespace textlayout 413 } // namespace skia 414 415 #endif // TextWrapper_DEFINED 416