• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 #include "modules/skparagraph/src/ParagraphImpl.h"
3 #include "modules/skparagraph/src/TextWrapper.h"
4 
5 namespace skia {
6 namespace textlayout {
7 
8 // Since we allow cluster clipping when they don't fit
9 // we have to work with stretches - parts of clusters
lookAhead(SkScalar maxWidth,Cluster * endOfClusters)10 void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters) {
11     fWords.startFrom(fEndLine.startCluster(), fEndLine.startPos());
12     fClusters.startFrom(fEndLine.startCluster(), fEndLine.startPos());
13     fClip.startFrom(fEndLine.startCluster(), fEndLine.startPos());
14     for (auto cluster = fEndLine.endCluster(); cluster < endOfClusters; ++cluster) {
15         if (fWords.width() + fClusters.width() + cluster->width() > maxWidth) {
16             if (cluster->isWhitespaces()) {
17                 // It's the end of the word
18                 fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, getClustersTrimmedWidth());
19                 fWords.extend(fClusters);
20                 break;
21             }
22             if (cluster->width() > maxWidth) {
23                 // Break the cluster into parts by glyph position
24                 auto delta = maxWidth - (fWords.width() + fClusters.width());
25                 fClip.extend(cluster, cluster->roundPos(delta));
26                 fTooLongCluster = true;
27                 fTooLongWord = true;
28                 break;
29             }
30 
31             // Walk further to see if there is a too long word, cluster or glyph
32             SkScalar nextWordLength = fClusters.width();
33             for (auto further = cluster; further != endOfClusters; ++further) {
34                 if (further->isSoftBreak() || further->isHardBreak()) {
35                     break;
36                 }
37                 nextWordLength += further->width();
38             }
39             if (nextWordLength > maxWidth) {
40                 // If the word is too long we can break it right now and hope it's enough
41                 fTooLongWord = true;
42             }
43 
44             // TODO: this is the place when we use hyphenation
45             fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, nextWordLength);
46             break;
47         }
48 
49         fClusters.extend(cluster);
50 
51         // Keep adding clusters/words
52         if (fClusters.endOfWord()) {
53             fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, getClustersTrimmedWidth());
54             fWords.extend(fClusters);
55         }
56 
57         if ((fHardLineBreak = cluster->isHardBreak())) {
58             // Stop at the hard line break
59             break;
60         }
61     }
62 }
63 
moveForward()64 void TextWrapper::moveForward() {
65     do {
66         if (fWords.width() > 0) {
67             fEndLine.extend(fWords);
68         } else if (fClusters.width() > 0) {
69             fEndLine.extend(fClusters);
70             fTooLongWord = false;
71         } else if (fClip.width() > 0) {
72             fEndLine.extend(fClip);
73             fTooLongWord = false;
74             fTooLongCluster = false;
75         } else {
76             break;
77         }
78     } while (fTooLongWord || fTooLongCluster);
79 }
80 
81 // Special case for start/end cluster since they can be clipped
trimEndSpaces(TextAlign align)82 void TextWrapper::trimEndSpaces(TextAlign align) {
83     // Remember the breaking position
84     fEndLine.saveBreak();
85     // Skip all space cluster at the end
86     //bool left = align == TextAlign::kStart || align == TextAlign::kLeft;
87     bool right = align == TextAlign::kRight || align == TextAlign::kEnd;
88     for (auto cluster = fEndLine.endCluster();
89          cluster >= fEndLine.startCluster() && cluster->isWhitespaces();
90          --cluster) {
91         if ((/*left && */cluster->run()->leftToRight()) ||
92             (right && !cluster->run()->leftToRight()) ||
93              align == TextAlign::kJustify || align == TextAlign::kCenter) {
94             fEndLine.trim(cluster);
95             continue;
96         } else {
97             break;
98         }
99     }
100     if (!right) {
101         fEndLine.trim();
102     }
103 }
104 
getClustersTrimmedWidth()105 SkScalar TextWrapper::getClustersTrimmedWidth() {
106     // Move the end of the line to the left
107     SkScalar width = fClusters.width();
108     auto cluster = fClusters.endCluster();
109     for (; cluster > fClusters.startCluster() && cluster->isWhitespaces(); --cluster) {
110         width -= cluster->width();
111     }
112     if (cluster >= fClusters.startCluster()) {
113         width -= (cluster->width() - cluster->trimmedWidth(cluster->endPos()));
114     }
115     return width;
116 }
117 
118 // Trim the beginning spaces in case of soft line break
trimStartSpaces(Cluster * endOfClusters)119 std::tuple<Cluster*, size_t, SkScalar> TextWrapper::trimStartSpaces(Cluster* endOfClusters) {
120 
121     if (fHardLineBreak) {
122         // End of line is always end of cluster, but need to skip \n
123         auto width = fEndLine.width();
124         auto cluster = fEndLine.endCluster() + 1;
125         while (cluster < fEndLine.breakCluster() && cluster->isWhitespaces()) {
126             width += cluster->width();
127             ++cluster;
128         }
129         return std::make_tuple(fEndLine.breakCluster() + 1, 0, width);
130     }
131 
132     auto width = fEndLine.withWithGhostSpaces(); //fEndLine.width();
133     auto cluster = fEndLine.breakCluster() + 1;
134     while (cluster < endOfClusters && cluster->isWhitespaces()) {
135         width += cluster->width();
136         ++cluster;
137     }
138     return std::make_tuple(cluster, 0, width);
139 }
140 
breakTextIntoLines(ParagraphImpl * parent,SkScalar maxWidth,const AddLineToParagraph & addLine)141 void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
142                                      SkScalar maxWidth,
143                                      const AddLineToParagraph& addLine) {
144     auto span = parent->clusters();
145     auto maxLines = parent->paragraphStyle().getMaxLines();
146     auto& ellipsisStr = parent->paragraphStyle().getEllipsis();
147     auto align = parent->paragraphStyle().getTextAlign();
148 
149     fHeight = 0;
150     fMinIntrinsicWidth = 0;
151     fMaxIntrinsicWidth = 0;
152     fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight());
153     auto end = span.end() - 1;
154     auto start = span.begin();
155     while (fEndLine.endCluster() != end) {
156         reset();
157 
158         lookAhead(maxWidth, end);
159         moveForward();
160 
161         // Do not trim end spaces on the naturally last line of the left aligned text
162         trimEndSpaces(align);
163 
164         // For soft line breaks add to the line all the spaces next to it
165         Cluster* startLine;
166         size_t pos;
167         SkScalar widthWithSpaces;
168         std::tie(startLine, pos, widthWithSpaces) = trimStartSpaces(end);
169 
170         auto lastLine = maxLines == std::numeric_limits<size_t>::max() ||
171             fLineNumber >= maxLines;
172         auto needEllipsis =
173             lastLine &&
174                 !fHardLineBreak &&
175                 fEndLine.endCluster() < end - 1 &&
176                 maxWidth != std::numeric_limits<SkScalar>::max() &&
177                 !ellipsisStr.isEmpty();
178         // TODO: perform ellipsis work here
179         if (parent->strutEnabled()) {
180             // Make sure font metrics are not less than the strut
181             parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
182         }
183         fMaxIntrinsicWidth = SkMaxScalar(fMaxIntrinsicWidth, fEndLine.width());
184         // TODO: keep start/end/break info for text and runs but in a better way that below
185         TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end + 1);
186         TextRange textWithSpaces(fEndLine.startCluster()->textRange().start, startLine->textRange().start);
187         if (fEndLine.breakCluster()->isHardBreak()) {
188             textWithSpaces.end = fEndLine.breakCluster()->textRange().start;
189         } else if (startLine == end) {
190             textWithSpaces.end = fEndLine.breakCluster()->textRange().end;
191         }
192         ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1);
193         ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start);
194         addLine(text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces,
195                 fEndLine.startPos(),
196                 fEndLine.endPos(),
197                 SkVector::Make(0, fHeight),
198                 SkVector::Make(fEndLine.width(), fEndLine.metrics().height()),
199                 fEndLine.metrics(),
200                 needEllipsis);
201 
202         // Start a new line
203         fHeight += fEndLine.metrics().height();
204 
205         if (!fHardLineBreak) {
206             fEndLine.clean();
207         }
208         fEndLine.startFrom(startLine, pos);
209         parent->fMaxWidthWithTrailingSpaces = SkMaxScalar(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces);
210 
211         if (needEllipsis || fLineNumber >= maxLines) {
212             break;
213         }
214         ++fLineNumber;
215     }
216 
217     if (fHardLineBreak) {
218         // Last character is a line break
219         if (parent->strutEnabled()) {
220             // Make sure font metrics are not less than the strut
221             parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
222         }
223         TextRange empty(fEndLine.breakCluster()->textRange().start, fEndLine.breakCluster()->textRange().start);
224         TextRange hardBreak(fEndLine.breakCluster()->textRange().end, fEndLine.breakCluster()->textRange().end);
225         ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.breakCluster() - start);
226         addLine(empty, hardBreak, clusters, clusters,
227                 0,
228                 0,
229                 0,
230                 SkVector::Make(0, fHeight),
231                 SkVector::Make(0, fEndLine.metrics().height()),
232                 fEndLine.metrics(),
233                 false);
234     }
235 }
236 
237 }  // namespace textlayout
238 }  // namespace skia
239