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