1// Copyright 2019 Google LLC.
2#ifndef TextWrapper_DEFINED
3#define TextWrapper_DEFINED
4
5#include <string>
6#include "include/ParagraphStyle.h"
7#include "include/core/SkSpan.h"
8#include "modules/skparagraph/src/TextLine.h"
9
10namespace skia {
11namespace textlayout {
12
13class ParagraphImpl;
14#ifdef OHOS_SUPPORT
15class TextTabAlign;
16#endif
17
18class TextWrapper {
19    class ClusterPos {
20    public:
21        ClusterPos() : fCluster(nullptr), fPos(0) {}
22        ClusterPos(Cluster* cluster, size_t pos) : fCluster(cluster), fPos(pos) {}
23        inline Cluster* cluster() const { return fCluster; }
24        inline size_t position() const { return fPos; }
25        inline void setPosition(size_t pos) { fPos = pos; }
26        void clean() {
27            fCluster = nullptr;
28            fPos = 0;
29        }
30        void move(bool up) {
31            fCluster += up ? 1 : -1;
32            fPos = up ? 0 : fCluster->endPos();
33        }
34
35    private:
36        Cluster* fCluster;
37        size_t fPos;
38    };
39    class TextStretch {
40    public:
41        TextStretch() : fStart(), fEnd(), fWidth(0), fWidthWithGhostSpaces(0) {}
42        TextStretch(Cluster* s, Cluster* e, bool forceStrut)
43                : fStart(s, 0), fEnd(e, e->endPos()), fMetrics(forceStrut), fWidth(0), fWidthWithGhostSpaces(0) {
44            for (auto c = s; c <= e; ++c) {
45                if (auto r = c->runOrNull()) {
46                    fMetrics.add(r);
47                }
48                if (c < e) {
49                    fWidth += c->width();
50                }
51            }
52            fWidthWithGhostSpaces = fWidth;
53        }
54
55        inline SkScalar width() const { return fWidth; }
56        SkScalar widthWithGhostSpaces() const { return fWidthWithGhostSpaces; }
57        inline Cluster* startCluster() const { return fStart.cluster(); }
58        inline Cluster* endCluster() const { return fEnd.cluster(); }
59        inline Cluster* breakCluster() const { return fBreak.cluster(); }
60        inline InternalLineMetrics& metrics() { return fMetrics; }
61        inline size_t startPos() const { return fStart.position(); }
62        inline size_t endPos() const { return fEnd.position(); }
63        bool endOfCluster() { return fEnd.position() == fEnd.cluster()->endPos(); }
64        bool endOfWord() {
65            return endOfCluster() &&
66                   (fEnd.cluster()->isHardBreak() || fEnd.cluster()->isSoftBreak());
67        }
68
69        void extend(TextStretch& stretch) {
70            fMetrics.add(stretch.fMetrics);
71            fEnd = stretch.fEnd;
72            fWidth += stretch.fWidth;
73            stretch.clean();
74        }
75
76        bool empty() { return fStart.cluster() == fEnd.cluster() &&
77                              fStart.position() == fEnd.position(); }
78
79        void setMetrics(const InternalLineMetrics& metrics) { fMetrics = metrics; }
80
81        void extend(Cluster* cluster) {
82            if (fStart.cluster() == nullptr) {
83                fStart = ClusterPos(cluster, cluster->startPos());
84            }
85            fEnd = ClusterPos(cluster, cluster->endPos());
86            // TODO: Make sure all the checks are correct and there are no unnecessary checks
87            auto& r = cluster->run();
88            if (!cluster->isHardBreak() && !r.isPlaceholder()) {
89                // We ignore metrics for \n as the Flutter does
90                fMetrics.add(&r);
91            }
92            fWidth += cluster->width();
93        }
94
95        void extend(Cluster* cluster, size_t pos) {
96            fEnd = ClusterPos(cluster, pos);
97            if (auto r = cluster->runOrNull()) {
98                fMetrics.add(r);
99            }
100        }
101
102        void startFrom(Cluster* cluster, size_t pos) {
103            fStart = ClusterPos(cluster, pos);
104            fEnd = ClusterPos(cluster, pos);
105            if (auto r = cluster->runOrNull()) {
106                // In case of placeholder we should ignore the default text style -
107                // we will pick up the correct one from the placeholder
108                if (!r->isPlaceholder()) {
109                    fMetrics.add(r);
110                }
111            }
112            fWidth = 0;
113        }
114
115        void saveBreak() {
116            fWidthWithGhostSpaces = fWidth;
117            fBreak = fEnd;
118        }
119
120        void restoreBreak() {
121            fWidth = fWidthWithGhostSpaces;
122            fEnd = fBreak;
123        }
124
125        void shiftBreak() {
126            fBreak.move(true);
127        }
128
129        void trim() {
130
131            if (fEnd.cluster() != nullptr &&
132                fEnd.cluster()->owner() != nullptr &&
133                fEnd.cluster()->runOrNull() != nullptr &&
134                fEnd.cluster()->run().placeholderStyle() == nullptr &&
135                fWidth > 0) {
136                fWidth -= (fEnd.cluster()->width() - fEnd.cluster()->trimmedWidth(fEnd.position()));
137            }
138        }
139
140        void trim(Cluster* cluster) {
141            SkASSERT(fEnd.cluster() == cluster);
142            if (fEnd.cluster() > fStart.cluster()) {
143                fEnd.move(false);
144                fWidth -= cluster->width();
145            } else {
146                fEnd.setPosition(fStart.position());
147                fWidth = 0;
148            }
149        }
150
151        void clean() {
152            fStart.clean();
153            fEnd.clean();
154            fWidth = 0;
155            fMetrics.clean();
156        }
157
158#ifdef OHOS_SUPPORT
159        void shiftWidth(SkScalar width) {
160            fWidth += width;
161        }
162#endif
163    private:
164        ClusterPos fStart;
165        ClusterPos fEnd;
166        ClusterPos fBreak;
167        InternalLineMetrics fMetrics;
168        SkScalar fWidth;
169        SkScalar fWidthWithGhostSpaces;
170    };
171
172public:
173    TextWrapper() {
174         fLineNumber = 1;
175         fHardLineBreak = false;
176         fExceededMaxLines = false;
177    }
178
179    using AddLineToParagraph = std::function<void(TextRange textExcludingSpaces,
180                                                  TextRange text,
181                                                  TextRange textIncludingNewlines,
182                                                  ClusterRange clusters,
183                                                  ClusterRange clustersWithGhosts,
184                                                  SkScalar addLineToParagraph,
185                                                  size_t startClip,
186                                                  size_t endClip,
187                                                  SkVector offset,
188                                                  SkVector advance,
189                                                  InternalLineMetrics metrics,
190                                                  bool addEllipsis,
191                                                  SkScalar lineIndent,
192                                                  SkScalar noIndentWidth)>;
193    void breakTextIntoLines(ParagraphImpl* parent,
194                            SkScalar maxWidth,
195                            const AddLineToParagraph& addLine);
196    void updateMetricsWithPlaceholder(std::vector<Run*>& runs, bool iterateByCluster);
197
198    SkScalar height() const { return fHeight; }
199    SkScalar minIntrinsicWidth() const { return fMinIntrinsicWidth; }
200    SkScalar maxIntrinsicWidth() const { return fMaxIntrinsicWidth; }
201    bool exceededMaxLines() const { return fExceededMaxLines; }
202
203private:
204#ifdef OHOS_SUPPORT
205    friend TextTabAlign;
206#endif
207    TextStretch fWords;
208    TextStretch fClusters;
209    TextStretch fClip;
210    TextStretch fEndLine;
211    size_t fLineNumber;
212    bool fTooLongWord;
213    bool fTooLongCluster;
214
215    bool fHardLineBreak;
216    bool fExceededMaxLines;
217
218    SkScalar fHeight;
219    SkScalar fMinIntrinsicWidth;
220    SkScalar fMaxIntrinsicWidth;
221
222    void reset() {
223        fWords.clean();
224        fClusters.clean();
225        fClip.clean();
226        fTooLongCluster = false;
227        fTooLongWord = false;
228        fHardLineBreak = false;
229    }
230
231    SkScalar calculateFakeSpacing(Cluster* cluster, bool autoSpacingEnable);
232    void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, WordBreakType wordBreakType,
233        bool autoSpacingEnable);
234    void moveForward(bool hasEllipsis, bool breakAll); // breakAll = true, break occurs after each character
235    void trimEndSpaces(TextAlign align);
236    std::tuple<Cluster*, size_t, SkScalar> trimStartSpaces(Cluster* endOfClusters);
237    SkScalar getClustersTrimmedWidth();
238    uint64_t CalculateBestScore(std::vector<SkScalar>& widthOut,
239        SkScalar maxWidth, ParagraphImpl* parent, size_t maxLines);
240};
241}  // namespace textlayout
242}  // namespace skia
243
244#endif  // TextWrapper_DEFINED
245