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