• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 #ifndef TextWrapper_DEFINED
3 #define TextWrapper_DEFINED
4 
5 #include <string>
6 #ifdef ENABLE_TEXT_ENHANCE
7 #include "include/ParagraphStyle.h"
8 #include "src/Run.h"
9 #endif
10 #include "include/core/SkSpan.h"
11 #include "modules/skparagraph/src/TextLine.h"
12 
13 #ifdef ENABLE_TEXT_ENHANCE
14 #include <list>
15 #include <vector>
16 
17 #include "include/TextStyle.h"
18 #include "modules/skparagraph/src/ParagraphImpl.h"
19 #include "SkScalar.h"
20 #endif
21 
22 namespace skia {
23 namespace textlayout {
24 
25 #ifdef ENABLE_TEXT_ENHANCE
26 const size_t STRATEGY_START_POS{2};
27 const size_t MIN_COST_POS{2};
28 const size_t MAX_LINES_LIMIT{1000000000};
29 #endif
30 
31 class ParagraphImpl;
32 #ifdef ENABLE_TEXT_ENHANCE
33 class TextTabAlign;
34 #endif
35 
36 class TextWrapper {
37     class ClusterPos {
38     public:
ClusterPos()39         ClusterPos() : fCluster(nullptr), fPos(0) {}
ClusterPos(Cluster * cluster,size_t pos)40         ClusterPos(Cluster* cluster, size_t pos) : fCluster(cluster), fPos(pos) {}
cluster()41         inline Cluster* cluster() const { return fCluster; }
position()42         inline size_t position() const { return fPos; }
setPosition(size_t pos)43         inline void setPosition(size_t pos) { fPos = pos; }
clean()44         void clean() {
45             fCluster = nullptr;
46             fPos = 0;
47         }
move(bool up)48         void move(bool up) {
49             fCluster += up ? 1 : -1;
50             fPos = up ? 0 : fCluster->endPos();
51         }
52 #ifdef ENABLE_TEXT_ENHANCE
setCluster(Cluster * cluster)53         void setCluster(Cluster* cluster) { fCluster = cluster; }
54 #endif
55 
56     private:
57         Cluster* fCluster;
58         size_t fPos;
59     };
60     class TextStretch {
61     public:
TextStretch()62         TextStretch() : fStart(), fEnd(), fWidth(0), fWidthWithGhostSpaces(0) {}
TextStretch(Cluster * s,Cluster * e,bool forceStrut)63         TextStretch(Cluster* s, Cluster* e, bool forceStrut)
64                 : fStart(s, 0), fEnd(e, e->endPos()), fMetrics(forceStrut), fWidth(0), fWidthWithGhostSpaces(0) {
65             for (auto c = s; c <= e; ++c) {
66                 if (auto r = c->runOrNull()) {
67                     fMetrics.add(r);
68                 }
69                 if (c < e) {
70                     fWidth += c->width();
71                 }
72             }
73             fWidthWithGhostSpaces = fWidth;
74         }
75 
76 #ifdef ENABLE_TEXT_ENHANCE
TextStretch(Cluster * c,bool forceStrut)77         TextStretch(Cluster* c, bool forceStrut)
78                 : fStart(c, 0), fEnd(c, c->endPos()), fMetrics(forceStrut), fWidth(0), fWidthWithGhostSpaces(0) {
79             if (auto r = c->runOrNull()) {
80                 fMetrics.add(r);
81             }
82             fWidth = c->width();
83             fWidthWithGhostSpaces = fWidth;
84         }
85 
split()86         std::vector<TextStretch> split() {
87             ParagraphImpl* owner = fStart.cluster()->getOwner();
88             Cluster* cluster = fStart.cluster();
89             std::vector<TextStretch> result{};
90             while(cluster <= fEnd.cluster()) {
91                 auto endIndex = (cluster)->textRange().end;
92                 Cluster* endCluster = &owner->cluster(
93                     std::min(owner->fClustersIndexFromCodeUnit[endIndex], owner->clusters().size() - 1));
94                 TextStretch singleClusterStretch = TextStretch(cluster, metrics().getForceStrut());
95                 result.push_back(singleClusterStretch);
96                 cluster = endCluster;
97             }
98             return result;
99         }
100 
setStartCluster(Cluster * cluster)101         void setStartCluster(Cluster* cluster) { fStart.setCluster(cluster); }
102 #endif
103 
width()104         inline SkScalar width() const { return fWidth; }
widthWithGhostSpaces()105         SkScalar widthWithGhostSpaces() const { return fWidthWithGhostSpaces; }
startCluster()106         inline Cluster* startCluster() const { return fStart.cluster(); }
endCluster()107         inline Cluster* endCluster() const { return fEnd.cluster(); }
breakCluster()108         inline Cluster* breakCluster() const { return fBreak.cluster(); }
metrics()109         inline InternalLineMetrics& metrics() { return fMetrics; }
startPos()110         inline size_t startPos() const { return fStart.position(); }
endPos()111         inline size_t endPos() const { return fEnd.position(); }
endOfCluster()112         bool endOfCluster() { return fEnd.position() == fEnd.cluster()->endPos(); }
endOfWord()113         bool endOfWord() {
114             return endOfCluster() &&
115                    (fEnd.cluster()->isHardBreak() || fEnd.cluster()->isSoftBreak());
116         }
117 
extend(TextStretch & stretch)118         void extend(TextStretch& stretch) {
119             fMetrics.add(stretch.fMetrics);
120             fEnd = stretch.fEnd;
121             fWidth += stretch.fWidth;
122             stretch.clean();
123         }
124 
empty()125         bool empty() { return fStart.cluster() == fEnd.cluster() &&
126                               fStart.position() == fEnd.position(); }
127 
setMetrics(const InternalLineMetrics & metrics)128         void setMetrics(const InternalLineMetrics& metrics) { fMetrics = metrics; }
129 
extend(Cluster * cluster)130         void extend(Cluster* cluster) {
131             if (fStart.cluster() == nullptr) {
132                 fStart = ClusterPos(cluster, cluster->startPos());
133             }
134             fEnd = ClusterPos(cluster, cluster->endPos());
135             // TODO: Make sure all the checks are correct and there are no unnecessary checks
136             auto& r = cluster->run();
137             if (!cluster->isHardBreak() && !r.isPlaceholder()) {
138                 // We ignore metrics for \n as the Flutter does
139                 fMetrics.add(&r);
140             }
141             fWidth += cluster->width();
142         }
143 
extend(Cluster * cluster,size_t pos)144         void extend(Cluster* cluster, size_t pos) {
145             fEnd = ClusterPos(cluster, pos);
146             if (auto r = cluster->runOrNull()) {
147                 fMetrics.add(r);
148             }
149         }
150 
startFrom(Cluster * cluster,size_t pos)151         void startFrom(Cluster* cluster, size_t pos) {
152             fStart = ClusterPos(cluster, pos);
153             fEnd = ClusterPos(cluster, pos);
154             if (auto r = cluster->runOrNull()) {
155                 // In case of placeholder we should ignore the default text style -
156                 // we will pick up the correct one from the placeholder
157                 if (!r->isPlaceholder()) {
158                     fMetrics.add(r);
159                 }
160             }
161             fWidth = 0;
162         }
163 
saveBreak()164         void saveBreak() {
165             fWidthWithGhostSpaces = fWidth;
166             fBreak = fEnd;
167         }
168 
restoreBreak()169         void restoreBreak() {
170             fWidth = fWidthWithGhostSpaces;
171             fEnd = fBreak;
172         }
173 
shiftBreak()174         void shiftBreak() {
175             fBreak.move(true);
176         }
177 
trim()178         void trim() {
179 
180             if (fEnd.cluster() != nullptr &&
181                 fEnd.cluster()->owner() != nullptr &&
182                 fEnd.cluster()->runOrNull() != nullptr &&
183                 fEnd.cluster()->run().placeholderStyle() == nullptr &&
184                 fWidth > 0) {
185                 fWidth -= (fEnd.cluster()->width() - fEnd.cluster()->trimmedWidth(fEnd.position()));
186             }
187         }
188 
trim(Cluster * cluster)189         void trim(Cluster* cluster) {
190             SkASSERT(fEnd.cluster() == cluster);
191             if (fEnd.cluster() > fStart.cluster()) {
192                 fEnd.move(false);
193                 fWidth -= cluster->width();
194             } else {
195                 fEnd.setPosition(fStart.position());
196                 fWidth = 0;
197             }
198         }
199 
clean()200         void clean() {
201             fStart.clean();
202             fEnd.clean();
203             fWidth = 0;
204             fMetrics.clean();
205         }
206 
207 #ifdef ENABLE_TEXT_ENHANCE
shiftWidth(SkScalar width)208         void shiftWidth(SkScalar width) {
209             fWidth += width;
210         }
211 #endif
212     private:
213         ClusterPos fStart;
214         ClusterPos fEnd;
215         ClusterPos fBreak;
216         InternalLineMetrics fMetrics;
217         SkScalar fWidth;
218         SkScalar fWidthWithGhostSpaces;
219     };
220 
221 public:
TextWrapper()222     TextWrapper() {
223          fLineNumber = 1;
224          fHardLineBreak = false;
225          fExceededMaxLines = false;
226     }
227 
228     using AddLineToParagraph = std::function<void(TextRange textExcludingSpaces,
229                                                   TextRange text,
230                                                   TextRange textIncludingNewlines,
231                                                   ClusterRange clusters,
232                                                   ClusterRange clustersWithGhosts,
233                                                   SkScalar AddLineToParagraph,
234                                                   size_t startClip,
235                                                   size_t endClip,
236                                                   SkVector offset,
237                                                   SkVector advance,
238                                                   InternalLineMetrics metrics,
239 #ifdef ENABLE_TEXT_ENHANCE
240                                                   bool addEllipsis,
241                                                   SkScalar lineIndent,
242                                                   SkScalar noIndentWidth)>;
243 #else
244                                                   bool addEllipsis)>;
245 #endif
246     void breakTextIntoLines(ParagraphImpl* parent,
247                             SkScalar maxWidth,
248                             const AddLineToParagraph& addLine);
249 #ifdef ENABLE_TEXT_ENHANCE
250     void updateMetricsWithPlaceholder(std::vector<Run*>& runs, bool iterateByCluster);
brokeLineWithHyphen()251     bool brokeLineWithHyphen() const { return fBrokeLineWithHyphen; }
252 #endif
height()253     SkScalar height() const { return fHeight; }
minIntrinsicWidth()254     SkScalar minIntrinsicWidth() const { return fMinIntrinsicWidth; }
maxIntrinsicWidth()255     SkScalar maxIntrinsicWidth() const { return fMaxIntrinsicWidth; }
exceededMaxLines()256     bool exceededMaxLines() const { return fExceededMaxLines; }
257 
258 private:
259 #ifdef ENABLE_TEXT_ENHANCE
260     struct FormattingContext {
261         bool unlimitedLines{false};
262         bool endlessLine{false};
263         bool hasEllipsis{false};
264         bool disableFirstAscent{false};
265         bool disableLastDescent{false};
266         size_t maxLines{0};
267         TextAlign align{TextAlign::kLeft};
268     };
269 
270     struct LineTextRanges {
271         TextRange textExcludingSpaces;
272         TextRange text;
273         TextRange textIncludingNewlines;
274         ClusterRange clusters;
275         ClusterRange clustersWithGhosts;
276     };
277 
278     friend TextTabAlign;
279 #endif
280     TextStretch fWords;
281     TextStretch fClusters;
282     TextStretch fClip;
283     TextStretch fEndLine;
284     size_t fLineNumber;
285     bool fTooLongWord;
286     bool fTooLongCluster;
287 
288     bool fHardLineBreak;
289     bool fExceededMaxLines;
290 
291 #ifdef ENABLE_TEXT_ENHANCE
292     SkScalar fHeight{0};
293     SkScalar fMinIntrinsicWidth{std::numeric_limits<SkScalar>::min()};
294     SkScalar fMaxIntrinsicWidth{std::numeric_limits<SkScalar>::min()};
295     bool fBrokeLineWithHyphen{false};
296     std::vector<TextStretch> fWordStretches;
297     std::vector<TextStretch> fLineStretches;
298     std::vector<SkScalar> fWordWidthGroups;
299     std::vector<std::vector<TextStretch>> fWordStretchesBatch;
300     std::vector<std::vector<SkScalar>> fWordWidthGroupsBatch;
301 #else
302     SkScalar fHeight;
303     SkScalar fMinIntrinsicWidth;
304     SkScalar fMaxIntrinsicWidth;
305 #endif
306 
reset()307     void reset() {
308         fWords.clean();
309         fClusters.clean();
310         fClip.clean();
311         fTooLongCluster = false;
312         fTooLongWord = false;
313         fHardLineBreak = false;
314 #ifdef ENABLE_TEXT_ENHANCE
315         fBrokeLineWithHyphen = false;
316         fWordStretches.clear();
317         fLineStretches.clear();
318         fStart = nullptr;
319         fEnd = nullptr;
320 #endif
321     }
322 
323 #ifdef ENABLE_TEXT_ENHANCE
324     void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, WordBreakType wordBreakType,
325                    bool needEllipsis);
326     void moveForward(bool hasEllipsis, bool breakAll); // breakAll = true, break occurs after each character
327     bool lookAheadByHyphen(Cluster* endOfClusters, SkScalar widthBeforeCluster, SkScalar maxWidth);
328     uint64_t CalculateBestScore(std::vector<SkScalar>& widthOut,
329         SkScalar maxWidth, ParagraphImpl* parent, size_t maxLines);
330     static size_t tryBreakWord(Cluster* startCluster,
331                                Cluster* endOfClusters,
332                                SkScalar widthBeforeCluster,
333                                SkScalar maxWidth);
334 
335     static void matchHyphenResult(const std::vector<uint8_t>& result, ParagraphImpl* owner, size_t& pos,
336                                   SkScalar maxWidth, SkScalar length);
337     static std::vector<uint8_t> findBreakPositions(Cluster* startCluster,
338                                                    Cluster* endOfClusters,
339                                                    SkScalar widthBeforeCluster,
340                                                    SkScalar maxWidth);
initParent(ParagraphImpl * parent)341     void initParent(ParagraphImpl* parent) { fParent = parent; }
342     void pushToWordStretches();
343     void pushToWordStretchesBatch();
344     void layoutLinesBalanced(
345         ParagraphImpl* parent, SkScalar maxWidth, const AddLineToParagraph& addLine);
346     void layoutLinesSimple(
347         ParagraphImpl* parent, SkScalar maxWidth, const AddLineToParagraph& addLine);
348     std::vector<SkScalar> generateWordsWidthInfo(const std::vector<TextStretch>& wordStretches);
349     std::vector<std::pair<size_t, size_t>> generateLinesGroupInfo(
350         const std::vector<float>& clustersWidth, SkScalar maxWidth);
351     void generateWordStretches(const SkSpan<Cluster>& span, WordBreakType wordBreakType);
352     void generateLineStretches(const std::vector<std::pair<size_t, size_t>>& linesGroupInfo,
353         std::vector<TextStretch>& wordStretches);
354     void preProcessingForLineStretches();
355     void extendCommonCluster(Cluster* cluster, TextTabAlign& textTabAlign,
356         SkScalar& totalFakeSpacing, WordBreakType wordBreakType);
357     SkScalar getTextStretchTrimmedEndSpaceWidth(const TextStretch& stretch);
358     void formalizedClusters(std::vector<TextStretch>& clusters, SkScalar limitWidth);
359     void generateTextLines(SkScalar maxWidth,
360                             const AddLineToParagraph& addLine,
361                             const SkSpan<Cluster>& span);
362     void initializeFormattingState(SkScalar maxWidth, const SkSpan<Cluster>& span);
363     void processLineStretches(SkScalar maxWidth, const AddLineToParagraph& addLine);
364     void finalizeTextLayout(const AddLineToParagraph& addLine);
365     void prepareLineForFormatting(TextStretch& line);
366     void formatCurrentLine(const AddLineToParagraph& addLine);
367     bool determineIfEllipsisNeeded();
368     void trimLineSpaces();
369     void handleSpecialCases(bool needEllipsis);
370     void updateLineMetrics();
371     void updatePlaceholderMetrics();
372     void adjustLineMetricsForFirstLastLine();
373     void applyStrutMetrics();
374     LineTextRanges calculateLineTextRanges();
375     SkScalar calculateLineHeight();
376     void addFormattedLineToParagraph(const AddLineToParagraph& addLine, bool needEllipsis);
377     void updateIntrinsicWidths();
378     bool shouldBreakFormattingLoop();
379     bool isLastLine() const;
380     void advanceToNextLine();
381     void prepareForNextLine();
382     void processRemainingClusters();
383     void handleHardBreak(float& lastWordLength);
384     void handleWhitespaceBreak(Cluster* cluster, float& lastWordLength);
385     void handlePlaceholder(Cluster* cluster, float& lastWordLength);
386     void handleRegularCluster(Cluster* cluster, float& lastWordLength);
387     void adjustMetricsForEmptyParagraph();
388     void addFinalLineBreakIfNeeded(const AddLineToParagraph& addLine);
389     void adjustFirstLastLineMetrics();
390 #else
391     void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack);
392     void moveForward(bool hasEllipsis);
393 #endif
394     void trimEndSpaces(TextAlign align);
395     std::tuple<Cluster*, size_t, SkScalar> trimStartSpaces(Cluster* endOfClusters);
396     SkScalar getClustersTrimmedWidth();
397 
398 #ifdef ENABLE_TEXT_ENHANCE
399     ParagraphImpl* fParent{nullptr};
400     FormattingContext fFormattingContext;
401     InternalLineMetrics fMaxRunMetrics;
402     SkScalar fSoftLineMaxIntrinsicWidth{0.0f};
403     SkScalar fCurrentLineWidthWithSpaces{0.0f};
404     SkScalar fNoIndentWidth{0.0f};
405     bool fFirstLine{false};
406     Cluster* fCurrentStartLine{nullptr};
407     size_t fCurrentStartPos{0};
408     Cluster* fStart{nullptr};
409     Cluster* fEnd{nullptr};
410 #endif
411 };
412 }  // namespace textlayout
413 }  // namespace skia
414 
415 #endif  // TextWrapper_DEFINED
416