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