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