• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 
3 #include "include/core/SkBlurTypes.h"
4 #include "include/core/SkFont.h"
5 #include "include/core/SkFontMetrics.h"
6 #include "include/core/SkMaskFilter.h"
7 #include "include/core/SkPaint.h"
8 #include "include/core/SkSpan.h"
9 #include "include/core/SkString.h"
10 #include "include/core/SkTextBlob.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/SkTemplates.h"
13 #include "include/private/SkTo.h"
14 #include "modules/skparagraph/include/DartTypes.h"
15 #include "modules/skparagraph/include/Metrics.h"
16 #include "modules/skparagraph/include/ParagraphPainter.h"
17 #include "modules/skparagraph/include/ParagraphStyle.h"
18 #include "modules/skparagraph/include/TextShadow.h"
19 #include "modules/skparagraph/include/TextStyle.h"
20 #include "modules/skparagraph/src/Decorations.h"
21 #include "modules/skparagraph/src/ParagraphImpl.h"
22 #include "modules/skparagraph/src/ParagraphPainterImpl.h"
23 #include "modules/skparagraph/src/TextLine.h"
24 #ifdef OHOS_SUPPORT
25 #include "modules/skparagraph/src/TextLineBaseImpl.h"
26 #endif
27 #include "modules/skshaper/include/SkShaper.h"
28 
29 #include <algorithm>
30 #include <iterator>
31 #include <limits>
32 #include <map>
33 #include <memory>
34 #include <tuple>
35 #include <type_traits>
36 #include <utility>
37 
38 #ifdef OHOS_SUPPORT
39 #include <cstddef>
40 #include <numeric>
41 #include <sstream>
42 #include <vector>
43 
44 #include "include/TextGlobalConfig.h"
45 #include "log.h"
46 #include "modules/skparagraph/src/RunBaseImpl.h"
47 #include "modules/skparagraph/src/TextLineBaseImpl.h"
48 #include "src/Run.h"
49 #include "SkScalar.h"
50 #include "TextParameter.h"
51 #include "TextLineJustify.h"
52 #endif
53 
54 namespace skia {
55 namespace textlayout {
56 #define MAX_INT_VALUE 0x7FFFFFFF
57 
58 #ifdef OHOS_SUPPORT
59 #define EMOJI_UNICODE_START 0x1F300
60 #define EMOJI_UNICODE_END 0x1F9EF
61 #define EMOJI_WIDTH 4
62 #endif
63 
64 namespace {
65 
66 // TODO: deal with all the intersection functionality
intersected(const TextRange & a,const TextRange & b)67 TextRange intersected(const TextRange& a, const TextRange& b) {
68     if (a.start == b.start && a.end == b.end) return a;
69     auto begin = std::max(a.start, b.start);
70     auto end = std::min(a.end, b.end);
71     return end >= begin ? TextRange(begin, end) : EMPTY_TEXT;
72 }
73 
intervalDifference(bool ltr,const TextRange & a,const TextRange & b)74 std::pair<TextRange, TextRange> intervalDifference(bool ltr, const TextRange& a, const TextRange& b) {
75     if (a.end <= b.start || b.end <= a.start) {
76         return ltr ? std::make_pair(a, EMPTY_RANGE) : std::make_pair(EMPTY_RANGE, a);
77     }
78     TextRange start = (a.start < b.start) ? TextRange{a.start, b.start} : EMPTY_RANGE;
79     TextRange end = (a.end > b.end) ? TextRange{b.end, a.end} : EMPTY_RANGE;
80     return ltr ? std::make_pair(start, end) : std::make_pair(end, start);
81 }
82 
littleRound(SkScalar a)83 SkScalar littleRound(SkScalar a) {
84     // This rounding is done to match Flutter tests. Must be removed..
85   return SkScalarRoundToScalar(a * 100.0)/100.0;
86 }
87 
operator *(const TextRange & a,const TextRange & b)88 TextRange operator*(const TextRange& a, const TextRange& b) {
89     if (a.start == b.start && a.end == b.end) return a;
90     auto begin = std::max(a.start, b.start);
91     auto end = std::min(a.end, b.end);
92     return end > begin ? TextRange(begin, end) : EMPTY_TEXT;
93 }
94 
compareRound(SkScalar a,SkScalar b,bool applyRoundingHack)95 int compareRound(SkScalar a, SkScalar b, bool applyRoundingHack) {
96     // There is a rounding error that gets bigger when maxWidth gets bigger
97     // VERY long zalgo text (> 100000) on a VERY long line (> 10000)
98     // Canvas scaling affects it
99     // Letter spacing affects it
100     // It has to be relative to be useful
101     auto base = std::max(SkScalarAbs(a), SkScalarAbs(b));
102     auto diff = SkScalarAbs(a - b);
103     if (nearlyZero(base) || diff / base < 0.001f) {
104         return 0;
105     }
106 
107     auto ra = a;
108     auto rb = b;
109 
110     if (applyRoundingHack) {
111         ra = littleRound(a);
112         rb = littleRound(b);
113     }
114     if (ra < rb) {
115         return -1;
116     } else {
117         return 1;
118     }
119 }
120 
121 #ifdef USE_SKIA_TXT
IsRSFontEquals(const RSFont & font0,const RSFont & font1)122 bool IsRSFontEquals(const RSFont& font0, const RSFont& font1) {
123     auto f0 = const_cast<RSFont&>(font0);
124     auto f1 = const_cast<RSFont&>(font1);
125     return f0.GetTypeface().get() == f1.GetTypeface().get() &&
126         f0.GetSize() == f1.GetSize() &&
127         f0.GetScaleX() == f1.GetScaleX() &&
128         f0.GetSkewX() == f1.GetSkewX() &&
129         f0.GetEdging() == f1.GetEdging() &&
130         f0.GetHinting() == f1.GetHinting();
131 }
132 #endif
133 
134 #ifdef OHOS_SUPPORT
135 #ifdef USE_SKIA_TXT
GetTextBlobSkTightBound(std::shared_ptr<RSTextBlob> blob,float offsetX,float offsetY,const SkRect & clipRect)136 SkRect GetTextBlobSkTightBound(std::shared_ptr<RSTextBlob> blob, float offsetX, float offsetY, const SkRect& clipRect)
137 {
138     if (blob == nullptr || blob->Bounds() == nullptr) {
139         return SkRect::MakeEmpty();
140     }
141 
142     RSRect bound = *blob->Bounds();
143     bound.Offset(offsetX, offsetY);
144     if (!clipRect.isEmpty()) {
145         bound.left_ = std::max(bound.left_, clipRect.fLeft);
146         bound.right_ = std::min(bound.right_, clipRect.fRight);
147     }
148     return SkRect::MakeLTRB(bound.left_, bound.top_, bound.right_, bound.bottom_);
149 }
150 #else
GetTextBlobSkTightBound(sk_sp<SkTextBlob> blob,float offsetX,float offsetY,const SkRect & clipRect)151 SkRect GetTextBlobSkTightBound(sk_sp<SkTextBlob> blob, float offsetX, float offsetY, const SkRect& clipRect)
152 {
153     if (blob == nullptr) {
154         return SkRect::MakeEmpty();
155     }
156 
157     SkRect bound = blob->bounds();
158     if (!clipRect.isEmpty()) {
159         bound.fLeft = std::max(bound.fLeft, clipRect.fLeft);
160         bound.fRight = std::min(bound.fRight, clipRect.fRight);
161     }
162     bound.offset(offsetX, offsetY);
163     return bound;
164 }
165 #endif
166 #endif
167 }  // namespace
168 
TextLine(ParagraphImpl * owner,SkVector offset,SkVector advance,BlockRange blocks,TextRange textExcludingSpaces,TextRange text,TextRange textIncludingNewlines,ClusterRange clusters,ClusterRange clustersWithGhosts,SkScalar widthWithSpaces,InternalLineMetrics sizes)169 TextLine::TextLine(ParagraphImpl* owner,
170                    SkVector offset,
171                    SkVector advance,
172                    BlockRange blocks,
173                    TextRange textExcludingSpaces,
174                    TextRange text,
175                    TextRange textIncludingNewlines,
176                    ClusterRange clusters,
177                    ClusterRange clustersWithGhosts,
178                    SkScalar widthWithSpaces,
179                    InternalLineMetrics sizes)
180         : fOwner(owner)
181         , fBlockRange(blocks)
182         , fTextExcludingSpaces(textExcludingSpaces)
183         , fText(text)
184         , fTextIncludingNewlines(textIncludingNewlines)
185         , fClusterRange(clusters)
186         , fGhostClusterRange(clustersWithGhosts)
187         , fRunsInVisualOrder()
188         , fAdvance(advance)
189         , fOffset(offset)
190         , fShift(0.0)
191         , fWidthWithSpaces(widthWithSpaces)
192         , fEllipsis(nullptr)
193         , fSizes(sizes)
194         , fHasBackground(false)
195         , fHasShadows(false)
196         , fHasDecorations(false)
197         , fIsArcText(false)
198         , fArcTextState(false)
199         , fAscentStyle(LineMetricStyle::CSS)
200         , fDescentStyle(LineMetricStyle::CSS)
201         , fTextBlobCachePopulated(false) {
202     // Reorder visual runs
203     auto& start = owner->cluster(fGhostClusterRange.start);
204     auto& end = owner->cluster(fGhostClusterRange.end - 1);
205     size_t numRuns = end.runIndex() - start.runIndex() + 1;
206 
207     for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
208         auto b = fOwner->styles().begin() + index;
209         if (b->fStyle.hasBackground()) {
210             fHasBackground = true;
211         }
212 
213 #ifdef OHOS_SUPPORT
214         if (b->fStyle.getDecorationType() != TextDecoration::kNoDecoration &&
215             b->fStyle.getDecorationThicknessMultiplier() > 0) {
216 #else
217         if (b->fStyle.getDecorationType() != TextDecoration::kNoDecoration) {
218 #endif
219             fHasDecorations = true;
220         }
221         if (b->fStyle.getShadowNumber() > 0) {
222             fHasShadows = true;
223         }
224     }
225 
226     // Get the logical order
227 
228     // This is just chosen to catch the common/fast cases. Feel free to tweak.
229     constexpr int kPreallocCount = 4;
230     SkAutoSTArray<kPreallocCount, SkUnicode::BidiLevel> runLevels(numRuns);
231     std::vector<RunIndex> placeholdersInOriginalOrder;
232     size_t runLevelsIndex = 0;
233     // Placeholders must be laid out using the original order in which they were added
234     // in the input. The API does not provide a way to indicate that a placeholder
235     // position was moved due to bidi reordering.
236     for (auto runIndex = start.runIndex(); runIndex <= end.runIndex(); ++runIndex) {
237         auto& run = fOwner->run(runIndex);
238         runLevels[runLevelsIndex++] = run.fBidiLevel;
239         fMaxRunMetrics.add(
240             InternalLineMetrics(run.correctAscent(), run.correctDescent(), run.fFontMetrics.fLeading));
241         if (run.isPlaceholder()) {
242             placeholdersInOriginalOrder.push_back(runIndex);
243         }
244     }
245     SkASSERT(runLevelsIndex == numRuns);
246 
247     SkAutoSTArray<kPreallocCount, int32_t> logicalOrder(numRuns);
248 
249     // TODO: hide all these logic in SkUnicode?
250     fOwner->getUnicode()->reorderVisual(runLevels.data(), numRuns, logicalOrder.data());
251     auto firstRunIndex = start.runIndex();
252     auto placeholderIter = placeholdersInOriginalOrder.begin();
253     for (auto index : logicalOrder) {
254         auto runIndex = firstRunIndex + index;
255         if (fOwner->run(runIndex).isPlaceholder()) {
256             fRunsInVisualOrder.push_back(*placeholderIter++);
257         } else {
258             fRunsInVisualOrder.push_back(runIndex);
259         }
260     }
261 
262     fTextRangeReplacedByEllipsis = EMPTY_RANGE;
263     fEllipsisIndex = EMPTY_INDEX;
264 #ifdef OHOS_SUPPORT
265     fHyphenIndex = EMPTY_INDEX;
266 #endif
267     fLastClipRunLtr = false;
268 }
269 
270 void TextLine::paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset) {
271 #ifdef OHOS_SUPPORT
272     prepareRoundRect();
273 #endif
274     fIsArcText = true;
275     if (pathParameters.hOffset != hOffset || pathParameters.vOffset != vOffset) {
276         fTextBlobCachePopulated = false;
277     }
278     pathParameters.recordPath = path;
279     pathParameters.hOffset = hOffset;
280     pathParameters.vOffset = vOffset;
281     this->ensureTextBlobCachePopulated();
282     for (auto& record : fTextBlobCache) {
283         record.paint(painter);
284     }
285 }
286 
287 void TextLine::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
288 #ifdef OHOS_SUPPORT
289     prepareRoundRect();
290     paintRoundRect(painter, x, y);
291 #endif
292     fIsArcText = false;
293 
294     if (fHasBackground) {
295 #ifdef OHOS_SUPPORT
296     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
297 #else
298     this->iterateThroughVisualRuns(false,
299 #endif
300         [painter, x, y, this]
301         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
302             *runWidthInLine = this->iterateThroughSingleRunByStyles(
303             TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kBackground,
304             [painter, x, y, run, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
305                 this->paintBackground(painter, x, y, textRange, style, context);
306             });
307         return true;
308         });
309     }
310 
311     if (fHasShadows) {
312 #ifdef OHOS_SUPPORT
313         this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
314 #else
315         this->iterateThroughVisualRuns(false,
316 #endif
317             [painter, x, y, this]
318             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
319             *runWidthInLine = this->iterateThroughSingleRunByStyles(
320                 TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kShadow,
321                 [painter, x, y, this]
322                 (TextRange textRange, const TextStyle& style, const ClipContext& context) {
323                     this->paintShadow(painter, x, y, textRange, style, context);
324                 });
325             return true;
326             });
327     }
328 
329     this->ensureTextBlobCachePopulated();
330 #ifdef OHOS_SUPPORT
331     if (!(fOwner->hasSkipTextBlobDrawing())) {
332         for (auto& record : fTextBlobCache) {
333             record.paint(painter, x, y);
334         }
335     }
336 #else
337     for (auto& record : fTextBlobCache) {
338         record.paint(painter, x, y);
339     }
340 #endif
341     if (fHasDecorations) {
342 #ifdef OHOS_SUPPORT
343         this->fDecorationContext = {0.0f, 0.0f, 0.0f};
344         // 16 is default value in placeholder-only scenario, calculated by the fontsize 14.
345         SkScalar maxLineHeightWithoutPlaceholder = 16;
346         this->iterateThroughVisualRuns(EllipsisReadStrategy::DEFAULT, true,
347             [painter, x, y, this, &maxLineHeightWithoutPlaceholder]
348             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
349                 *runWidthInLine = this->iterateThroughSingleRunByStyles(
350                     TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange,
351                     StyleType::kDecorations, [painter, x, y, this, &maxLineHeightWithoutPlaceholder]
352                     (TextRange textRange, const TextStyle& style, const ClipContext& context) {
353                     if (style.getDecoration().fType == TextDecoration::kUnderline) {
354                         SkScalar tmpThick = this->calculateThickness(style, context);
355                         fDecorationContext.thickness = fDecorationContext.thickness > tmpThick ?
356                             fDecorationContext.thickness : tmpThick;
357                     }
358                     auto cur = context.run;
359                     if (cur != nullptr && !cur->isPlaceholder()) {
360                         SkScalar height = round(cur->correctDescent() - cur->correctAscent() + cur->correctLeading());
361                         maxLineHeightWithoutPlaceholder = maxLineHeightWithoutPlaceholder < height ?
362                             height : maxLineHeightWithoutPlaceholder;
363                     }
364                 });
365                 return true;
366         });
367         // 16% of row height wihtout placeholder.
368         fDecorationContext.underlinePosition = maxLineHeightWithoutPlaceholder * 0.16 + baseline();
369         fDecorationContext.textBlobTop = maxLineHeightWithoutPlaceholder * 0.16;
370         fDecorationContext.lineHeight = sizes().height();
371 #endif
372 
373 #ifdef OHOS_SUPPORT
374         this->iterateThroughVisualRuns(EllipsisReadStrategy::DEFAULT, true,
375 #else
376         this->iterateThroughVisualRuns(false,
377 #endif
378             [painter, x, y, this]
379             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
380                 *runWidthInLine = this->iterateThroughSingleRunByStyles(
381                 TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kDecorations,
382                 [painter, x, y, this]
383                 (TextRange textRange, const TextStyle& style, const ClipContext& context) {
384                     this->paintDecorations(painter, x, y, textRange, style, context);
385                 });
386                 return true;
387         });
388     }
389 }
390 
391 #ifdef OHOS_SUPPORT
392 bool TextLine::hasBackgroundRect(const RoundRectAttr& attr) {
393     return attr.roundRectStyle.color != 0 && attr.rect.width() > 0;
394 }
395 
396 void TextLine::computeRoundRect(int& index, int& preIndex, std::vector<Run*>& groupRuns, Run* run) {
397     int runCount = fRoundRectAttrs.size();
398     if (index >= runCount) {
399         return;
400     }
401 
402     bool leftRound = false;
403     bool rightRound = false;
404     if (hasBackgroundRect(fRoundRectAttrs[index])) {
405         int styleId = fRoundRectAttrs[index].styleId;
406         // index - 1 is previous index, -1 is the invalid styleId
407         int preStyleId = (index == 0 ? -1 : fRoundRectAttrs[index - 1].styleId);
408         // runCount - 1 is the last run index, index + 1 is next run index, -1 is the invalid styleId
409         int nextStyleId = (index == (runCount - 1) ? -1 : fRoundRectAttrs[index + 1].styleId);
410         // index - preIndex > 1 means the left run has no background rect
411         leftRound = (preIndex < 0 || index - preIndex > 1 || preStyleId != styleId);
412         // runCount - 1 is the last run index
413         rightRound = (index == runCount - 1 || !hasBackgroundRect(fRoundRectAttrs[index + 1]) ||
414             nextStyleId != styleId);
415         preIndex = index;
416         groupRuns.push_back(run);
417     } else if (!groupRuns.empty()) {
418         groupRuns.erase(groupRuns.begin(), groupRuns.end());
419     }
420     if (leftRound && rightRound) {
421         fRoundRectAttrs[index].fRoundRectType = RoundRectType::ALL;
422     } else if (leftRound) {
423         fRoundRectAttrs[index].fRoundRectType = RoundRectType::LEFT_ONLY;
424     } else if (rightRound) {
425         fRoundRectAttrs[index].fRoundRectType = RoundRectType::RIGHT_ONLY;
426     } else {
427         fRoundRectAttrs[index].fRoundRectType = RoundRectType::NONE;
428     }
429 
430     if (rightRound && !groupRuns.empty()) {
431         double maxRoundRectRadius = MAX_INT_VALUE;
432         double minTop = MAX_INT_VALUE;
433         double maxBottom = 0;
434         for (auto &gRun : groupRuns) {
435             RoundRectAttr& attr = fRoundRectAttrs[gRun->getIndexInLine()];
436             maxRoundRectRadius = std::fmin(std::fmin(attr.rect.width(), attr.rect.height()), maxRoundRectRadius);
437             minTop = std::fmin(minTop, attr.rect.top());
438             maxBottom = std::fmax(maxBottom, attr.rect.bottom());
439         }
440         for (auto &gRun : groupRuns) {
441             gRun->setMaxRoundRectRadius(maxRoundRectRadius);
442             gRun->setTopInGroup(minTop - gRun->offset().y());
443             gRun->setBottomInGroup(maxBottom - gRun->offset().y());
444         }
445         groupRuns.erase(groupRuns.begin(), groupRuns.end());
446     }
447     index++;
448 }
449 
450 void TextLine::prepareRoundRect() {
451     fRoundRectAttrs.clear();
452     std::vector<Run*> allRuns;
453     this->iterateThroughVisualRuns(
454         EllipsisReadStrategy::READ_REPLACED_WORD, true,
455         [this, &allRuns](const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
456             *runWidthInLine = this->iterateThroughSingleRunByStyles(
457                     TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kBackground,
458                     [run, this, &allRuns](TextRange textRange, const TextStyle& style, const ClipContext& context) {
459                         fRoundRectAttrs.push_back({style.getStyleId(), style.getBackgroundRect(), context.clip, run});
460                         allRuns.push_back(const_cast<Run*>(run));
461                     });
462             return true;
463         });
464 
465     std::vector<Run*> groupRuns;
466     int index = 0;
467     int preIndex = -1;
468     for (auto& run : allRuns) {
469         run->setIndexInLine(static_cast<size_t>(index));
470         computeRoundRect(index, preIndex, groupRuns, run);
471     }
472 }
473 #endif
474 
475 void TextLine::ensureTextBlobCachePopulated() {
476     if (fTextBlobCachePopulated && fArcTextState == fIsArcText) {
477         return;
478     }
479     fTextBlobCache.clear();
480     if (fBlockRange.width() == 1 &&
481         fRunsInVisualOrder.size() == 1 &&
482         fEllipsis == nullptr &&
483 #ifdef OHOS_SUPPORT
484         fHyphenRun == nullptr &&
485 #endif
486         fOwner->run(fRunsInVisualOrder[0]).placeholderStyle() == nullptr) {
487         if (fClusterRange.width() == 0) {
488             return;
489         }
490         // Most common and most simple case
491         const auto& style = fOwner->block(fBlockRange.start).fStyle;
492         const auto& run = fOwner->run(fRunsInVisualOrder[0]);
493         auto clip = SkRect::MakeXYWH(0.0f, this->sizes().runTop(&run, this->fAscentStyle),
494                                      fAdvance.fX,
495                                      run.calculateHeight(this->fAscentStyle, this->fDescentStyle));
496 
497         auto& start = fOwner->cluster(fClusterRange.start);
498         auto& end = fOwner->cluster(fClusterRange.end - 1);
499         SkASSERT(start.runIndex() == end.runIndex());
500         GlyphRange glyphs;
501         if (run.leftToRight()) {
502             glyphs = GlyphRange(start.startPos(),
503                                 end.isHardBreak() ? end.startPos() : end.endPos());
504         } else {
505             glyphs = GlyphRange(end.startPos(),
506                                 start.isHardBreak() ? start.startPos() : start.endPos());
507         }
508         ClipContext context = {/*run=*/&run,
509                                /*pos=*/glyphs.start,
510                                /*size=*/glyphs.width(),
511                                /*fTextShift=*/-run.positionX(glyphs.start), // starting position
512                                /*clip=*/clip,                               // entire line
513                                /*fExcludedTrailingSpaces=*/0.0f,            // no need for that
514                                /*clippingNeeded=*/false};                   // no need for that
515         this->buildTextBlob(fTextExcludingSpaces, style, context);
516     } else {
517 #ifdef OHOS_SUPPORT
518         this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_ELLIPSIS_WORD, false,
519 #else
520         this->iterateThroughVisualRuns(false,
521 #endif
522            [this](const Run* run,
523                   SkScalar runOffsetInLine,
524                   TextRange textRange,
525                   SkScalar* runWidthInLine) {
526                if (run->placeholderStyle() != nullptr) {
527                    *runWidthInLine = run->advance().fX;
528                    return true;
529                }
530                *runWidthInLine = this->iterateThroughSingleRunByStyles(
531                    TextAdjustment::GlyphCluster,
532                    run,
533                    runOffsetInLine,
534                    textRange,
535                    StyleType::kForeground,
536                    [this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
537                        this->buildTextBlob(textRange, style, context);
538                    });
539                return true;
540            });
541     }
542     fTextBlobCachePopulated = true;
543     fArcTextState = fIsArcText;
544     pathParameters.recordPath = nullptr;
545 }
546 
547 void TextLine::format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal) {
548     SkScalar delta = maxWidth - this->widthWithEllipsisSpaces();
549 #ifdef OHOS_SUPPORT
550     if (fOwner->paragraphStyle().getTrailingSpaceOptimized()) {
551         delta = maxWidth - this->width();
552     }
553     delta = (delta < 0) ? 0 : delta;
554 #else
555     if (delta <= 0) {
556         return;
557     }
558 #endif
559     // We do nothing for left align
560     if (align == TextAlign::kJustify) {
561         if (!this->endsWithHardLineBreak()) {
562             this->justify(maxWidth);
563         } else if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
564             // Justify -> Right align
565             fShift = delta;
566         }
567     } else if (align == TextAlign::kRight) {
568 #ifdef OHOS_SUPPORT
569         auto lastCluster = fOwner->clusters()[fGhostClusterRange.end - 1];
570         bool isRTLWhiteSpace = lastCluster.isWhitespaceBreak() && !lastCluster.run().leftToRight();
571         // Only be entered when the text alignment direction is RTL and the last character is an RTL whitespace
572         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl && isRTLWhiteSpace) {
573             fShift = maxWidth - this->width();
574         } else {
575             fShift = delta;
576         }
577 #else
578         fShift = delta;
579 #endif
580     } else if (align == TextAlign::kCenter) {
581         fShift = delta / 2;
582     }
583 }
584 
585 #ifdef OHOS_SUPPORT
586 SkScalar TextLine::autoSpacing() {
587     if (!fOwner->isAutoSpaceEnabled()) {
588         return 0;
589     }
590     SkScalar spacing = 0.0;
591     auto prevCluster = fOwner->cluster(fGhostClusterRange.start);
592     for (auto clusterIndex = fGhostClusterRange.start + 1; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
593         auto prevSpacing = spacing;
594         auto& cluster = fOwner->cluster(clusterIndex);
595         spacing += cluster.needAutoSpacing() ? prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO : 0;
596         spacingCluster(&cluster, spacing, prevSpacing);
597         prevCluster = cluster;
598     }
599     this->fWidthWithSpaces += spacing;
600     this->fAdvance.fX += spacing;
601     return spacing;
602 }
603 #endif
604 
605 void TextLine::scanStyles(StyleType styleType, const RunStyleVisitor& visitor) {
606     if (this->empty()) {
607         return;
608     }
609 
610 #ifdef OHOS_SUPPORT
611     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
612 #else
613     this->iterateThroughVisualRuns(false,
614 #endif
615             [this, visitor, styleType](
616                     const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width) {
617                 *width = this->iterateThroughSingleRunByStyles(
618                         TextAdjustment::GlyphCluster,
619                         run,
620                         runOffset,
621                         textRange,
622                         styleType,
623                         [visitor](TextRange textRange,
624                                   const TextStyle& style,
625                                   const ClipContext& context) {
626                             visitor(textRange, style, context);
627                         });
628                 return true;
629             });
630 }
631 
632 SkRect TextLine::extendHeight(const ClipContext& context) const {
633     SkRect result = context.clip;
634     result.fBottom += std::max(this->fMaxRunMetrics.height() - this->height(), 0.0f);
635     return result;
636 }
637 
638 void TextLine::buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context) {
639     if (context.run->placeholderStyle() != nullptr) {
640         return;
641     }
642 
643     fTextBlobCache.emplace_back();
644     TextBlobRecord& record = fTextBlobCache.back();
645 
646     if (style.hasForeground()) {
647         record.fPaint = style.getForegroundPaintOrID();
648     } else {
649         std::get<SkPaint>(record.fPaint).setColor(style.getColor());
650     }
651     record.fVisitor_Run = context.run;
652     record.fVisitor_Pos = context.pos;
653     record.fVisitor_Size = context.size;
654 
655     // TODO: This is the change for flutter, must be removed later
656 #ifndef USE_SKIA_TXT
657     SkTextBlobBuilder builder;
658 #else
659     RSTextBlobBuilder builder;
660 #endif
661     if (pathParameters.recordPath) {
662         context.run->copyTo(builder,
663                             pathParameters.recordPath,
664                             pathParameters.hOffset,
665                             pathParameters.vOffset,
666                             context.fTextShift,
667                             SkToU32(context.pos),
668                             context.size);
669     } else {
670         context.run->copyTo(builder, SkToU32(context.pos), context.size);
671     }
672 #ifdef OHOS_SUPPORT
673     // when letterspacing < 0, it causes the font is cliped. so the record fClippingNeeded is set false
674 #else
675     record.fClippingNeeded = context.clippingNeeded;
676 #endif
677     if (context.clippingNeeded) {
678         record.fClipRect = extendHeight(context).makeOffset(this->offset());
679     } else {
680         record.fClipRect = context.clip.makeOffset(this->offset());
681     }
682 
683     SkASSERT(nearlyEqual(context.run->baselineShift(), style.getBaselineShift()));
684 #ifdef OHOS_SUPPORT
685     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getTotalVerticalShift() + 0.5);
686     if (fOwner->getParagraphStyle().getVerticalAlignment() != TextVerticalAlign::BASELINE) {
687         correctedBaseline = SkScalarFloorToScalar(this->baseline() + context.run->getRunTotalShift() + 0.5);
688     }
689 #else
690     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5);
691 #endif
692 #ifndef USE_SKIA_TXT
693     record.fBlob = builder.make();
694     if (record.fBlob != nullptr) {
695         record.fBounds.joinPossiblyEmptyRect(record.fBlob->bounds());
696     }
697 #else
698     record.fBlob = builder.Make();
699     if (record.fBlob != nullptr) {
700         auto bounds = record.fBlob->Bounds();
701         if (bounds) {
702             record.fBounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(
703                 bounds->left_, bounds->top_, bounds->right_, bounds->bottom_
704             ));
705         }
706     }
707 #endif
708 
709     record.fOffset = SkPoint::Make(this->offset().fX + context.fTextShift,
710 #ifdef OHOS_SUPPORT
711         this->offset().fY + correctedBaseline - (context.run ? context.run->fCompressionBaselineShift : 0));
712 #else
713                                    this->offset().fY + correctedBaseline);
714 #endif
715 #ifdef OHOS_SUPPORT
716 #ifndef USE_SKIA_TXT
717     SkFont font;
718 #else
719     RSFont font;
720 #endif
721     if (record.fBlob != nullptr && record.fVisitor_Run != nullptr) {
722         font = record.fVisitor_Run->font();
723         if (font.GetTypeface() != nullptr &&
724             (font.GetTypeface()->GetFamilyName().find("Emoji") != std::string::npos ||
725             font.GetTypeface()->GetFamilyName().find("emoji") != std::string::npos)) {
726                 record.fBlob->SetEmoji(true);
727         }
728     }
729 #endif
730 }
731 
732 void TextLine::TextBlobRecord::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
733     if (fClippingNeeded) {
734         painter->save();
735         painter->clipRect(fClipRect.makeOffset(x, y));
736     }
737     painter->drawTextBlob(fBlob, x + fOffset.x(), y + fOffset.y(), fPaint);
738     if (fClippingNeeded) {
739         painter->restore();
740     }
741 }
742 
743 void TextLine::TextBlobRecord::paint(ParagraphPainter* painter) {
744     if (fClippingNeeded) {
745         painter->save();
746     }
747     painter->drawTextBlob(fBlob, 0, 0, fPaint);
748     if (fClippingNeeded) {
749         painter->restore();
750     }
751 }
752 
753 void TextLine::paintBackground(ParagraphPainter* painter,
754                                SkScalar x,
755                                SkScalar y,
756                                TextRange textRange,
757                                const TextStyle& style,
758                                const ClipContext& context) const {
759     if (style.hasBackground()) {
760         painter->drawRect(context.clip.makeOffset(this->offset() + SkPoint::Make(x, y)),
761                           style.getBackgroundPaintOrID());
762     }
763 }
764 
765 #ifdef OHOS_SUPPORT
766 void TextLine::paintRoundRect(ParagraphPainter* painter, SkScalar x, SkScalar y) const {
767     for (const RoundRectAttr& attr : fRoundRectAttrs) {
768         if (attr.roundRectStyle.color == 0) {
769             continue;
770         }
771         const Run* run = attr.run;
772 
773         SkScalar ltRadius = 0.0f;
774         SkScalar rtRadius = 0.0f;
775         SkScalar rbRadius = 0.0f;
776         SkScalar lbRadius = 0.0f;
777         RoundRectType rType = attr.fRoundRectType;
778         if (rType == RoundRectType::ALL || rType == RoundRectType::LEFT_ONLY) {
779             ltRadius = std::fmin(attr.roundRectStyle.leftTopRadius, run->getMaxRoundRectRadius());
780             lbRadius = std::fmin(attr.roundRectStyle.leftBottomRadius, run->getMaxRoundRectRadius());
781         }
782         if (rType == RoundRectType::ALL || rType == RoundRectType::RIGHT_ONLY) {
783             rtRadius = std::fmin(attr.roundRectStyle.rightTopRadius, run->getMaxRoundRectRadius());
784             rbRadius = std::fmin(attr.roundRectStyle.rightBottomRadius, run->getMaxRoundRectRadius());
785         }
786         const SkVector radii[4] = {
787             {ltRadius, ltRadius}, {rtRadius, rtRadius}, {rbRadius, rbRadius}, {lbRadius, lbRadius}};
788         SkRect skRect(
789             SkRect::MakeLTRB(attr.rect.left(), run->getTopInGroup(), attr.rect.right(), run->getBottomInGroup()));
790         SkRRect skRRect;
791         skRRect.setRectRadii(skRect, radii);
792         skRRect.offset(x + this->offset().x(), y + this->offset().y());
793         painter->drawRRect(skRRect, attr.roundRectStyle.color);
794     }
795 }
796 #endif
797 
798 void TextLine::paintShadow(ParagraphPainter* painter,
799                            SkScalar x,
800                            SkScalar y,
801                            TextRange textRange,
802                            const TextStyle& style,
803                            const ClipContext& context) const {
804 #ifdef OHOS_SUPPORT
805     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + context.run->getRunTotalShift() + 0.5);
806 #else
807     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5);
808 #endif
809 
810     for (TextShadow shadow : style.getShadows()) {
811         if (!shadow.hasShadow()) continue;
812 
813 #ifndef USE_SKIA_TXT
814         SkTextBlobBuilder builder;
815 #else
816         RSTextBlobBuilder builder;
817 #endif
818         context.run->copyTo(builder, context.pos, context.size);
819 
820         if (context.clippingNeeded) {
821             painter->save();
822             SkRect clip = extendHeight(context);
823             clip.offset(x, y);
824             clip.offset(this->offset());
825             painter->clipRect(clip);
826         }
827 #ifndef USE_SKIA_TXT
828         auto blob = builder.make();
829 #else
830         auto blob = builder.Make();
831 #endif
832         painter->drawTextShadow(blob,
833             x + this->offset().fX + shadow.fOffset.x() + context.fTextShift,
834 #ifdef OHOS_SUPPORT
835             y + this->offset().fY + shadow.fOffset.y() + correctedBaseline -
836                 (context.run ? context.run->fCompressionBaselineShift : 0),
837 #else
838             y + this->offset().fY + shadow.fOffset.y() + correctedBaseline,
839 #endif
840             shadow.fColor,
841             SkDoubleToScalar(shadow.fBlurSigma));
842         if (context.clippingNeeded) {
843             painter->restore();
844         }
845     }
846 }
847 
848 SkScalar TextLine::calculateThickness(const TextStyle& style, const ClipContext& content)
849 {
850     Decorations decoration;
851     return decoration.calculateThickness(style, content);
852 }
853 
854 void TextLine::paintDecorations(ParagraphPainter* painter, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
855     ParagraphPainterAutoRestore ppar(painter);
856 #ifdef OHOS_SUPPORT
857     if (fOwner->getParagraphStyle().getVerticalAlignment() == TextVerticalAlign::BASELINE) {
858         painter->translate(x + this->offset().fX, y + this->offset().fY + style.getTotalVerticalShift());
859     } else {
860         painter->translate(x + this->offset().fX, y + this->offset().fY + context.run->baselineShift());
861     }
862 #else
863     painter->translate(x + this->offset().fX, y + this->offset().fY + style.getBaselineShift());
864 #endif
865     Decorations decorations;
866 #ifdef OHOS_SUPPORT
867     decorations.setVerticalAlignment(fOwner->getParagraphStyle().getVerticalAlignment());
868     decorations.setDecorationContext(fDecorationContext);
869     SkScalar correctedBaseline = 0.0f;
870     if (fOwner->getParagraphStyle().getVerticalAlignment() == TextVerticalAlign::BASELINE) {
871         correctedBaseline = SkScalarFloorToScalar(-this->sizes().rawAscent() + style.getTotalVerticalShift() + 0.5);
872     } else {
873         correctedBaseline = SkScalarFloorToScalar(-this->sizes().rawAscent() + context.run->baselineShift() + 0.5);
874     }
875 #else
876     decorations.setDecorationContext(fDecorationContext);
877     SkScalar correctedBaseline = SkScalarFloorToScalar(-this->sizes().rawAscent() + style.getBaselineShift() + 0.5);
878 #endif
879     decorations.paint(painter, style, context, correctedBaseline);
880 }
881 
882 #ifdef OHOS_SUPPORT
883 void TextLine::justify(SkScalar maxWidth)
884 {
885     TextLineJustify textLineJustify(*this);
886     if (textLineJustify.justify(maxWidth)) {
887         this->fWidthWithSpaces += (maxWidth - this->widthWithoutEllipsis());
888         this->fAdvance.fX = maxWidth;
889     }
890 }
891 
892 void TextLine::updateClusterOffsets(const Cluster* cluster, SkScalar shift, SkScalar prevShift)
893 {
894     this->shiftCluster(cluster, shift, prevShift);
895 }
896 
897 void TextLine::justifyUpdateRtlWidth(const SkScalar maxWidth, const SkScalar textLen)
898 {
899     if (this->fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
900         // Justify -> Right align
901         this->fShift = maxWidth - textLen;
902     }
903 }
904 #else
905 void TextLine::justify(SkScalar maxWidth) {
906     int whitespacePatches = 0;
907     SkScalar textLen = 0;
908     bool whitespacePatch = false;
909     // Take leading whitespaces width but do not increment a whitespace patch number
910     bool leadingWhitespaces = false;
911     this->iterateThroughClustersInGlyphsOrder(false, false,
912         [&](const Cluster* cluster, ClusterIndex index, bool ghost) {
913             if (cluster->isWhitespaceBreak()) {
914                 if (index == 0) {
915                     leadingWhitespaces = true;
916                 } else if (!whitespacePatch && !leadingWhitespaces) {
917                     // We only count patches BETWEEN words, not before
918                     ++whitespacePatches;
919                 }
920                 whitespacePatch = !leadingWhitespaces;
921             } else if (cluster->isIdeographic()) {
922                 // Whitespace break before and after
923                 if (!whitespacePatch && index != 0) {
924                     // We only count patches BETWEEN words, not before
925                     ++whitespacePatches; // before
926                 }
927                 whitespacePatch = true;
928                 leadingWhitespaces = false;
929                 ++whitespacePatches;    // after
930             } else {
931                 whitespacePatch = false;
932                 leadingWhitespaces = false;
933             }
934             textLen += cluster->width();
935             return true;
936         });
937 
938     if (whitespacePatch) {
939         // We only count patches BETWEEN words, not after
940         --whitespacePatches;
941     }
942     if (whitespacePatches == 0) {
943         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
944             // Justify -> Right align
945             fShift = maxWidth - textLen;
946         }
947         return;
948     }
949     SkScalar step = (maxWidth - textLen) / whitespacePatches;
950     SkScalar shift = 0.0f;
951     SkScalar prevShift = 0.0f;
952 
953     // Deal with the ghost spaces
954     auto ghostShift = maxWidth - this->fAdvance.fX;
955     // Spread the extra whitespaces
956     whitespacePatch = false;
957     // Do not break on leading whitespaces
958     leadingWhitespaces = false;
959     this->iterateThroughClustersInGlyphsOrder(false, true, [&](const Cluster* cluster, ClusterIndex index, bool ghost) {
960 
961         if (ghost) {
962             if (cluster->run().leftToRight()) {
963                 this->shiftCluster(cluster, ghostShift, ghostShift);
964             }
965             return true;
966         }
967 
968         if (cluster->isWhitespaceBreak()) {
969             if (index == 0) {
970                 leadingWhitespaces = true;
971             } else if (!whitespacePatch && !leadingWhitespaces) {
972                 shift += step;
973                 whitespacePatch = true;
974                 --whitespacePatches;
975             }
976         } else if (cluster->isIdeographic()) {
977             if (!whitespacePatch && index != 0) {
978                 shift += step;
979                --whitespacePatches;
980             }
981             whitespacePatch = false;
982             leadingWhitespaces = false;
983         } else {
984             whitespacePatch = false;
985             leadingWhitespaces = false;
986         }
987         this->shiftCluster(cluster, shift, prevShift);
988         prevShift = shift;
989         // We skip ideographic whitespaces
990         if (!cluster->isWhitespaceBreak() && cluster->isIdeographic()) {
991             shift += step;
992             whitespacePatch = true;
993             --whitespacePatches;
994         }
995         return true;
996     });
997 
998     if (whitespacePatch && whitespacePatches < 0) {
999         whitespacePatches++;
1000         shift -= step;
1001     }
1002 
1003     SkAssertResult(nearlyEqual(shift, maxWidth - textLen));
1004     SkASSERT(whitespacePatches == 0);
1005 
1006     this->fWidthWithSpaces += ghostShift;
1007     this->fAdvance.fX = maxWidth;
1008 }
1009 #endif
1010 
1011 void TextLine::shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift) {
1012 
1013     auto& run = cluster->run();
1014     auto start = cluster->startPos();
1015     auto end = cluster->endPos();
1016 
1017     if (end == run.size()) {
1018         // Set the same shift for the fake last glyph (to avoid all extra checks)
1019         ++end;
1020     }
1021 
1022     if (run.fJustificationShifts.empty()) {
1023         // Do not fill this array until needed
1024         run.fJustificationShifts.push_back_n(run.size() + 1, { 0, 0 });
1025     }
1026 
1027     for (size_t pos = start; pos < end; ++pos) {
1028         run.fJustificationShifts[pos] = { shift, prevShift };
1029     }
1030 }
1031 
1032 void TextLine::spacingCluster(const Cluster* cluster, SkScalar spacing, SkScalar prevSpacing) {
1033     auto& run = cluster->run();
1034     auto start = cluster->startPos();
1035     auto end = cluster->endPos();
1036     if (end == run.size()) {
1037         // Set the same shift for the fake last glyph (to avoid all extra checks)
1038         ++end;
1039     }
1040 
1041     if (run.fAutoSpacings.empty()) {
1042         // Do not fill this array until needed
1043         run.fAutoSpacings.push_back_n(run.size() + 1, { 0, 0 });
1044     }
1045 
1046     for (size_t pos = start; pos < end; ++pos) {
1047         run.fAutoSpacings[pos] = { spacing, prevSpacing};
1048     }
1049 }
1050 
1051 void TextLine::countWord(int& wordCount, bool& inWord) {
1052     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
1053         auto& cluster = fOwner->cluster(clusterIndex);
1054         if (cluster.isWordBreak()) {
1055             inWord = false;
1056         } else if (!inWord) {
1057             ++wordCount;
1058             inWord = true;
1059         }
1060     }
1061 }
1062 
1063 SkScalar TextLine::usingAutoSpaceWidth(const Cluster* cluster) const
1064 {
1065     if (cluster == nullptr) {
1066         return 0.0f;
1067     }
1068     return fOwner->clusterUsingAutoSpaceWidth(*cluster);
1069 }
1070 
1071 void TextLine::ellipsisNotFitProcess(EllipsisModal ellipsisModal) {
1072     if (fEllipsis) {
1073         return;
1074     }
1075 
1076     // Weird situation: ellipsis does not fit; no ellipsis then
1077     switch (ellipsisModal) {
1078         case EllipsisModal::TAIL:
1079             fClusterRange.end = fClusterRange.start;
1080             fGhostClusterRange.end = fClusterRange.start;
1081             fText.end = fText.start;
1082             fTextIncludingNewlines.end = fTextIncludingNewlines.start;
1083             fTextExcludingSpaces.end = fTextExcludingSpaces.start;
1084             fAdvance.fX = 0;
1085             break;
1086         case EllipsisModal::HEAD:
1087             fClusterRange.start = fClusterRange.end;
1088             fGhostClusterRange.start = fClusterRange.end;
1089             fText.start = fText.end;
1090             fTextIncludingNewlines.start = fTextIncludingNewlines.end;
1091             fTextExcludingSpaces.start = fTextExcludingSpaces.end;
1092             fAdvance.fX = 0;
1093             break;
1094         default:
1095             return;
1096     }
1097 }
1098 
1099 #ifdef OHOS_SUPPORT
1100 void TextLine::createTailEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr, WordBreakType wordBreakType) {
1101     // Replace some clusters with the ellipsis
1102     // Go through the clusters in the reverse logical order
1103     // taking off cluster by cluster until the ellipsis fits
1104     SkScalar width = fAdvance.fX;
1105     RunIndex lastRun = EMPTY_RUN;
1106     std::unique_ptr<Run> ellipsisRun;
1107     int wordCount = 0;
1108     bool inWord = false;
1109 
1110     countWord(wordCount, inWord);
1111 
1112     if (fClusterRange.width() == 0 && fGhostClusterRange.width() > 0) {
1113         // Only be entered when line is empty.
1114         handleTailEllipsisInEmptyLine(ellipsisRun, ellipsis, width, wordBreakType);
1115         return;
1116     }
1117 
1118     bool iterForWord = false;
1119     for (auto clusterIndex = fClusterRange.end; clusterIndex > fClusterRange.start; --clusterIndex) {
1120         auto& cluster = fOwner->cluster(clusterIndex - 1);
1121         // Shape the ellipsis if the run has changed
1122         if (lastRun != cluster.runIndex()) {
1123             ellipsisRun = this->shapeEllipsis(ellipsis, &cluster);
1124             // We may need to continue
1125             lastRun = cluster.runIndex();
1126         }
1127 
1128         if (!cluster.isWordBreak()) {
1129             inWord = true;
1130         } else if (inWord) {
1131             --wordCount;
1132             inWord = false;
1133         }
1134         // See if it fits
1135         if (ellipsisRun != nullptr && width + ellipsisRun->advance().fX > maxWidth) {
1136             if (!cluster.isHardBreak()) {
1137                 width -= usingAutoSpaceWidth(&cluster);
1138             }
1139             // Continue if the ellipsis does not fit
1140             iterForWord = (wordCount != 1 && wordBreakType != WordBreakType::BREAK_ALL && !cluster.isWordBreak());
1141             if (std::floor(width) > 0) {
1142                 continue;
1143             }
1144         }
1145 
1146         if (iterForWord && !cluster.isWordBreak()) {
1147             width -= usingAutoSpaceWidth(&cluster);
1148             if (std::floor(width) > 0) {
1149                 continue;
1150             }
1151         }
1152 
1153         fEllipsis = std::move(ellipsisRun);
1154         fEllipsis->fTextRange = TextRange(cluster.textRange().end, cluster.textRange().end + ellipsis.size());
1155         TailEllipsisUpdateLine(cluster, width, clusterIndex, wordBreakType);
1156 
1157         break;
1158     }
1159 
1160     fWidthWithSpaces = width;
1161 
1162     ellipsisNotFitProcess(EllipsisModal::TAIL);
1163 }
1164 
1165 void TextLine::handleTailEllipsisInEmptyLine(std::unique_ptr<Run>& ellipsisRun, const SkString& ellipsis,
1166     SkScalar width, WordBreakType wordBreakType)
1167 {
1168     auto& cluster = fOwner->cluster(fClusterRange.start);
1169     ellipsisRun = this->shapeEllipsis(ellipsis, &cluster);
1170     fEllipsis = std::move(ellipsisRun);
1171     fEllipsis->fTextRange = TextRange(cluster.textRange().end, cluster.textRange().end + ellipsis.size());
1172     TailEllipsisUpdateLine(cluster, width, fGhostClusterRange.end, wordBreakType);
1173     fWidthWithSpaces = width;
1174     ellipsisNotFitProcess(EllipsisModal::TAIL);
1175 }
1176 
1177 void TextLine::TailEllipsisUpdateLine(Cluster& cluster, float width, size_t clusterIndex, WordBreakType wordBreakType)
1178 {
1179     // We found enough room for the ellipsis
1180     fAdvance.fX = width;
1181     fEllipsis->setOwner(fOwner);
1182     fEllipsis->fClusterStart = cluster.textRange().end;
1183 
1184     // Let's update the line
1185     fTextRangeReplacedByEllipsis = TextRange(cluster.textRange().end, fOwner->text().size());
1186     fClusterRange.end = clusterIndex;
1187     fGhostClusterRange.end = fClusterRange.end;
1188     // Get the last run directions after clipping
1189     fEllipsisIndex = cluster.runIndex();
1190     fLastClipRunLtr = fOwner->run(fEllipsisIndex).leftToRight();
1191     fText.end = cluster.textRange().end;
1192     fTextIncludingNewlines.end = cluster.textRange().end;
1193     fTextExcludingSpaces.end = cluster.textRange().end;
1194 
1195     if (SkScalarNearlyZero(width)) {
1196         fRunsInVisualOrder.reset();
1197     }
1198 }
1199 #endif
1200 
1201 #ifdef OHOS_SUPPORT
1202 void TextLine::createHeadEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool) {
1203     if (fAdvance.fX <= maxWidth) {
1204         return;
1205     }
1206     SkScalar width = fAdvance.fX;
1207     std::unique_ptr<Run> ellipsisRun;
1208     RunIndex lastRun = EMPTY_RUN;
1209     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
1210         auto& cluster = fOwner->cluster(clusterIndex);
1211         // Shape the ellipsis if the run has changed
1212         if (lastRun != cluster.runIndex()) {
1213             ellipsisRun = this->shapeEllipsis(ellipsis, &cluster);
1214             // We may need to continue
1215             lastRun = cluster.runIndex();
1216         }
1217         // See if it fits
1218         if (ellipsisRun && width + ellipsisRun->advance().fX > maxWidth) {
1219             width -= usingAutoSpaceWidth(&cluster);
1220             // Continue if the ellipsis does not fit
1221             if (std::floor(width) > 0) {
1222                 continue;
1223             }
1224         }
1225 
1226         // Get the last run directions after clipping
1227         fEllipsisIndex = cluster.runIndex();
1228         fLastClipRunLtr = fOwner->run(fEllipsisIndex).leftToRight();
1229 
1230         // We found enough room for the ellipsis
1231         fAdvance.fX = width + ellipsisRun->advance().fX;
1232         fEllipsis = std::move(ellipsisRun);
1233         fEllipsis->setOwner(fOwner);
1234         fTextRangeReplacedByEllipsis = TextRange(0, cluster.textRange().start);
1235         fClusterRange.start = clusterIndex;
1236         fGhostClusterRange.start = fClusterRange.start;
1237         fEllipsis->fClusterStart = 0;
1238         fText.start = cluster.textRange().start;
1239         fTextIncludingNewlines.start = cluster.textRange().start;
1240         fTextExcludingSpaces.start = cluster.textRange().start;
1241         break;
1242     }
1243 
1244     fWidthWithSpaces = width;
1245 
1246     ellipsisNotFitProcess(EllipsisModal::HEAD);
1247 }
1248 
1249 void TextLine::createMiddleEllipsis(SkScalar maxWidth, const SkString& ellipsis) {
1250     if (fAdvance.fX <= maxWidth) {
1251         return;
1252     }
1253 
1254     //initial params
1255     SkScalar startWidth = 0.0f;
1256     SkScalar endWidth = 0.0f;
1257     ClusterIndex startIndex = fGhostClusterRange.start;
1258     ClusterIndex endIndex = fGhostClusterRange.end - 1;
1259     std::unique_ptr<Run> ellipsisRun;
1260     RunIndex lastRun = EMPTY_RUN;
1261     bool addStart = false;
1262     // Fill in content at both side of the ellipsis
1263     while (startIndex < endIndex) {
1264         addStart = (startWidth <= endWidth);
1265         if (addStart) {
1266             Cluster& startCluster = fOwner->cluster(startIndex);
1267             if (lastRun != startCluster.runIndex()) {
1268                 ellipsisRun = this->shapeEllipsis(ellipsis, &startCluster);
1269                 lastRun = startCluster.runIndex();
1270             }
1271             startWidth += usingAutoSpaceWidth(&fOwner->cluster(startIndex++));
1272         } else {
1273             endWidth += usingAutoSpaceWidth(&fOwner->cluster(endIndex--));
1274             if (fOwner->cluster(endIndex).isStartCombineBreak()) {
1275                 continue;
1276             }
1277         }
1278         if (ellipsisRun != nullptr && (startWidth + endWidth + ellipsisRun->advance().fX) >= maxWidth) {
1279             break;
1280         }
1281     }
1282     // fallback one unit
1283     if (addStart) {
1284         startWidth -= usingAutoSpaceWidth(&fOwner->cluster(--startIndex));
1285         if (lastRun != fOwner->cluster(startIndex).runIndex()) {
1286             ellipsisRun = this->shapeEllipsis(ellipsis, &fOwner->cluster(startIndex));
1287         }
1288     } else {
1289         Cluster endCluster;
1290         do {
1291             endWidth -= usingAutoSpaceWidth(&fOwner->cluster(++endIndex));
1292             endCluster = fOwner->cluster(endIndex);
1293         } while (endCluster.isEndCombineBreak());
1294     }
1295 
1296     // update line params
1297     if (ellipsisRun != nullptr) {
1298         fEllipsis = std::move(ellipsisRun);
1299         middleEllipsisUpdateLine(startIndex, endIndex, (startWidth + endWidth));
1300     }
1301 }
1302 
1303 void TextLine::middleEllipsisUpdateLine(ClusterIndex& startIndex, ClusterIndex& endIndex, SkScalar width)
1304 {
1305     const Cluster& startCluster = fOwner->cluster(startIndex);
1306     const Cluster& endCluster = fOwner->cluster(endIndex);
1307     fEllipsis->fTextRange = TextRange(startCluster.textRange().start, startCluster.textRange().start + fEllipsis->size());
1308     fEllipsis->setOwner(fOwner);
1309     fEllipsis->fClusterStart = startCluster.textRange().start;
1310     fEllipsisIndex = startCluster.runIndex();
1311 
1312     fTextRangeReplacedByEllipsis = TextRange(startCluster.textRange().start, endCluster.textRange().end);
1313     fAdvance.fX = width;
1314     fWidthWithSpaces = fAdvance.fX;
1315 
1316     if (SkScalarNearlyZero(fAdvance.fX)) {
1317         fRunsInVisualOrder.reset();
1318     }
1319 }
1320 #endif
1321 
1322 static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
1323     SkUnichar val = SkUTF::NextUTF8(ptr, end);
1324     return val < 0 ? 0xFFFD : val;
1325 }
1326 
1327 std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Cluster* cluster) {
1328 #ifdef OHOS_SUPPORT
1329     fEllipsisString = ellipsis;
1330 #endif
1331     return shapeString(ellipsis, cluster);
1332 }
1333 
1334 std::unique_ptr<Run> TextLine::shapeString(const SkString& str, const Cluster* cluster) {
1335     class ShapeHandler final : public SkShaper::RunHandler {
1336     public:
1337         ShapeHandler(SkScalar lineHeight, bool useHalfLeading, SkScalar baselineShift, const SkString& str)
1338             : fRun(nullptr), fLineHeight(lineHeight), fUseHalfLeading(useHalfLeading), fBaselineShift(baselineShift), fStr(str) {}
1339         std::unique_ptr<Run> run() & { return std::move(fRun); }
1340 
1341     private:
1342         void beginLine() override {}
1343 
1344         void runInfo(const RunInfo&) override {}
1345 
1346         void commitRunInfo() override {}
1347 
1348         Buffer runBuffer(const RunInfo& info) override {
1349             SkASSERT(!fRun);
1350             fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fUseHalfLeading, fBaselineShift, 0, 0);
1351             return fRun->newRunBuffer();
1352         }
1353 
1354         void commitRunBuffer(const RunInfo& info) override {
1355             fRun->fAdvance.fX = info.fAdvance.fX;
1356             fRun->fAdvance.fY = fRun->advance().fY;
1357             fRun->fPlaceholderIndex = std::numeric_limits<size_t>::max();
1358             // this may not be fully accurate, but limiting the changes to the text line
1359             fRun->fEllipsis = true;
1360         }
1361 
1362         void commitLine() override {}
1363 
1364         std::unique_ptr<Run> fRun;
1365         SkScalar fLineHeight;
1366         bool fUseHalfLeading;
1367         SkScalar fBaselineShift;
1368         SkString fStr;
1369     };
1370 
1371     const Run& run = cluster->run();
1372     TextStyle textStyle = fOwner->paragraphStyle().getTextStyle();
1373     for (auto i = fBlockRange.start; i < fBlockRange.end; ++i) {
1374         auto& block = fOwner->block(i);
1375         if (run.leftToRight() && cluster->textRange().end <= block.fRange.end) {
1376             textStyle = block.fStyle;
1377             break;
1378         } else if (!run.leftToRight() && cluster->textRange().start <= block.fRange.end) {
1379             textStyle = block.fStyle;
1380             break;
1381         }
1382     }
1383 
1384 #ifndef USE_SKIA_TXT
1385     auto shaped = [&](sk_sp<SkTypeface> typeface, bool fallback) -> std::unique_ptr<Run> {
1386 #else
1387     auto shaped = [&](std::shared_ptr<RSTypeface> typeface, bool fallback) -> std::unique_ptr<Run> {
1388 #endif
1389         ShapeHandler handler(run.heightMultiplier(), run.useHalfLeading(), run.baselineShift(), str);
1390 #ifndef USE_SKIA_TXT
1391         SkFont font(typeface, textStyle.getFontSize());
1392         font.setEdging(SkFont::Edging::kAntiAlias);
1393         font.setHinting(SkFontHinting::kSlight);
1394         font.setSubpixel(true);
1395 #else
1396         RSFont font(typeface, textStyle.getCorrectFontSize(), 1, 0);
1397         font.SetEdging(RSDrawing::FontEdging::ANTI_ALIAS);
1398         font.SetHinting(RSDrawing::FontHinting::SLIGHT);
1399         font.SetSubpixel(true);
1400 #endif
1401 
1402 #ifndef USE_SKIA_TXT
1403         std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder(
1404                             fOwner->getUnicode()->copy(),
1405                             fallback ? SkFontMgr::RefDefault() : SkFontMgr::RefEmpty());
1406 #else
1407         std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder(
1408                             fOwner->getUnicode()->copy(),
1409                             fallback ? RSFontMgr::CreateDefaultFontMgr() : RSFontMgr::CreateDefaultFontMgr());
1410 #endif
1411         shaper->shape(str.c_str(),
1412                       str.size(),
1413                       font,
1414                       true,
1415                       std::numeric_limits<SkScalar>::max(),
1416                       &handler);
1417         auto run = handler.run();
1418         run->fTextRange = TextRange(0, str.size());
1419         run->fOwner = fOwner;
1420         return run;
1421     };
1422 
1423     // Check all allowed fonts
1424     auto typefaces = fOwner->fontCollection()->findTypefaces(
1425             textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
1426     for (const auto& typeface : typefaces) {
1427         auto run = shaped(typeface, false);
1428         if (run->isResolved()) {
1429             return run;
1430         }
1431     }
1432 
1433     // Try the fallback
1434     if (fOwner->fontCollection()->fontFallbackEnabled()) {
1435         const char* ch = str.c_str();
1436         SkUnichar unicode = nextUtf8Unit(&ch, str.c_str() + str.size());
1437 
1438         auto typeface = fOwner->fontCollection()->defaultFallback(
1439             unicode, textStyle.getFontStyle(), textStyle.getLocale());
1440         if (typeface) {
1441             if (textStyle.getFontArguments()) {
1442                 typeface = fOwner->fontCollection()->CloneTypeface(typeface, textStyle.getFontArguments());
1443             }
1444             auto run = shaped(typeface, true);
1445             if (run->isResolved()) {
1446                 return run;
1447             }
1448         }
1449     }
1450 
1451     // Check the current font
1452 #ifndef USE_SKIA_TXT
1453     auto finalRun = shaped(run.fFont.refTypeface(), false);
1454 #else
1455     auto finalRun = shaped(const_cast<RSFont&>(run.fFont).GetTypeface(), false);
1456 #endif
1457     if (finalRun->isResolved()) {
1458         return finalRun;
1459     }
1460     return finalRun;
1461 }
1462 
1463 #ifdef OHOS_SUPPORT
1464 void TextLine::measureTextWithSpacesAtTheEnd(ClipContext& context, bool includeGhostSpaces) const
1465 {
1466     // Special judgment for the middle ellipsis, reason: inconsistent width behavior between
1467     // the middle tail ellipsis and the head ellipsis
1468     SkScalar lineWidth = fOwner->needCreateMiddleEllipsis() ? width() : fAdvance.fX;
1469     if (compareRound(context.clip.fRight, lineWidth, fOwner->getApplyRoundingHack()) > 0 && !includeGhostSpaces
1470         && lineWidth > 0) {
1471         // There are few cases when we need it.
1472         // The most important one: we measure the text with spaces at the end (or at the beginning in RTL)
1473         // and we should ignore these spaces
1474         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kLtr) {
1475             // We only use this member for LTR
1476             context.fExcludedTrailingSpaces = std::max(context.clip.fRight - lineWidth, 0.0f);
1477             context.clippingNeeded = true;
1478             context.clip.fRight = lineWidth;
1479         }
1480     }
1481 }
1482 
1483 namespace {
1484 std::string toHexString(int32_t decimal) {
1485     std::stringstream ss;
1486     ss << std::hex << decimal;
1487     return ss.str();
1488 }
1489 
1490 void logUnicodeDataAroundIndex(ParagraphImpl* fOwner, TextIndex index) {
1491     const auto& unicodeText = fOwner->unicodeText();
1492     if (unicodeText.size() == 0) {
1493         return;
1494     }
1495     size_t unicodeIndex = fOwner->getUnicodeIndex(index);
1496 
1497     size_t start = (unicodeIndex > 4) ? unicodeIndex - 4 : 0;
1498     size_t end = std::min(unicodeIndex + 4, unicodeText.size() - 1);
1499 
1500     std::string logMsg = "Unicode around index " + std::to_string(index) + ": [";
1501     for (size_t i = start; i <= end; ++i) {
1502         SkUnichar unicode = unicodeText[i];
1503         logMsg += (i == unicodeIndex) ? "{" : "";
1504         logMsg += "U+" + toHexString(unicode);
1505         logMsg += (i == unicodeIndex) ? "}" : "";
1506         if (i < end) {
1507             logMsg += ", ";
1508         }
1509     }
1510     logMsg += "]";
1511     TEXT_LOGW_LIMIT3_HOUR("%{public}s", logMsg.c_str());
1512 }
1513 
1514 ClusterIndex getValidClusterIndex(ParagraphImpl* fOwner, const TextIndex& primaryIndex,
1515     const TextIndex& fallbackIndex) {
1516     // We need to find the best cluster index for the given text index
1517     // The primary plan is the original text index, the fallback plan is the next one
1518     // We prefer plan primary if clusterIndex is not empty
1519     ClusterIndex clusterIndex = fOwner->clusterIndex(primaryIndex);
1520     if (clusterIndex == EMPTY_INDEX) {
1521         TEXT_LOGW("Warning: clusterIndex is EMPTY_INDEX");
1522         logUnicodeDataAroundIndex(fOwner, primaryIndex);
1523         clusterIndex = fOwner->clusterIndex(fallbackIndex);
1524     }
1525     return clusterIndex;
1526 }
1527 
1528 void adjustTextRange(TextRange& textRange, const Run* run, TextLine::TextAdjustment textAdjustment) {
1529     while (true) {
1530         TextRange updatedTextRange;
1531         std::tie(std::ignore, updatedTextRange.start, updatedTextRange.end) = run->findLimitingGlyphClusters(textRange);
1532         if ((textAdjustment & TextLine::TextAdjustment::Grapheme) == 0) {
1533             textRange = updatedTextRange;
1534             break;
1535         }
1536         std::tie(std::ignore, updatedTextRange.start, updatedTextRange.end) =
1537             run->findLimitingGraphemes(updatedTextRange);
1538         if (updatedTextRange == textRange) {
1539             break;
1540         }
1541         textRange = updatedTextRange;
1542     }
1543 }
1544 } // namespace
1545 
1546 TextLine::ClipContext TextLine::getRunClipContextByRange(
1547     const Run* run, TextRange textRange, TextLine::TextAdjustment textAdjustment, SkScalar textStartInLine) const {
1548     ClipContext result = {run, 0, run->size(), 0, SkRect::MakeEmpty(), 0, false};
1549     TextRange originalTextRange(textRange); // We need it for proportional measurement
1550     // Find [start:end] clusters for the text
1551     adjustTextRange(textRange, run, textAdjustment);
1552 
1553     Cluster* start = &fOwner->cluster(getValidClusterIndex(fOwner, textRange.start, originalTextRange.start));
1554     Cluster* end = &fOwner->cluster(getValidClusterIndex(fOwner, textRange.end - (textRange.width() == 0 ? 0 : 1),
1555         originalTextRange.end - (originalTextRange.width() == 0 ? 0 : 1)));
1556 
1557     if (!run->leftToRight()) {
1558         std::swap(start, end);
1559     }
1560     result.pos = start->startPos();
1561     result.size = (end->isHardBreak() ? end->startPos() : end->endPos()) - start->startPos();
1562     auto textStartInRun = run->positionX(start->startPos());
1563 
1564     if (!run->leftToRight()) {
1565         std::swap(start, end);
1566     }
1567     // Calculate the clipping rectangle for the text with cluster edges
1568     // There are 2 cases:
1569     // EOL (when we expect the last cluster clipped without any spaces)
1570     // Anything else (when we want the cluster width contain all the spaces -
1571     // coming from letter spacing or word spacing or justification)
1572     result.clip = SkRect::MakeXYWH(0, sizes().runTop(run, this->fAscentStyle),
1573         run->calculateWidth(result.pos, result.pos + result.size, false),
1574         run->calculateHeight(this->fAscentStyle, this->fDescentStyle));
1575     // Correct the width in case the text edges don't match clusters
1576     auto leftCorrection = start->sizeToChar(originalTextRange.start);
1577     auto rightCorrection = end->sizeFromChar(originalTextRange.end - 1);
1578     result.clippingNeeded = leftCorrection != 0 || rightCorrection != 0;
1579     if (run->leftToRight()) {
1580         result.clip.fLeft += leftCorrection;
1581         result.clip.fRight -= rightCorrection;
1582         textStartInLine -= leftCorrection;
1583     } else {
1584         result.clip.fRight -= leftCorrection;
1585         result.clip.fLeft += rightCorrection;
1586         textStartInLine -= rightCorrection;
1587     }
1588     result.clip.offset(textStartInLine, 0);
1589     // The text must be aligned with the lineOffset
1590     result.fTextShift = textStartInLine - textStartInRun;
1591     return result;
1592 }
1593 
1594 TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange, const Run* run, SkScalar runOffsetInLine,
1595     SkScalar textOffsetInRunInLine, bool includeGhostSpaces, TextAdjustment textAdjustment) const {
1596     ClipContext result = {run, 0, run->size(), 0, SkRect::MakeEmpty(), 0, false};
1597 
1598     if (run->fEllipsis) {
1599         // Both ellipsis and placeholders can only be measured as one glyph
1600         result.fTextShift = runOffsetInLine;
1601         result.clip = SkRect::MakeXYWH(runOffsetInLine, sizes().runTop(run, this->fAscentStyle), run->advance().fX,
1602             run->calculateHeight(this->fAscentStyle, this->fDescentStyle));
1603         return result;
1604     } else if (run->isPlaceholder()) {
1605         result.fTextShift = runOffsetInLine;
1606         if (SkScalarIsFinite(run->fFontMetrics.fAscent)) {
1607             result.clip = SkRect::MakeXYWH(runOffsetInLine, sizes().runTop(run, this->fAscentStyle), run->advance().fX,
1608                 run->calculateHeight(this->fAscentStyle, this->fDescentStyle));
1609         } else {
1610             result.clip = SkRect::MakeXYWH(runOffsetInLine, run->fFontMetrics.fAscent, run->advance().fX, 0);
1611         }
1612         return result;
1613     } else if (textRange.empty()) {
1614         return result;
1615     }
1616     auto textStartInLine = runOffsetInLine + textOffsetInRunInLine;
1617     result = getRunClipContextByRange(run, textRange, textAdjustment, textStartInLine);
1618     measureTextWithSpacesAtTheEnd(result, includeGhostSpaces);
1619     return result;
1620 }
1621 #else
1622 TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
1623                                                         const Run* run,
1624                                                         SkScalar runOffsetInLine,
1625                                                         SkScalar textOffsetInRunInLine,
1626                                                         bool includeGhostSpaces,
1627                                                         TextAdjustment textAdjustment) const {
1628     ClipContext result = { run, 0, run->size(), 0, SkRect::MakeEmpty(), 0, false };
1629 
1630     if (run->fEllipsis) {
1631         // Both ellipsis and placeholders can only be measured as one glyph
1632         result.fTextShift = runOffsetInLine;
1633         result.clip = SkRect::MakeXYWH(runOffsetInLine,
1634                                        sizes().runTop(run, this->fAscentStyle),
1635                                        run->advance().fX,
1636                                        run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1637         return result;
1638     } else if (run->isPlaceholder()) {
1639         result.fTextShift = runOffsetInLine;
1640         if (SkScalarIsFinite(run->fFontMetrics.fAscent)) {
1641           result.clip = SkRect::MakeXYWH(runOffsetInLine,
1642                                          sizes().runTop(run, this->fAscentStyle),
1643                                          run->advance().fX,
1644                                          run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1645         } else {
1646             result.clip = SkRect::MakeXYWH(runOffsetInLine, run->fFontMetrics.fAscent, run->advance().fX, 0);
1647         }
1648         return result;
1649     } else if (textRange.empty()) {
1650         return result;
1651     }
1652 
1653     TextRange originalTextRange(textRange); // We need it for proportional measurement
1654     // Find [start:end] clusters for the text
1655     while (true) {
1656         // Update textRange by cluster edges (shift start up to the edge of the cluster)
1657         // TODO: remove this limitation?
1658         TextRange updatedTextRange;
1659         bool found;
1660         std::tie(found, updatedTextRange.start, updatedTextRange.end) =
1661                                         run->findLimitingGlyphClusters(textRange);
1662         if (!found) {
1663             return result;
1664         }
1665 
1666         if ((textAdjustment & TextAdjustment::Grapheme) == 0) {
1667             textRange = updatedTextRange;
1668             break;
1669         }
1670 
1671         // Update text range by grapheme edges (shift start up to the edge of the grapheme)
1672         std::tie(found, updatedTextRange.start, updatedTextRange.end) =
1673                                     run->findLimitingGraphemes(updatedTextRange);
1674         if (updatedTextRange == textRange) {
1675             break;
1676         }
1677 
1678         // Some clusters are inside graphemes and we need to adjust them
1679         //SkDebugf("Correct range: [%d:%d) -> [%d:%d)\n", textRange.start, textRange.end, startIndex, endIndex);
1680         textRange = updatedTextRange;
1681 
1682         // Move the start until it's on the grapheme edge (and glypheme, too)
1683     }
1684     Cluster* start = &fOwner->cluster(fOwner->clusterIndex(textRange.start));
1685     Cluster* end = &fOwner->cluster(fOwner->clusterIndex(textRange.end - (textRange.width() == 0 ? 0 : 1)));
1686 
1687     if (!run->leftToRight()) {
1688         std::swap(start, end);
1689     }
1690     result.pos = start->startPos();
1691     result.size = (end->isHardBreak() ? end->startPos() : end->endPos()) - start->startPos();
1692     auto textStartInRun = run->positionX(start->startPos());
1693     auto textStartInLine = runOffsetInLine + textOffsetInRunInLine;
1694     if (!run->leftToRight()) {
1695         std::swap(start, end);
1696     }
1697 /*
1698     if (!run->fJustificationShifts.empty()) {
1699         SkDebugf("Justification for [%d:%d)\n", textRange.start, textRange.end);
1700         for (auto i = result.pos; i < result.pos + result.size; ++i) {
1701             auto j = run->fJustificationShifts[i];
1702             SkDebugf("[%d] = %f %f\n", i, j.fX, j.fY);
1703         }
1704     }
1705 */
1706     // Calculate the clipping rectangle for the text with cluster edges
1707     // There are 2 cases:
1708     // EOL (when we expect the last cluster clipped without any spaces)
1709     // Anything else (when we want the cluster width contain all the spaces -
1710     // coming from letter spacing or word spacing or justification)
1711     result.clip =
1712             SkRect::MakeXYWH(0,
1713                              sizes().runTop(run, this->fAscentStyle),
1714                              run->calculateWidth(result.pos, result.pos + result.size, false),
1715                              run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1716 
1717     // Correct the width in case the text edges don't match clusters
1718     // TODO: This is where we get smart about selecting a part of a cluster
1719     //  by shaping each grapheme separately and then use the result sizes
1720     //  to calculate the proportions
1721     auto leftCorrection = start->sizeToChar(originalTextRange.start);
1722     auto rightCorrection = end->sizeFromChar(originalTextRange.end - 1);
1723     /*
1724     SkDebugf("[%d: %d) => [%d: %d), @%d, %d: [%f:%f) + [%f:%f) = ", // جَآَهُ
1725              originalTextRange.start, originalTextRange.end, textRange.start, textRange.end,
1726              result.pos, result.size,
1727              result.clip.fLeft, result.clip.fRight, leftCorrection, rightCorrection);
1728      */
1729     result.clippingNeeded = leftCorrection != 0 || rightCorrection != 0;
1730     if (run->leftToRight()) {
1731         result.clip.fLeft += leftCorrection;
1732         result.clip.fRight -= rightCorrection;
1733         textStartInLine -= leftCorrection;
1734     } else {
1735         result.clip.fRight -= leftCorrection;
1736         result.clip.fLeft += rightCorrection;
1737         textStartInLine -= rightCorrection;
1738     }
1739 
1740     result.clip.offset(textStartInLine, 0);
1741     //SkDebugf("@%f[%f:%f)\n", textStartInLine, result.clip.fLeft, result.clip.fRight);
1742 
1743     if (compareRound(result.clip.fRight, fAdvance.fX, fOwner->getApplyRoundingHack()) > 0 && !includeGhostSpaces) {
1744         // There are few cases when we need it.
1745         // The most important one: we measure the text with spaces at the end (or at the beginning in RTL)
1746         // and we should ignore these spaces
1747         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kLtr) {
1748             // We only use this member for LTR
1749             result.fExcludedTrailingSpaces = std::max(result.clip.fRight - fAdvance.fX, 0.0f);
1750             result.clippingNeeded = true;
1751             result.clip.fRight = fAdvance.fX;
1752         }
1753     }
1754 
1755     if (result.clip.width() < 0) {
1756         // Weird situation when glyph offsets move the glyph to the left
1757         // (happens with zalgo texts, for instance)
1758         result.clip.fRight = result.clip.fLeft;
1759     }
1760 
1761     // The text must be aligned with the lineOffset
1762     result.fTextShift = textStartInLine - textStartInRun;
1763 
1764     return result;
1765 }
1766 #endif
1767 
1768 void TextLine::iterateThroughClustersInGlyphsOrder(bool reversed,
1769                                                    bool includeGhosts,
1770                                                    const ClustersVisitor& visitor) const {
1771     // Walk through the clusters in the logical order (or reverse)
1772     SkSpan<const size_t> runs(fRunsInVisualOrder.data(), fRunsInVisualOrder.size());
1773     bool ignore = false;
1774     ClusterIndex index = 0;
1775     directional_for_each(runs, !reversed, [&](decltype(runs[0]) r) {
1776         if (ignore) return;
1777         auto run = this->fOwner->run(r);
1778         auto trimmedRange = fClusterRange.intersection(run.clusterRange());
1779         auto trailedRange = fGhostClusterRange.intersection(run.clusterRange());
1780         SkASSERT(trimmedRange.start == trailedRange.start);
1781 
1782         auto trailed = fOwner->clusters(trailedRange);
1783         auto trimmed = fOwner->clusters(trimmedRange);
1784         directional_for_each(trailed, reversed != run.leftToRight(), [&](Cluster& cluster) {
1785             if (ignore) return;
1786             bool ghost =  &cluster >= trimmed.end();
1787             if (!includeGhosts && ghost) {
1788                 return;
1789             }
1790             if (!visitor(&cluster, index++, ghost)) {
1791 
1792                 ignore = true;
1793                 return;
1794             }
1795         });
1796     });
1797 }
1798 
1799 #ifdef OHOS_SUPPORT
1800 void TextLine::computeNextPaintGlyphRange(ClipContext& context,
1801     const TextRange& lastGlyphRange, StyleType styleType) const
1802 {
1803     if (styleType != StyleType::kForeground) {
1804         return;
1805     }
1806     TextRange curGlyphRange = TextRange(context.pos, context.pos + context.size);
1807     auto intersect = intersected(lastGlyphRange, curGlyphRange);
1808     if (intersect == EMPTY_TEXT || (intersect.start != curGlyphRange.start && intersect.end != curGlyphRange.end)) {
1809         return;
1810     }
1811     if (intersect.start == curGlyphRange.start) {
1812         curGlyphRange = TextRange(intersect.end, curGlyphRange.end);
1813     } else if (intersect.end == curGlyphRange.end) {
1814         curGlyphRange = TextRange(curGlyphRange.start, intersect.start);
1815     }
1816 
1817     context.pos = curGlyphRange.start;
1818     context.size = curGlyphRange.width();
1819 }
1820 #endif
1821 
1822 SkScalar TextLine::iterateThroughSingleRunByStyles(TextAdjustment textAdjustment,
1823                                                    const Run* run,
1824                                                    SkScalar runOffset,
1825                                                    TextRange textRange,
1826                                                    StyleType styleType,
1827                                                    const RunStyleVisitor& visitor) const {
1828     auto includeGhostSpaces = (styleType == StyleType::kDecorations || styleType == StyleType::kBackground ||
1829         styleType == StyleType::kNone);
1830     auto correctContext = [&](TextRange textRange, SkScalar textOffsetInRun) -> ClipContext {
1831         auto result = this->measureTextInsideOneRun(
1832             textRange, run, runOffset, textOffsetInRun, includeGhostSpaces, textAdjustment);
1833         if (styleType == StyleType::kDecorations) {
1834             // Decorations are drawn based on the real font metrics (regardless of styles and strut)
1835             result.clip.fTop = this->sizes().runTop(run, LineMetricStyle::CSS) - run->baselineShift();
1836             result.clip.fBottom = result.clip.fTop +
1837                                   run->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS);
1838 #ifdef OHOS_SUPPORT
1839             result.fIsTrimTrailingSpaceWidth = false;
1840             if (fOwner->paragraphStyle().getTrailingSpaceOptimized() &&
1841                 run->isTrailingSpaceIncluded(fClusterRange, fGhostClusterRange)) {
1842                 result.fTrailingSpaceWidth = spacesWidth() < 0 ? 0 : spacesWidth();
1843                 if (!run->leftToRight() && (fGhostClusterRange.width() > 0 &&
1844                     fOwner->cluster(fGhostClusterRange.end - 1).isHardBreak())) {
1845                     result.fTrailingSpaceWidth += fOwner->cluster(fGhostClusterRange.end - 1).width();
1846                 }
1847                 result.fIsTrimTrailingSpaceWidth = true;
1848             }
1849 #endif
1850         }
1851         return result;
1852     };
1853 
1854     if (run->fEllipsis) {
1855         // Extra efforts to get the ellipsis text style
1856         ClipContext clipContext = correctContext(run->textRange(), 0.0f);
1857         for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
1858             auto block = fOwner->styles().begin() + index;
1859 #ifdef OHOS_SUPPORT
1860             TextRange intersect = intersected(block->fRange,
1861                 TextRange(run->textRange().start - 1, run->textRange().end));
1862             if (intersect.width() > 0) {
1863                 visitor(fTextRangeReplacedByEllipsis, block->fStyle, clipContext);
1864                 return run->advance().fX;
1865             }
1866 #else
1867            if (block->fRange.start >= run->fClusterStart && block->fRange.end < run->fClusterStart) {
1868                visitor(fTextRangeReplacedByEllipsis, block->fStyle, clipContext);
1869                return run->advance().fX;
1870            }
1871 #endif
1872         }
1873         SkASSERT(false);
1874     }
1875 
1876     if (styleType == StyleType::kNone) {
1877         ClipContext clipContext = correctContext(textRange, 0.0f);
1878 #ifdef OHOS_SUPPORT
1879         if (clipContext.clip.height() > 0 ||
1880             (run->isPlaceholder() && SkScalarNearlyZero(clipContext.clip.height()))) {
1881 #else
1882         if (clipContext.clip.height() > 0) {
1883 #endif
1884             visitor(textRange, TextStyle(), clipContext);
1885             return clipContext.clip.width();
1886         } else {
1887             return 0;
1888         }
1889     }
1890 
1891     TextIndex start = EMPTY_INDEX;
1892     size_t size = 0;
1893     const TextStyle* prevStyle = nullptr;
1894     SkScalar textOffsetInRun = 0;
1895 #ifdef OHOS_SUPPORT
1896     TextRange lastGlyphRange = EMPTY_TEXT;
1897 #endif
1898     const BlockIndex blockRangeSize = fBlockRange.end - fBlockRange.start;
1899     for (BlockIndex index = 0; index <= blockRangeSize; ++index) {
1900 
1901         TextRange intersect;
1902         TextStyle* style = nullptr;
1903         if (index < blockRangeSize) {
1904             auto block = fOwner->styles().begin() +
1905                  (run->leftToRight() ? fBlockRange.start + index : fBlockRange.end - index - 1);
1906 
1907             // Get the text
1908             intersect = intersected(block->fRange, textRange);
1909             if (intersect.width() == 0) {
1910                 if (start == EMPTY_INDEX) {
1911                     // This style is not applicable to the text yet
1912                     continue;
1913                 } else {
1914                     // We have found all the good styles already
1915                     // but we need to process the last one of them
1916                     intersect = TextRange(start, start + size);
1917                     index = fBlockRange.end;
1918                 }
1919             } else {
1920                 // Get the style
1921                 style = &block->fStyle;
1922                 if (start != EMPTY_INDEX && style->matchOneAttribute(styleType, *prevStyle)) {
1923                     size += intersect.width();
1924                     // RTL text intervals move backward
1925                     start = std::min(intersect.start, start);
1926                     continue;
1927                 } else if (start == EMPTY_INDEX ) {
1928                     // First time only
1929                     prevStyle = style;
1930                     size = intersect.width();
1931                     start = intersect.start;
1932                     continue;
1933                 }
1934             }
1935         } else if (prevStyle != nullptr) {
1936             // This is the last style
1937         } else {
1938             break;
1939         }
1940 
1941         // We have the style and the text
1942         auto runStyleTextRange = TextRange(start, start + size);
1943         ClipContext clipContext = correctContext(runStyleTextRange, textOffsetInRun);
1944         textOffsetInRun += clipContext.clip.width();
1945         if (clipContext.clip.height() == 0) {
1946             continue;
1947         }
1948 
1949         RectStyle temp;
1950         if (styleType == StyleType::kBackground &&
1951             prevStyle->getBackgroundRect() != temp &&
1952             prevStyle->getHeight() != 0) {
1953 #ifdef OHOS_SUPPORT
1954                 clipContext.clip.fTop = run->fFontMetrics.fAscent + this->baseline() + run->fBaselineShift
1955                     + run->getVerticalAlignShift();
1956 #else
1957                 clipContext.clip.fTop = run->fFontMetrics.fAscent - run->fCorrectAscent;
1958 #endif
1959                 clipContext.clip.fBottom = clipContext.clip.fTop + run->fFontMetrics.fDescent -
1960                     run->fFontMetrics.fAscent;
1961         }
1962 #ifdef OHOS_SUPPORT
1963         computeNextPaintGlyphRange(clipContext, lastGlyphRange, styleType);
1964         if (clipContext.size != 0) {
1965             lastGlyphRange = TextRange(clipContext.pos, clipContext.pos + clipContext.size);
1966         }
1967 #endif
1968         visitor(runStyleTextRange, *prevStyle, clipContext);
1969 
1970         // Start all over again
1971         prevStyle = style;
1972         start = intersect.start;
1973         size = intersect.width();
1974     }
1975     return textOffsetInRun;
1976 }
1977 
1978 #ifdef OHOS_SUPPORT
1979 bool TextLine::processEllipsisRun(IterateRunsContext& context,
1980                                   EllipsisReadStrategy ellipsisReadStrategy,
1981                                   const RunVisitor& visitor,
1982                                   SkScalar& runWidthInLine) const {
1983     context.isAlreadyUseEllipsis = true;
1984     return processInsertedRun(fEllipsis.get(), context.runOffset, ellipsisReadStrategy,
1985                               visitor, runWidthInLine);
1986 }
1987 
1988 bool TextLine::processInsertedRun(const Run* extra,
1989                                   SkScalar& runOffset,
1990                                   EllipsisReadStrategy ellipsisReadStrategy,
1991                                   const RunVisitor& visitor,
1992                                   SkScalar& runWidthInLine) const {
1993     runOffset += extra->offset().fX;
1994     if (ellipsisReadStrategy == EllipsisReadStrategy::READ_REPLACED_WORD) {
1995         if (!visitor(extra, runOffset, fTextRangeReplacedByEllipsis, &runWidthInLine)) {
1996             LOGE("Visitor process ellipsis replace word error!");
1997             return false;
1998         }
1999     } else if (ellipsisReadStrategy == EllipsisReadStrategy::READ_ELLIPSIS_WORD) {
2000         if (!visitor(extra, runOffset, extra->textRange(), &runWidthInLine)) {
2001             LOGE("Visitor process ellipsis word error!");
2002             return false;
2003         }
2004     } else {
2005         runWidthInLine = extra->advance().fX;
2006     }
2007     return true;
2008 }
2009 #endif
2010 
2011 #ifdef OHOS_SUPPORT
2012 void TextLine::iterateThroughVisualRuns(EllipsisReadStrategy ellipsisReadStrategy, bool includingGhostSpaces,
2013                                         const RunVisitor& visitor) const {
2014     IterateRunsContext context;
2015     if (fEllipsis != nullptr) {
2016         if (fOwner->needCreateMiddleEllipsis()) {
2017             context.ellipsisMode = EllipsisModal::MIDDLE;
2018         } else if (fIsTextLineEllipsisHeadModal || fOwner->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD) {
2019             context.ellipsisMode = EllipsisModal::HEAD;
2020         }
2021     }
2022     auto textRange = includingGhostSpaces ? this->textWithNewlines() : this->trimmedText();
2023 
2024     if (fRunsInVisualOrder.size() == 0) {
2025         if (fEllipsis != nullptr) {
2026             if (!processEllipsisRun(context, ellipsisReadStrategy, visitor, context.width)) {
2027                 return;
2028             }
2029             context.totalWidth += context.width;
2030         }
2031         if (fHyphenRun != nullptr) { // not sure if this is basically valid in real life
2032             if (!processInsertedRun(fHyphenRun.get(), context.runOffset, ellipsisReadStrategy, visitor,
2033                                     context.width)) {
2034                 return;
2035             }
2036             context.totalWidth += context.width;
2037         }
2038     }
2039 
2040     for (auto& runIndex : fRunsInVisualOrder) {
2041         context.runIndex = runIndex;
2042         // add the lastClipRun's left ellipsis if necessary
2043         if (!context.isAlreadyUseEllipsis && fEllipsisIndex == runIndex &&
2044             ((!fLastClipRunLtr && context.ellipsisMode != EllipsisModal::HEAD &&
2045             context.ellipsisMode != EllipsisModal::MIDDLE) ||
2046             (context.ellipsisMode == EllipsisModal::HEAD && fLastClipRunLtr))) {
2047             if (!processEllipsisRun(context, ellipsisReadStrategy, visitor, context.width)) {
2048                 return;
2049             }
2050             context.runOffset += context.width;
2051             context.totalWidth += context.width;
2052         }
2053 
2054         const auto run = &this->fOwner->run(runIndex);
2055         context.lineIntersection = intersected(run->textRange(), textRange);
2056         if (context.lineIntersection.width() == 0 && this->width() != 0) {
2057             // TODO: deal with empty runs in a better way
2058             continue;
2059         }
2060         if (!run->leftToRight() && context.runOffset == 0 && includingGhostSpaces) {
2061             // runOffset does not take in account a possibility
2062             // that RTL run could start before the line (trailing spaces)
2063             // so we need to do runOffset -= "trailing whitespaces length"
2064             TextRange whitespaces =
2065                 intersected(TextRange(fTextExcludingSpaces.end, fTextIncludingNewlines.end), run->fTextRange);
2066             if (whitespaces.width() > 0) {
2067                 auto whitespacesLen =
2068                     measureTextInsideOneRun(whitespaces, run, context.runOffset, 0, true, TextAdjustment::GlyphCluster)
2069                         .clip.width();
2070                 context.runOffset -= whitespacesLen;
2071             }
2072         }
2073         if (context.ellipsisMode == EllipsisModal::MIDDLE) {
2074             if (!handleMiddleEllipsisMode(run, context, ellipsisReadStrategy, visitor)) {
2075                 return;
2076             }
2077         } else {
2078             if (!visitor(run, context.runOffset, context.lineIntersection, &context.width)) {
2079                 return;
2080             }
2081             context.runOffset += context.width;
2082             context.totalWidth += context.width;
2083         }
2084 
2085         // add the lastClipRun's right ellipsis if necessary
2086         if (!context.isAlreadyUseEllipsis && fEllipsisIndex == runIndex) {
2087             if (!processEllipsisRun(context, ellipsisReadStrategy, visitor, context.width)) {
2088                 return;
2089             }
2090             context.runOffset += context.width;
2091             context.totalWidth += context.width;
2092         }
2093         if (runIndex == fHyphenIndex) {
2094             if (!processInsertedRun(fHyphenRun.get(), context.runOffset, ellipsisReadStrategy, visitor,
2095                                     context.width)) {
2096                 return;
2097             }
2098             context.runOffset += context.width;
2099             context.totalWidth += context.width;
2100         }
2101     }
2102 
2103     if (!includingGhostSpaces && compareRound(context.totalWidth, this->width(), fOwner->getApplyRoundingHack()) != 0) {
2104         // This is a very important assert!
2105         // It asserts that 2 different ways of calculation come with the same results
2106         SkDebugf("ASSERT: %f != %f\n", context.totalWidth, this->width());
2107         SkASSERT(false);
2108     }
2109 }
2110 
2111 bool TextLine::handleMiddleEllipsisMode(const Run* run, IterateRunsContext& context,
2112                                         EllipsisReadStrategy& ellipsisReadStrategy, const RunVisitor& visitor) const {
2113     std::pair<TextRange, TextRange> cutRanges =
2114         intervalDifference(run->leftToRight(), context.lineIntersection, fTextRangeReplacedByEllipsis);
2115 
2116     if (cutRanges.first.start != EMPTY_RANGE.start) {
2117         if (!visitor(run, context.runOffset, cutRanges.first, &context.width)) {
2118             return false;
2119         }
2120         context.runOffset += context.width;
2121         context.totalWidth += context.width;
2122     }
2123 
2124     if ((cutRanges.first.start != EMPTY_RANGE.start || cutRanges.second.start != EMPTY_RANGE.start)
2125         && !context.isAlreadyUseEllipsis && fEllipsisIndex == context.runIndex) {
2126         if (!processEllipsisRun(context, ellipsisReadStrategy, visitor, context.width)) {
2127             return false;
2128         }
2129         context.runOffset += context.width;
2130         context.totalWidth += context.width;
2131     }
2132 
2133     if (cutRanges.second.start != EMPTY_RANGE.start) {
2134         if (!visitor(run, context.runOffset, cutRanges.second, &context.width)) {
2135             return false;
2136         }
2137         context.runOffset += context.width;
2138         context.totalWidth += context.width;
2139     }
2140     return true;
2141 }
2142 #else
2143 void TextLine::iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& visitor) const {
2144 
2145     // Walk through all the runs that intersect with the line in visual order
2146     SkScalar width = 0;
2147     SkScalar runOffset = 0;
2148     SkScalar totalWidth = 0;
2149     auto textRange = includingGhostSpaces ? this->textWithNewlines() : this->trimmedText();
2150     for (auto& runIndex : fRunsInVisualOrder) {
2151 
2152         const auto run = &this->fOwner->run(runIndex);
2153         auto lineIntersection = intersected(run->textRange(), textRange);
2154         if (lineIntersection.width() == 0 && this->width() != 0) {
2155             // TODO: deal with empty runs in a better way
2156             continue;
2157         }
2158         if (!run->leftToRight() && runOffset == 0 && includingGhostSpaces) {
2159             // runOffset does not take in account a possibility
2160             // that RTL run could start before the line (trailing spaces)
2161             // so we need to do runOffset -= "trailing whitespaces length"
2162             TextRange whitespaces = intersected(
2163                     TextRange(fTextExcludingSpaces.end, fTextIncludingNewlines.end), run->fTextRange);
2164             if (whitespaces.width() > 0) {
2165                 auto whitespacesLen = measureTextInsideOneRun(whitespaces, run, runOffset, 0, true, false).clip.width();
2166                 runOffset -= whitespacesLen;
2167             }
2168         }
2169         runOffset += width;
2170         totalWidth += width;
2171         if (!visitor(run, runOffset, lineIntersection, &width)) {
2172             return;
2173         }
2174     }
2175 
2176     runOffset += width;
2177     totalWidth += width;
2178 
2179     if (fEllipsis != nullptr) {
2180         if (visitor(fEllipsis.get(), runOffset, fEllipsis->textRange(), &width)) {
2181             totalWidth += width;
2182         }
2183     }
2184 
2185     // This is a very important assert!
2186     // It asserts that 2 different ways of calculation come with the same results
2187     if (!includingGhostSpaces && compareRound(totalWidth, this->width()) != 0) {
2188         SkDebugf("ASSERT: %f != %f\n", totalWidth, this->width());
2189         SkASSERT(false);
2190     }
2191 }
2192 #endif
2193 
2194 SkVector TextLine::offset() const {
2195     return fOffset + SkVector::Make(fShift, 0);
2196 }
2197 
2198 LineMetrics TextLine::getMetrics() const {
2199     LineMetrics result;
2200 
2201     // Fill out the metrics
2202     fOwner->ensureUTF16Mapping();
2203     result.fStartIndex = fOwner->getUTF16Index(fTextExcludingSpaces.start);
2204     result.fEndExcludingWhitespaces = fOwner->getUTF16Index(fTextExcludingSpaces.end);
2205     result.fEndIndex = fOwner->getUTF16Index(fText.end);
2206     result.fEndIncludingNewline = fOwner->getUTF16Index(fTextIncludingNewlines.end);
2207     result.fHardBreak = endsWithHardLineBreak();
2208     result.fAscent = - fMaxRunMetrics.ascent();
2209     result.fDescent = fMaxRunMetrics.descent();
2210     result.fUnscaledAscent = - fMaxRunMetrics.ascent(); // TODO: implement
2211     result.fHeight = fAdvance.fY;
2212     result.fWidth = fAdvance.fX;
2213     if (fOwner->getApplyRoundingHack()) {
2214         result.fHeight = littleRound(result.fHeight);
2215         result.fWidth = littleRound(result.fWidth);
2216     }
2217     result.fLeft = this->offset().fX;
2218     // This is Flutter definition of a baseline
2219     result.fBaseline = this->offset().fY + this->height() - this->sizes().descent();
2220     result.fLineNumber = this - fOwner->lines().begin();
2221     result.fWidthWithSpaces = fWidthWithSpaces;
2222     result.fTopHeight = this->offset().fY;
2223 
2224     // Fill out the style parts
2225 #ifdef OHOS_SUPPORT
2226     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
2227 #else
2228     this->iterateThroughVisualRuns(false,
2229 #endif
2230         [this, &result]
2231         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
2232         if (run->placeholderStyle() != nullptr) {
2233             *runWidthInLine = run->advance().fX;
2234             return true;
2235         }
2236         *runWidthInLine = this->iterateThroughSingleRunByStyles(
2237         TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kForeground,
2238         [&result, &run](TextRange textRange, const TextStyle& style, const ClipContext& context) {
2239 #ifndef USE_SKIA_TXT
2240             SkFontMetrics fontMetrics;
2241             run->fFont.getMetrics(&fontMetrics);
2242 #else
2243             RSFontMetrics fontMetrics;
2244             run->fFont.GetMetrics(&fontMetrics);
2245 #endif
2246 #ifdef OHOS_SUPPORT
2247             auto decompressFont = run->fFont;
2248             scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS);
2249             metricsIncludeFontPadding(&fontMetrics, decompressFont);
2250 #endif
2251             StyleMetrics styleMetrics(&style, fontMetrics);
2252             result.fLineMetrics.emplace(textRange.start, styleMetrics);
2253         });
2254         return true;
2255     });
2256 
2257     return result;
2258 }
2259 
2260 bool TextLine::isFirstLine() const {
2261     return this == &fOwner->lines().front();
2262 }
2263 
2264 bool TextLine::isLastLine() const {
2265     return this == &fOwner->lines().back();
2266 }
2267 
2268 bool TextLine::endsWithHardLineBreak() const {
2269     // TODO: For some reason Flutter imagines a hard line break at the end of the last line.
2270     //  To be removed...
2271     return (fGhostClusterRange.width() > 0 && fOwner->cluster(fGhostClusterRange.end - 1).isHardBreak()) ||
2272            fEllipsis != nullptr ||
2273            fGhostClusterRange.end == fOwner->clusters().size() - 1;
2274 }
2275 #ifdef OHOS_SUPPORT
2276 bool TextLine::endsWithOnlyHardBreak() const
2277 {
2278     return (fGhostClusterRange.width() > 0 && fOwner->cluster(fGhostClusterRange.end - 1).isHardBreak());
2279 }
2280 #endif
2281 
2282 void TextLine::getRectsForRange(TextRange textRange0,
2283                                 RectHeightStyle rectHeightStyle,
2284                                 RectWidthStyle rectWidthStyle,
2285                                 std::vector<TextBox>& boxes) const
2286 {
2287     const Run* lastRun = nullptr;
2288     auto startBox = boxes.size();
2289 #ifdef OHOS_SUPPORT
2290     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
2291 #else
2292     this->iterateThroughVisualRuns(true,
2293 #endif
2294         [textRange0, rectHeightStyle, rectWidthStyle, &boxes, &lastRun, startBox, this]
2295         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
2296         *runWidthInLine = this->iterateThroughSingleRunByStyles(
2297         TextAdjustment::GraphemeGluster, run, runOffsetInLine, textRange, StyleType::kNone,
2298         [run, runOffsetInLine, textRange0, rectHeightStyle, rectWidthStyle, &boxes, &lastRun, startBox, this]
2299         (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& lineContext) {
2300 
2301             auto intersect = textRange * textRange0;
2302 #ifdef OHOS_SUPPORT
2303             if (intersect.empty() && !(this->fBreakWithHyphen && textRange0.end == fText.end && run->isEllipsis())) {
2304 #else
2305             if (intersect.empty()) {
2306 #endif
2307                 return true;
2308             }
2309 
2310             auto paragraphStyle = fOwner->paragraphStyle();
2311 
2312             // Found a run that intersects with the text
2313             auto context = this->measureTextInsideOneRun(
2314                     intersect, run, runOffsetInLine, 0, true, TextAdjustment::GraphemeGluster);
2315             SkRect clip = context.clip;
2316             clip.offset(lineContext.fTextShift - context.fTextShift, 0);
2317 
2318             switch (rectHeightStyle) {
2319                 case RectHeightStyle::kMax:
2320                     // TODO: Change it once flutter rolls into google3
2321                     //  (probably will break things if changed before)
2322 #ifdef OHOS_SUPPORT
2323                     if (endsWithOnlyHardBreak() && fOwner->paragraphStyle().getParagraphSpacing() > 0) {
2324                         clip.fBottom = this->height() - fOwner->paragraphStyle().getParagraphSpacing();
2325                     } else {
2326                         clip.fBottom = this->height();
2327                     }
2328 #else
2329                     clip.fBottom = this->height();
2330 #endif
2331                     clip.fTop = this->sizes().delta();
2332                     break;
2333                 case RectHeightStyle::kIncludeLineSpacingTop: {
2334                     clip.fBottom = this->height();
2335                     clip.fTop = this->sizes().delta();
2336                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
2337                     if (isFirstLine()) {
2338                         clip.fTop += verticalShift;
2339                     }
2340                     break;
2341                 }
2342                 case RectHeightStyle::kIncludeLineSpacingMiddle: {
2343                     clip.fBottom = this->height();
2344                     clip.fTop = this->sizes().delta();
2345                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
2346                     clip.offset(0, verticalShift / 2.0);
2347                     if (isFirstLine()) {
2348                         clip.fTop += verticalShift / 2.0;
2349                     }
2350                     if (isLastLine()) {
2351                         clip.fBottom -= verticalShift / 2.0;
2352                     }
2353                     break;
2354                  }
2355                 case RectHeightStyle::kIncludeLineSpacingBottom: {
2356                     clip.fBottom = this->height();
2357                     clip.fTop = this->sizes().delta();
2358                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
2359                     clip.offset(0, verticalShift);
2360                     if (isLastLine()) {
2361                         clip.fBottom -= verticalShift;
2362                     }
2363                     break;
2364                 }
2365                 case RectHeightStyle::kStrut: {
2366                     const auto& strutStyle = paragraphStyle.getStrutStyle();
2367                     if (strutStyle.getStrutEnabled()
2368                         && strutStyle.getFontSize() > 0) {
2369                         auto strutMetrics = fOwner->strutMetrics();
2370                         auto top = this->baseline();
2371                         clip.fTop = top + strutMetrics.ascent();
2372                         clip.fBottom = top + strutMetrics.descent();
2373                     }
2374                 }
2375                 break;
2376                 case RectHeightStyle::kTight: {
2377                     if (run->fHeightMultiplier <= 0) {
2378                         break;
2379                     }
2380                     const auto effectiveBaseline = this->baseline() + this->sizes().delta();
2381                     clip.fTop = effectiveBaseline + run->ascent();
2382                     clip.fBottom = effectiveBaseline + run->descent();
2383                 }
2384                 break;
2385                 default:
2386                     SkASSERT(false);
2387                 break;
2388             }
2389 
2390             // Separate trailing spaces and move them in the default order of the paragraph
2391             // in case the run order and the paragraph order don't match
2392             SkRect trailingSpaces = SkRect::MakeEmpty();
2393             if (this->trimmedText().end <this->textWithNewlines().end && // Line has trailing space
2394                 this->textWithNewlines().end == intersect.end &&         // Range is at the end of the line
2395                 this->trimmedText().end > intersect.start)               // Range has more than just spaces
2396             {
2397                 auto delta = this->spacesWidth();
2398                 trailingSpaces = SkRect::MakeXYWH(0, 0, 0, 0);
2399                 // There are trailing spaces in this run
2400                 if (paragraphStyle.getTextAlign() == TextAlign::kJustify && isLastLine())
2401                 {
2402                     // TODO: this is just a patch. Make it right later (when it's clear what and how)
2403                     trailingSpaces = clip;
2404                     if(run->leftToRight()) {
2405                         trailingSpaces.fLeft = this->width();
2406                         clip.fRight = this->width();
2407                     } else {
2408                         trailingSpaces.fRight = 0;
2409                         clip.fLeft = 0;
2410                     }
2411                 } else if (paragraphStyle.getTextDirection() == TextDirection::kRtl &&
2412                     !run->leftToRight())
2413                 {
2414                     // Split
2415                     trailingSpaces = clip;
2416                     trailingSpaces.fLeft = - delta;
2417                     trailingSpaces.fRight = 0;
2418                     clip.fLeft += delta;
2419                 } else if (paragraphStyle.getTextDirection() == TextDirection::kLtr &&
2420                     run->leftToRight())
2421                 {
2422                     // Split
2423                     trailingSpaces = clip;
2424                     trailingSpaces.fLeft = this->width();
2425                     trailingSpaces.fRight = trailingSpaces.fLeft + delta;
2426                     clip.fRight -= delta;
2427                 }
2428             }
2429 
2430             clip.offset(this->offset());
2431             if (trailingSpaces.width() > 0) {
2432                 trailingSpaces.offset(this->offset());
2433             }
2434 
2435             // Check if we can merge two boxes instead of adding a new one
2436             auto merge = [&lastRun, &context, &boxes](SkRect clip) {
2437                 bool mergedBoxes = false;
2438                 if (!boxes.empty() &&
2439                     lastRun != nullptr &&
2440                     context.run->leftToRight() == lastRun->leftToRight() &&
2441                     lastRun->placeholderStyle() == nullptr &&
2442                     context.run->placeholderStyle() == nullptr &&
2443                     nearlyEqual(lastRun->heightMultiplier(),
2444                                 context.run->heightMultiplier()) &&
2445 #ifndef USE_SKIA_TXT
2446                     lastRun->font() == context.run->font())
2447 #else
2448                     IsRSFontEquals(lastRun->font(), context.run->font()))
2449 #endif
2450                 {
2451                     auto& lastBox = boxes.back();
2452                     if (nearlyEqual(lastBox.rect.fTop, clip.fTop) &&
2453                         nearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
2454                             (nearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
2455                              nearlyEqual(lastBox.rect.fRight, clip.fLeft)))
2456                     {
2457                         lastBox.rect.fLeft = std::min(lastBox.rect.fLeft, clip.fLeft);
2458                         lastBox.rect.fRight = std::max(lastBox.rect.fRight, clip.fRight);
2459                         mergedBoxes = true;
2460                     }
2461                 }
2462                 lastRun = context.run;
2463                 return mergedBoxes;
2464             };
2465 
2466             if (!merge(clip)) {
2467                 boxes.emplace_back(clip, context.run->getTextDirection());
2468             }
2469             if (!nearlyZero(trailingSpaces.width()) && !merge(trailingSpaces)) {
2470                 boxes.emplace_back(trailingSpaces, paragraphStyle.getTextDirection());
2471             }
2472 
2473             if (rectWidthStyle == RectWidthStyle::kMax && !isLastLine()) {
2474                 // Align the very left/right box horizontally
2475                 auto lineStart = this->offset().fX;
2476                 auto lineEnd = this->offset().fX + this->width();
2477                 auto left = boxes[startBox];
2478                 auto right = boxes.back();
2479                 if (left.rect.fLeft > lineStart && left.direction == TextDirection::kRtl) {
2480                     left.rect.fRight = left.rect.fLeft;
2481                     left.rect.fLeft = 0;
2482                     boxes.insert(boxes.begin() + startBox + 1, left);
2483                 }
2484                 if (right.direction == TextDirection::kLtr &&
2485                     right.rect.fRight >= lineEnd &&
2486                     right.rect.fRight < fOwner->widthWithTrailingSpaces()) {
2487                     right.rect.fLeft = right.rect.fRight;
2488                     right.rect.fRight = fOwner->widthWithTrailingSpaces();
2489                     boxes.emplace_back(right);
2490                 }
2491             }
2492 
2493             return true;
2494         });
2495         return true;
2496     });
2497     if (fOwner->getApplyRoundingHack()) {
2498         for (auto& r : boxes) {
2499             r.rect.fLeft = littleRound(r.rect.fLeft);
2500             r.rect.fRight = littleRound(r.rect.fRight);
2501             r.rect.fTop = littleRound(r.rect.fTop);
2502             r.rect.fBottom = littleRound(r.rect.fBottom);
2503         }
2504     }
2505 }
2506 
2507 #ifdef OHOS_SUPPORT
2508 void TextLine::extendCoordinateRange(PositionWithAffinity& positionWithAffinity) {
2509     if (fEllipsis == nullptr) {
2510         return;
2511     }
2512     // Extending coordinate index if the ellipsis's run is selected.
2513     EllipsisModal ellipsisModal = fOwner->paragraphStyle().getEllipsisMod();
2514     if (ellipsisModal == EllipsisModal::TAIL) {
2515         if (static_cast<size_t>(positionWithAffinity.position) > fOwner->getEllipsisTextRange().start &&
2516             static_cast<size_t>(positionWithAffinity.position) <= fOwner->getEllipsisTextRange().end) {
2517             positionWithAffinity.position = static_cast<int32_t>(fOwner->getEllipsisTextRange().end);
2518         }
2519     } else if (ellipsisModal == EllipsisModal::HEAD) {
2520         if (static_cast<size_t>(positionWithAffinity.position) >= fOwner->getEllipsisTextRange().start &&
2521             static_cast<size_t>(positionWithAffinity.position) < fOwner->getEllipsisTextRange().end) {
2522             positionWithAffinity.position = static_cast<int32_t>(fOwner->getEllipsisTextRange().start);
2523         }
2524     }
2525 }
2526 #endif
2527 
2528 PositionWithAffinity TextLine::getGlyphPositionAtCoordinate(SkScalar dx) {
2529 
2530     if (SkScalarNearlyZero(this->width()) && SkScalarNearlyZero(this->spacesWidth())) {
2531         // TODO: this is one of the flutter changes that have to go away eventually
2532         //  Empty line is a special case in txtlib (but only when there are no spaces, too)
2533         auto utf16Index = fOwner->getUTF16Index(this->fTextExcludingSpaces.end);
2534         return { SkToS32(utf16Index) , kDownstream };
2535     }
2536 
2537     PositionWithAffinity result(0, Affinity::kDownstream);
2538 #ifdef OHOS_SUPPORT
2539     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
2540 #else
2541     this->iterateThroughVisualRuns(true,
2542 #endif
2543         [this, dx, &result]
2544         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
2545             bool keepLooking = true;
2546             if (fHyphenRun.get() == run) {
2547                 return keepLooking;
2548             }
2549             *runWidthInLine = this->iterateThroughSingleRunByStyles(
2550             TextAdjustment::GraphemeGluster, run, runOffsetInLine, textRange, StyleType::kNone,
2551             [this, run, dx, &result, &keepLooking]
2552             (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context0) {
2553 
2554                 SkScalar offsetX = this->offset().fX;
2555                 ClipContext context = context0;
2556 
2557                 // Correct the clip size because libtxt counts trailing spaces
2558                 if (run->leftToRight()) {
2559                     context.clip.fRight += context.fExcludedTrailingSpaces; // extending clip to the right
2560                 } else {
2561                     // Clip starts from 0; we cannot extend it to the left from that
2562                 }
2563                 // However, we need to offset the clip
2564                 context.clip.offset(offsetX, 0.0f);
2565 
2566                 // This patch will help us to avoid a floating point error
2567                 if (SkScalarNearlyEqual(context.clip.fRight, dx, 0.01f)) {
2568                     context.clip.fRight = dx;
2569                 }
2570 
2571                 if (dx <= context.clip.fLeft) {
2572                     // All the other runs are placed right of this one
2573                     auto utf16Index = fOwner->getUTF16Index(context.run->globalClusterIndex(context.pos));
2574                     if (run->leftToRight()) {
2575                         result = { SkToS32(utf16Index), kDownstream };
2576                         keepLooking = false;
2577                     } else {
2578 #ifdef OHOS_SUPPORT
2579                         result = { SkToS32(utf16Index + 1), kUpstream };
2580                         size_t glyphCnt = context.run->glyphs().size();
2581                         if ((glyphCnt != 0) && ((context.run->fUtf8Range.size() / glyphCnt) == EMOJI_WIDTH)) {
2582                             result = { SkToS32(utf16Index + 2), kUpstream };
2583                         }
2584 #else
2585                         result = { SkToS32(utf16Index + 1), kUpstream};
2586 #endif
2587                         // If we haven't reached the end of the run we need to keep looking
2588                         keepLooking = context.pos != 0;
2589                     }
2590                     // For RTL we go another way
2591                     return !run->leftToRight();
2592                 }
2593 
2594                 if (dx >= context.clip.fRight) {
2595                     // We have to keep looking ; just in case keep the last one as the closest
2596 #ifdef OHOS_SUPPORT
2597                     auto utf16Index = fOwner->getUTF16IndexWithOverflowCheck(
2598                         context.run->globalClusterIndex(context.pos + context.size));
2599 #else
2600                     auto utf16Index = fOwner->getUTF16Index(context.run->globalClusterIndex(context.pos + context.size));
2601 #endif
2602                     if (run->leftToRight()) {
2603                         result = {SkToS32(utf16Index), kUpstream};
2604                     } else {
2605                         result = {SkToS32(utf16Index), kDownstream};
2606                     }
2607                     // For RTL we go another way
2608                     return run->leftToRight();
2609                 }
2610 
2611                 // So we found the run that contains our coordinates
2612                 // Find the glyph position in the run that is the closest left of our point
2613                 // TODO: binary search
2614                 size_t found = context.pos;
2615                 for (size_t index = context.pos; index < context.pos + context.size; ++index) {
2616                     // TODO: this rounding is done to match Flutter tests. Must be removed..
2617                     auto end = context.run->positionX(index) + context.fTextShift + offsetX;
2618                     if (fOwner->getApplyRoundingHack()) {
2619                         end = littleRound(end);
2620                     }
2621                     if (end > dx) {
2622                         break;
2623                     } else if (end == dx && !context.run->leftToRight()) {
2624                         // When we move RTL variable end points to the beginning of the code point which is included
2625                         found = index;
2626                         break;
2627                     }
2628                     found = index;
2629                 }
2630 
2631                 SkScalar glyphemePosLeft = context.run->positionX(found) + context.fTextShift + offsetX;
2632                 SkScalar glyphemesWidth = context.run->positionX(found + 1) - context.run->positionX(found);
2633 
2634                 // Find the grapheme range that contains the point
2635                 auto clusterIndex8 = context.run->globalClusterIndex(found);
2636                 auto clusterEnd8 = context.run->globalClusterIndex(found + 1);
2637                 auto graphemes = fOwner->countSurroundingGraphemes({clusterIndex8, clusterEnd8});
2638 
2639                 SkScalar center = glyphemePosLeft + glyphemesWidth * fOwner->getTextSplitRatio();
2640                 if (graphemes.size() > 1) {
2641                     // Calculate the position proportionally based on grapheme count
2642                     SkScalar averageGraphemeWidth = glyphemesWidth / graphemes.size();
2643                     SkScalar delta = dx - glyphemePosLeft;
2644                     int graphemeIndex = SkScalarNearlyZero(averageGraphemeWidth)
2645                                          ? 0
2646                                          : SkScalarFloorToInt(delta / averageGraphemeWidth);
2647                     auto graphemeCenter = glyphemePosLeft + graphemeIndex * averageGraphemeWidth +
2648                                           averageGraphemeWidth * fOwner->getTextSplitRatio();
2649                     auto graphemeUtf8Index = graphemes[graphemeIndex];
2650                     if ((dx < graphemeCenter) == context.run->leftToRight()) {
2651                         size_t utf16Index = fOwner->getUTF16Index(graphemeUtf8Index);
2652                         result = { SkToS32(utf16Index), kDownstream };
2653                     } else {
2654 #ifdef OHOS_SUPPORT
2655                         const size_t currentIdx = static_cast<size_t>(graphemeIndex);
2656                         size_t nextGraphemeUtf8Index = (currentIdx + 1 < graphemes.size())
2657                             ? graphemes[currentIdx + 1] : clusterEnd8;
2658                         size_t utf16Index = fOwner->getUTF16IndexWithOverflowCheck(nextGraphemeUtf8Index);
2659 #else
2660                         size_t utf16Index = fOwner->getUTF16Index(graphemeUtf8Index + 1);
2661 #endif
2662                         result = { SkToS32(utf16Index), kUpstream };
2663                     }
2664                     // Keep UTF16 index as is
2665                 } else if ((dx < center) == context.run->leftToRight()) {
2666 #ifdef OHOS_SUPPORT
2667                     size_t utf16Index = fOwner->getUTF16IndexWithOverflowCheck(clusterIndex8);
2668 #else
2669                     size_t utf16Index = fOwner->getUTF16Index(clusterIndex8);
2670 #endif
2671                     result = { SkToS32(utf16Index), kDownstream };
2672                 } else {
2673 #ifdef OHOS_SUPPORT
2674                     size_t utf16Index = 0;
2675                     size_t glyphCnt = context.run->glyphs().size();
2676                     if ((glyphCnt != 0) && !context.run->leftToRight() && ((
2677                         context.run->fUtf8Range.size() / glyphCnt) == EMOJI_WIDTH)) {
2678                         utf16Index = fOwner->getUTF16Index(clusterIndex8) + 2;
2679                     } else if (!context.run->leftToRight()) {
2680                         utf16Index = fOwner->getUTF16Index(clusterIndex8) + 1;
2681                     } else {
2682                         utf16Index = fOwner->getUTF16IndexWithOverflowCheck(clusterEnd8);
2683                     }
2684 #else
2685                     size_t utf16Index = context.run->leftToRight()
2686                                                 ? fOwner->getUTF16Index(clusterEnd8)
2687                                                 : fOwner->getUTF16Index(clusterIndex8) + 1;
2688 #endif
2689                     result = { SkToS32(utf16Index), kUpstream };
2690                 }
2691 
2692                 return keepLooking = false;
2693 
2694             });
2695             return keepLooking;
2696         }
2697     );
2698 
2699 #ifdef OHOS_SUPPORT
2700     extendCoordinateRange(result);
2701 #endif
2702 
2703     return result;
2704 }
2705 
2706 void TextLine::getRectsForPlaceholders(std::vector<TextBox>& boxes) {
2707 #ifdef OHOS_SUPPORT
2708     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
2709 #else
2710     this->iterateThroughVisualRuns(true,
2711 #endif
2712         [&boxes, this](const Run* run, SkScalar runOffset, TextRange textRange,
2713                         SkScalar* width) {
2714                 auto context = this->measureTextInsideOneRun(
2715                         textRange, run, runOffset, 0, true, TextAdjustment::GraphemeGluster);
2716                 *width = context.clip.width();
2717 
2718             if (textRange.width() == 0) {
2719                 return true;
2720             }
2721             if (!run->isPlaceholder()) {
2722                 return true;
2723             }
2724 
2725             SkRect clip = context.clip;
2726             clip.offset(this->offset());
2727 
2728             if (fOwner->getApplyRoundingHack()) {
2729                 clip.fLeft = littleRound(clip.fLeft);
2730                 clip.fRight = littleRound(clip.fRight);
2731                 clip.fTop = littleRound(clip.fTop);
2732                 clip.fBottom = littleRound(clip.fBottom);
2733             }
2734             boxes.emplace_back(clip, run->getTextDirection());
2735             return true;
2736         });
2737 }
2738 
2739 size_t TextLine::getGlyphCount() const
2740 {
2741     size_t glyphCount = 0;
2742     for (auto& blob: fTextBlobCache) {
2743         glyphCount += blob.fVisitor_Size;
2744     }
2745     return glyphCount;
2746 }
2747 
2748 #ifdef OHOS_SUPPORT
2749 std::vector<std::unique_ptr<RunBase>> TextLine::getGlyphRuns() const
2750 {
2751     std::vector<std::unique_ptr<RunBase>> runBases;
2752     size_t num = 0;
2753     // Gets the offset position of the current line across the paragraph
2754     size_t pos = fClusterRange.start;
2755     size_t trailSpaces = 0;
2756     for (auto& blob: fTextBlobCache) {
2757         ++num;
2758         if (blob.fVisitor_Size == 0) {
2759             continue;
2760         }
2761         if (num == fTextBlobCache.size()) {
2762             // Counts how many tabs have been removed from the end of the current line
2763             trailSpaces = fGhostClusterRange.width() - fClusterRange.width();
2764         }
2765         std::unique_ptr<RunBaseImpl> runBaseImplPtr = std::make_unique<RunBaseImpl>(
2766             blob.fBlob, blob.fOffset, blob.fPaint, blob.fClippingNeeded, blob.fClipRect,
2767             blob.fVisitor_Run, blob.fVisitor_Pos, pos, trailSpaces, blob.fVisitor_Size);
2768 
2769         // Calculate the position of each blob, relative to the entire paragraph
2770         pos += blob.fVisitor_Size;
2771         runBases.emplace_back(std::move(runBaseImplPtr));
2772     }
2773     return runBases;
2774 }
2775 #else
2776 std::vector<std::unique_ptr<RunBase>> TextLine::getGlyphRuns() const
2777 {
2778     std::vector<std::unique_ptr<RunBase>> runBases;
2779     for (auto& blob: fTextBlobCache) {
2780         std::unique_ptr<RunBaseImpl> runBaseImplPtr = std::make_unique<RunBaseImpl>(
2781             blob.fBlob, blob.fOffset, blob.fPaint, blob.fClippingNeeded, blob.fClipRect,
2782             blob.fVisitor_Run, blob.fVisitor_Pos, blob.fVisitor_Size);
2783         runBases.emplace_back(std::move(runBaseImplPtr));
2784     }
2785     return runBases;
2786 }
2787 #endif
2788 
2789 #ifdef OHOS_SUPPORT
2790 int getEndWhitespaceCount(const ClusterRange& range, ParagraphImpl* owner)
2791 {
2792     if (owner == nullptr) {
2793         return 0;
2794     }
2795 
2796     int endWhitespaceCount = 0;
2797     for (auto clusterIndex = range.end - 1; clusterIndex >= range.start; clusterIndex--) {
2798         if (!owner->cluster(clusterIndex).isWhitespaceBreak()) {
2799             break;
2800         }
2801 
2802         endWhitespaceCount++;
2803         if (clusterIndex == range.start) {
2804             break;
2805         }
2806     }
2807 
2808     return endWhitespaceCount;
2809 }
2810 
2811 std::unique_ptr<TextLineBase> TextLine::createTruncatedLine(double width, EllipsisModal ellipsisMode,
2812     const std::string& ellipsisStr)
2813 {
2814     if (width > 0 && (ellipsisMode == EllipsisModal::HEAD || ellipsisMode == EllipsisModal::TAIL)) {
2815         TextLine textLine = CloneSelf();
2816         SkScalar widthVal = static_cast<SkScalar>(width);
2817         if (widthVal < widthWithEllipsisSpaces() && !ellipsisStr.empty()) {
2818             if (ellipsisMode == EllipsisModal::HEAD) {
2819                 textLine.fIsTextLineEllipsisHeadModal = true;
2820                 textLine.setTextBlobCachePopulated(false);
2821                 textLine.createHeadEllipsis(widthVal, SkString(ellipsisStr), true);
2822             } else if (ellipsisMode == EllipsisModal::TAIL) {
2823                 textLine.fIsTextLineEllipsisHeadModal = false;
2824                 textLine.setTextBlobCachePopulated(false);
2825                 int endWhitespaceCount = getEndWhitespaceCount(fGhostClusterRange, fOwner);
2826                 textLine.fGhostClusterRange.end -= static_cast<size_t>(endWhitespaceCount);
2827                 textLine.createTailEllipsis(widthVal, SkString(ellipsisStr), true, fOwner->getWordBreakType());
2828             }
2829         }
2830         return std::make_unique<TextLineBaseImpl>(std::make_unique<TextLine>(std::move(textLine)));
2831     }
2832 
2833     return nullptr;
2834 }
2835 
2836 double TextLine::getTypographicBounds(double* ascent, double* descent, double* leading) const
2837 {
2838     if (ascent == nullptr || descent == nullptr || leading == nullptr) {
2839         return 0.0;
2840     }
2841 
2842     *ascent = fMaxRunMetrics.ascent();
2843     *descent = fMaxRunMetrics.descent();
2844     *leading = fMaxRunMetrics.leading();
2845     return widthWithEllipsisSpaces();
2846 }
2847 
2848 size_t getPrevGlyphsIndex(const ClusterRange& range, ParagraphImpl* owner, RunIndex& prevRunIndex)
2849 {
2850     if (owner == nullptr) {
2851         return 0;
2852     }
2853 
2854     size_t glyphsIndex = 0;
2855     auto clusterIndex = range.start - 1;
2856     prevRunIndex = owner->cluster(clusterIndex).runIndex();
2857     if (prevRunIndex != owner->cluster(range.start).runIndex()) {
2858         // Belongs to a different run.
2859         return 0;
2860     }
2861 
2862     for (; clusterIndex >= 0; clusterIndex--) {
2863         RunIndex runIndex = owner->cluster(clusterIndex).runIndex();
2864         if (prevRunIndex != runIndex) {
2865             // Found a different run.
2866             break;
2867         }
2868 
2869         glyphsIndex++;
2870 
2871         if (clusterIndex == 0) {
2872             // All belong to the first run.
2873             break;
2874         }
2875     }
2876 
2877     return glyphsIndex;
2878 }
2879 
2880 #ifndef USE_SKIA_TXT
2881 std::vector<SkRect> getAllRectInfo(const ClusterRange& range, ParagraphImpl* owner)
2882 {
2883     std::vector<SkRect> rectVec;
2884 #else
2885 std::vector<RSRect> getAllRectInfo(const ClusterRange& range, ParagraphImpl* owner)
2886 {
2887     std::vector<RSRect> rectVec;
2888 #endif
2889     if (owner == nullptr) {
2890         return rectVec;
2891     }
2892 
2893     // If it is not the first line, you need to get the GlyphsIndex of the first character.
2894     size_t glyphsIndex  = 0;
2895     RunIndex prevRunIndex = 0;
2896     if (range.start > 0) {
2897         glyphsIndex = getPrevGlyphsIndex(range, owner, prevRunIndex);
2898     }
2899 
2900     for (auto clusterIndex = range.start; clusterIndex < range.end; clusterIndex++) {
2901         RunIndex runIndex = owner->cluster(clusterIndex).runIndex();
2902         if (prevRunIndex != runIndex) {
2903             glyphsIndex = 0;
2904         }
2905 
2906         auto run = owner->cluster(clusterIndex).runOrNull();
2907         if (run == nullptr) {
2908             break;
2909         }
2910 
2911         SkGlyphID glyphId = run->glyphs()[glyphsIndex];
2912 #ifndef USE_SKIA_TXT
2913         SkRect glyphBounds;
2914         run->font().getBounds(&glyphId, 1, &glyphBounds, nullptr);
2915 #else
2916         RSRect glyphBounds;
2917         run->font().GetWidths(&glyphId, 1, nullptr, &glyphBounds);
2918 #endif
2919         rectVec.push_back(glyphBounds);
2920         glyphsIndex++;
2921         prevRunIndex = runIndex;
2922     }
2923 
2924     return rectVec;
2925 }
2926 
2927 RSRect getClusterRangeBounds(const ClusterRange& range, ParagraphImpl* owner) {
2928     auto rectVec = getAllRectInfo(range, owner);
2929     RSRect finalRect{0.0, 0.0, 0.0, 0.0};
2930     for (const auto& rect : rectVec) {
2931         finalRect.Join(rect);
2932     }
2933     return finalRect;
2934 }
2935 
2936 bool TextLine::isLineHeightDominatedByRun(const Run& run) {
2937     return SkScalarNearlyEqual(run.ascent(), sizes().ascent()) &&
2938         SkScalarNearlyEqual(run.descent(), sizes().descent());
2939 }
2940 
2941 void TextLine::updateBlobShift(const Run& run, SkScalar& verticalShift) {
2942     Block& block = fOwner->getBlockByRun(run);
2943     if (nearlyZero(block.fStyle.getVerticalAlignShift())) {
2944         block.fStyle.setVerticalAlignShift(run.getVerticalAlignShift());
2945     }
2946 
2947     if (fOwner->getParagraphStyle().getVerticalAlignment() == TextVerticalAlign::BOTTOM) {
2948         verticalShift = std::min(block.fStyle.getVerticalAlignShift(), run.getVerticalAlignShift());
2949     } else {
2950         verticalShift = std::max(block.fStyle.getVerticalAlignShift(), run.getVerticalAlignShift());
2951     }
2952     block.fStyle.setVerticalAlignShift(verticalShift);
2953 }
2954 
2955 void TextLine::resetBlobShift(const Run& run) {
2956     Block& block = fOwner->getBlockByRun(run);
2957     block.fStyle.setVerticalAlignShift(0.0);
2958 }
2959 
2960 void TextLine::updateBlobAndRunShift(Run& run) {
2961     SkScalar verticalShift{0.0};
2962     updateBlobShift(run, verticalShift);
2963     Block& block = fOwner->getBlockByRun(run);
2964     TextRange range = block.fRange;
2965     // Update run's vertical shift by text style
2966     size_t textIndex{range.start};
2967     while (textIndex < range.end) {
2968         ClusterIndex clusterIndex = fOwner->clusterIndex(textIndex);
2969         if (clusterIndex < clusters().start || clusterIndex > run.clusterRange().start) {
2970             break;
2971         }
2972         Run& run = fOwner->runByCluster(clusterIndex);
2973         run.setVerticalAlignShift(verticalShift);
2974         textIndex = run.textRange().end;
2975     }
2976 
2977     if (textIndex == range.end) {
2978         return;
2979     }
2980     // Update textStyle's vertical shift base on run
2981     for (auto& block : fOwner->exportTextStyles()) {
2982         if (block.fRange.start < run.textRange().start || block.fRange.start >= run.textRange().end) {
2983             continue;
2984         }
2985         block.fStyle.setVerticalAlignShift(verticalShift);
2986     }
2987 }
2988 
2989 void TextLine::shiftPlaceholderByVerticalAlignMode(Run& run, TextVerticalAlign VerticalAlignment) {
2990     if (!run.isPlaceholder() || !fOwner->IsPlaceholderAlignedFollowParagraph(run.fPlaceholderIndex)) {
2991         return;
2992     }
2993 
2994     PlaceholderAlignment aligment{PlaceholderAlignment::kAboveBaseline};
2995     switch (VerticalAlignment) {
2996         case TextVerticalAlign::TOP:
2997             aligment = PlaceholderAlignment::kTop;
2998             break;
2999         case TextVerticalAlign::CENTER:
3000             aligment = PlaceholderAlignment::kMiddle;
3001             break;
3002         case TextVerticalAlign::BOTTOM:
3003             aligment = PlaceholderAlignment::kBottom;
3004             break;
3005         case TextVerticalAlign::BASELINE:
3006             aligment = PlaceholderAlignment::kAboveBaseline;
3007             break;
3008         default:
3009             break;
3010     }
3011     if (fOwner->setPlaceholderAlignment(run.fPlaceholderIndex, aligment)) {
3012         run.updateMetrics(&fSizes);
3013     }
3014 }
3015 
3016 void TextLine::shiftTextByVerticalAlignment(Run& run, TextVerticalAlign VerticalAlignment) {
3017     SkScalar shift = 0.0f;
3018     switch (VerticalAlignment) {
3019         case TextVerticalAlign::TOP:
3020             shift = sizes().ascent() - run.ascent();
3021             break;
3022         case TextVerticalAlign::CENTER:
3023             // Make the current run distance equal to the line's upper and lower boundaries
3024             shift = (sizes().ascent() + sizes().descent() - run.descent() - run.ascent()) / 2;
3025             break;
3026         case TextVerticalAlign::BOTTOM:
3027             shift = sizes().descent() - run.descent();
3028             if (shift < 0) {
3029                 shift = 0;
3030             }
3031             break;
3032         default:
3033             break;
3034     }
3035     run.setVerticalAlignShift(shift);
3036 }
3037 
3038 void TextLine::applyPlaceholderVerticalShift() {
3039     TextVerticalAlign VerticalAlignment = fOwner->getParagraphStyle().getVerticalAlignment();
3040 
3041     ClusterRange clustersRange = clusters();
3042     ClusterIndex curClusterIndex = clustersRange.start;
3043     // Reset textStyle vertical shift for current line's first run
3044     const Run& run = fOwner->runByCluster(curClusterIndex);
3045     resetBlobShift(run);
3046 
3047     while (curClusterIndex < clustersRange.end) {
3048         Run& run = fOwner->runByCluster(curClusterIndex);
3049         ClusterRange groupClusterRange = {std::max(curClusterIndex, run.clusterRange().start),
3050             std::min(clustersRange.end, run.clusterRange().end)};
3051 
3052         if (run.isPlaceholder()) {
3053             shiftPlaceholderByVerticalAlignMode(run, VerticalAlignment);
3054         }
3055         curClusterIndex = groupClusterRange.end;
3056     }
3057 }
3058 
3059 void TextLine::applyVerticalShift() {
3060     TextVerticalAlign verticalAlignment = fOwner->getParagraphStyle().getVerticalAlignment();
3061     if (verticalAlignment == TextVerticalAlign::BASELINE) {
3062         return;
3063     }
3064 
3065     ClusterRange clustersRange = clustersWithSpaces();
3066     ClusterIndex curClusterIndex = clustersRange.start;
3067     while (curClusterIndex < clustersRange.end) {
3068         Run& run = fOwner->runByCluster(curClusterIndex);
3069         if (run.isPlaceholder()) {
3070             shiftPlaceholderByVerticalAlignMode(run, verticalAlignment);
3071             curClusterIndex = run.clusterRange().end;
3072             continue;
3073         }
3074         shiftTextByVerticalAlignment(run, verticalAlignment);
3075         curClusterIndex = run.clusterRange().end;
3076     }
3077 
3078     if (ellipsis()) {
3079         shiftTextByVerticalAlignment(*ellipsis(), verticalAlignment);
3080     }
3081 }
3082 
3083 void TextLine::refresh() {
3084     // Refresh line runs order
3085     auto& start = fOwner->cluster(clustersWithSpaces().start);
3086     auto& end = fOwner->cluster(clustersWithSpaces().end - 1);
3087     size_t numRuns = end.runIndex() - start.runIndex() + 1;
3088     constexpr int kPreallocCount = 4;
3089     SkAutoSTArray<kPreallocCount, SkUnicode::BidiLevel> runLevels(numRuns);
3090     size_t runLevelsIndex = 0;
3091     std::vector<RunIndex> placeholdersInOriginalOrder;
3092     InternalLineMetrics maxRunMetrics = getMaxRunMetrics();
3093     for (auto runIndex = start.runIndex(); runIndex <= end.runIndex(); ++runIndex) {
3094         Run& run = fOwner->run(runIndex);
3095         runLevels[runLevelsIndex++] = run.fBidiLevel;
3096         maxRunMetrics.add(
3097             InternalLineMetrics(run.correctAscent(), run.correctDescent(), run.fFontMetrics.fLeading));
3098         if (run.isPlaceholder()) {
3099             placeholdersInOriginalOrder.push_back(runIndex);
3100         }
3101     }
3102     setMaxRunMetrics(maxRunMetrics);
3103     SkAutoSTArray<kPreallocCount, int32_t> logicalOrder(numRuns);
3104     fOwner->getUnicode()->reorderVisual(runLevels.data(), numRuns, logicalOrder.data());
3105     auto firstRunIndex = start.runIndex();
3106     auto placeholderIter = placeholdersInOriginalOrder.begin();
3107     SkSTArray<1, size_t, true> runsInVisualOrder{};
3108     for (auto index : logicalOrder) {
3109         auto runIndex = firstRunIndex + index;
3110         if (fOwner->run(runIndex).isPlaceholder()) {
3111             runsInVisualOrder.push_back(*placeholderIter++);
3112         } else {
3113             runsInVisualOrder.push_back(runIndex);
3114         }
3115     }
3116     setLineAllRuns(runsInVisualOrder);
3117 
3118     if (ellipsis()) {
3119         ClusterIndex clusterIndex = 0;
3120         if (fOwner->getParagraphStyle().getEllipsisMod() == EllipsisModal::HEAD) {
3121             clusterIndex = clusters().start;
3122         } else {
3123             TextIndex textIndex = ellipsis()->textRange().start == 0 ? 0 : ellipsis()->textRange().start - 1;
3124             if (textIndex > 0) {
3125                 textIndex--;
3126             }
3127             clusterIndex = fOwner->clusterIndex(textIndex);
3128         }
3129         size_t runIndex = fOwner->cluster(clusterIndex).runIndex();
3130         ellipsis()->fIndex = runIndex;
3131         setEllipsisRunIndex(runIndex);
3132     }
3133 }
3134 
3135 RSRect TextLine::getImageBounds() const
3136 {
3137     // Look for the first non-space character from the end and get its advance and index
3138     // to calculate the final image bounds.
3139     SkRect rect = {0.0, 0.0, 0.0, 0.0};
3140     int endWhitespaceCount = getEndWhitespaceCount(fGhostClusterRange, fOwner);
3141     size_t endWhitespaceCountVal = static_cast<size_t>(endWhitespaceCount);
3142     if (endWhitespaceCountVal == (fGhostClusterRange.end - fGhostClusterRange.start)) {
3143         // Full of Spaces.
3144         return {};
3145     }
3146     SkScalar endAdvance = usingAutoSpaceWidth(&fOwner->cluster(fGhostClusterRange.end - endWhitespaceCountVal - 1));
3147 
3148     // The first space width of the line needs to be added to the x value.
3149     SkScalar startWhitespaceAdvance = 0.0;
3150     size_t startWhitespaceCount = 0;
3151     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; clusterIndex++) {
3152         if (fOwner->cluster(clusterIndex).isWhitespaceBreak()) {
3153             startWhitespaceAdvance += fOwner->cluster(clusterIndex).width();
3154             startWhitespaceCount++;
3155         } else {
3156             break;
3157         }
3158     }
3159 
3160     // Gets rect information for all characters in line.
3161     auto rectVec = getAllRectInfo(fGhostClusterRange, fOwner);
3162     // Calculate the final y and height.
3163     auto joinRect = rectVec[startWhitespaceCount];
3164     for (size_t i = startWhitespaceCount + 1; i < rectVec.size() - endWhitespaceCountVal; ++i) {
3165         joinRect.Join(rectVec[i]);
3166     }
3167 
3168     SkScalar lineWidth = width();
3169     auto endRect = rectVec[rectVec.size() - endWhitespaceCountVal - 1];
3170 #ifndef USE_SKIA_TXT
3171     SkScalar x = rectVec[startWhitespaceCount].x() + startWhitespaceAdvance;
3172     SkScalar y = joinRect.bottom();
3173     SkScalar width = lineWidth - (endAdvance - endRect.x() - endRect.width()) - x;
3174     SkScalar height = joinRect.height();
3175 #else
3176     SkScalar x = rectVec[startWhitespaceCount].GetLeft() + startWhitespaceAdvance;
3177     SkScalar y = joinRect.GetBottom();
3178     SkScalar width = lineWidth - (endAdvance - endRect.GetLeft() - endRect.GetWidth()) - x;
3179     SkScalar height = joinRect.GetHeight();
3180 #endif
3181 
3182     rect.setXYWH(x, y, width, height);
3183     return {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom};
3184 }
3185 
3186 double TextLine::getTrailingSpaceWidth() const
3187 {
3188     return spacesWidth();
3189 }
3190 
3191 int32_t TextLine::getStringIndexForPosition(SkPoint point) const
3192 {
3193     int32_t index = fGhostClusterRange.start;
3194     double offset = point.x();
3195     if (offset >= widthWithEllipsisSpaces()) {
3196         index = fGhostClusterRange.end;
3197     } else if (offset > 0) {
3198         double curOffset = 0.0;
3199         for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
3200             double characterWidth = usingAutoSpaceWidth(&fOwner->cluster(clusterIndex));
3201             if (offset <= curOffset + characterWidth / 2) {
3202                 return index;
3203             }
3204             index++;
3205             curOffset += characterWidth;
3206         }
3207     }
3208 
3209     return index;
3210 }
3211 
3212 double TextLine::getOffsetForStringIndex(int32_t index) const
3213 {
3214     double offset = 0.0;
3215     if (index <= 0) {
3216         return offset;
3217     }
3218 
3219     size_t indexVal = static_cast<size_t>(index);
3220     if (indexVal >= fGhostClusterRange.end) {
3221         offset = widthWithEllipsisSpaces();
3222     } else if (indexVal > fGhostClusterRange.start) {
3223         size_t clusterIndex = fGhostClusterRange.start;
3224         while (clusterIndex < fGhostClusterRange.end) {
3225             offset += usingAutoSpaceWidth(&fOwner->cluster(clusterIndex));
3226             if (++clusterIndex == indexVal) {
3227                 break;
3228             }
3229         }
3230     }
3231 
3232     return offset;
3233 }
3234 
3235 std::map<int32_t, double> TextLine::getIndexAndOffsets(bool& isHardBreak) const
3236 {
3237     std::map<int32_t, double> offsetMap;
3238     double offset = 0.0;
3239     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
3240         auto& cluster = fOwner->cluster(clusterIndex);
3241         offset += usingAutoSpaceWidth(&cluster);
3242         isHardBreak = cluster.isHardBreak();
3243         if (!isHardBreak) {
3244             offsetMap[clusterIndex] = offset;
3245         }
3246     }
3247     return offsetMap;
3248 }
3249 
3250 double TextLine::getAlignmentOffset(double alignmentFactor, double alignmentWidth) const
3251 {
3252     double lineWidth = width();
3253     if (alignmentWidth <= lineWidth) {
3254         return 0.0;
3255     }
3256 
3257     double offset = 0.0;
3258     TextDirection textDirection = fOwner->paragraphStyle().getTextDirection();
3259     if (alignmentFactor <= 0) {
3260         // Flush left.
3261         if (textDirection == TextDirection::kRtl) {
3262             offset =  lineWidth - alignmentWidth;
3263         }
3264     } else if (alignmentFactor < 1) {
3265         // Align according to the alignmentFactor.
3266         if (textDirection == TextDirection::kLtr) {
3267             offset = (alignmentWidth - lineWidth) * alignmentFactor;
3268         } else {
3269             offset = (lineWidth - alignmentWidth) * (1 - alignmentFactor);
3270         }
3271     } else {
3272         // Flush right.
3273         if (textDirection == TextDirection::kLtr) {
3274             offset = alignmentWidth - lineWidth;
3275         }
3276     }
3277 
3278     return offset;
3279 }
3280 
3281 SkRect TextLine::computeShadowRect(SkScalar x, SkScalar y, const TextStyle& style, const ClipContext& context) const
3282 {
3283     SkScalar offsetX = x + this->fOffset.fX;
3284     SkScalar offsetY = y + this->fOffset.fY -
3285         (context.run ? context.run->fCompressionBaselineShift : 0);
3286     SkRect shadowRect = SkRect::MakeEmpty();
3287 
3288     for (const TextShadow& shadow : style.getShadows()) {
3289         if (!shadow.hasShadow()) {
3290             continue;
3291         }
3292 
3293         SkScalar blurSigma = SkDoubleToScalar(shadow.fBlurSigma);
3294         SkRect rect = context.clip
3295             .makeOffset(offsetX + shadow.fOffset.fX, offsetY + shadow.fOffset.fY)
3296             .makeOutset(blurSigma, blurSigma);
3297         shadowRect.join(rect);
3298     }
3299     return shadowRect;
3300 }
3301 
3302 SkRect TextLine::getAllShadowsRect(SkScalar x, SkScalar y) const
3303 {
3304     if (!fHasShadows) {
3305         return SkRect::MakeEmpty();
3306     }
3307     SkRect paintRegion = SkRect::MakeEmpty();
3308     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
3309         [&paintRegion, x, y, this]
3310         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
3311             if (runWidthInLine == nullptr) {
3312                 return true;
3313             }
3314             *runWidthInLine = this->iterateThroughSingleRunByStyles(
3315                 TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kShadow,
3316                 [&paintRegion, x, y, this]
3317                 (TextRange textRange, const TextStyle& style, const ClipContext& context) {
3318                     SkRect rect = computeShadowRect(x, y, style, context);
3319                     paintRegion.join(rect);
3320                 });
3321             return true;
3322         });
3323     return paintRegion;
3324 }
3325 
3326 SkRect TextLine::generatePaintRegion(SkScalar x, SkScalar y)
3327 {
3328     SkRect paintRegion = SkRect::MakeXYWH(x, y, 0, 0);
3329     fIsArcText = false;
3330 
3331     SkRect rect = getAllShadowsRect(x, y);
3332     paintRegion.join(rect);
3333 
3334     this->ensureTextBlobCachePopulated();
3335     for (auto& record : fTextBlobCache) {
3336         rect = GetTextBlobSkTightBound(record.fBlob, x + record.fOffset.fX, y + record.fOffset.fY, record.fClipRect);
3337         paintRegion.join(rect);
3338     }
3339 
3340     return paintRegion;
3341 }
3342 #endif
3343 
3344 TextLine TextLine::CloneSelf()
3345 {
3346     TextLine textLine;
3347     textLine.fBlockRange = this->fBlockRange;
3348     textLine.fTextExcludingSpaces = this->fTextExcludingSpaces;
3349     textLine.fText = this->fText;
3350     textLine.fTextIncludingNewlines = this->fTextIncludingNewlines;
3351     textLine.fClusterRange = this->fClusterRange;
3352 
3353     textLine.fGhostClusterRange = this->fGhostClusterRange;
3354     textLine.fRunsInVisualOrder = this->fRunsInVisualOrder;
3355     textLine.fAdvance = this->fAdvance;
3356     textLine.fOffset = this->fOffset;
3357     textLine.fShift = this->fShift;
3358 
3359     textLine.fWidthWithSpaces = this->fWidthWithSpaces;
3360     if (this->fEllipsis) {
3361         textLine.fEllipsis = std::make_unique<Run>(*this->fEllipsis);
3362     }
3363 
3364     textLine.fSizes = this->fSizes;
3365     textLine.fMaxRunMetrics = this->fMaxRunMetrics;
3366     textLine.fHasBackground = this->fHasBackground;
3367     textLine.fHasShadows = this->fHasShadows;
3368     textLine.fHasDecorations = this->fHasDecorations;
3369     textLine.fAscentStyle = this->fAscentStyle;
3370     textLine.fDescentStyle = this->fDescentStyle;
3371     textLine.fTextBlobCachePopulated = this->fTextBlobCachePopulated;
3372 #ifdef OHOS_SUPPORT
3373     textLine.fOwner = this->fOwner;
3374     textLine.fIsTextLineEllipsisHeadModal = this->fIsTextLineEllipsisHeadModal;
3375     textLine.fEllipsisString = this->fEllipsisString;
3376     textLine.fBreakWithHyphen = this->fBreakWithHyphen;
3377     if (this->fHyphenRun) {
3378         textLine.fHyphenRun = std::make_unique<Run>(*this->fHyphenRun);
3379     }
3380     textLine.fHyphenIndex = this->fHyphenIndex;
3381     textLine.fRoundRectAttrs = this->fRoundRectAttrs;
3382 #endif
3383     textLine.fTextBlobCache = this->fTextBlobCache;
3384     textLine.fTextRangeReplacedByEllipsis = this->fTextRangeReplacedByEllipsis;
3385     textLine.fEllipsisIndex = this->fEllipsisIndex;
3386     textLine.fLastClipRunLtr = this->fLastClipRunLtr;
3387     return textLine;
3388 }
3389 
3390 #ifdef OHOS_SUPPORT
3391 void TextLine::setBreakWithHyphen(bool breakWithHyphen)
3392 {
3393     fBreakWithHyphen = breakWithHyphen;
3394     if (!breakWithHyphen) {
3395         if (fHyphenRun != nullptr) {
3396             fWidthWithSpaces -= fHyphenRun->fAdvance.fX;
3397         }
3398         fHyphenRun.reset();
3399         fHyphenIndex = EMPTY_INDEX;
3400     } else {
3401         auto endIx = fClusterRange.end - 1;
3402         // if we don't have hyphen run, shape it
3403         auto& cluster = fOwner->cluster(endIx);
3404         SkString dash("-");
3405         if (fHyphenRun == nullptr) {
3406             fHyphenRun = shapeString(dash, &cluster);
3407             fHyphenRun->setOwner(fOwner);
3408         }
3409 
3410         fHyphenRun->fTextRange = TextRange(cluster.textRange().end, cluster.textRange().end + 1);
3411         fHyphenRun->fClusterStart = cluster.textRange().end;
3412 
3413         fAdvance.fX += fHyphenRun->fAdvance.fX;
3414         fWidthWithSpaces = fAdvance.fX;
3415         fGhostClusterRange.end = fClusterRange.end;
3416         fHyphenIndex = cluster.runIndex();
3417         fText.end = cluster.textRange().end;
3418         fTextIncludingNewlines.end = cluster.textRange().end;
3419         fTextExcludingSpaces.end = cluster.textRange().end;
3420     }
3421 }
3422 
3423 bool TextLine::getBreakWithHyphen() const
3424 {
3425     return fBreakWithHyphen;
3426 }
3427 
3428 void TextLine::updateTextLinePaintAttributes() {
3429     fHasBackground = false;
3430     fHasDecorations = false;
3431     fHasShadows = false;
3432     for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
3433         auto textStyleBlock = fOwner->styles().begin() + index;
3434         if (textStyleBlock->fStyle.hasBackground()) {
3435             fHasBackground = true;
3436         }
3437         if (textStyleBlock->fStyle.getDecorationType() != TextDecoration::kNoDecoration &&
3438             textStyleBlock->fStyle.getDecorationThicknessMultiplier() > 0) {
3439             fHasDecorations = true;
3440         }
3441         if (textStyleBlock->fStyle.getShadowNumber() > 0) {
3442             fHasShadows = true;
3443         }
3444     }
3445 }
3446 #endif
3447 
3448 }  // namespace textlayout
3449 }  // namespace skia
3450