Dealing with ghost spaces

Change-Id: I9cf133e915658f17d00f279ee1fa2662effa2021
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/231646
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index 4b585a4..49bd652 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -79,16 +79,27 @@
 }
 
 // Special case for start/end cluster since they can be clipped
-void TextWrapper::trimEndSpaces() {
+void TextWrapper::trimEndSpaces(TextAlign align) {
     // Remember the breaking position
     fEndLine.saveBreak();
-    // Move the end of the line to the left
+    // Skip all space cluster at the end
+    //bool left = align == TextAlign::kStart || align == TextAlign::kLeft;
+    bool right = align == TextAlign::kRight || align == TextAlign::kEnd;
     for (auto cluster = fEndLine.endCluster();
          cluster >= fEndLine.startCluster() && cluster->isWhitespaces();
          --cluster) {
-        fEndLine.trim(cluster);
+        if ((/*left && */cluster->run()->leftToRight()) ||
+            (right && !cluster->run()->leftToRight()) ||
+             align == TextAlign::kJustify || align == TextAlign::kCenter) {
+            fEndLine.trim(cluster);
+            continue;
+        } else {
+            break;
+        }
     }
-    fEndLine.trim();
+    if (!right) {
+        fEndLine.trim();
+    }
 }
 
 SkScalar TextWrapper::getClustersTrimmedWidth() {
@@ -105,26 +116,26 @@
 }
 
 // Trim the beginning spaces in case of soft line break
-void TextWrapper::trimStartSpaces(Cluster* endOfClusters) {
-    // Restore the breaking position
-    fEndLine.restoreBreak();
-    fEndLine.nextPos();
+std::tuple<Cluster*, size_t, SkScalar> TextWrapper::trimStartSpaces(Cluster* endOfClusters) {
+
     if (fHardLineBreak) {
         // End of line is always end of cluster, but need to skip \n
-        fEndLine.startFrom(fEndLine.endCluster(), 0);
-        return;
-    }
-    if (fEndLine.endPos() != 0) {
-        // Clipping
-        fEndLine.startFrom(fEndLine.endCluster(), fEndLine.endPos());
-        return;
+        auto width = fEndLine.width();
+        auto cluster = fEndLine.endCluster() + 1;
+        while (cluster < fEndLine.breakCluster() && cluster->isWhitespaces()) {
+            width += cluster->width();
+            ++cluster;
+        }
+        return { fEndLine.breakCluster() + 1, 0, width };
     }
 
-    auto cluster = fEndLine.endCluster();
+    auto width = fEndLine.width();
+    auto cluster = fEndLine.breakCluster() + 1;
     while (cluster < endOfClusters && cluster->isWhitespaces()) {
+        width += cluster->width();
         ++cluster;
     }
-    fEndLine.startFrom(cluster, 0);
+    return { cluster, 0, width };
 }
 
 void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
@@ -132,13 +143,15 @@
                                      const AddLineToParagraph& addLine) {
     auto span = parent->clusters();
     auto maxLines = parent->paragraphStyle().getMaxLines();
-    auto ellipsisStr = parent->paragraphStyle().getEllipsis();
+    auto& ellipsisStr = parent->paragraphStyle().getEllipsis();
+    auto align = parent->paragraphStyle().getTextAlign();
 
     fHeight = 0;
     fMinIntrinsicWidth = 0;
     fMaxIntrinsicWidth = 0;
-    fEndLine = TextStretch(span.begin(), span.begin());
-    auto end = &span.back();
+    fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight());
+    auto end = span.end() - 1;
+    auto start = span.begin();
     while (fEndLine.endCluster() != end) {
         reset();
 
@@ -146,7 +159,13 @@
         moveForward();
 
         // Do not trim end spaces on the naturally last line of the left aligned text
-        trimEndSpaces();
+        trimEndSpaces(align);
+
+        // For soft line breaks add to the line all the spaces next to it
+        Cluster* startLine;
+        size_t pos;
+        SkScalar widthWithSpaces;
+        std::tie(startLine, pos, widthWithSpaces) = trimStartSpaces(end);
 
         auto lastLine = maxLines == std::numeric_limits<size_t>::max() ||
             fLineNumber >= maxLines;
@@ -159,15 +178,20 @@
         // TODO: perform ellipsis work here
         if (parent->strutEnabled()) {
             // Make sure font metrics are not less than the strut
-            parent->strutMetrics().updateLineMetrics(fEndLine.metrics(),
-                                                     parent->strutForceHeight());
+            parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
         }
         fMaxIntrinsicWidth = SkMaxScalar(fMaxIntrinsicWidth, fEndLine.width());
         // TODO: keep start/end/break info for text and runs but in a better way that below
         TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end);
-        TextRange textWithSpaces(fEndLine.startCluster()->textRange().start, fEndLine.breakCluster()->textRange().end);
-        ClusterRange clusters(fEndLine.startCluster() - parent->clusters().begin(), fEndLine.endCluster() - parent->clusters().begin() + 1);
-        addLine(text, textWithSpaces, clusters,
+        TextRange textWithSpaces(fEndLine.startCluster()->textRange().start, startLine->textRange().start);
+        if (fEndLine.breakCluster()->isHardBreak()) {
+            textWithSpaces.end = fEndLine.breakCluster()->textRange().start;
+        } else if (startLine == end) {
+            textWithSpaces.end = fEndLine.breakCluster()->textRange().end;
+        }
+        ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start);
+        ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start);
+        addLine(text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces,
                 fEndLine.startPos(),
                 fEndLine.endPos(),
                 SkVector::Make(0, fHeight),
@@ -178,7 +202,11 @@
         // Start a new line
         fHeight += fEndLine.metrics().height();
 
-        trimStartSpaces(end);
+        if (!fHardLineBreak) {
+            fEndLine.clean();
+        }
+        fEndLine.startFrom(startLine, pos);
+        parent->fMaxWidthWithTrailingSpaces = SkMaxScalar(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces);
 
         if (needEllipsis || fLineNumber >= maxLines) {
             break;
@@ -190,12 +218,13 @@
         // Last character is a line break
         if (parent->strutEnabled()) {
             // Make sure font metrics are not less than the strut
-            parent->strutMetrics().updateLineMetrics(fEndLine.metrics(),
-                                                     parent->strutForceHeight());
+            parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
         }
         TextRange empty(fEndLine.breakCluster()->textRange().start, fEndLine.breakCluster()->textRange().start);
-        ClusterRange clusters(fEndLine.breakCluster() - parent->clusters().begin(), fEndLine.breakCluster() - parent->clusters().begin());
-        addLine(empty, empty, clusters,
+        TextRange hardBreak(fEndLine.breakCluster()->textRange().end, fEndLine.breakCluster()->textRange().end);
+        ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.breakCluster() - start);
+        addLine(empty, hardBreak, clusters, clusters,
+                0,
                 0,
                 0,
                 SkVector::Make(0, fHeight),