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 | |
9 | namespace skia { |
10 | namespace textlayout { |
11 | |
12 | class ParagraphImpl; |
13 | |
14 | class 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 | |
148 | public: |
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 | |
175 | private: |
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 | |