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