xref: /aosp_15_r20/external/skia/modules/skparagraph/src/TextWrapper.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker // Copyright 2019 Google LLC.
2*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skparagraph/src/ParagraphImpl.h"
3*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skparagraph/src/TextWrapper.h"
4*c8dee2aaSAndroid Build Coastguard Worker 
5*c8dee2aaSAndroid Build Coastguard Worker namespace skia {
6*c8dee2aaSAndroid Build Coastguard Worker namespace textlayout {
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker namespace {
9*c8dee2aaSAndroid Build Coastguard Worker struct LineBreakerWithLittleRounding {
LineBreakerWithLittleRoundingskia::textlayout::__anonb82136770111::LineBreakerWithLittleRounding10*c8dee2aaSAndroid Build Coastguard Worker     LineBreakerWithLittleRounding(SkScalar maxWidth, bool applyRoundingHack)
11*c8dee2aaSAndroid Build Coastguard Worker         : fLower(maxWidth - 0.25f)
12*c8dee2aaSAndroid Build Coastguard Worker         , fMaxWidth(maxWidth)
13*c8dee2aaSAndroid Build Coastguard Worker         , fUpper(maxWidth + 0.25f)
14*c8dee2aaSAndroid Build Coastguard Worker         , fApplyRoundingHack(applyRoundingHack) {}
15*c8dee2aaSAndroid Build Coastguard Worker 
breakLineskia::textlayout::__anonb82136770111::LineBreakerWithLittleRounding16*c8dee2aaSAndroid Build Coastguard Worker     bool breakLine(SkScalar width) const {
17*c8dee2aaSAndroid Build Coastguard Worker         if (width < fLower) {
18*c8dee2aaSAndroid Build Coastguard Worker             return false;
19*c8dee2aaSAndroid Build Coastguard Worker         } else if (width > fUpper) {
20*c8dee2aaSAndroid Build Coastguard Worker             return true;
21*c8dee2aaSAndroid Build Coastguard Worker         }
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker         auto val = std::fabs(width);
24*c8dee2aaSAndroid Build Coastguard Worker         SkScalar roundedWidth;
25*c8dee2aaSAndroid Build Coastguard Worker         if (fApplyRoundingHack) {
26*c8dee2aaSAndroid Build Coastguard Worker             if (val < 10000) {
27*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarRoundToScalar(width * 100) * (1.0f/100);
28*c8dee2aaSAndroid Build Coastguard Worker             } else if (val < 100000) {
29*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarRoundToScalar(width *  10) * (1.0f/10);
30*c8dee2aaSAndroid Build Coastguard Worker             } else {
31*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarFloorToScalar(width);
32*c8dee2aaSAndroid Build Coastguard Worker             }
33*c8dee2aaSAndroid Build Coastguard Worker         } else {
34*c8dee2aaSAndroid Build Coastguard Worker             if (val < 10000) {
35*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarFloorToScalar(width * 100) * (1.0f/100);
36*c8dee2aaSAndroid Build Coastguard Worker             } else if (val < 100000) {
37*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarFloorToScalar(width *  10) * (1.0f/10);
38*c8dee2aaSAndroid Build Coastguard Worker             } else {
39*c8dee2aaSAndroid Build Coastguard Worker                 roundedWidth = SkScalarFloorToScalar(width);
40*c8dee2aaSAndroid Build Coastguard Worker             }
41*c8dee2aaSAndroid Build Coastguard Worker         }
42*c8dee2aaSAndroid Build Coastguard Worker         return roundedWidth > fMaxWidth;
43*c8dee2aaSAndroid Build Coastguard Worker     }
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar fLower, fMaxWidth, fUpper;
46*c8dee2aaSAndroid Build Coastguard Worker     const bool fApplyRoundingHack;
47*c8dee2aaSAndroid Build Coastguard Worker };
48*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
49*c8dee2aaSAndroid Build Coastguard Worker 
50*c8dee2aaSAndroid Build Coastguard Worker // Since we allow cluster clipping when they don't fit
51*c8dee2aaSAndroid Build Coastguard Worker // we have to work with stretches - parts of clusters
lookAhead(SkScalar maxWidth,Cluster * endOfClusters,bool applyRoundingHack)52*c8dee2aaSAndroid Build Coastguard Worker void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack) {
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker     reset();
55*c8dee2aaSAndroid Build Coastguard Worker     fEndLine.metrics().clean();
56*c8dee2aaSAndroid Build Coastguard Worker     fWords.startFrom(fEndLine.startCluster(), fEndLine.startPos());
57*c8dee2aaSAndroid Build Coastguard Worker     fClusters.startFrom(fEndLine.startCluster(), fEndLine.startPos());
58*c8dee2aaSAndroid Build Coastguard Worker     fClip.startFrom(fEndLine.startCluster(), fEndLine.startPos());
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker     LineBreakerWithLittleRounding breaker(maxWidth, applyRoundingHack);
61*c8dee2aaSAndroid Build Coastguard Worker     Cluster* nextNonBreakingSpace = nullptr;
62*c8dee2aaSAndroid Build Coastguard Worker     for (auto cluster = fEndLine.endCluster(); cluster < endOfClusters; ++cluster) {
63*c8dee2aaSAndroid Build Coastguard Worker         if (cluster->isHardBreak()) {
64*c8dee2aaSAndroid Build Coastguard Worker         } else if (
65*c8dee2aaSAndroid Build Coastguard Worker                 // TODO: Trying to deal with flutter rounding problem. Must be removed...
66*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar width = fWords.width() + fClusters.width() + cluster->width();
67*c8dee2aaSAndroid Build Coastguard Worker                 breaker.breakLine(width)) {
68*c8dee2aaSAndroid Build Coastguard Worker             if (cluster->isWhitespaceBreak()) {
69*c8dee2aaSAndroid Build Coastguard Worker                 // It's the end of the word
70*c8dee2aaSAndroid Build Coastguard Worker                 fClusters.extend(cluster);
71*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth());
72*c8dee2aaSAndroid Build Coastguard Worker                 fWords.extend(fClusters);
73*c8dee2aaSAndroid Build Coastguard Worker                 continue;
74*c8dee2aaSAndroid Build Coastguard Worker             } else if (cluster->run().isPlaceholder()) {
75*c8dee2aaSAndroid Build Coastguard Worker                 if (!fClusters.empty()) {
76*c8dee2aaSAndroid Build Coastguard Worker                     // Placeholder ends the previous word
77*c8dee2aaSAndroid Build Coastguard Worker                     fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, this->getClustersTrimmedWidth());
78*c8dee2aaSAndroid Build Coastguard Worker                     fWords.extend(fClusters);
79*c8dee2aaSAndroid Build Coastguard Worker                 }
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker                 if (cluster->width() > maxWidth && fWords.empty()) {
82*c8dee2aaSAndroid Build Coastguard Worker                     // Placeholder is the only text and it's longer than the line;
83*c8dee2aaSAndroid Build Coastguard Worker                     // it does not count in fMinIntrinsicWidth
84*c8dee2aaSAndroid Build Coastguard Worker                     fClusters.extend(cluster);
85*c8dee2aaSAndroid Build Coastguard Worker                     fTooLongCluster = true;
86*c8dee2aaSAndroid Build Coastguard Worker                     fTooLongWord = true;
87*c8dee2aaSAndroid Build Coastguard Worker                 } else {
88*c8dee2aaSAndroid Build Coastguard Worker                     // Placeholder does not fit the line; it will be considered again on the next line
89*c8dee2aaSAndroid Build Coastguard Worker                 }
90*c8dee2aaSAndroid Build Coastguard Worker                 break;
91*c8dee2aaSAndroid Build Coastguard Worker             }
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker             // Walk further to see if there is a too long word, cluster or glyph
94*c8dee2aaSAndroid Build Coastguard Worker             SkScalar nextWordLength = fClusters.width();
95*c8dee2aaSAndroid Build Coastguard Worker             SkScalar nextShortWordLength = nextWordLength;
96*c8dee2aaSAndroid Build Coastguard Worker             for (auto further = cluster; further != endOfClusters; ++further) {
97*c8dee2aaSAndroid Build Coastguard Worker                 if (further->isSoftBreak() || further->isHardBreak() || further->isWhitespaceBreak()) {
98*c8dee2aaSAndroid Build Coastguard Worker                     break;
99*c8dee2aaSAndroid Build Coastguard Worker                 }
100*c8dee2aaSAndroid Build Coastguard Worker                 if (further->run().isPlaceholder()) {
101*c8dee2aaSAndroid Build Coastguard Worker                   // Placeholder ends the word
102*c8dee2aaSAndroid Build Coastguard Worker                   break;
103*c8dee2aaSAndroid Build Coastguard Worker                 }
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker                 if (nextWordLength > 0 && nextWordLength <= maxWidth && further->isIntraWordBreak()) {
106*c8dee2aaSAndroid Build Coastguard Worker                     // The cluster is spaces but not the end of the word in a normal sense
107*c8dee2aaSAndroid Build Coastguard Worker                     nextNonBreakingSpace = further;
108*c8dee2aaSAndroid Build Coastguard Worker                     nextShortWordLength = nextWordLength;
109*c8dee2aaSAndroid Build Coastguard Worker                 }
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker                 if (maxWidth == 0) {
112*c8dee2aaSAndroid Build Coastguard Worker                     // This is a tricky flutter case: layout(width:0) places 1 cluster on each line
113*c8dee2aaSAndroid Build Coastguard Worker                     nextWordLength = std::max(nextWordLength, further->width());
114*c8dee2aaSAndroid Build Coastguard Worker                 } else {
115*c8dee2aaSAndroid Build Coastguard Worker                     nextWordLength += further->width();
116*c8dee2aaSAndroid Build Coastguard Worker                 }
117*c8dee2aaSAndroid Build Coastguard Worker             }
118*c8dee2aaSAndroid Build Coastguard Worker             if (nextWordLength > maxWidth) {
119*c8dee2aaSAndroid Build Coastguard Worker                 if (nextNonBreakingSpace != nullptr) {
120*c8dee2aaSAndroid Build Coastguard Worker                     // We only get here if the non-breaking space improves our situation
121*c8dee2aaSAndroid Build Coastguard Worker                     // (allows us to break the text to fit the word)
122*c8dee2aaSAndroid Build Coastguard Worker                     if (SkScalar shortLength = fWords.width() + nextShortWordLength;
123*c8dee2aaSAndroid Build Coastguard Worker                         !breaker.breakLine(shortLength)) {
124*c8dee2aaSAndroid Build Coastguard Worker                         // We can add the short word to the existing line
125*c8dee2aaSAndroid Build Coastguard Worker                         fClusters = TextStretch(fClusters.startCluster(), nextNonBreakingSpace, fClusters.metrics().getForceStrut());
126*c8dee2aaSAndroid Build Coastguard Worker                         fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextShortWordLength);
127*c8dee2aaSAndroid Build Coastguard Worker                         fWords.extend(fClusters);
128*c8dee2aaSAndroid Build Coastguard Worker                     } else {
129*c8dee2aaSAndroid Build Coastguard Worker                         // We can place the short word on the next line
130*c8dee2aaSAndroid Build Coastguard Worker                         fClusters.clean();
131*c8dee2aaSAndroid Build Coastguard Worker                     }
132*c8dee2aaSAndroid Build Coastguard Worker                     // Either way we are not in "word is too long" situation anymore
133*c8dee2aaSAndroid Build Coastguard Worker                     break;
134*c8dee2aaSAndroid Build Coastguard Worker                 }
135*c8dee2aaSAndroid Build Coastguard Worker                 // If the word is too long we can break it right now and hope it's enough
136*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextWordLength);
137*c8dee2aaSAndroid Build Coastguard Worker                 if (fClusters.endPos() - fClusters.startPos() > 1 ||
138*c8dee2aaSAndroid Build Coastguard Worker                     fWords.empty()) {
139*c8dee2aaSAndroid Build Coastguard Worker                     fTooLongWord = true;
140*c8dee2aaSAndroid Build Coastguard Worker                 } else {
141*c8dee2aaSAndroid Build Coastguard Worker                     // Even if the word is too long there is a very little space on this line.
142*c8dee2aaSAndroid Build Coastguard Worker                     // let's deal with it on the next line.
143*c8dee2aaSAndroid Build Coastguard Worker                 }
144*c8dee2aaSAndroid Build Coastguard Worker             }
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker             if (cluster->width() > maxWidth) {
147*c8dee2aaSAndroid Build Coastguard Worker                 fClusters.extend(cluster);
148*c8dee2aaSAndroid Build Coastguard Worker                 fTooLongCluster = true;
149*c8dee2aaSAndroid Build Coastguard Worker                 fTooLongWord = true;
150*c8dee2aaSAndroid Build Coastguard Worker             }
151*c8dee2aaSAndroid Build Coastguard Worker             break;
152*c8dee2aaSAndroid Build Coastguard Worker         }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker         if (cluster->run().isPlaceholder()) {
155*c8dee2aaSAndroid Build Coastguard Worker             if (!fClusters.empty()) {
156*c8dee2aaSAndroid Build Coastguard Worker                 // Placeholder ends the previous word (placeholders are ignored in trimming)
157*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth());
158*c8dee2aaSAndroid Build Coastguard Worker                 fWords.extend(fClusters);
159*c8dee2aaSAndroid Build Coastguard Worker             }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker             // Placeholder is separate word and its width now is counted in minIntrinsicWidth
162*c8dee2aaSAndroid Build Coastguard Worker             fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width());
163*c8dee2aaSAndroid Build Coastguard Worker             fWords.extend(cluster);
164*c8dee2aaSAndroid Build Coastguard Worker         } else {
165*c8dee2aaSAndroid Build Coastguard Worker             fClusters.extend(cluster);
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker             // Keep adding clusters/words
168*c8dee2aaSAndroid Build Coastguard Worker             if (fClusters.endOfWord()) {
169*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, getClustersTrimmedWidth());
170*c8dee2aaSAndroid Build Coastguard Worker                 fWords.extend(fClusters);
171*c8dee2aaSAndroid Build Coastguard Worker             }
172*c8dee2aaSAndroid Build Coastguard Worker         }
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker         if ((fHardLineBreak = cluster->isHardBreak())) {
175*c8dee2aaSAndroid Build Coastguard Worker             // Stop at the hard line break
176*c8dee2aaSAndroid Build Coastguard Worker             break;
177*c8dee2aaSAndroid Build Coastguard Worker         }
178*c8dee2aaSAndroid Build Coastguard Worker     }
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker 
moveForward(bool hasEllipsis)181*c8dee2aaSAndroid Build Coastguard Worker void TextWrapper::moveForward(bool hasEllipsis) {
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker     // We normally break lines by words.
184*c8dee2aaSAndroid Build Coastguard Worker     // The only way we may go to clusters is if the word is too long or
185*c8dee2aaSAndroid Build Coastguard Worker     // it's the first word and it has an ellipsis attached to it.
186*c8dee2aaSAndroid Build Coastguard Worker     // If nothing fits we show the clipping.
187*c8dee2aaSAndroid Build Coastguard Worker     if (!fWords.empty()) {
188*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.extend(fWords);
189*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_IGNORE_SKPARAGRAPH_ELLIPSIS_FIX
190*c8dee2aaSAndroid Build Coastguard Worker         if (!fTooLongWord || hasEllipsis) { // Ellipsis added to a word
191*c8dee2aaSAndroid Build Coastguard Worker #else
192*c8dee2aaSAndroid Build Coastguard Worker         if (!fTooLongWord && !hasEllipsis) { // Ellipsis added to a grapheme
193*c8dee2aaSAndroid Build Coastguard Worker #endif
194*c8dee2aaSAndroid Build Coastguard Worker             return;
195*c8dee2aaSAndroid Build Coastguard Worker         }
196*c8dee2aaSAndroid Build Coastguard Worker     }
197*c8dee2aaSAndroid Build Coastguard Worker     if (!fClusters.empty()) {
198*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.extend(fClusters);
199*c8dee2aaSAndroid Build Coastguard Worker         if (!fTooLongCluster) {
200*c8dee2aaSAndroid Build Coastguard Worker             return;
201*c8dee2aaSAndroid Build Coastguard Worker         }
202*c8dee2aaSAndroid Build Coastguard Worker     }
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     if (!fClip.empty()) {
205*c8dee2aaSAndroid Build Coastguard Worker         // Flutter: forget the clipped cluster but keep the metrics
206*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.metrics().add(fClip.metrics());
207*c8dee2aaSAndroid Build Coastguard Worker     }
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker // Special case for start/end cluster since they can be clipped
211*c8dee2aaSAndroid Build Coastguard Worker void TextWrapper::trimEndSpaces(TextAlign align) {
212*c8dee2aaSAndroid Build Coastguard Worker     // Remember the breaking position
213*c8dee2aaSAndroid Build Coastguard Worker     fEndLine.saveBreak();
214*c8dee2aaSAndroid Build Coastguard Worker     // Skip all space cluster at the end
215*c8dee2aaSAndroid Build Coastguard Worker     for (auto cluster = fEndLine.endCluster();
216*c8dee2aaSAndroid Build Coastguard Worker          cluster >= fEndLine.startCluster() && cluster->isWhitespaceBreak();
217*c8dee2aaSAndroid Build Coastguard Worker          --cluster) {
218*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.trim(cluster);
219*c8dee2aaSAndroid Build Coastguard Worker     }
220*c8dee2aaSAndroid Build Coastguard Worker     fEndLine.trim();
221*c8dee2aaSAndroid Build Coastguard Worker }
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker SkScalar TextWrapper::getClustersTrimmedWidth() {
224*c8dee2aaSAndroid Build Coastguard Worker     // Move the end of the line to the left
225*c8dee2aaSAndroid Build Coastguard Worker     SkScalar width = 0;
226*c8dee2aaSAndroid Build Coastguard Worker     bool trailingSpaces = true;
227*c8dee2aaSAndroid Build Coastguard Worker     for (auto cluster = fClusters.endCluster(); cluster >= fClusters.startCluster(); --cluster) {
228*c8dee2aaSAndroid Build Coastguard Worker         if (cluster->run().isPlaceholder()) {
229*c8dee2aaSAndroid Build Coastguard Worker             continue;
230*c8dee2aaSAndroid Build Coastguard Worker         }
231*c8dee2aaSAndroid Build Coastguard Worker         if (trailingSpaces) {
232*c8dee2aaSAndroid Build Coastguard Worker             if (!cluster->isWhitespaceBreak()) {
233*c8dee2aaSAndroid Build Coastguard Worker                 width += cluster->trimmedWidth(cluster->endPos());
234*c8dee2aaSAndroid Build Coastguard Worker                 trailingSpaces = false;
235*c8dee2aaSAndroid Build Coastguard Worker             }
236*c8dee2aaSAndroid Build Coastguard Worker             continue;
237*c8dee2aaSAndroid Build Coastguard Worker         }
238*c8dee2aaSAndroid Build Coastguard Worker         width += cluster->width();
239*c8dee2aaSAndroid Build Coastguard Worker     }
240*c8dee2aaSAndroid Build Coastguard Worker     return width;
241*c8dee2aaSAndroid Build Coastguard Worker }
242*c8dee2aaSAndroid Build Coastguard Worker 
243*c8dee2aaSAndroid Build Coastguard Worker // Trim the beginning spaces in case of soft line break
244*c8dee2aaSAndroid Build Coastguard Worker std::tuple<Cluster*, size_t, SkScalar> TextWrapper::trimStartSpaces(Cluster* endOfClusters) {
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker     if (fHardLineBreak) {
247*c8dee2aaSAndroid Build Coastguard Worker         // End of line is always end of cluster, but need to skip \n
248*c8dee2aaSAndroid Build Coastguard Worker         auto width = fEndLine.width();
249*c8dee2aaSAndroid Build Coastguard Worker         auto cluster = fEndLine.endCluster() + 1;
250*c8dee2aaSAndroid Build Coastguard Worker         while (cluster < fEndLine.breakCluster() && cluster->isWhitespaceBreak())  {
251*c8dee2aaSAndroid Build Coastguard Worker             width += cluster->width();
252*c8dee2aaSAndroid Build Coastguard Worker             ++cluster;
253*c8dee2aaSAndroid Build Coastguard Worker         }
254*c8dee2aaSAndroid Build Coastguard Worker         return std::make_tuple(fEndLine.breakCluster() + 1, 0, width);
255*c8dee2aaSAndroid Build Coastguard Worker     }
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     // breakCluster points to the end of the line;
258*c8dee2aaSAndroid Build Coastguard Worker     // It's a soft line break so we need to move lineStart forward skipping all the spaces
259*c8dee2aaSAndroid Build Coastguard Worker     auto width = fEndLine.widthWithGhostSpaces();
260*c8dee2aaSAndroid Build Coastguard Worker     auto cluster = fEndLine.breakCluster() + 1;
261*c8dee2aaSAndroid Build Coastguard Worker     while (cluster < endOfClusters && cluster->isWhitespaceBreak()) {
262*c8dee2aaSAndroid Build Coastguard Worker         width += cluster->width();
263*c8dee2aaSAndroid Build Coastguard Worker         ++cluster;
264*c8dee2aaSAndroid Build Coastguard Worker     }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     if (fEndLine.breakCluster()->isWhitespaceBreak() && fEndLine.breakCluster() < endOfClusters) {
267*c8dee2aaSAndroid Build Coastguard Worker         // In case of a soft line break by the whitespace
268*c8dee2aaSAndroid Build Coastguard Worker         // fBreak should point to the beginning of the next line
269*c8dee2aaSAndroid Build Coastguard Worker         // (it only matters when there are trailing spaces)
270*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.shiftBreak();
271*c8dee2aaSAndroid Build Coastguard Worker     }
272*c8dee2aaSAndroid Build Coastguard Worker 
273*c8dee2aaSAndroid Build Coastguard Worker     return std::make_tuple(cluster, 0, width);
274*c8dee2aaSAndroid Build Coastguard Worker }
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker // TODO: refactor the code for line ending (with/without ellipsis)
277*c8dee2aaSAndroid Build Coastguard Worker void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
278*c8dee2aaSAndroid Build Coastguard Worker                                      SkScalar maxWidth,
279*c8dee2aaSAndroid Build Coastguard Worker                                      const AddLineToParagraph& addLine) {
280*c8dee2aaSAndroid Build Coastguard Worker     fHeight = 0;
281*c8dee2aaSAndroid Build Coastguard Worker     fMinIntrinsicWidth = std::numeric_limits<SkScalar>::min();
282*c8dee2aaSAndroid Build Coastguard Worker     fMaxIntrinsicWidth = std::numeric_limits<SkScalar>::min();
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker     auto span = parent->clusters();
285*c8dee2aaSAndroid Build Coastguard Worker     if (span.empty()) {
286*c8dee2aaSAndroid Build Coastguard Worker         return;
287*c8dee2aaSAndroid Build Coastguard Worker     }
288*c8dee2aaSAndroid Build Coastguard Worker     auto maxLines = parent->paragraphStyle().getMaxLines();
289*c8dee2aaSAndroid Build Coastguard Worker     auto align = parent->paragraphStyle().effective_align();
290*c8dee2aaSAndroid Build Coastguard Worker     auto unlimitedLines = maxLines == std::numeric_limits<size_t>::max();
291*c8dee2aaSAndroid Build Coastguard Worker     auto endlessLine = !SkIsFinite(maxWidth);
292*c8dee2aaSAndroid Build Coastguard Worker     auto hasEllipsis = parent->paragraphStyle().ellipsized();
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker     auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent;
295*c8dee2aaSAndroid Build Coastguard Worker     auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent;
296*c8dee2aaSAndroid Build Coastguard Worker     bool firstLine = true; // We only interested in fist line if we have to disable the first ascent
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker     SkScalar softLineMaxIntrinsicWidth = 0;
299*c8dee2aaSAndroid Build Coastguard Worker     fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight());
300*c8dee2aaSAndroid Build Coastguard Worker     auto end = span.end() - 1;
301*c8dee2aaSAndroid Build Coastguard Worker     auto start = span.begin();
302*c8dee2aaSAndroid Build Coastguard Worker     InternalLineMetrics maxRunMetrics;
303*c8dee2aaSAndroid Build Coastguard Worker     bool needEllipsis = false;
304*c8dee2aaSAndroid Build Coastguard Worker     while (fEndLine.endCluster() != end) {
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker         this->lookAhead(maxWidth, end, parent->getApplyRoundingHack());
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker         auto lastLine = (hasEllipsis && unlimitedLines) || fLineNumber >= maxLines;
309*c8dee2aaSAndroid Build Coastguard Worker         needEllipsis = hasEllipsis && !endlessLine && lastLine;
310*c8dee2aaSAndroid Build Coastguard Worker 
311*c8dee2aaSAndroid Build Coastguard Worker         this->moveForward(needEllipsis);
312*c8dee2aaSAndroid Build Coastguard Worker         needEllipsis &= fEndLine.endCluster() < end - 1; // Only if we have some text to ellipsize
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker         // Do not trim end spaces on the naturally last line of the left aligned text
315*c8dee2aaSAndroid Build Coastguard Worker         this->trimEndSpaces(align);
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker         // For soft line breaks add to the line all the spaces next to it
318*c8dee2aaSAndroid Build Coastguard Worker         Cluster* startLine;
319*c8dee2aaSAndroid Build Coastguard Worker         size_t pos;
320*c8dee2aaSAndroid Build Coastguard Worker         SkScalar widthWithSpaces;
321*c8dee2aaSAndroid Build Coastguard Worker         std::tie(startLine, pos, widthWithSpaces) = this->trimStartSpaces(end);
322*c8dee2aaSAndroid Build Coastguard Worker 
323*c8dee2aaSAndroid Build Coastguard Worker         if (needEllipsis && !fHardLineBreak) {
324*c8dee2aaSAndroid Build Coastguard Worker             // This is what we need to do to preserve a space before the ellipsis
325*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.restoreBreak();
326*c8dee2aaSAndroid Build Coastguard Worker             widthWithSpaces = fEndLine.widthWithGhostSpaces();
327*c8dee2aaSAndroid Build Coastguard Worker         }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker         // If the line is empty with the hard line break, let's take the paragraph font (flutter???)
330*c8dee2aaSAndroid Build Coastguard Worker         if (fEndLine.metrics().isClean()) {
331*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.setMetrics(parent->getEmptyMetrics());
332*c8dee2aaSAndroid Build Coastguard Worker         }
333*c8dee2aaSAndroid Build Coastguard Worker 
334*c8dee2aaSAndroid Build Coastguard Worker         // Deal with placeholder clusters == runs[@size==1]
335*c8dee2aaSAndroid Build Coastguard Worker         Run* lastRun = nullptr;
336*c8dee2aaSAndroid Build Coastguard Worker         for (auto cluster = fEndLine.startCluster(); cluster <= fEndLine.endCluster(); ++cluster) {
337*c8dee2aaSAndroid Build Coastguard Worker             auto r = cluster->runOrNull();
338*c8dee2aaSAndroid Build Coastguard Worker             if (r == lastRun) {
339*c8dee2aaSAndroid Build Coastguard Worker                 continue;
340*c8dee2aaSAndroid Build Coastguard Worker             }
341*c8dee2aaSAndroid Build Coastguard Worker             lastRun = r;
342*c8dee2aaSAndroid Build Coastguard Worker             if (lastRun->placeholderStyle() != nullptr) {
343*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(lastRun->size() == 1);
344*c8dee2aaSAndroid Build Coastguard Worker                 // Update the placeholder metrics so we can get the placeholder positions later
345*c8dee2aaSAndroid Build Coastguard Worker                 // and the line metrics (to make sure the placeholder fits)
346*c8dee2aaSAndroid Build Coastguard Worker                 lastRun->updateMetrics(&fEndLine.metrics());
347*c8dee2aaSAndroid Build Coastguard Worker             }
348*c8dee2aaSAndroid Build Coastguard Worker         }
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker         // Before we update the line metrics with struts,
351*c8dee2aaSAndroid Build Coastguard Worker         // let's save it for GetRectsForRange(RectHeightStyle::kMax)
352*c8dee2aaSAndroid Build Coastguard Worker         maxRunMetrics = fEndLine.metrics();
353*c8dee2aaSAndroid Build Coastguard Worker         maxRunMetrics.fForceStrut = false;
354*c8dee2aaSAndroid Build Coastguard Worker 
355*c8dee2aaSAndroid Build Coastguard Worker         // TODO: keep start/end/break info for text and runs but in a better way that below
356*c8dee2aaSAndroid Build Coastguard Worker         TextRange textExcludingSpaces(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end);
357*c8dee2aaSAndroid Build Coastguard Worker         TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.breakCluster()->textRange().start);
358*c8dee2aaSAndroid Build Coastguard Worker         TextRange textIncludingNewlines(fEndLine.startCluster()->textRange().start, startLine->textRange().start);
359*c8dee2aaSAndroid Build Coastguard Worker         if (startLine == end) {
360*c8dee2aaSAndroid Build Coastguard Worker             textIncludingNewlines.end = parent->text().size();
361*c8dee2aaSAndroid Build Coastguard Worker             text.end = parent->text().size();
362*c8dee2aaSAndroid Build Coastguard Worker         }
363*c8dee2aaSAndroid Build Coastguard Worker         ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1);
364*c8dee2aaSAndroid Build Coastguard Worker         ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start);
365*c8dee2aaSAndroid Build Coastguard Worker 
366*c8dee2aaSAndroid Build Coastguard Worker         if (disableFirstAscent && firstLine) {
367*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent;
368*c8dee2aaSAndroid Build Coastguard Worker         }
369*c8dee2aaSAndroid Build Coastguard Worker         if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak ))) {
370*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
371*c8dee2aaSAndroid Build Coastguard Worker         }
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker         if (parent->strutEnabled()) {
374*c8dee2aaSAndroid Build Coastguard Worker             // Make sure font metrics are not less than the strut
375*c8dee2aaSAndroid Build Coastguard Worker             parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
376*c8dee2aaSAndroid Build Coastguard Worker         }
377*c8dee2aaSAndroid Build Coastguard Worker 
378*c8dee2aaSAndroid Build Coastguard Worker         SkScalar lineHeight = fEndLine.metrics().height();
379*c8dee2aaSAndroid Build Coastguard Worker         firstLine = false;
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker         if (fEndLine.empty()) {
382*c8dee2aaSAndroid Build Coastguard Worker             // Correct text and clusters (make it empty for an empty line)
383*c8dee2aaSAndroid Build Coastguard Worker             textExcludingSpaces.end = textExcludingSpaces.start;
384*c8dee2aaSAndroid Build Coastguard Worker             clusters.end = clusters.start;
385*c8dee2aaSAndroid Build Coastguard Worker         }
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker         // In case of a force wrapping we don't have a break cluster and have to use the end cluster
388*c8dee2aaSAndroid Build Coastguard Worker         text.end = std::max(text.end, textExcludingSpaces.end);
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker         addLine(textExcludingSpaces,
391*c8dee2aaSAndroid Build Coastguard Worker                 text,
392*c8dee2aaSAndroid Build Coastguard Worker                 textIncludingNewlines, clusters, clustersWithGhosts, widthWithSpaces,
393*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.startPos(),
394*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.endPos(),
395*c8dee2aaSAndroid Build Coastguard Worker                 SkVector::Make(0, fHeight),
396*c8dee2aaSAndroid Build Coastguard Worker                 SkVector::Make(fEndLine.width(), lineHeight),
397*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.metrics(),
398*c8dee2aaSAndroid Build Coastguard Worker                 needEllipsis && !fHardLineBreak);
399*c8dee2aaSAndroid Build Coastguard Worker 
400*c8dee2aaSAndroid Build Coastguard Worker         softLineMaxIntrinsicWidth += widthWithSpaces;
401*c8dee2aaSAndroid Build Coastguard Worker 
402*c8dee2aaSAndroid Build Coastguard Worker         fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
403*c8dee2aaSAndroid Build Coastguard Worker         if (fHardLineBreak) {
404*c8dee2aaSAndroid Build Coastguard Worker             softLineMaxIntrinsicWidth = 0;
405*c8dee2aaSAndroid Build Coastguard Worker         }
406*c8dee2aaSAndroid Build Coastguard Worker         // Start a new line
407*c8dee2aaSAndroid Build Coastguard Worker         fHeight += lineHeight;
408*c8dee2aaSAndroid Build Coastguard Worker         if (!fHardLineBreak || startLine != end) {
409*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.clean();
410*c8dee2aaSAndroid Build Coastguard Worker         }
411*c8dee2aaSAndroid Build Coastguard Worker         fEndLine.startFrom(startLine, pos);
412*c8dee2aaSAndroid Build Coastguard Worker         parent->fMaxWidthWithTrailingSpaces = std::max(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces);
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker         if (hasEllipsis && unlimitedLines) {
415*c8dee2aaSAndroid Build Coastguard Worker             // There is one case when we need an ellipsis on a separate line
416*c8dee2aaSAndroid Build Coastguard Worker             // after a line break when width is infinite
417*c8dee2aaSAndroid Build Coastguard Worker             if (!fHardLineBreak) {
418*c8dee2aaSAndroid Build Coastguard Worker                 break;
419*c8dee2aaSAndroid Build Coastguard Worker             }
420*c8dee2aaSAndroid Build Coastguard Worker         } else if (lastLine) {
421*c8dee2aaSAndroid Build Coastguard Worker             // There is nothing more to draw
422*c8dee2aaSAndroid Build Coastguard Worker             fHardLineBreak = false;
423*c8dee2aaSAndroid Build Coastguard Worker             break;
424*c8dee2aaSAndroid Build Coastguard Worker         }
425*c8dee2aaSAndroid Build Coastguard Worker 
426*c8dee2aaSAndroid Build Coastguard Worker         ++fLineNumber;
427*c8dee2aaSAndroid Build Coastguard Worker     }
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker     // We finished formatting the text but we need to scan the rest for some numbers
430*c8dee2aaSAndroid Build Coastguard Worker     // TODO: make it a case of a normal flow
431*c8dee2aaSAndroid Build Coastguard Worker     if (fEndLine.endCluster() != nullptr) {
432*c8dee2aaSAndroid Build Coastguard Worker         auto lastWordLength = 0.0f;
433*c8dee2aaSAndroid Build Coastguard Worker         auto cluster = fEndLine.endCluster();
434*c8dee2aaSAndroid Build Coastguard Worker         while (cluster != end || cluster->endPos() < end->endPos()) {
435*c8dee2aaSAndroid Build Coastguard Worker             fExceededMaxLines = true;
436*c8dee2aaSAndroid Build Coastguard Worker             if (cluster->isHardBreak()) {
437*c8dee2aaSAndroid Build Coastguard Worker                 // Hard line break ends the word and the line
438*c8dee2aaSAndroid Build Coastguard Worker                 fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
439*c8dee2aaSAndroid Build Coastguard Worker                 softLineMaxIntrinsicWidth = 0;
440*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
441*c8dee2aaSAndroid Build Coastguard Worker                 lastWordLength = 0;
442*c8dee2aaSAndroid Build Coastguard Worker             } else if (cluster->isWhitespaceBreak()) {
443*c8dee2aaSAndroid Build Coastguard Worker                 // Whitespaces end the word
444*c8dee2aaSAndroid Build Coastguard Worker                 softLineMaxIntrinsicWidth += cluster->width();
445*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
446*c8dee2aaSAndroid Build Coastguard Worker                 lastWordLength = 0;
447*c8dee2aaSAndroid Build Coastguard Worker             } else if (cluster->run().isPlaceholder()) {
448*c8dee2aaSAndroid Build Coastguard Worker                 // Placeholder ends the previous word and creates a separate one
449*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
450*c8dee2aaSAndroid Build Coastguard Worker                 // Placeholder width now counts in fMinIntrinsicWidth
451*c8dee2aaSAndroid Build Coastguard Worker                 softLineMaxIntrinsicWidth += cluster->width();
452*c8dee2aaSAndroid Build Coastguard Worker                 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width());
453*c8dee2aaSAndroid Build Coastguard Worker                 lastWordLength = 0;
454*c8dee2aaSAndroid Build Coastguard Worker             } else {
455*c8dee2aaSAndroid Build Coastguard Worker                 // Nothing out of ordinary - just add this cluster to the word and to the line
456*c8dee2aaSAndroid Build Coastguard Worker                 softLineMaxIntrinsicWidth += cluster->width();
457*c8dee2aaSAndroid Build Coastguard Worker                 lastWordLength += cluster->width();
458*c8dee2aaSAndroid Build Coastguard Worker             }
459*c8dee2aaSAndroid Build Coastguard Worker             ++cluster;
460*c8dee2aaSAndroid Build Coastguard Worker         }
461*c8dee2aaSAndroid Build Coastguard Worker         fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
462*c8dee2aaSAndroid Build Coastguard Worker         fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
463*c8dee2aaSAndroid Build Coastguard Worker 
464*c8dee2aaSAndroid Build Coastguard Worker         if (parent->lines().empty()) {
465*c8dee2aaSAndroid Build Coastguard Worker             // In case we could not place even a single cluster on the line
466*c8dee2aaSAndroid Build Coastguard Worker             if (disableFirstAscent) {
467*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent;
468*c8dee2aaSAndroid Build Coastguard Worker             }
469*c8dee2aaSAndroid Build Coastguard Worker             if (disableLastDescent && !fHardLineBreak) {
470*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
471*c8dee2aaSAndroid Build Coastguard Worker             }
472*c8dee2aaSAndroid Build Coastguard Worker             fHeight = std::max(fHeight, fEndLine.metrics().height());
473*c8dee2aaSAndroid Build Coastguard Worker         }
474*c8dee2aaSAndroid Build Coastguard Worker     }
475*c8dee2aaSAndroid Build Coastguard Worker 
476*c8dee2aaSAndroid Build Coastguard Worker     if (fHardLineBreak) {
477*c8dee2aaSAndroid Build Coastguard Worker         if (disableLastDescent) {
478*c8dee2aaSAndroid Build Coastguard Worker             fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
479*c8dee2aaSAndroid Build Coastguard Worker         }
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker         // Last character is a line break
482*c8dee2aaSAndroid Build Coastguard Worker         if (parent->strutEnabled()) {
483*c8dee2aaSAndroid Build Coastguard Worker             // Make sure font metrics are not less than the strut
484*c8dee2aaSAndroid Build Coastguard Worker             parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
485*c8dee2aaSAndroid Build Coastguard Worker         }
486*c8dee2aaSAndroid Build Coastguard Worker 
487*c8dee2aaSAndroid Build Coastguard Worker         ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.endCluster() - start);
488*c8dee2aaSAndroid Build Coastguard Worker         addLine(fEndLine.breakCluster()->textRange(),
489*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.breakCluster()->textRange(),
490*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.endCluster()->textRange(),
491*c8dee2aaSAndroid Build Coastguard Worker                 clusters,
492*c8dee2aaSAndroid Build Coastguard Worker                 clusters,
493*c8dee2aaSAndroid Build Coastguard Worker                 0,
494*c8dee2aaSAndroid Build Coastguard Worker                 0,
495*c8dee2aaSAndroid Build Coastguard Worker                 0,
496*c8dee2aaSAndroid Build Coastguard Worker                 SkVector::Make(0, fHeight),
497*c8dee2aaSAndroid Build Coastguard Worker                 SkVector::Make(0, fEndLine.metrics().height()),
498*c8dee2aaSAndroid Build Coastguard Worker                 fEndLine.metrics(),
499*c8dee2aaSAndroid Build Coastguard Worker                 needEllipsis);
500*c8dee2aaSAndroid Build Coastguard Worker         fHeight += fEndLine.metrics().height();
501*c8dee2aaSAndroid Build Coastguard Worker         parent->lines().back().setMaxRunMetrics(maxRunMetrics);
502*c8dee2aaSAndroid Build Coastguard Worker     }
503*c8dee2aaSAndroid Build Coastguard Worker 
504*c8dee2aaSAndroid Build Coastguard Worker     if (parent->lines().empty()) {
505*c8dee2aaSAndroid Build Coastguard Worker         return;
506*c8dee2aaSAndroid Build Coastguard Worker     }
507*c8dee2aaSAndroid Build Coastguard Worker     // Correct line metric styles for the first and for the last lines if needed
508*c8dee2aaSAndroid Build Coastguard Worker     if (disableFirstAscent) {
509*c8dee2aaSAndroid Build Coastguard Worker         parent->lines().front().setAscentStyle(LineMetricStyle::Typographic);
510*c8dee2aaSAndroid Build Coastguard Worker     }
511*c8dee2aaSAndroid Build Coastguard Worker     if (disableLastDescent) {
512*c8dee2aaSAndroid Build Coastguard Worker         parent->lines().back().setDescentStyle(LineMetricStyle::Typographic);
513*c8dee2aaSAndroid Build Coastguard Worker     }
514*c8dee2aaSAndroid Build Coastguard Worker }
515*c8dee2aaSAndroid Build Coastguard Worker 
516*c8dee2aaSAndroid Build Coastguard Worker }  // namespace textlayout
517*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skia
518