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 #include "modules/skshaper/include/SkShaper.h"
25 
26 #include <algorithm>
27 #include <iterator>
28 #include <limits>
29 #include <map>
30 #include <memory>
31 #include <tuple>
32 #include <type_traits>
33 #include <utility>
34 
35 #ifdef OHOS_SUPPORT
36 #include "log.h"
37 #include "modules/skparagraph/src/RunBaseImpl.h"
38 #include "modules/skparagraph/src/TextLineBaseImpl.h"
39 #include "TextParameter.h"
40 #endif
41 
42 namespace skia {
43 namespace textlayout {
44 #define MAX_INT_VALUE 0x7FFFFFFF
45 #define EMOJI_UNICODE_START 0x1F300
46 #define EMOJI_UNICODE_END 0x1F9EF
47 #define EMOJI_WIDTH 4
48 
49 namespace {
50 
51 // TODO: deal with all the intersection functionality
intersected(const TextRange& a, const TextRange& b)52 TextRange intersected(const TextRange& a, const TextRange& b) {
53     if (a.start == b.start && a.end == b.end) return a;
54     auto begin = std::max(a.start, b.start);
55     auto end = std::min(a.end, b.end);
56     return end >= begin ? TextRange(begin, end) : EMPTY_TEXT;
57 }
58 
littleRound(SkScalar a)59 SkScalar littleRound(SkScalar a) {
60     // This rounding is done to match Flutter tests. Must be removed..
61   return SkScalarRoundToScalar(a * 100.0)/100.0;
62 }
63 
operator *(const TextRange& a, const TextRange& b)64 TextRange operator*(const TextRange& a, const TextRange& b) {
65     if (a.start == b.start && a.end == b.end) return a;
66     auto begin = std::max(a.start, b.start);
67     auto end = std::min(a.end, b.end);
68     return end > begin ? TextRange(begin, end) : EMPTY_TEXT;
69 }
70 
compareRound(SkScalar a, SkScalar b, bool applyRoundingHack)71 int compareRound(SkScalar a, SkScalar b, bool applyRoundingHack) {
72     // There is a rounding error that gets bigger when maxWidth gets bigger
73     // VERY long zalgo text (> 100000) on a VERY long line (> 10000)
74     // Canvas scaling affects it
75     // Letter spacing affects it
76     // It has to be relative to be useful
77     auto base = std::max(SkScalarAbs(a), SkScalarAbs(b));
78     auto diff = SkScalarAbs(a - b);
79     if (nearlyZero(base) || diff / base < 0.001f) {
80         return 0;
81     }
82 
83     auto ra = a;
84     auto rb = b;
85 
86     if (applyRoundingHack) {
87         ra = littleRound(a);
88         rb = littleRound(b);
89     }
90     if (ra < rb) {
91         return -1;
92     } else {
93         return 1;
94     }
95 }
96 
97 #ifdef USE_SKIA_TXT
IsRSFontEquals(const RSFont& font0, const RSFont& font1)98 bool IsRSFontEquals(const RSFont& font0, const RSFont& font1) {
99     auto f0 = const_cast<RSFont&>(font0);
100     auto f1 = const_cast<RSFont&>(font1);
101     return f0.GetTypeface().get() == f1.GetTypeface().get() &&
102         f0.GetSize() == f1.GetSize() &&
103         f0.GetScaleX() == f1.GetScaleX() &&
104         f0.GetSkewX() == f1.GetSkewX() &&
105         f0.GetEdging() == f1.GetEdging() &&
106         f0.GetHinting() == f1.GetHinting();
107 }
108 #endif
109 
110 }  // namespace
111 
TextLine(ParagraphImpl* owner, SkVector offset, SkVector advance, BlockRange blocks, TextRange textExcludingSpaces, TextRange text, TextRange textIncludingNewlines, ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar widthWithSpaces, InternalLineMetrics sizes)112 TextLine::TextLine(ParagraphImpl* owner,
113                    SkVector offset,
114                    SkVector advance,
115                    BlockRange blocks,
116                    TextRange textExcludingSpaces,
117                    TextRange text,
118                    TextRange textIncludingNewlines,
119                    ClusterRange clusters,
120                    ClusterRange clustersWithGhosts,
121                    SkScalar widthWithSpaces,
122                    InternalLineMetrics sizes)
123         : fOwner(owner)
124         , fBlockRange(blocks)
125         , fTextExcludingSpaces(textExcludingSpaces)
126         , fText(text)
127         , fTextIncludingNewlines(textIncludingNewlines)
128         , fClusterRange(clusters)
129         , fGhostClusterRange(clustersWithGhosts)
130         , fRunsInVisualOrder()
131         , fAdvance(advance)
132         , fOffset(offset)
133         , fShift(0.0)
134         , fWidthWithSpaces(widthWithSpaces)
135         , fEllipsis(nullptr)
136         , fSizes(sizes)
137         , fHasBackground(false)
138         , fHasShadows(false)
139         , fHasDecorations(false)
140         , fIsArcText(false)
141         , fArcTextState(false)
142         , fAscentStyle(LineMetricStyle::CSS)
143         , fDescentStyle(LineMetricStyle::CSS)
144         , fTextBlobCachePopulated(false) {
145     // Reorder visual runs
146     auto& start = owner->cluster(fGhostClusterRange.start);
147     auto& end = owner->cluster(fGhostClusterRange.end - 1);
148     size_t numRuns = end.runIndex() - start.runIndex() + 1;
149 
150     for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
151         auto b = fOwner->styles().begin() + index;
152         if (b->fStyle.hasBackground()) {
153             fHasBackground = true;
154         }
155 
156 #ifdef OHOS_SUPPORT
157         if (b->fStyle.getDecorationType() != TextDecoration::kNoDecoration &&
158             b->fStyle.getDecorationThicknessMultiplier() > 0) {
159 #else
160         if (b->fStyle.getDecorationType() != TextDecoration::kNoDecoration) {
161 #endif
162             fHasDecorations = true;
163         }
164         if (b->fStyle.getShadowNumber() > 0) {
165             fHasShadows = true;
166         }
167     }
168 
169     // Get the logical order
170 
171     // This is just chosen to catch the common/fast cases. Feel free to tweak.
172     constexpr int kPreallocCount = 4;
173     SkAutoSTArray<kPreallocCount, SkUnicode::BidiLevel> runLevels(numRuns);
174     std::vector<RunIndex> placeholdersInOriginalOrder;
175     size_t runLevelsIndex = 0;
176     // Placeholders must be laid out using the original order in which they were added
177     // in the input. The API does not provide a way to indicate that a placeholder
178     // position was moved due to bidi reordering.
179     for (auto runIndex = start.runIndex(); runIndex <= end.runIndex(); ++runIndex) {
180         auto& run = fOwner->run(runIndex);
181         runLevels[runLevelsIndex++] = run.fBidiLevel;
182         fMaxRunMetrics.add(
183             InternalLineMetrics(run.correctAscent(), run.correctDescent(), run.fFontMetrics.fLeading));
184         if (run.isPlaceholder()) {
185             placeholdersInOriginalOrder.push_back(runIndex);
186         }
187     }
188     SkASSERT(runLevelsIndex == numRuns);
189 
190     SkAutoSTArray<kPreallocCount, int32_t> logicalOrder(numRuns);
191 
192     // TODO: hide all these logic in SkUnicode?
193     fOwner->getUnicode()->reorderVisual(runLevels.data(), numRuns, logicalOrder.data());
194     auto firstRunIndex = start.runIndex();
195     auto placeholderIter = placeholdersInOriginalOrder.begin();
196     for (auto index : logicalOrder) {
197         auto runIndex = firstRunIndex + index;
198         if (fOwner->run(runIndex).isPlaceholder()) {
199             fRunsInVisualOrder.push_back(*placeholderIter++);
200         } else {
201             fRunsInVisualOrder.push_back(runIndex);
202         }
203     }
204 
205     fTextRangeReplacedByEllipsis = EMPTY_RANGE;
206     fEllipsisIndex = EMPTY_INDEX;
207     fLastClipRunLtr = false;
208 }
209 
210 void TextLine::paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset) {
211     prepareRoundRect();
212     fIsArcText = true;
213     if (pathParameters.hOffset != hOffset || pathParameters.vOffset != vOffset) {
214         fTextBlobCachePopulated = false;
215     }
216     pathParameters.recordPath = path;
217     pathParameters.hOffset = hOffset;
218     pathParameters.vOffset = vOffset;
219     this->ensureTextBlobCachePopulated();
220     for (auto& record : fTextBlobCache) {
221         record.paint(painter);
222     }
223 }
224 
225 void TextLine::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
226     prepareRoundRect();
227     fIsArcText = false;
228 #ifdef OHOS_SUPPORT
229     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
230 #else
231     this->iterateThroughVisualRuns(false,
232 #endif
233         [painter, x, y, this]
234         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
235             *runWidthInLine = this->iterateThroughSingleRunByStyles(
236             TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kBackground,
237             [painter, x, y, run, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
238                 if (fHasBackground) {
239                     this->paintBackground(painter, x, y, textRange, style, context);
240                 }
241                 paintRoundRect(painter, x, y, run);
242             });
243         return true;
244         });
245 
246     if (fHasShadows) {
247 #ifdef OHOS_SUPPORT
248         this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
249 #else
250         this->iterateThroughVisualRuns(false,
251 #endif
252             [painter, x, y, this]
253             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
254             *runWidthInLine = this->iterateThroughSingleRunByStyles(
255                 TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kShadow,
256                 [painter, x, y, this]
257                 (TextRange textRange, const TextStyle& style, const ClipContext& context) {
258                     this->paintShadow(painter, x, y, textRange, style, context);
259                 });
260             return true;
261             });
262     }
263 
264     this->ensureTextBlobCachePopulated();
265 
266     for (auto& record : fTextBlobCache) {
267         record.paint(painter, x, y);
268     }
269 
270     if (fHasDecorations) {
271         this->fDecorationContext = {0.0f, 0.0f, 0.0f};
272 #ifdef OHOS_SUPPORT
273         this->iterateThroughVisualRuns(EllipsisReadStrategy::DEFAULT, true,
274 #else
275         this->iterateThroughVisualRuns(false,
276 #endif
277             [painter, x, y, this]
278             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
279                 *runWidthInLine = this->iterateThroughSingleRunByStyles(
280                     TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange,
281                     StyleType::kDecorations, [painter, x, y, this]
282                     (TextRange textRange, const TextStyle& style, const ClipContext& context) {
283                     if (style.getDecoration().fType == TextDecoration::kUnderline) {
284                         SkScalar tmpThick = this->calculateThickness(style, context);
285                         fDecorationContext.thickness = fDecorationContext.thickness > tmpThick ?
286                             fDecorationContext.thickness : tmpThick;
287                     }
288                 });
289                 return true;
290         });
291 #ifdef OHOS_SUPPORT
292         this->iterateThroughVisualRuns(EllipsisReadStrategy::DEFAULT, true,
293 #else
294         this->iterateThroughVisualRuns(false,
295 #endif
296             [painter, x, y, this]
297             (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
298                 *runWidthInLine = this->iterateThroughSingleRunByStyles(
299                 TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kDecorations,
300                 [painter, x, y, this]
301                 (TextRange textRange, const TextStyle& style, const ClipContext& context) {
302                     // 12% of row height.
303                     fDecorationContext.underlinePosition = (fSizes.height() * 0.12 + this->baseline());
304                     fDecorationContext.textBlobTop = fSizes.height() * 0.12;
305                     this->paintDecorations(painter, x, y, textRange, style, context);
306                 });
307                 return true;
308         });
309     }
310 }
311 
312 bool TextLine::hasBackgroundRect(const RoundRectAttr& attr) {
313     return attr.roundRectStyle.color != 0 && attr.rect.width() > 0;
314 }
315 
316 void TextLine::computeRoundRect(int& index, int& preIndex, std::vector<Run*>& groupRuns, Run* run) {
317     int runCount = roundRectAttrs.size();
318     if (index >= runCount) {
319         return;
320     }
321 
322     bool leftRound = false;
323     bool rightRound = false;
324     if (hasBackgroundRect(roundRectAttrs[index])) {
325         int styleId = roundRectAttrs[index].styleId;
326         // index - 1 is previous index, -1 is the invalid styleId
327         int preStyleId = index == 0 ? -1 : roundRectAttrs[index - 1].styleId;
328         // runCount - 1 is the last run index, index + 1 is next run index, -1 is the invalid styleId
329         int nextStyleId = index == runCount - 1 ? -1 : roundRectAttrs[index + 1].styleId;
330         // index - preIndex > 1 means the left run has no background rect
331         leftRound = (preIndex < 0 || index - preIndex > 1 || preStyleId != styleId);
332         // runCount - 1 is the last run index
333         rightRound = (index == runCount - 1 || !hasBackgroundRect(roundRectAttrs[index + 1]) ||
334             nextStyleId != styleId);
335         preIndex = index;
336         groupRuns.push_back(run);
337     } else if (!groupRuns.empty()) {
338         groupRuns.erase(groupRuns.begin(), groupRuns.end());
339     }
340     if (leftRound && rightRound) {
341         run->setRoundRectType(RoundRectType::ALL);
342     } else if (leftRound) {
343         run->setRoundRectType(RoundRectType::LEFT_ONLY);
344     } else if (rightRound) {
345         run->setRoundRectType(RoundRectType::RIGHT_ONLY);
346     } else {
347         run->setRoundRectType(RoundRectType::NONE);
348     }
349 
350     if (rightRound && !groupRuns.empty()) {
351         double maxRoundRectRadius = MAX_INT_VALUE;
352         double minTop = MAX_INT_VALUE;
353         double maxBottom = 0;
354         for (auto &gRun : groupRuns) {
355             RoundRectAttr& attr = roundRectAttrs[gRun->getIndexInLine()];
356             maxRoundRectRadius = std::fmin(std::fmin(attr.rect.width(), attr.rect.height()), maxRoundRectRadius);
357             minTop = std::fmin(minTop, attr.rect.top());
358             maxBottom = std::fmax(maxBottom, attr.rect.bottom());
359         }
360         for (auto &gRun : groupRuns) {
361             gRun->setMaxRoundRectRadius(maxRoundRectRadius);
362             gRun->setTopInGroup(minTop - gRun->offset().y());
363             gRun->setBottomInGroup(maxBottom - gRun->offset().y());
364         }
365         groupRuns.erase(groupRuns.begin(), groupRuns.end());
366     }
367     index++;
368 }
369 
370 void TextLine::prepareRoundRect() {
371     roundRectAttrs.clear();
372 #ifdef OHOS_SUPPORT
373         this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
374 #else
375         this->iterateThroughVisualRuns(true,
376 #endif
377         [this](const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
378             *runWidthInLine = this->iterateThroughSingleRunByStyles(
379             TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kBackground,
380             [run, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
381                 roundRectAttrs.push_back({style.getStyleId(), style.getBackgroundRect(), context.clip});
382             });
383             return true;
384         });
385 
386     std::vector<Run*> groupRuns;
387     int index = 0;
388     int preIndex = -1;
389     for (auto& runIndex : fRunsInVisualOrder) {
390         auto run = &this->fOwner->run(runIndex);
391         run->setIndexInLine(static_cast<size_t>(index));
392         computeRoundRect(index, preIndex, groupRuns, run);
393     }
394 }
395 
396 void TextLine::ensureTextBlobCachePopulated() {
397     if (fTextBlobCachePopulated && fArcTextState == fIsArcText) {
398         return;
399     }
400     fTextBlobCache.clear();
401     if (fBlockRange.width() == 1 &&
402         fRunsInVisualOrder.size() == 1 &&
403         fEllipsis == nullptr &&
404         fOwner->run(fRunsInVisualOrder[0]).placeholderStyle() == nullptr) {
405         if (fClusterRange.width() == 0) {
406             return;
407         }
408         // Most common and most simple case
409         const auto& style = fOwner->block(fBlockRange.start).fStyle;
410         const auto& run = fOwner->run(fRunsInVisualOrder[0]);
411         auto clip = SkRect::MakeXYWH(0.0f, this->sizes().runTop(&run, this->fAscentStyle),
412                                      fAdvance.fX,
413                                      run.calculateHeight(this->fAscentStyle, this->fDescentStyle));
414 
415         auto& start = fOwner->cluster(fClusterRange.start);
416         auto& end = fOwner->cluster(fClusterRange.end - 1);
417         SkASSERT(start.runIndex() == end.runIndex());
418         GlyphRange glyphs;
419         if (run.leftToRight()) {
420             glyphs = GlyphRange(start.startPos(),
421                                 end.isHardBreak() ? end.startPos() : end.endPos());
422         } else {
423             glyphs = GlyphRange(end.startPos(),
424                                 start.isHardBreak() ? start.startPos() : start.endPos());
425         }
426         ClipContext context = {/*run=*/&run,
427                                /*pos=*/glyphs.start,
428                                /*size=*/glyphs.width(),
429                                /*fTextShift=*/-run.positionX(glyphs.start), // starting position
430                                /*clip=*/clip,                               // entire line
431                                /*fExcludedTrailingSpaces=*/0.0f,            // no need for that
432                                /*clippingNeeded=*/false};                   // no need for that
433         this->buildTextBlob(fTextExcludingSpaces, style, context);
434     } else {
435 #ifdef OHOS_SUPPORT
436         this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_ELLIPSIS_WORD, false,
437 #else
438         this->iterateThroughVisualRuns(false,
439 #endif
440            [this](const Run* run,
441                   SkScalar runOffsetInLine,
442                   TextRange textRange,
443                   SkScalar* runWidthInLine) {
444                if (run->placeholderStyle() != nullptr) {
445                    *runWidthInLine = run->advance().fX;
446                    return true;
447                }
448                *runWidthInLine = this->iterateThroughSingleRunByStyles(
449                    TextAdjustment::GlyphCluster,
450                    run,
451                    runOffsetInLine,
452                    textRange,
453                    StyleType::kForeground,
454                    [this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
455                        this->buildTextBlob(textRange, style, context);
456                    });
457                return true;
458            });
459     }
460     fTextBlobCachePopulated = true;
461     fArcTextState = fIsArcText;
462     pathParameters.recordPath = nullptr;
463 }
464 
465 void TextLine::format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal) {
466     SkScalar delta = maxWidth - this->widthWithEllipsisSpaces();
467     if (delta <= 0) {
468         return;
469     }
470 
471     // We do nothing for left align
472     if (align == TextAlign::kJustify) {
473         if (!this->endsWithHardLineBreak()) {
474             this->justify(maxWidth);
475         } else if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
476             // Justify -> Right align
477             fShift = delta;
478         }
479     } else if (align == TextAlign::kRight) {
480         fShift = delta;
481     } else if (align == TextAlign::kCenter) {
482         fShift = delta / 2;
483     }
484 }
485 
486 SkScalar TextLine::calculateSpacing(const Cluster prevCluster, const Cluster curCluster)
487 {
488     if (prevCluster.isWhitespaceBreak() || curCluster.isWhitespaceBreak()) {
489         return 0;
490     }
491     if (prevCluster.isHardBreak() || curCluster.isHardBreak()) {
492         return 0;
493     }
494     if (prevCluster.isCopyright() || curCluster.isCopyright()) {
495         return prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO;
496     }
497     if ((curCluster.isCJK() && prevCluster.isWestern()) || (curCluster.isWestern() && prevCluster.isCJK())) {
498         return prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO;
499     }
500     return 0;
501 }
502 
503 #ifdef OHOS_SUPPORT
504 SkScalar TextLine::autoSpacing() {
505     if (!TextParameter::GetAutoSpacingEnable()) {
506         return 0;
507     }
508     SkScalar spacing = 0.0;
509     auto prevCluster = fOwner->cluster(fClusterRange.start);
510     for (auto clusterIndex = fClusterRange.start + 1; clusterIndex < fClusterRange.end; ++clusterIndex) {
511         auto prevSpacing = spacing;
512         auto& cluster = fOwner->cluster(clusterIndex);
513         spacing += calculateSpacing(prevCluster, cluster);
514         spacingCluster(&cluster, spacing, prevSpacing);
515         prevCluster = cluster;
516     }
517     this->fWidthWithSpaces += spacing;
518     this->fAdvance.fX += spacing;
519     return spacing;
520 }
521 #endif
522 
523 void TextLine::scanStyles(StyleType styleType, const RunStyleVisitor& visitor) {
524     if (this->empty()) {
525         return;
526     }
527 
528 #ifdef OHOS_SUPPORT
529     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
530 #else
531     this->iterateThroughVisualRuns(false,
532 #endif
533             [this, visitor, styleType](
534                     const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width) {
535                 *width = this->iterateThroughSingleRunByStyles(
536                         TextAdjustment::GlyphCluster,
537                         run,
538                         runOffset,
539                         textRange,
540                         styleType,
541                         [visitor](TextRange textRange,
542                                   const TextStyle& style,
543                                   const ClipContext& context) {
544                             visitor(textRange, style, context);
545                         });
546                 return true;
547             });
548 }
549 
550 SkRect TextLine::extendHeight(const ClipContext& context) const {
551     SkRect result = context.clip;
552     result.fBottom += std::max(this->fMaxRunMetrics.height() - this->height(), 0.0f);
553     return result;
554 }
555 
556 void TextLine::buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context) {
557     if (context.run->placeholderStyle() != nullptr) {
558         return;
559     }
560 
561     fTextBlobCache.emplace_back();
562     TextBlobRecord& record = fTextBlobCache.back();
563 
564     if (style.hasForeground()) {
565         record.fPaint = style.getForegroundPaintOrID();
566     } else {
567         std::get<SkPaint>(record.fPaint).setColor(style.getColor());
568     }
569     record.fVisitor_Run = context.run;
570     record.fVisitor_Pos = context.pos;
571     record.fVisitor_Size = context.size;
572 
573     // TODO: This is the change for flutter, must be removed later
574 #ifndef USE_SKIA_TXT
575     SkTextBlobBuilder builder;
576 #else
577     RSTextBlobBuilder builder;
578 #endif
579     if (pathParameters.recordPath) {
580         context.run->copyTo(builder,
581                             pathParameters.recordPath,
582                             pathParameters.hOffset,
583                             pathParameters.vOffset,
584                             context.fTextShift,
585                             SkToU32(context.pos),
586                             context.size);
587     } else {
588         context.run->copyTo(builder, SkToU32(context.pos), context.size);
589     }
590 #ifdef OHOS_SUPPORT
591     // when letterspacing < 0, it causes the font is cliped. so the record fClippingNeeded is set false
592 #else
593     record.fClippingNeeded = context.clippingNeeded;
594 #endif
595     if (context.clippingNeeded) {
596         record.fClipRect = extendHeight(context).makeOffset(this->offset());
597     } else {
598         record.fClipRect = context.clip.makeOffset(this->offset());
599     }
600 
601     SkASSERT(nearlyEqual(context.run->baselineShift(), style.getBaselineShift()));
602     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() +  0.5);
603 #ifndef USE_SKIA_TXT
604     record.fBlob = builder.make();
605     if (record.fBlob != nullptr) {
606         record.fBounds.joinPossiblyEmptyRect(record.fBlob->bounds());
607     }
608 #else
609     record.fBlob = builder.Make();
610     if (record.fBlob != nullptr) {
611         auto bounds = record.fBlob->Bounds();
612         if (bounds) {
613             record.fBounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(
614                 bounds->left_, bounds->top_, bounds->right_, bounds->bottom_
615             ));
616         }
617     }
618 #endif
619 
620     record.fOffset = SkPoint::Make(this->offset().fX + context.fTextShift,
621 #ifdef OHOS_SUPPORT
622         this->offset().fY + correctedBaseline - (context.run ? context.run->fCompressionBaselineShift : 0));
623 #else
624                                    this->offset().fY + correctedBaseline);
625 #endif
626 #ifdef OHOS_SUPPORT
627 #ifndef USE_SKIA_TXT
628     SkFont font;
629 #else
630     RSFont font;
631 #endif
632     if (record.fBlob != nullptr && record.fVisitor_Run != nullptr) {
633         font = record.fVisitor_Run->font();
634         if (font.GetTypeface() != nullptr &&
635             (font.GetTypeface()->GetFamilyName().find("Emoji") != std::string::npos ||
636             font.GetTypeface()->GetFamilyName().find("emoji") != std::string::npos)) {
637                 record.fBlob->SetEmoji(true);
638         }
639     }
640 #endif
641 }
642 
643 void TextLine::TextBlobRecord::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
644     if (fClippingNeeded) {
645         painter->save();
646         painter->clipRect(fClipRect.makeOffset(x, y));
647     }
648     painter->drawTextBlob(fBlob, x + fOffset.x(), y + fOffset.y(), fPaint);
649     if (fClippingNeeded) {
650         painter->restore();
651     }
652 }
653 
654 void TextLine::TextBlobRecord::paint(ParagraphPainter* painter) {
655     if (fClippingNeeded) {
656         painter->save();
657     }
658     painter->drawTextBlob(fBlob, 0, 0, fPaint);
659     if (fClippingNeeded) {
660         painter->restore();
661     }
662 }
663 
664 void TextLine::paintBackground(ParagraphPainter* painter,
665                                SkScalar x,
666                                SkScalar y,
667                                TextRange textRange,
668                                const TextStyle& style,
669                                const ClipContext& context) const {
670     if (style.hasBackground()) {
671         painter->drawRect(context.clip.makeOffset(this->offset() + SkPoint::Make(x, y)),
672                           style.getBackgroundPaintOrID());
673     }
674 }
675 
676 void TextLine::paintRoundRect(ParagraphPainter* painter, SkScalar x, SkScalar y, const Run* run) const {
677     size_t index = run->getIndexInLine();
678     if (index >= roundRectAttrs.size()) {
679         return;
680     }
681 
682     const RoundRectAttr& attr = roundRectAttrs[index];
683     if (attr.roundRectStyle.color == 0) {
684         return;
685     }
686 
687     SkScalar ltRadius = 0.0f;
688     SkScalar rtRadius = 0.0f;
689     SkScalar rbRadius = 0.0f;
690     SkScalar lbRadius = 0.0f;
691     RoundRectType rType = run->getRoundRectType();
692     if (rType == RoundRectType::ALL || rType == RoundRectType::LEFT_ONLY) {
693         ltRadius = std::fmin(attr.roundRectStyle.leftTopRadius, run->getMaxRoundRectRadius());
694         lbRadius = std::fmin(attr.roundRectStyle.leftBottomRadius, run->getMaxRoundRectRadius());
695     }
696     if (rType == RoundRectType::ALL || rType == RoundRectType::RIGHT_ONLY) {
697         rtRadius = std::fmin(attr.roundRectStyle.rightTopRadius, run->getMaxRoundRectRadius());
698         rbRadius = std::fmin(attr.roundRectStyle.rightBottomRadius, run->getMaxRoundRectRadius());
699     }
700     const SkVector radii[4] = {{ltRadius, ltRadius}, {rtRadius, rtRadius}, {rbRadius, rbRadius}, {lbRadius, lbRadius}};
701     SkRect skRect(SkRect::MakeLTRB(attr.rect.left(), run->getTopInGroup(), attr.rect.right(),
702         run->getBottomInGroup()));
703     SkRRect skRRect;
704     skRRect.setRectRadii(skRect, radii);
705     skRRect.offset(x + this->offset().x(), y + this->offset().y());
706     painter->drawRRect(skRRect, attr.roundRectStyle.color);
707 }
708 
709 void TextLine::paintShadow(ParagraphPainter* painter,
710                            SkScalar x,
711                            SkScalar y,
712                            TextRange textRange,
713                            const TextStyle& style,
714                            const ClipContext& context) const {
715     SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5);
716 
717     for (TextShadow shadow : style.getShadows()) {
718         if (!shadow.hasShadow()) continue;
719 
720 #ifndef USE_SKIA_TXT
721         SkTextBlobBuilder builder;
722 #else
723         RSTextBlobBuilder builder;
724 #endif
725         context.run->copyTo(builder, context.pos, context.size);
726 
727         if (context.clippingNeeded) {
728             painter->save();
729             SkRect clip = extendHeight(context);
730             clip.offset(x, y);
731             clip.offset(this->offset());
732             painter->clipRect(clip);
733         }
734 #ifndef USE_SKIA_TXT
735         auto blob = builder.make();
736 #else
737         auto blob = builder.Make();
738 #endif
739         painter->drawTextShadow(blob,
740             x + this->offset().fX + shadow.fOffset.x() + context.fTextShift,
741             y + this->offset().fY + shadow.fOffset.y() + correctedBaseline,
742             shadow.fColor,
743             SkDoubleToScalar(shadow.fBlurSigma));
744         if (context.clippingNeeded) {
745             painter->restore();
746         }
747     }
748 }
749 
750 SkScalar TextLine::calculateThickness(const TextStyle& style, const ClipContext& content)
751 {
752     Decorations decoration;
753     return decoration.calculateThickness(style, content);
754 }
755 
756 void TextLine::paintDecorations(ParagraphPainter* painter, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
757     ParagraphPainterAutoRestore ppar(painter);
758     painter->translate(x + this->offset().fX, y + this->offset().fY + style.getBaselineShift());
759     Decorations decorations;
760     decorations.setDecorationContext(fDecorationContext);
761     SkScalar correctedBaseline = SkScalarFloorToScalar(-this->sizes().rawAscent() + style.getBaselineShift() + 0.5);
762     decorations.paint(painter, style, context, correctedBaseline);
763 }
764 
765 void TextLine::justify(SkScalar maxWidth) {
766     int whitespacePatches = 0;
767     SkScalar textLen = 0;
768     bool whitespacePatch = false;
769     // Take leading whitespaces width but do not increment a whitespace patch number
770     bool leadingWhitespaces = false;
771     this->iterateThroughClustersInGlyphsOrder(false, false,
772         [&](const Cluster* cluster, ClusterIndex index, bool ghost) {
773             if (cluster->isWhitespaceBreak()) {
774                 if (index == 0) {
775                     leadingWhitespaces = true;
776                 } else if (!whitespacePatch && !leadingWhitespaces) {
777                     // We only count patches BETWEEN words, not before
778                     ++whitespacePatches;
779                 }
780                 whitespacePatch = !leadingWhitespaces;
781             } else if (cluster->isIdeographic()) {
782                 // Whitespace break before and after
783                 if (!whitespacePatch && index != 0) {
784                     // We only count patches BETWEEN words, not before
785                     ++whitespacePatches; // before
786                 }
787                 whitespacePatch = true;
788                 leadingWhitespaces = false;
789                 ++whitespacePatches;    // after
790             } else {
791                 whitespacePatch = false;
792                 leadingWhitespaces = false;
793             }
794             textLen += cluster->width();
795             return true;
796         });
797 
798     if (whitespacePatch) {
799         // We only count patches BETWEEN words, not after
800         --whitespacePatches;
801     }
802     if (whitespacePatches == 0) {
803         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kRtl) {
804             // Justify -> Right align
805             fShift = maxWidth - textLen;
806         }
807         return;
808     }
809 
810     SkScalar step = (maxWidth - textLen) / whitespacePatches;
811     SkScalar shift = 0.0f;
812     SkScalar prevShift = 0.0f;
813 
814     // Deal with the ghost spaces
815     auto ghostShift = maxWidth - this->fAdvance.fX;
816     // Spread the extra whitespaces
817     whitespacePatch = false;
818     // Do not break on leading whitespaces
819     leadingWhitespaces = false;
820     this->iterateThroughClustersInGlyphsOrder(false, true, [&](const Cluster* cluster, ClusterIndex index, bool ghost) {
821 
822         if (ghost) {
823             if (cluster->run().leftToRight()) {
824                 this->shiftCluster(cluster, ghostShift, ghostShift);
825             }
826             return true;
827         }
828 
829         if (cluster->isWhitespaceBreak()) {
830             if (index == 0) {
831                 leadingWhitespaces = true;
832             } else if (!whitespacePatch && !leadingWhitespaces) {
833                 shift += step;
834                 whitespacePatch = true;
835                 --whitespacePatches;
836             }
837         } else if (cluster->isIdeographic()) {
838             if (!whitespacePatch && index != 0) {
839                 shift += step;
840                --whitespacePatches;
841             }
842             whitespacePatch = false;
843             leadingWhitespaces = false;
844         } else {
845             whitespacePatch = false;
846             leadingWhitespaces = false;
847         }
848         this->shiftCluster(cluster, shift, prevShift);
849         prevShift = shift;
850         // We skip ideographic whitespaces
851         if (!cluster->isWhitespaceBreak() && cluster->isIdeographic()) {
852             shift += step;
853             whitespacePatch = true;
854             --whitespacePatches;
855         }
856         return true;
857     });
858 
859     if (whitespacePatch && whitespacePatches < 0) {
860         whitespacePatches++;
861         shift -= step;
862     }
863 
864     SkAssertResult(nearlyEqual(shift, maxWidth - textLen));
865     SkASSERT(whitespacePatches == 0);
866 
867     this->fWidthWithSpaces += ghostShift;
868     this->fAdvance.fX = maxWidth;
869 }
870 
871 void TextLine::shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift) {
872 
873     auto& run = cluster->run();
874     auto start = cluster->startPos();
875     auto end = cluster->endPos();
876 
877     if (end == run.size()) {
878         // Set the same shift for the fake last glyph (to avoid all extra checks)
879         ++end;
880     }
881 
882     if (run.fJustificationShifts.empty()) {
883         // Do not fill this array until needed
884         run.fJustificationShifts.push_back_n(run.size() + 1, { 0, 0 });
885     }
886 
887     for (size_t pos = start; pos < end; ++pos) {
888         run.fJustificationShifts[pos] = { shift, prevShift };
889     }
890 }
891 
892 void TextLine::spacingCluster(const Cluster* cluster, SkScalar spacing, SkScalar prevSpacing) {
893     auto& run = cluster->run();
894     auto start = cluster->startPos();
895     auto end = cluster->endPos();
896     if (end == run.size()) {
897         // Set the same shift for the fake last glyph (to avoid all extra checks)
898         ++end;
899     }
900 
901     if (run.fAutoSpacings.empty()) {
902         // Do not fill this array until needed
903         run.fAutoSpacings.push_back_n(run.size() + 1, { 0, 0 });
904     }
905 
906     for (size_t pos = start; pos < end; ++pos) {
907         run.fAutoSpacings[pos] = { spacing, prevSpacing};
908     }
909 }
910 
911 void TextLine::countWord(int& wordCount, bool& inWord) {
912     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
913         auto& cluster = fOwner->cluster(clusterIndex);
914         if (cluster.isWordBreak()) {
915             inWord = false;
916         } else if (!inWord) {
917             ++wordCount;
918             inWord = true;
919         }
920     }
921 }
922 
923 void TextLine::ellipsisNotFitProcess(EllipsisModal ellipsisModal) {
924     if (fEllipsis) {
925         return;
926     }
927 
928     // Weird situation: ellipsis does not fit; no ellipsis then
929     switch (ellipsisModal) {
930         case EllipsisModal::TAIL:
931             fClusterRange.end = fClusterRange.start;
932             fGhostClusterRange.end = fClusterRange.start;
933             fText.end = fText.start;
934             fTextIncludingNewlines.end = fTextIncludingNewlines.start;
935             fTextExcludingSpaces.end = fTextExcludingSpaces.start;
936             fAdvance.fX = 0;
937             break;
938         case EllipsisModal::HEAD:
939             fClusterRange.start = fClusterRange.end;
940             fGhostClusterRange.start = fClusterRange.end;
941             fText.start = fText.end;
942             fTextIncludingNewlines.start = fTextIncludingNewlines.end;
943             fTextExcludingSpaces.start = fTextExcludingSpaces.end;
944             fAdvance.fX = 0;
945             break;
946         default:
947             return;
948     }
949 }
950 
951 void TextLine::createTailEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr, WordBreakType wordBreakType) {
952     // Replace some clusters with the ellipsis
953     // Go through the clusters in the reverse logical order
954     // taking off cluster by cluster until the ellipsis fits
955     SkScalar width = fAdvance.fX;
956     RunIndex lastRun = EMPTY_RUN;
957     std::unique_ptr<Run> ellipsisRun;
958     int wordCount = 0;
959     bool inWord = false;
960 
961     countWord(wordCount, inWord);
962 
963     bool iterForWord = false;
964 
965     for (auto clusterIndex = fGhostClusterRange.end; clusterIndex > fGhostClusterRange.start; --clusterIndex) {
966         auto& cluster = fOwner->cluster(clusterIndex - 1);
967         // Shape the ellipsis if the run has changed
968         if (lastRun != cluster.runIndex()) {
969             ellipsisRun = this->shapeEllipsis(ellipsis, &cluster);
970             // We may need to continue
971             lastRun = cluster.runIndex();
972         }
973 
974         if (!cluster.isWordBreak()) {
975             inWord = true;
976         } else if (inWord) {
977             --wordCount;
978             inWord = false;
979         }
980         // See if it fits
981         if (width + ellipsisRun->advance().fX > maxWidth) {
982             if (!cluster.isHardBreak()) {
983                 width -= cluster.width();
984             }
985             // Continue if the ellipsis does not fit
986             iterForWord = (wordCount != 1 && wordBreakType != WordBreakType::BREAK_ALL && !cluster.isWordBreak());
987             if (std::floor(width) > 0) {
988                 continue;
989             }
990         }
991 
992         if (iterForWord && !cluster.isWordBreak()) {
993             width -= cluster.width();
994             if (std::floor(width) > 0) {
995                 continue;
996             }
997         }
998 
999         // Get the last run directions after clipping
1000         fEllipsisIndex = cluster.runIndex();
1001         fLastClipRunLtr = fOwner->run(fEllipsisIndex).leftToRight();
1002 
1003         // We found enough room for the ellipsis
1004         fAdvance.fX = width;
1005         fEllipsis = std::move(ellipsisRun);
1006         fEllipsis->setOwner(fOwner);
1007         fTextRangeReplacedByEllipsis = TextRange(cluster.textRange().end, fOwner->text().size());
1008 
1009         // Let's update the line
1010         fClusterRange.end = clusterIndex;
1011         fGhostClusterRange.end = fClusterRange.end;
1012 #ifdef OHOS_SUPPORT
1013         fEllipsis->fTextRange =
1014                 TextRange(cluster.textRange().end, cluster.textRange().end + ellipsis.size());
1015         fEllipsis->fClusterStart = cluster.textRange().end;
1016 #else
1017         fEllipsis->fClusterStart = cluster.textRange().start;
1018 #endif
1019         fText.end = cluster.textRange().end;
1020         fTextIncludingNewlines.end = cluster.textRange().end;
1021         fTextExcludingSpaces.end = cluster.textRange().end;
1022 
1023         if (SkScalarNearlyZero(width)) {
1024             fRunsInVisualOrder.reset();
1025         }
1026 
1027         break;
1028     }
1029 
1030     fWidthWithSpaces = width;
1031 
1032     ellipsisNotFitProcess(EllipsisModal::TAIL);
1033 }
1034 
1035 #ifdef OHOS_SUPPORT
1036 void TextLine::createHeadEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool) {
1037     if (fAdvance.fX <= maxWidth) {
1038         return;
1039     }
1040     SkScalar width = fAdvance.fX;
1041     std::unique_ptr<Run> ellipsisRun;
1042     RunIndex lastRun = EMPTY_RUN;
1043     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
1044         auto& cluster = fOwner->cluster(clusterIndex);
1045         // Shape the ellipsis if the run has changed
1046         if (lastRun != cluster.runIndex()) {
1047             ellipsisRun = this->shapeEllipsis(ellipsis, &cluster);
1048             // We may need to continue
1049             lastRun = cluster.runIndex();
1050         }
1051         // See if it fits
1052         if (width + ellipsisRun->advance().fX > maxWidth) {
1053             width -= cluster.width();
1054             // Continue if the ellipsis does not fit
1055             if (std::floor(width) > 0) {
1056                 continue;
1057             }
1058         }
1059 
1060         // Get the last run directions after clipping
1061         fEllipsisIndex = cluster.runIndex();
1062         fLastClipRunLtr = fOwner->run(fEllipsisIndex).leftToRight();
1063 
1064         // We found enough room for the ellipsis
1065         fAdvance.fX = width + ellipsisRun->advance().fX;
1066         fEllipsis = std::move(ellipsisRun);
1067         fEllipsis->setOwner(fOwner);
1068         fTextRangeReplacedByEllipsis = TextRange(0, cluster.textRange().start);
1069         fClusterRange.start = clusterIndex;
1070         fGhostClusterRange.start = fClusterRange.start;
1071         fEllipsis->fClusterStart = 0;
1072         fText.start = cluster.textRange().start;
1073         fTextIncludingNewlines.start = cluster.textRange().start;
1074         fTextExcludingSpaces.start = cluster.textRange().start;
1075         break;
1076     }
1077 
1078     fWidthWithSpaces = width;
1079 
1080     ellipsisNotFitProcess(EllipsisModal::HEAD);
1081 }
1082 #endif
1083 
1084 static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
1085     SkUnichar val = SkUTF::NextUTF8(ptr, end);
1086     return val < 0 ? 0xFFFD : val;
1087 }
1088 
1089 std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Cluster* cluster) {
1090 
1091     class ShapeHandler final : public SkShaper::RunHandler {
1092     public:
1093         ShapeHandler(SkScalar lineHeight, bool useHalfLeading, SkScalar baselineShift, const SkString& ellipsis)
1094             : fRun(nullptr), fLineHeight(lineHeight), fUseHalfLeading(useHalfLeading), fBaselineShift(baselineShift), fEllipsis(ellipsis) {}
1095         std::unique_ptr<Run> run() & { return std::move(fRun); }
1096 
1097     private:
1098         void beginLine() override {}
1099 
1100         void runInfo(const RunInfo&) override {}
1101 
1102         void commitRunInfo() override {}
1103 
1104         Buffer runBuffer(const RunInfo& info) override {
1105             SkASSERT(!fRun);
1106             fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fUseHalfLeading, fBaselineShift, 0, 0);
1107             return fRun->newRunBuffer();
1108         }
1109 
1110         void commitRunBuffer(const RunInfo& info) override {
1111             fRun->fAdvance.fX = info.fAdvance.fX;
1112             fRun->fAdvance.fY = fRun->advance().fY;
1113             fRun->fPlaceholderIndex = std::numeric_limits<size_t>::max();
1114             fRun->fEllipsis = true;
1115         }
1116 
1117         void commitLine() override {}
1118 
1119         std::unique_ptr<Run> fRun;
1120         SkScalar fLineHeight;
1121         bool fUseHalfLeading;
1122         SkScalar fBaselineShift;
1123         SkString fEllipsis;
1124     };
1125 
1126     const Run& run = cluster->run();
1127     TextStyle textStyle = fOwner->paragraphStyle().getTextStyle();
1128     for (auto i = fBlockRange.start; i < fBlockRange.end; ++i) {
1129         auto& block = fOwner->block(i);
1130         if (run.leftToRight() && cluster->textRange().end <= block.fRange.end) {
1131             textStyle = block.fStyle;
1132             break;
1133         } else if (!run.leftToRight() && cluster->textRange().start <= block.fRange.end) {
1134             textStyle = block.fStyle;
1135             break;
1136         }
1137     }
1138 
1139 #ifndef USE_SKIA_TXT
1140     auto shaped = [&](sk_sp<SkTypeface> typeface, bool fallback) -> std::unique_ptr<Run> {
1141 #else
1142     auto shaped = [&](std::shared_ptr<RSTypeface> typeface, bool fallback) -> std::unique_ptr<Run> {
1143 #endif
1144         ShapeHandler handler(run.heightMultiplier(), run.useHalfLeading(), run.baselineShift(), ellipsis);
1145 #ifndef USE_SKIA_TXT
1146         SkFont font(typeface, textStyle.getFontSize());
1147         font.setEdging(SkFont::Edging::kAntiAlias);
1148         font.setHinting(SkFontHinting::kSlight);
1149         font.setSubpixel(true);
1150 #else
1151         RSFont font(typeface, textStyle.getFontSize(), 1, 0);
1152         font.SetEdging(RSDrawing::FontEdging::ANTI_ALIAS);
1153         font.SetHinting(RSDrawing::FontHinting::SLIGHT);
1154         font.SetSubpixel(true);
1155 #endif
1156 
1157 #ifndef USE_SKIA_TXT
1158         std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder(
1159                             fOwner->getUnicode()->copy(),
1160                             fallback ? SkFontMgr::RefDefault() : SkFontMgr::RefEmpty());
1161 #else
1162         std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder(
1163                             fOwner->getUnicode()->copy(),
1164                             fallback ? RSFontMgr::CreateDefaultFontMgr() : RSFontMgr::CreateDefaultFontMgr());
1165 #endif
1166         shaper->shape(ellipsis.c_str(),
1167                       ellipsis.size(),
1168                       font,
1169                       true,
1170                       std::numeric_limits<SkScalar>::max(),
1171                       &handler);
1172         auto ellipsisRun = handler.run();
1173         ellipsisRun->fTextRange = TextRange(0, ellipsis.size());
1174         ellipsisRun->fOwner = fOwner;
1175         return ellipsisRun;
1176     };
1177 
1178     // Check all allowed fonts
1179     auto typefaces = fOwner->fontCollection()->findTypefaces(
1180             textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
1181     for (const auto& typeface : typefaces) {
1182         auto ellipsisRun = shaped(typeface, false);
1183         if (ellipsisRun->isResolved()) {
1184             return ellipsisRun;
1185         }
1186     }
1187 
1188     // Try the fallback
1189     if (fOwner->fontCollection()->fontFallbackEnabled()) {
1190         const char* ch = ellipsis.c_str();
1191         SkUnichar unicode = nextUtf8Unit(&ch, ellipsis.c_str() + ellipsis.size());
1192 
1193         auto typeface = fOwner->fontCollection()->defaultFallback(
1194             unicode, textStyle.getFontStyle(), textStyle.getLocale());
1195         if (typeface) {
1196             if (textStyle.getFontArguments()) {
1197                 typeface = fOwner->fontCollection()->CloneTypeface(typeface, textStyle.getFontArguments());
1198             }
1199             auto ellipsisRun = shaped(typeface, true);
1200             if (ellipsisRun->isResolved()) {
1201                 return ellipsisRun;
1202             }
1203         }
1204     }
1205 
1206     // Check the current font
1207 #ifndef USE_SKIA_TXT
1208     auto ellipsisRun = shaped(run.fFont.refTypeface(), false);
1209 #else
1210     auto ellipsisRun = shaped(const_cast<RSFont&>(run.fFont).GetTypeface(), false);
1211 #endif
1212     if (ellipsisRun->isResolved()) {
1213         return ellipsisRun;
1214     }
1215     return ellipsisRun;
1216 }
1217 
1218 #ifdef OHOS_SUPPORT
1219 void TextLine::measureTextWithSpacesAtTheEnd(ClipContext& context, bool includeGhostSpaces) const
1220 {
1221     if (compareRound(context.clip.fRight, fAdvance.fX, fOwner->getApplyRoundingHack()) > 0 && !includeGhostSpaces &&
1222         fAdvance.fX > 0) {
1223         // There are few cases when we need it.
1224         // The most important one: we measure the text with spaces at the end (or at the beginning in RTL)
1225         // and we should ignore these spaces
1226         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kLtr) {
1227             // We only use this member for LTR
1228             context.fExcludedTrailingSpaces = std::max(context.clip.fRight - fAdvance.fX, 0.0f);
1229             context.clippingNeeded = true;
1230             context.clip.fRight = fAdvance.fX;
1231         }
1232     }
1233 }
1234 #endif
1235 
1236 TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
1237                                                         const Run* run,
1238                                                         SkScalar runOffsetInLine,
1239                                                         SkScalar textOffsetInRunInLine,
1240                                                         bool includeGhostSpaces,
1241                                                         TextAdjustment textAdjustment) const {
1242     ClipContext result = { run, 0, run->size(), 0, SkRect::MakeEmpty(), 0, false };
1243 
1244     if (run->fEllipsis) {
1245         // Both ellipsis and placeholders can only be measured as one glyph
1246         result.fTextShift = runOffsetInLine;
1247         result.clip = SkRect::MakeXYWH(runOffsetInLine,
1248                                        sizes().runTop(run, this->fAscentStyle),
1249                                        run->advance().fX,
1250                                        run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1251         return result;
1252     } else if (run->isPlaceholder()) {
1253         result.fTextShift = runOffsetInLine;
1254         if (SkScalarIsFinite(run->fFontMetrics.fAscent)) {
1255           result.clip = SkRect::MakeXYWH(runOffsetInLine,
1256                                          sizes().runTop(run, this->fAscentStyle),
1257                                          run->advance().fX,
1258                                          run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1259         } else {
1260             result.clip = SkRect::MakeXYWH(runOffsetInLine, run->fFontMetrics.fAscent, run->advance().fX, 0);
1261         }
1262         return result;
1263     } else if (textRange.empty()) {
1264         return result;
1265     }
1266 
1267     TextRange originalTextRange(textRange); // We need it for proportional measurement
1268     // Find [start:end] clusters for the text
1269     while (true) {
1270         // Update textRange by cluster edges (shift start up to the edge of the cluster)
1271         // TODO: remove this limitation?
1272         TextRange updatedTextRange;
1273         bool found;
1274         std::tie(found, updatedTextRange.start, updatedTextRange.end) =
1275                                         run->findLimitingGlyphClusters(textRange);
1276         if (!found) {
1277             return result;
1278         }
1279 
1280         if ((textAdjustment & TextAdjustment::Grapheme) == 0) {
1281             textRange = updatedTextRange;
1282             break;
1283         }
1284 
1285         // Update text range by grapheme edges (shift start up to the edge of the grapheme)
1286         std::tie(found, updatedTextRange.start, updatedTextRange.end) =
1287                                     run->findLimitingGraphemes(updatedTextRange);
1288         if (updatedTextRange == textRange) {
1289             break;
1290         }
1291 
1292         // Some clusters are inside graphemes and we need to adjust them
1293         //SkDebugf("Correct range: [%d:%d) -> [%d:%d)\n", textRange.start, textRange.end, startIndex, endIndex);
1294         textRange = updatedTextRange;
1295 
1296         // Move the start until it's on the grapheme edge (and glypheme, too)
1297     }
1298     Cluster* start = &fOwner->cluster(fOwner->clusterIndex(textRange.start));
1299     Cluster* end = &fOwner->cluster(fOwner->clusterIndex(textRange.end - (textRange.width() == 0 ? 0 : 1)));
1300 
1301     if (!run->leftToRight()) {
1302         std::swap(start, end);
1303     }
1304     result.pos = start->startPos();
1305     result.size = (end->isHardBreak() ? end->startPos() : end->endPos()) - start->startPos();
1306     auto textStartInRun = run->positionX(start->startPos());
1307     auto textStartInLine = runOffsetInLine + textOffsetInRunInLine;
1308     if (!run->leftToRight()) {
1309         std::swap(start, end);
1310     }
1311 /*
1312     if (!run->fJustificationShifts.empty()) {
1313         SkDebugf("Justification for [%d:%d)\n", textRange.start, textRange.end);
1314         for (auto i = result.pos; i < result.pos + result.size; ++i) {
1315             auto j = run->fJustificationShifts[i];
1316             SkDebugf("[%d] = %f %f\n", i, j.fX, j.fY);
1317         }
1318     }
1319 */
1320     // Calculate the clipping rectangle for the text with cluster edges
1321     // There are 2 cases:
1322     // EOL (when we expect the last cluster clipped without any spaces)
1323     // Anything else (when we want the cluster width contain all the spaces -
1324     // coming from letter spacing or word spacing or justification)
1325     result.clip =
1326             SkRect::MakeXYWH(0,
1327                              sizes().runTop(run, this->fAscentStyle),
1328                              run->calculateWidth(result.pos, result.pos + result.size, false),
1329                              run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
1330 
1331     // Correct the width in case the text edges don't match clusters
1332     // TODO: This is where we get smart about selecting a part of a cluster
1333     //  by shaping each grapheme separately and then use the result sizes
1334     //  to calculate the proportions
1335     auto leftCorrection = start->sizeToChar(originalTextRange.start);
1336     auto rightCorrection = end->sizeFromChar(originalTextRange.end - 1);
1337     /*
1338     SkDebugf("[%d: %d) => [%d: %d), @%d, %d: [%f:%f) + [%f:%f) = ", // جَآَهُ
1339              originalTextRange.start, originalTextRange.end, textRange.start, textRange.end,
1340              result.pos, result.size,
1341              result.clip.fLeft, result.clip.fRight, leftCorrection, rightCorrection);
1342      */
1343     result.clippingNeeded = leftCorrection != 0 || rightCorrection != 0;
1344     if (run->leftToRight()) {
1345         result.clip.fLeft += leftCorrection;
1346         result.clip.fRight -= rightCorrection;
1347         textStartInLine -= leftCorrection;
1348     } else {
1349         result.clip.fRight -= leftCorrection;
1350         result.clip.fLeft += rightCorrection;
1351         textStartInLine -= rightCorrection;
1352     }
1353 
1354     result.clip.offset(textStartInLine, 0);
1355     //SkDebugf("@%f[%f:%f)\n", textStartInLine, result.clip.fLeft, result.clip.fRight);
1356 
1357 #ifdef OHOS_SUPPORT
1358     measureTextWithSpacesAtTheEnd(result, includeGhostSpaces);
1359 #else
1360     if (compareRound(result.clip.fRight, fAdvance.fX, fOwner->getApplyRoundingHack()) > 0 && !includeGhostSpaces) {
1361         // There are few cases when we need it.
1362         // The most important one: we measure the text with spaces at the end (or at the beginning in RTL)
1363         // and we should ignore these spaces
1364         if (fOwner->paragraphStyle().getTextDirection() == TextDirection::kLtr) {
1365             // We only use this member for LTR
1366             result.fExcludedTrailingSpaces = std::max(result.clip.fRight - fAdvance.fX, 0.0f);
1367             result.clippingNeeded = true;
1368             result.clip.fRight = fAdvance.fX;
1369         }
1370     }
1371 
1372     if (result.clip.width() < 0) {
1373         // Weird situation when glyph offsets move the glyph to the left
1374         // (happens with zalgo texts, for instance)
1375         result.clip.fRight = result.clip.fLeft;
1376     }
1377 #endif
1378 
1379     // The text must be aligned with the lineOffset
1380     result.fTextShift = textStartInLine - textStartInRun;
1381 
1382     return result;
1383 }
1384 
1385 void TextLine::iterateThroughClustersInGlyphsOrder(bool reversed,
1386                                                    bool includeGhosts,
1387                                                    const ClustersVisitor& visitor) const {
1388     // Walk through the clusters in the logical order (or reverse)
1389     SkSpan<const size_t> runs(fRunsInVisualOrder.data(), fRunsInVisualOrder.size());
1390     bool ignore = false;
1391     ClusterIndex index = 0;
1392     directional_for_each(runs, !reversed, [&](decltype(runs[0]) r) {
1393         if (ignore) return;
1394         auto run = this->fOwner->run(r);
1395         auto trimmedRange = fClusterRange.intersection(run.clusterRange());
1396         auto trailedRange = fGhostClusterRange.intersection(run.clusterRange());
1397         SkASSERT(trimmedRange.start == trailedRange.start);
1398 
1399         auto trailed = fOwner->clusters(trailedRange);
1400         auto trimmed = fOwner->clusters(trimmedRange);
1401         directional_for_each(trailed, reversed != run.leftToRight(), [&](Cluster& cluster) {
1402             if (ignore) return;
1403             bool ghost =  &cluster >= trimmed.end();
1404             if (!includeGhosts && ghost) {
1405                 return;
1406             }
1407             if (!visitor(&cluster, index++, ghost)) {
1408 
1409                 ignore = true;
1410                 return;
1411             }
1412         });
1413     });
1414 }
1415 
1416 #ifdef OHOS_SUPPORT
1417 void TextLine::computeNextPaintGlyphRange(ClipContext& context,
1418     const TextRange& lastGlyphRange, StyleType styleType) const
1419 {
1420     if (styleType != StyleType::kForeground) {
1421         return;
1422     }
1423     TextRange curGlyphRange = TextRange(context.pos, context.pos + context.size);
1424     auto intersect = intersected(lastGlyphRange, curGlyphRange);
1425     if (intersect == EMPTY_TEXT || (intersect.start != curGlyphRange.start && intersect.end != curGlyphRange.end)) {
1426         return;
1427     }
1428     if (intersect.start == curGlyphRange.start) {
1429         curGlyphRange = TextRange(intersect.end, curGlyphRange.end);
1430     } else if (intersect.end == curGlyphRange.end) {
1431         curGlyphRange = TextRange(curGlyphRange.start, intersect.start);
1432     }
1433 
1434     context.pos = curGlyphRange.start;
1435     context.size = curGlyphRange.width();
1436 }
1437 #endif
1438 
1439 SkScalar TextLine::iterateThroughSingleRunByStyles(TextAdjustment textAdjustment,
1440                                                    const Run* run,
1441                                                    SkScalar runOffset,
1442                                                    TextRange textRange,
1443                                                    StyleType styleType,
1444                                                    const RunStyleVisitor& visitor) const {
1445     auto includeGhostSpaces = (styleType == StyleType::kDecorations || styleType == StyleType::kBackground ||
1446         styleType == StyleType::kNone);
1447     auto correctContext = [&](TextRange textRange, SkScalar textOffsetInRun) -> ClipContext {
1448         auto result = this->measureTextInsideOneRun(
1449             textRange, run, runOffset, textOffsetInRun, includeGhostSpaces, textAdjustment);
1450         if (styleType == StyleType::kDecorations) {
1451             // Decorations are drawn based on the real font metrics (regardless of styles and strut)
1452             result.clip.fTop = this->sizes().runTop(run, LineMetricStyle::CSS) - run->baselineShift();
1453             result.clip.fBottom = result.clip.fTop +
1454                                   run->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS);
1455         }
1456         return result;
1457     };
1458 
1459     if (run->fEllipsis) {
1460         // Extra efforts to get the ellipsis text style
1461         ClipContext clipContext = correctContext(run->textRange(), 0.0f);
1462         for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
1463             auto block = fOwner->styles().begin() + index;
1464 #ifdef OHOS_SUPPORT
1465             TextRange intersect = intersected(block->fRange,
1466                 TextRange(fEllipsis->textRange().start - 1, fEllipsis->textRange().end));
1467             if (intersect.width() > 0) {
1468                 visitor(fTextRangeReplacedByEllipsis, block->fStyle, clipContext);
1469                 return run->advance().fX;
1470             }
1471 #else
1472            if (block->fRange.start >= run->fClusterStart && block->fRange.end < run->fClusterStart) {
1473                visitor(fTextRangeReplacedByEllipsis, block->fStyle, clipContext);
1474                return run->advance().fX;
1475            }
1476 #endif
1477         }
1478         SkASSERT(false);
1479     }
1480 
1481     if (styleType == StyleType::kNone) {
1482         ClipContext clipContext = correctContext(textRange, 0.0f);
1483 #ifdef OHOS_SUPPORT
1484         if (clipContext.clip.height() > 0 ||
1485             (run->isPlaceholder() && SkScalarNearlyZero(clipContext.clip.height()))) {
1486 #else
1487         if (clipContext.clip.height() > 0) {
1488 #endif
1489             visitor(textRange, TextStyle(), clipContext);
1490             return clipContext.clip.width();
1491         } else {
1492             return 0;
1493         }
1494     }
1495 
1496     TextIndex start = EMPTY_INDEX;
1497     size_t size = 0;
1498     const TextStyle* prevStyle = nullptr;
1499     SkScalar textOffsetInRun = 0;
1500 #ifdef OHOS_SUPPORT
1501     TextRange lastGlyphRange = EMPTY_TEXT;
1502 #endif
1503     const BlockIndex blockRangeSize = fBlockRange.end - fBlockRange.start;
1504     for (BlockIndex index = 0; index <= blockRangeSize; ++index) {
1505 
1506         TextRange intersect;
1507         TextStyle* style = nullptr;
1508         if (index < blockRangeSize) {
1509             auto block = fOwner->styles().begin() +
1510                  (run->leftToRight() ? fBlockRange.start + index : fBlockRange.end - index - 1);
1511 
1512             // Get the text
1513             intersect = intersected(block->fRange, textRange);
1514             if (intersect.width() == 0) {
1515                 if (start == EMPTY_INDEX) {
1516                     // This style is not applicable to the text yet
1517                     continue;
1518                 } else {
1519                     // We have found all the good styles already
1520                     // but we need to process the last one of them
1521                     intersect = TextRange(start, start + size);
1522                     index = fBlockRange.end;
1523                 }
1524             } else {
1525                 // Get the style
1526                 style = &block->fStyle;
1527                 if (start != EMPTY_INDEX && style->matchOneAttribute(styleType, *prevStyle)) {
1528                     size += intersect.width();
1529                     // RTL text intervals move backward
1530                     start = std::min(intersect.start, start);
1531                     continue;
1532                 } else if (start == EMPTY_INDEX ) {
1533                     // First time only
1534                     prevStyle = style;
1535                     size = intersect.width();
1536                     start = intersect.start;
1537                     continue;
1538                 }
1539             }
1540         } else if (prevStyle != nullptr) {
1541             // This is the last style
1542         } else {
1543             break;
1544         }
1545 
1546         // We have the style and the text
1547         auto runStyleTextRange = TextRange(start, start + size);
1548         ClipContext clipContext = correctContext(runStyleTextRange, textOffsetInRun);
1549         textOffsetInRun += clipContext.clip.width();
1550         if (clipContext.clip.height() == 0) {
1551             continue;
1552         }
1553 
1554         RectStyle temp;
1555         if (styleType == StyleType::kBackground &&
1556             prevStyle->getBackgroundRect() != temp &&
1557             prevStyle->getHeight() != 0) {
1558 #ifdef OHOS_SUPPORT
1559                 clipContext.clip.fTop = run->fFontMetrics.fAscent + this->baseline();
1560 #else
1561                 clipContext.clip.fTop = run->fFontMetrics.fAscent - run->fCorrectAscent;
1562 #endif
1563                 clipContext.clip.fBottom = clipContext.clip.fTop + run->fFontMetrics.fDescent -
1564                     run->fFontMetrics.fAscent;
1565         }
1566 #ifdef OHOS_SUPPORT
1567         computeNextPaintGlyphRange(clipContext, lastGlyphRange, styleType);
1568         if (clipContext.size != 0) {
1569             lastGlyphRange = TextRange(clipContext.pos, clipContext.pos + clipContext.size);
1570         }
1571 #endif
1572         visitor(runStyleTextRange, *prevStyle, clipContext);
1573 
1574         // Start all over again
1575         prevStyle = style;
1576         start = intersect.start;
1577         size = intersect.width();
1578     }
1579     return textOffsetInRun;
1580 }
1581 
1582 #ifdef OHOS_SUPPORT
1583 bool TextLine::processEllipsisRun(bool& isAlreadyUseEllipsis,
1584                                   SkScalar& runOffset,
1585                                   EllipsisReadStrategy ellipsisReadStrategy,
1586                                   const RunVisitor& visitor,
1587                                   SkScalar& runWidthInLine) const {
1588     isAlreadyUseEllipsis = true;
1589     runOffset += this->ellipsis()->offset().fX;
1590     if (ellipsisReadStrategy == EllipsisReadStrategy::READ_REPLACED_WORD) {
1591         if (!visitor(ellipsis(), runOffset, fTextRangeReplacedByEllipsis, &runWidthInLine)) {
1592             LOGE("Visitor process ellipsis replace word error!");
1593             return false;
1594         }
1595     } else if (ellipsisReadStrategy == EllipsisReadStrategy::READ_ELLIPSIS_WORD) {
1596         if (!visitor(ellipsis(), runOffset, ellipsis()->textRange(), &runWidthInLine)) {
1597             LOGE("Visitor process ellipsis word error!");
1598             return false;
1599         }
1600     } else {
1601         runWidthInLine = this->ellipsis()->advance().fX;
1602     }
1603     return true;
1604 }
1605 #endif
1606 
1607 #ifdef OHOS_SUPPORT
1608 void TextLine::iterateThroughVisualRuns(EllipsisReadStrategy ellipsisReadStrategy,
1609                                         bool includingGhostSpaces,
1610                                         const RunVisitor& visitor) const {
1611     // Walk through all the runs that intersect with the line in visual order
1612     SkScalar width = 0;
1613     SkScalar runOffset = 0;
1614     SkScalar totalWidth = 0;
1615 #ifdef OHOS_SUPPORT
1616     bool ellipsisModeIsHead = fIsTextLineEllipsisHeadModal ? true :
1617             fOwner->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD;
1618 #else
1619     bool ellipsisModeIsHead = fOwner->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD;
1620 #endif
1621     bool isAlreadyUseEllipsis = false;
1622     auto textRange = includingGhostSpaces ? this->textWithNewlines() : this->trimmedText();
1623 
1624     if (fRunsInVisualOrder.size() == 0 && fEllipsis != nullptr) {
1625         if (!processEllipsisRun(isAlreadyUseEllipsis, runOffset, ellipsisReadStrategy, visitor, width)) {
1626             return;
1627         }
1628         totalWidth += width;
1629     }
1630 
1631     for (auto& runIndex : fRunsInVisualOrder) {
1632         // add the lastClipRun's left ellipsis if necessary
1633         if (!isAlreadyUseEllipsis && fEllipsisIndex == runIndex &&
1634             ((!fLastClipRunLtr && !ellipsisModeIsHead) || (ellipsisModeIsHead && fLastClipRunLtr))) {
1635             if (!processEllipsisRun(isAlreadyUseEllipsis, runOffset, ellipsisReadStrategy, visitor, width)) {
1636                 return;
1637             }
1638             runOffset += width;
1639             totalWidth += width;
1640         }
1641 
1642         const auto run = &this->fOwner->run(runIndex);
1643         auto lineIntersection = intersected(run->textRange(), textRange);
1644         if (lineIntersection.width() == 0 && this->width() != 0) {
1645             // TODO: deal with empty runs in a better way
1646             continue;
1647         }
1648         if (!run->leftToRight() && runOffset == 0 && includingGhostSpaces) {
1649             // runOffset does not take in account a possibility
1650             // that RTL run could start before the line (trailing spaces)
1651             // so we need to do runOffset -= "trailing whitespaces length"
1652             TextRange whitespaces = intersected(
1653                     TextRange(fTextExcludingSpaces.end, fTextIncludingNewlines.end), run->fTextRange);
1654             if (whitespaces.width() > 0) {
1655                 auto whitespacesLen = measureTextInsideOneRun(whitespaces, run, runOffset, 0, true,
1656                                                               TextAdjustment::GlyphCluster).clip.width();
1657                 runOffset -= whitespacesLen;
1658             }
1659         }
1660 
1661         if (!visitor(run, runOffset, lineIntersection, &width)) {
1662             return;
1663         }
1664 
1665         runOffset += width;
1666         totalWidth += width;
1667 
1668         // add the lastClipRun's right ellipsis if necessary
1669         if (!isAlreadyUseEllipsis && fEllipsisIndex == runIndex) {
1670             if (!processEllipsisRun(isAlreadyUseEllipsis, runOffset, ellipsisReadStrategy, visitor, width)) {
1671                 return;
1672             }
1673             runOffset += width;
1674             totalWidth += width;
1675         }
1676     }
1677 
1678     if (!includingGhostSpaces && compareRound(totalWidth, this->width(), fOwner->getApplyRoundingHack()) != 0) {
1679     // This is a very important assert!
1680     // It asserts that 2 different ways of calculation come with the same results
1681         SkDebugf("ASSERT: %f != %f\n", totalWidth, this->width());
1682         SkASSERT(false);
1683     }
1684 }
1685 #else
1686 void TextLine::iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& visitor) const {
1687 
1688     // Walk through all the runs that intersect with the line in visual order
1689     SkScalar width = 0;
1690     SkScalar runOffset = 0;
1691     SkScalar totalWidth = 0;
1692     auto textRange = includingGhostSpaces ? this->textWithNewlines() : this->trimmedText();
1693     for (auto& runIndex : fRunsInVisualOrder) {
1694 
1695         const auto run = &this->fOwner->run(runIndex);
1696         auto lineIntersection = intersected(run->textRange(), textRange);
1697         if (lineIntersection.width() == 0 && this->width() != 0) {
1698             // TODO: deal with empty runs in a better way
1699             continue;
1700         }
1701         if (!run->leftToRight() && runOffset == 0 && includingGhostSpaces) {
1702             // runOffset does not take in account a possibility
1703             // that RTL run could start before the line (trailing spaces)
1704             // so we need to do runOffset -= "trailing whitespaces length"
1705             TextRange whitespaces = intersected(
1706                     TextRange(fTextExcludingSpaces.end, fTextIncludingNewlines.end), run->fTextRange);
1707             if (whitespaces.width() > 0) {
1708                 auto whitespacesLen = measureTextInsideOneRun(whitespaces, run, runOffset, 0, true, false).clip.width();
1709                 runOffset -= whitespacesLen;
1710             }
1711         }
1712         runOffset += width;
1713         totalWidth += width;
1714         if (!visitor(run, runOffset, lineIntersection, &width)) {
1715             return;
1716         }
1717     }
1718 
1719     runOffset += width;
1720     totalWidth += width;
1721 
1722     if (this->ellipsis() != nullptr) {
1723         if (visitor(ellipsis(), runOffset, ellipsis()->textRange(), &width)) {
1724             totalWidth += width;
1725         }
1726     }
1727 
1728     // This is a very important assert!
1729     // It asserts that 2 different ways of calculation come with the same results
1730     if (!includingGhostSpaces && compareRound(totalWidth, this->width()) != 0) {
1731         SkDebugf("ASSERT: %f != %f\n", totalWidth, this->width());
1732         SkASSERT(false);
1733     }
1734 }
1735 #endif
1736 
1737 SkVector TextLine::offset() const {
1738     return fOffset + SkVector::Make(fShift, 0);
1739 }
1740 
1741 LineMetrics TextLine::getMetrics() const {
1742     LineMetrics result;
1743 
1744     // Fill out the metrics
1745     fOwner->ensureUTF16Mapping();
1746     result.fStartIndex = fOwner->getUTF16Index(fTextExcludingSpaces.start);
1747     result.fEndExcludingWhitespaces = fOwner->getUTF16Index(fTextExcludingSpaces.end);
1748     result.fEndIndex = fOwner->getUTF16Index(fText.end);
1749     result.fEndIncludingNewline = fOwner->getUTF16Index(fTextIncludingNewlines.end);
1750     result.fHardBreak = endsWithHardLineBreak();
1751     result.fAscent = - fMaxRunMetrics.ascent();
1752     result.fDescent = fMaxRunMetrics.descent();
1753     result.fUnscaledAscent = - fMaxRunMetrics.ascent(); // TODO: implement
1754     result.fHeight = fAdvance.fY;
1755     result.fWidth = fAdvance.fX;
1756     if (fOwner->getApplyRoundingHack()) {
1757         result.fHeight = littleRound(result.fHeight);
1758         result.fWidth = littleRound(result.fWidth);
1759     }
1760     result.fLeft = this->offset().fX;
1761     // This is Flutter definition of a baseline
1762     result.fBaseline = this->offset().fY + this->height() - this->sizes().descent();
1763     result.fLineNumber = this - fOwner->lines().begin();
1764     result.fWidthWithSpaces = fWidthWithSpaces;
1765     result.fTopHeight = this->offset().fY;
1766 
1767     // Fill out the style parts
1768 #ifdef OHOS_SUPPORT
1769     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, false,
1770 #else
1771     this->iterateThroughVisualRuns(false,
1772 #endif
1773         [this, &result]
1774         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
1775         if (run->placeholderStyle() != nullptr) {
1776             *runWidthInLine = run->advance().fX;
1777             return true;
1778         }
1779         *runWidthInLine = this->iterateThroughSingleRunByStyles(
1780         TextAdjustment::GlyphCluster, run, runOffsetInLine, textRange, StyleType::kForeground,
1781         [&result, &run](TextRange textRange, const TextStyle& style, const ClipContext& context) {
1782 #ifndef USE_SKIA_TXT
1783             SkFontMetrics fontMetrics;
1784             run->fFont.getMetrics(&fontMetrics);
1785 #else
1786             RSFontMetrics fontMetrics;
1787             run->fFont.GetMetrics(&fontMetrics);
1788 #endif
1789 #ifdef OHOS_SUPPORT
1790             auto decompressFont = run->fFont;
1791             scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS);
1792             metricsIncludeFontPadding(&fontMetrics, decompressFont);
1793 #endif
1794             StyleMetrics styleMetrics(&style, fontMetrics);
1795             result.fLineMetrics.emplace(textRange.start, styleMetrics);
1796         });
1797         return true;
1798     });
1799 
1800     return result;
1801 }
1802 
1803 bool TextLine::isFirstLine() const {
1804     return this == &fOwner->lines().front();
1805 }
1806 
1807 bool TextLine::isLastLine() const {
1808     return this == &fOwner->lines().back();
1809 }
1810 
1811 bool TextLine::endsWithHardLineBreak() const {
1812     // TODO: For some reason Flutter imagines a hard line break at the end of the last line.
1813     //  To be removed...
1814     return (fGhostClusterRange.width() > 0 && fOwner->cluster(fGhostClusterRange.end - 1).isHardBreak()) ||
1815            fEllipsis != nullptr ||
1816            fGhostClusterRange.end == fOwner->clusters().size() - 1;
1817 }
1818 
1819 void TextLine::getRectsForRange(TextRange textRange0,
1820                                 RectHeightStyle rectHeightStyle,
1821                                 RectWidthStyle rectWidthStyle,
1822                                 std::vector<TextBox>& boxes) const
1823 {
1824     const Run* lastRun = nullptr;
1825     auto startBox = boxes.size();
1826 #ifdef OHOS_SUPPORT
1827     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
1828 #else
1829     this->iterateThroughVisualRuns(true,
1830 #endif
1831         [textRange0, rectHeightStyle, rectWidthStyle, &boxes, &lastRun, startBox, this]
1832         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
1833         *runWidthInLine = this->iterateThroughSingleRunByStyles(
1834         TextAdjustment::GraphemeGluster, run, runOffsetInLine, textRange, StyleType::kNone,
1835         [run, runOffsetInLine, textRange0, rectHeightStyle, rectWidthStyle, &boxes, &lastRun, startBox, this]
1836         (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& lineContext) {
1837 
1838             auto intersect = textRange * textRange0;
1839             if (intersect.empty()) {
1840                 return true;
1841             }
1842 
1843             auto paragraphStyle = fOwner->paragraphStyle();
1844 
1845             // Found a run that intersects with the text
1846             auto context = this->measureTextInsideOneRun(
1847                     intersect, run, runOffsetInLine, 0, true, TextAdjustment::GraphemeGluster);
1848             SkRect clip = context.clip;
1849             clip.offset(lineContext.fTextShift - context.fTextShift, 0);
1850 
1851             switch (rectHeightStyle) {
1852                 case RectHeightStyle::kMax:
1853                     // TODO: Change it once flutter rolls into google3
1854                     //  (probably will break things if changed before)
1855                     clip.fBottom = this->height();
1856                     clip.fTop = this->sizes().delta();
1857                     break;
1858                 case RectHeightStyle::kIncludeLineSpacingTop: {
1859                     clip.fBottom = this->height();
1860                     clip.fTop = this->sizes().delta();
1861                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
1862                     if (isFirstLine()) {
1863                         clip.fTop += verticalShift;
1864                     }
1865                     break;
1866                 }
1867                 case RectHeightStyle::kIncludeLineSpacingMiddle: {
1868                     clip.fBottom = this->height();
1869                     clip.fTop = this->sizes().delta();
1870                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
1871                     clip.offset(0, verticalShift / 2.0);
1872                     if (isFirstLine()) {
1873                         clip.fTop += verticalShift / 2.0;
1874                     }
1875                     if (isLastLine()) {
1876                         clip.fBottom -= verticalShift / 2.0;
1877                     }
1878                     break;
1879                  }
1880                 case RectHeightStyle::kIncludeLineSpacingBottom: {
1881                     clip.fBottom = this->height();
1882                     clip.fTop = this->sizes().delta();
1883                     auto verticalShift = this->sizes().rawAscent() - this->sizes().ascent();
1884                     clip.offset(0, verticalShift);
1885                     if (isLastLine()) {
1886                         clip.fBottom -= verticalShift;
1887                     }
1888                     break;
1889                 }
1890                 case RectHeightStyle::kStrut: {
1891                     const auto& strutStyle = paragraphStyle.getStrutStyle();
1892                     if (strutStyle.getStrutEnabled()
1893                         && strutStyle.getFontSize() > 0) {
1894                         auto strutMetrics = fOwner->strutMetrics();
1895                         auto top = this->baseline();
1896                         clip.fTop = top + strutMetrics.ascent();
1897                         clip.fBottom = top + strutMetrics.descent();
1898                     }
1899                 }
1900                 break;
1901                 case RectHeightStyle::kTight: {
1902                     if (run->fHeightMultiplier <= 0) {
1903                         break;
1904                     }
1905                     const auto effectiveBaseline = this->baseline() + this->sizes().delta();
1906                     clip.fTop = effectiveBaseline + run->ascent();
1907                     clip.fBottom = effectiveBaseline + run->descent();
1908                 }
1909                 break;
1910                 default:
1911                     SkASSERT(false);
1912                 break;
1913             }
1914 
1915             // Separate trailing spaces and move them in the default order of the paragraph
1916             // in case the run order and the paragraph order don't match
1917             SkRect trailingSpaces = SkRect::MakeEmpty();
1918             if (this->trimmedText().end <this->textWithNewlines().end && // Line has trailing space
1919                 this->textWithNewlines().end == intersect.end &&         // Range is at the end of the line
1920                 this->trimmedText().end > intersect.start)               // Range has more than just spaces
1921             {
1922                 auto delta = this->spacesWidth();
1923                 trailingSpaces = SkRect::MakeXYWH(0, 0, 0, 0);
1924                 // There are trailing spaces in this run
1925                 if (paragraphStyle.getTextAlign() == TextAlign::kJustify && isLastLine())
1926                 {
1927                     // TODO: this is just a patch. Make it right later (when it's clear what and how)
1928                     trailingSpaces = clip;
1929                     if(run->leftToRight()) {
1930                         trailingSpaces.fLeft = this->width();
1931                         clip.fRight = this->width();
1932                     } else {
1933                         trailingSpaces.fRight = 0;
1934                         clip.fLeft = 0;
1935                     }
1936                 } else if (paragraphStyle.getTextDirection() == TextDirection::kRtl &&
1937                     !run->leftToRight())
1938                 {
1939                     // Split
1940                     trailingSpaces = clip;
1941                     trailingSpaces.fLeft = - delta;
1942                     trailingSpaces.fRight = 0;
1943                     clip.fLeft += delta;
1944                 } else if (paragraphStyle.getTextDirection() == TextDirection::kLtr &&
1945                     run->leftToRight())
1946                 {
1947                     // Split
1948                     trailingSpaces = clip;
1949                     trailingSpaces.fLeft = this->width();
1950                     trailingSpaces.fRight = trailingSpaces.fLeft + delta;
1951                     clip.fRight -= delta;
1952                 }
1953             }
1954 
1955             clip.offset(this->offset());
1956             if (trailingSpaces.width() > 0) {
1957                 trailingSpaces.offset(this->offset());
1958             }
1959 
1960             // Check if we can merge two boxes instead of adding a new one
1961             auto merge = [&lastRun, &context, &boxes](SkRect clip) {
1962                 bool mergedBoxes = false;
1963                 if (!boxes.empty() &&
1964                     lastRun != nullptr &&
1965                     context.run->leftToRight() == lastRun->leftToRight() &&
1966                     lastRun->placeholderStyle() == nullptr &&
1967                     context.run->placeholderStyle() == nullptr &&
1968                     nearlyEqual(lastRun->heightMultiplier(),
1969                                 context.run->heightMultiplier()) &&
1970 #ifndef USE_SKIA_TXT
1971                     lastRun->font() == context.run->font())
1972 #else
1973                     IsRSFontEquals(lastRun->font(), context.run->font()))
1974 #endif
1975                 {
1976                     auto& lastBox = boxes.back();
1977                     if (nearlyEqual(lastBox.rect.fTop, clip.fTop) &&
1978                         nearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
1979                             (nearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
1980                              nearlyEqual(lastBox.rect.fRight, clip.fLeft)))
1981                     {
1982                         lastBox.rect.fLeft = std::min(lastBox.rect.fLeft, clip.fLeft);
1983                         lastBox.rect.fRight = std::max(lastBox.rect.fRight, clip.fRight);
1984                         mergedBoxes = true;
1985                     }
1986                 }
1987                 lastRun = context.run;
1988                 return mergedBoxes;
1989             };
1990 
1991             if (!merge(clip)) {
1992                 boxes.emplace_back(clip, context.run->getTextDirection());
1993             }
1994             if (!nearlyZero(trailingSpaces.width()) && !merge(trailingSpaces)) {
1995                 boxes.emplace_back(trailingSpaces, paragraphStyle.getTextDirection());
1996             }
1997 
1998             if (rectWidthStyle == RectWidthStyle::kMax && !isLastLine()) {
1999                 // Align the very left/right box horizontally
2000                 auto lineStart = this->offset().fX;
2001                 auto lineEnd = this->offset().fX + this->width();
2002                 auto left = boxes[startBox];
2003                 auto right = boxes.back();
2004                 if (left.rect.fLeft > lineStart && left.direction == TextDirection::kRtl) {
2005                     left.rect.fRight = left.rect.fLeft;
2006                     left.rect.fLeft = 0;
2007                     boxes.insert(boxes.begin() + startBox + 1, left);
2008                 }
2009                 if (right.direction == TextDirection::kLtr &&
2010                     right.rect.fRight >= lineEnd &&
2011                     right.rect.fRight < fOwner->widthWithTrailingSpaces()) {
2012                     right.rect.fLeft = right.rect.fRight;
2013                     right.rect.fRight = fOwner->widthWithTrailingSpaces();
2014                     boxes.emplace_back(right);
2015                 }
2016             }
2017 
2018             return true;
2019         });
2020         return true;
2021     });
2022     if (fOwner->getApplyRoundingHack()) {
2023         for (auto& r : boxes) {
2024             r.rect.fLeft = littleRound(r.rect.fLeft);
2025             r.rect.fRight = littleRound(r.rect.fRight);
2026             r.rect.fTop = littleRound(r.rect.fTop);
2027             r.rect.fBottom = littleRound(r.rect.fBottom);
2028         }
2029     }
2030 }
2031 
2032 PositionWithAffinity TextLine::getGlyphPositionAtCoordinate(SkScalar dx) {
2033 
2034     if (SkScalarNearlyZero(this->width()) && SkScalarNearlyZero(this->spacesWidth())) {
2035         // TODO: this is one of the flutter changes that have to go away eventually
2036         //  Empty line is a special case in txtlib (but only when there are no spaces, too)
2037         auto utf16Index = fOwner->getUTF16Index(this->fTextExcludingSpaces.end);
2038         return { SkToS32(utf16Index) , kDownstream };
2039     }
2040 
2041     PositionWithAffinity result(0, Affinity::kDownstream);
2042 #ifdef OHOS_SUPPORT
2043     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
2044 #else
2045     this->iterateThroughVisualRuns(true,
2046 #endif
2047         [this, dx, &result]
2048         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
2049             bool keepLooking = true;
2050             *runWidthInLine = this->iterateThroughSingleRunByStyles(
2051             TextAdjustment::GraphemeGluster, run, runOffsetInLine, textRange, StyleType::kNone,
2052             [this, run, dx, &result, &keepLooking]
2053             (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context0) {
2054 
2055                 SkScalar offsetX = this->offset().fX;
2056                 ClipContext context = context0;
2057 
2058                 // Correct the clip size because libtxt counts trailing spaces
2059                 if (run->leftToRight()) {
2060                     context.clip.fRight += context.fExcludedTrailingSpaces; // extending clip to the right
2061                 } else {
2062                     // Clip starts from 0; we cannot extend it to the left from that
2063                 }
2064                 // However, we need to offset the clip
2065                 context.clip.offset(offsetX, 0.0f);
2066 
2067                 // This patch will help us to avoid a floating point error
2068                 if (SkScalarNearlyEqual(context.clip.fRight, dx, 0.01f)) {
2069                     context.clip.fRight = dx;
2070                 }
2071 
2072                 if (dx <= context.clip.fLeft) {
2073                     // All the other runs are placed right of this one
2074                     auto utf16Index = fOwner->getUTF16Index(context.run->globalClusterIndex(context.pos));
2075                     if (run->leftToRight()) {
2076                         result = { SkToS32(utf16Index), kDownstream};
2077                         keepLooking = false;
2078                     } else {
2079 #ifdef OHOS_SUPPORT
2080                         result = { SkToS32(utf16Index + 1), kUpstream};
2081                         size_t glyphCnt = context.run->glyphs().size();
2082                         if ((glyphCnt != 0) && (context.run->fUtf8Range.end() - context.run->fUtf8Range.begin()) /
2083                             glyphCnt == EMOJI_WIDTH) {
2084                             result = { SkToS32(utf16Index + 2), kUpstream};
2085                         }
2086 #else
2087                         result = { SkToS32(utf16Index + 1), kUpstream};
2088 #endif
2089                         // If we haven't reached the end of the run we need to keep looking
2090                         keepLooking = context.pos != 0;
2091                     }
2092                     // For RTL we go another way
2093                     return !run->leftToRight();
2094                 }
2095 
2096                 if (dx >= context.clip.fRight) {
2097                     // We have to keep looking ; just in case keep the last one as the closest
2098                     auto utf16Index = fOwner->getUTF16Index(context.run->globalClusterIndex(context.pos + context.size));
2099                     if (run->leftToRight()) {
2100                         result = {SkToS32(utf16Index), kUpstream};
2101                     } else {
2102                         result = {SkToS32(utf16Index), kDownstream};
2103                     }
2104                     // For RTL we go another way
2105                     return run->leftToRight();
2106                 }
2107 
2108                 // So we found the run that contains our coordinates
2109                 // Find the glyph position in the run that is the closest left of our point
2110                 // TODO: binary search
2111                 size_t found = context.pos;
2112                 for (size_t index = context.pos; index < context.pos + context.size; ++index) {
2113                     // TODO: this rounding is done to match Flutter tests. Must be removed..
2114                     auto end = context.run->positionX(index) + context.fTextShift + offsetX;
2115                     if (fOwner->getApplyRoundingHack()) {
2116                         end = littleRound(end);
2117                     }
2118                     if (end > dx) {
2119                         break;
2120                     } else if (end == dx && !context.run->leftToRight()) {
2121                         // When we move RTL variable end points to the beginning of the code point which is included
2122                         found = index;
2123                         break;
2124                     }
2125                     found = index;
2126                 }
2127 
2128                 SkScalar glyphemePosLeft = context.run->positionX(found) + context.fTextShift + offsetX;
2129                 SkScalar glyphemesWidth = context.run->positionX(found + 1) - context.run->positionX(found);
2130 
2131                 // Find the grapheme range that contains the point
2132                 auto clusterIndex8 = context.run->globalClusterIndex(found);
2133                 auto clusterEnd8 = context.run->globalClusterIndex(found + 1);
2134                 auto graphemes = fOwner->countSurroundingGraphemes({clusterIndex8, clusterEnd8});
2135 
2136                 SkScalar center = (context.clip.right() + context.clip.left()) / 2;
2137                 if (graphemes.size() > 1) {
2138                     // Calculate the position proportionally based on grapheme count
2139                     SkScalar averageGraphemeWidth = glyphemesWidth / graphemes.size();
2140                     SkScalar delta = dx - glyphemePosLeft;
2141                     int graphemeIndex = SkScalarNearlyZero(averageGraphemeWidth)
2142                                          ? 0
2143                                          : SkScalarFloorToInt(delta / averageGraphemeWidth);
2144                     auto graphemeCenter = glyphemePosLeft + graphemeIndex * averageGraphemeWidth +
2145                                           averageGraphemeWidth * fOwner->getTextSplitRatio();
2146                     auto graphemeUtf8Index = graphemes[graphemeIndex];
2147                     if ((dx < graphemeCenter) == context.run->leftToRight()) {
2148                         size_t utf16Index = fOwner->getUTF16Index(graphemeUtf8Index);
2149                         result = { SkToS32(utf16Index), kDownstream };
2150                     } else {
2151                         size_t utf16Index = fOwner->getUTF16Index(graphemeUtf8Index + 1);
2152                         result = { SkToS32(utf16Index), kUpstream };
2153                     }
2154                     // Keep UTF16 index as is
2155                 } else if ((dx < center) == context.run->leftToRight()) {
2156                     size_t utf16Index = fOwner->getUTF16Index(clusterIndex8);
2157                     result = { SkToS32(utf16Index), kDownstream };
2158                 } else {
2159 #ifdef OHOS_SUPPORT
2160                     size_t utf16Index = 0;
2161                     size_t glyphCnt = context.run->glyphs().size();
2162                     if ((glyphCnt != 0) && !context.run->leftToRight() && (context.run->fUtf8Range.end() -
2163                         context.run->fUtf8Range.begin()) / glyphCnt == EMOJI_WIDTH) {
2164                         utf16Index = fOwner->getUTF16Index(clusterIndex8) + 2;
2165                     } else if (!context.run->leftToRight()) {
2166                         utf16Index = fOwner->getUTF16Index(clusterIndex8) + 1;
2167                     } else {
2168                         utf16Index = fOwner->getUTF16Index(clusterEnd8);
2169                     }
2170 #else
2171                     size_t utf16Index = context.run->leftToRight()
2172                                                 ? fOwner->getUTF16Index(clusterEnd8)
2173                                                 : fOwner->getUTF16Index(clusterIndex8) + 1;
2174 #endif
2175                     result = { SkToS32(utf16Index), kUpstream };
2176                 }
2177 
2178                 return keepLooking = false;
2179 
2180             });
2181             return keepLooking;
2182         }
2183     );
2184     return result;
2185 }
2186 
2187 void TextLine::getRectsForPlaceholders(std::vector<TextBox>& boxes) {
2188 #ifdef OHOS_SUPPORT
2189     this->iterateThroughVisualRuns(EllipsisReadStrategy::READ_REPLACED_WORD, true,
2190 #else
2191     this->iterateThroughVisualRuns( true,
2192 #endif
2193         [&boxes, this](const Run* run, SkScalar runOffset, TextRange textRange,
2194                         SkScalar* width) {
2195                 auto context = this->measureTextInsideOneRun(
2196                         textRange, run, runOffset, 0, true, TextAdjustment::GraphemeGluster);
2197                 *width = context.clip.width();
2198 
2199             if (textRange.width() == 0) {
2200                 return true;
2201             }
2202             if (!run->isPlaceholder()) {
2203                 return true;
2204             }
2205 
2206             SkRect clip = context.clip;
2207             clip.offset(this->offset());
2208 
2209             if (fOwner->getApplyRoundingHack()) {
2210                 clip.fLeft = littleRound(clip.fLeft);
2211                 clip.fRight = littleRound(clip.fRight);
2212                 clip.fTop = littleRound(clip.fTop);
2213                 clip.fBottom = littleRound(clip.fBottom);
2214             }
2215             boxes.emplace_back(clip, run->getTextDirection());
2216             return true;
2217         });
2218 }
2219 
2220 size_t TextLine::getGlyphCount() const
2221 {
2222     size_t glyphCount = 0;
2223     for (auto& blob: fTextBlobCache) {
2224         glyphCount += blob.fVisitor_Size;
2225     }
2226     return glyphCount;
2227 }
2228 
2229 #ifdef OHOS_SUPPORT
2230 std::vector<std::unique_ptr<RunBase>> TextLine::getGlyphRuns() const
2231 {
2232     std::vector<std::unique_ptr<RunBase>> runBases;
2233     size_t num = 0;
2234     // Gets the offset position of the current line across the paragraph
2235     size_t pos = fClusterRange.start;
2236     size_t trailSpaces = 0;
2237     for (auto& blob: fTextBlobCache) {
2238         ++num;
2239         if (blob.fVisitor_Size == 0) {
2240             continue;
2241         }
2242         if (num == fTextBlobCache.size()) {
2243             // Counts how many tabs have been removed from the end of the current line
2244             trailSpaces = fGhostClusterRange.width() - fClusterRange.width();
2245         }
2246         std::unique_ptr<RunBaseImpl> runBaseImplPtr = std::make_unique<RunBaseImpl>(
2247             blob.fBlob, blob.fOffset, blob.fPaint, blob.fClippingNeeded, blob.fClipRect,
2248             blob.fVisitor_Run, blob.fVisitor_Pos, pos, trailSpaces, blob.fVisitor_Size);
2249 
2250         // Calculate the position of each blob, relative to the entire paragraph
2251         pos += blob.fVisitor_Size;
2252         runBases.emplace_back(std::move(runBaseImplPtr));
2253     }
2254     return runBases;
2255 }
2256 #else
2257 std::vector<std::unique_ptr<RunBase>> TextLine::getGlyphRuns() const
2258 {
2259     std::vector<std::unique_ptr<RunBase>> runBases;
2260     for (auto& blob: fTextBlobCache) {
2261         std::unique_ptr<RunBaseImpl> runBaseImplPtr = std::make_unique<RunBaseImpl>(
2262             blob.fBlob, blob.fOffset, blob.fPaint, blob.fClippingNeeded, blob.fClipRect,
2263             blob.fVisitor_Run, blob.fVisitor_Pos, blob.fVisitor_Size);
2264         runBases.emplace_back(std::move(runBaseImplPtr));
2265     }
2266     return runBases;
2267 }
2268 #endif
2269 
2270 #ifdef OHOS_SUPPORT
2271 int getEndWhitespaceCount(const ClusterRange& range, ParagraphImpl* owner)
2272 {
2273     if (owner == nullptr) {
2274         return 0;
2275     }
2276 
2277     int endWhitespaceCount = 0;
2278     for (auto clusterIndex = range.end - 1; clusterIndex >= range.start; clusterIndex--) {
2279         if (!owner->cluster(clusterIndex).isWhitespaceBreak()) {
2280             break;
2281         }
2282 
2283         endWhitespaceCount++;
2284         if (clusterIndex == range.start) {
2285             break;
2286         }
2287     }
2288 
2289     return endWhitespaceCount;
2290 }
2291 
2292 std::unique_ptr<TextLineBase> TextLine::createTruncatedLine(double width, EllipsisModal ellipsisMode,
2293     const std::string& ellipsisStr)
2294 {
2295     if (width > 0 && (ellipsisMode == EllipsisModal::HEAD || ellipsisMode == EllipsisModal::TAIL)) {
2296         TextLine textLine = CloneSelf();
2297         if (width < widthWithEllipsisSpaces() && !ellipsisStr.empty()) {
2298             if (ellipsisMode == EllipsisModal::HEAD) {
2299                 textLine.fIsTextLineEllipsisHeadModal = true;
2300                 textLine.setTextBlobCachePopulated(false);
2301                 textLine.createHeadEllipsis(width, SkString(ellipsisStr), true);
2302             } else if (ellipsisMode == EllipsisModal::TAIL) {
2303                 textLine.fIsTextLineEllipsisHeadModal = false;
2304                 textLine.setTextBlobCachePopulated(false);
2305                 int endWhitespaceCount = getEndWhitespaceCount(fGhostClusterRange, fOwner);
2306                 textLine.fGhostClusterRange.end -= endWhitespaceCount;
2307                 textLine.createTailEllipsis(width, SkString(ellipsisStr), true, fOwner->getWordBreakType());
2308             }
2309         }
2310         return std::make_unique<TextLineBaseImpl>(std::make_unique<TextLine>(std::move(textLine)));
2311     }
2312 
2313     return nullptr;
2314 }
2315 
2316 double TextLine::getTypographicBounds(double* ascent, double* descent, double* leading) const
2317 {
2318     if (ascent == nullptr || descent == nullptr || leading == nullptr) {
2319         return 0.0;
2320     }
2321 
2322     *ascent = std::abs(fMaxRunMetrics.ascent());
2323     *descent = std::abs(fMaxRunMetrics.descent());
2324     *leading = fMaxRunMetrics.leading();
2325     return widthWithEllipsisSpaces();
2326 }
2327 
2328 size_t getPrevGlyphsIndex(const ClusterRange& range, ParagraphImpl* owner, RunIndex& prevRunIndex)
2329 {
2330     if (owner == nullptr) {
2331         return 0;
2332     }
2333 
2334     size_t glyphsIndex = 0;
2335     auto clusterIndex = range.start - 1;
2336     prevRunIndex = owner->cluster(clusterIndex).runIndex();
2337     if (prevRunIndex != owner->cluster(range.start).runIndex()) {
2338         // Belongs to a different run.
2339         return 0;
2340     }
2341 
2342     for (; clusterIndex >= 0; clusterIndex--) {
2343         RunIndex runIndex = owner->cluster(clusterIndex).runIndex();
2344         if (prevRunIndex != runIndex) {
2345             // Found a different run.
2346             break;
2347         }
2348 
2349         glyphsIndex++;
2350 
2351         if (clusterIndex == 0) {
2352             // All belong to the first run.
2353             break;
2354         }
2355     }
2356 
2357     return glyphsIndex;
2358 }
2359 
2360 #ifndef USE_SKIA_TXT
2361 std::vector<SkRect> getAllRectInfo(const ClusterRange& range, ParagraphImpl* owner)
2362 {
2363     std::vector<SkRect> rectVec;
2364 #else
2365 std::vector<RSRect> getAllRectInfo(const ClusterRange& range, ParagraphImpl* owner)
2366 {
2367     std::vector<RSRect> rectVec;
2368 #endif
2369     if (owner == nullptr) {
2370         return rectVec;
2371     }
2372 
2373     // If it is not the first line, you need to get the GlyphsIndex of the first character.
2374     size_t glyphsIndex  = 0;
2375     RunIndex prevRunIndex = 0;
2376     if (range.start > 0) {
2377         glyphsIndex = getPrevGlyphsIndex(range, owner, prevRunIndex);
2378     }
2379 
2380     for (auto clusterIndex = range.start; clusterIndex < range.end; clusterIndex++) {
2381         RunIndex runIndex = owner->cluster(clusterIndex).runIndex();
2382         if (prevRunIndex != runIndex) {
2383             glyphsIndex = 0;
2384         }
2385 
2386         auto run = owner->cluster(clusterIndex).runOrNull();
2387         if (run == nullptr) {
2388             break;
2389         }
2390 
2391         SkGlyphID glyphId = run->glyphs()[glyphsIndex];
2392 #ifndef USE_SKIA_TXT
2393         SkRect glyphBounds;
2394         run->font().getBounds(&glyphId, 1, &glyphBounds, nullptr);
2395 #else
2396         RSRect glyphBounds;
2397         run->font().GetWidths(&glyphId, 1, nullptr, &glyphBounds);
2398 #endif
2399         rectVec.push_back(glyphBounds);
2400         glyphsIndex++;
2401         prevRunIndex = runIndex;
2402     }
2403 
2404     return rectVec;
2405 }
2406 
2407 RSRect TextLine::getImageBounds() const
2408 {
2409     // Look for the first non-space character from the end and get its advance and index
2410     // to calculate the final image bounds.
2411     SkRect rect = {0.0, 0.0, 0.0, 0.0};
2412     int endWhitespaceCount = getEndWhitespaceCount(fGhostClusterRange, fOwner);
2413     if (endWhitespaceCount == (fGhostClusterRange.end - fGhostClusterRange.start)) {
2414         // Full of Spaces.
2415         return {};
2416     }
2417     SkScalar endAdvance = fOwner->cluster(fGhostClusterRange.end - endWhitespaceCount - 1).width();
2418 
2419     // The first space width of the line needs to be added to the x value.
2420     SkScalar startWhitespaceAdvance = 0.0;
2421     int startWhitespaceCount = 0;
2422     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; clusterIndex++) {
2423         if (fOwner->cluster(clusterIndex).isWhitespaceBreak()) {
2424             startWhitespaceAdvance += fOwner->cluster(clusterIndex).width();
2425             startWhitespaceCount++;
2426         } else {
2427             break;
2428         }
2429     }
2430 
2431     // Gets rect information for all characters in line.
2432     auto rectVec = getAllRectInfo(fGhostClusterRange, fOwner);
2433     // Calculate the final y and height.
2434     auto joinRect = rectVec[startWhitespaceCount];
2435     for (int i = startWhitespaceCount + 1; i < rectVec.size() - endWhitespaceCount; ++i) {
2436         joinRect.Join(rectVec[i]);
2437     }
2438 
2439     SkScalar lineWidth = width();
2440     auto endRect = rectVec[rectVec.size() - endWhitespaceCount - 1];
2441 #ifndef USE_SKIA_TXT
2442     SkScalar x = rectVec[startWhitespaceCount].x() + startWhitespaceAdvance;
2443     SkScalar y = joinRect.bottom();
2444     SkScalar width = lineWidth - (endAdvance - endRect.x() - endRect.width()) - x;
2445     SkScalar height = joinRect.height();
2446 #else
2447     SkScalar x = rectVec[startWhitespaceCount].GetLeft() + startWhitespaceAdvance;
2448     SkScalar y = joinRect.GetBottom();
2449     SkScalar width = lineWidth - (endAdvance - endRect.GetLeft() - endRect.GetWidth()) - x;
2450     SkScalar height = joinRect.GetHeight();
2451 #endif
2452 
2453     rect.setXYWH(x, y, width, height);
2454     return {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom};
2455 }
2456 
2457 double TextLine::getTrailingSpaceWidth() const
2458 {
2459     return spacesWidth();
2460 }
2461 
2462 int32_t TextLine::getStringIndexForPosition(SkPoint point) const
2463 {
2464     int32_t index = fGhostClusterRange.start;
2465     double offset = point.x();
2466     if (offset >= widthWithEllipsisSpaces()) {
2467         index = fGhostClusterRange.end;
2468     } else if (offset > 0) {
2469         double curOffset = 0.0;
2470         for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
2471             double characterWidth = fOwner->cluster(clusterIndex).width();
2472             if (offset <= curOffset + characterWidth / 2) {
2473                 return index;
2474             }
2475             index++;
2476             curOffset += characterWidth;
2477         }
2478     }
2479 
2480     return index;
2481 }
2482 
2483 double TextLine::getOffsetForStringIndex(int32_t index) const
2484 {
2485     double offset = 0.0;
2486     if (index <= 0) {
2487         return offset;
2488     }
2489 
2490     if (index >= fGhostClusterRange.end) {
2491         offset = widthWithEllipsisSpaces();
2492     } else if (index > fGhostClusterRange.start) {
2493         size_t clusterIndex = fGhostClusterRange.start;
2494         while (clusterIndex < fGhostClusterRange.end) {
2495             offset += fOwner->cluster(clusterIndex).width();
2496             if (++clusterIndex == index) {
2497                 break;
2498             }
2499         }
2500     }
2501 
2502     return offset;
2503 }
2504 
2505 std::map<int32_t, double> TextLine::getIndexAndOffsets(bool& isHardBreak) const
2506 {
2507     std::map<int32_t, double> offsetMap;
2508     double offset = 0.0;
2509     for (auto clusterIndex = fGhostClusterRange.start; clusterIndex < fGhostClusterRange.end; ++clusterIndex) {
2510         auto& cluster = fOwner->cluster(clusterIndex);
2511         offset += cluster.width();
2512         isHardBreak = cluster.isHardBreak();
2513         if (!isHardBreak) {
2514             offsetMap[clusterIndex] = offset;
2515         }
2516     }
2517     return offsetMap;
2518 }
2519 
2520 double TextLine::getAlignmentOffset(double alignmentFactor, double alignmentWidth) const
2521 {
2522     double lineWidth = width();
2523     if (alignmentWidth <= lineWidth) {
2524         return 0.0;
2525     }
2526 
2527     double offset = 0.0;
2528     TextDirection textDirection = fOwner->paragraphStyle().getTextDirection();
2529     if (alignmentFactor <= 0) {
2530         // Flush left.
2531         if (textDirection == TextDirection::kRtl) {
2532             offset =  lineWidth - alignmentWidth;
2533         }
2534     } else if (alignmentFactor < 1) {
2535         // Align according to the alignmentFactor.
2536         if (textDirection == TextDirection::kLtr) {
2537             offset = (alignmentWidth - lineWidth) * alignmentFactor;
2538         } else {
2539             offset = (lineWidth - alignmentWidth) * (1 - alignmentFactor);
2540         }
2541     } else {
2542         // Flush right.
2543         if (textDirection == TextDirection::kLtr) {
2544             offset = alignmentWidth - lineWidth;
2545         }
2546     }
2547 
2548     return offset;
2549 }
2550 #endif
2551 
2552 TextLine TextLine::CloneSelf()
2553 {
2554     TextLine textLine;
2555     textLine.fBlockRange = this->fBlockRange;
2556     textLine.fTextExcludingSpaces = this->fTextExcludingSpaces;
2557     textLine.fText = this->fText;
2558     textLine.fTextIncludingNewlines = this->fTextIncludingNewlines;
2559     textLine.fClusterRange = this->fClusterRange;
2560 
2561     textLine.fGhostClusterRange = this->fGhostClusterRange;
2562     textLine.fRunsInVisualOrder = this->fRunsInVisualOrder;
2563     textLine.fAdvance = this->fAdvance;
2564     textLine.fOffset = this->fOffset;
2565     textLine.fShift = this->fShift;
2566 
2567     textLine.fWidthWithSpaces = this->fWidthWithSpaces;
2568     if (this->fEllipsis) {
2569         textLine.fEllipsis = std::make_unique<Run>(*this->fEllipsis);
2570     }
2571 
2572     textLine.fSizes = this->fSizes;
2573     textLine.fMaxRunMetrics = this->fMaxRunMetrics;
2574     textLine.fHasBackground = this->fHasBackground;
2575     textLine.fHasShadows = this->fHasShadows;
2576     textLine.fHasDecorations = this->fHasDecorations;
2577     textLine.fAscentStyle = this->fAscentStyle;
2578     textLine.fDescentStyle = this->fDescentStyle;
2579     textLine.fTextBlobCachePopulated = this->fTextBlobCachePopulated;
2580 #ifdef OHOS_SUPPORT
2581     textLine.fOwner = this->fOwner;
2582     textLine.fIsTextLineEllipsisHeadModal = this->fIsTextLineEllipsisHeadModal;
2583 #endif
2584 
2585     textLine.roundRectAttrs = this->roundRectAttrs;
2586     textLine.fTextBlobCache = this->fTextBlobCache;
2587     textLine.fTextRangeReplacedByEllipsis = this->fTextRangeReplacedByEllipsis;
2588     textLine.fEllipsisIndex = this->fEllipsisIndex;
2589     textLine.fLastClipRunLtr = this->fLastClipRunLtr;
2590     return textLine;
2591 }
2592 }  // namespace textlayout
2593 }  // namespace skia
2594