Small changes

Change-Id: Ic9c41a2dd11b4df8ab24037df0109e36536ec6c3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257892
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skparagraph/include/TextStyle.h b/modules/skparagraph/include/TextStyle.h
index 85ab428..387bf82 100644
--- a/modules/skparagraph/include/TextStyle.h
+++ b/modules/skparagraph/include/TextStyle.h
@@ -243,15 +243,13 @@
     Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {}
     Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {}
 
-    Block(const Block& other) {
-        fRange = other.fRange;
-        fStyle = other.fStyle;
-    }
+    Block(const Block& other) : fRange(other.fRange), fStyle(other.fStyle) {}
 
     void add(TextRange tail) {
         SkASSERT(fRange.end == tail.start);
         fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width());
     }
+
     TextRange fRange;
     TextStyle fStyle;
 };
@@ -273,13 +271,12 @@
             , fBlocksBefore(blocksBefore)
             , fTextBefore(textBefore) {}
 
-    Placeholder(const Placeholder& other) {
-        fRange = other.fRange;
-        fStyle = other.fStyle;
-        fTextStyle = other.fTextStyle;
-        fBlocksBefore = other.fBlocksBefore;
-        fTextBefore = other.fTextBefore;
-    }
+    Placeholder(const Placeholder& other)
+            : fRange(other.fRange)
+            , fStyle(other.fStyle)
+            , fTextStyle(other.fTextStyle)
+            , fBlocksBefore(other.fBlocksBefore)
+            , fTextBefore(other.fTextBefore) {}
 
     TextRange fRange;
     PlaceholderStyle fStyle;
diff --git a/modules/skparagraph/src/OneLineShaper.cpp b/modules/skparagraph/src/OneLineShaper.cpp
index 2974e5e..f777ac8 100644
--- a/modules/skparagraph/src/OneLineShaper.cpp
+++ b/modules/skparagraph/src/OneLineShaper.cpp
@@ -339,6 +339,7 @@
 void OneLineShaper::iterateThroughFontStyles(SkSpan<Block> styleSpan,
                                              const ShapeSingleFontVisitor& visitor) {
     Block combinedBlock;
+
     for (auto& block : styleSpan) {
         SkASSERT(combinedBlock.fRange.width() == 0 ||
                  combinedBlock.fRange.end == block.fRange.start);
@@ -460,11 +461,12 @@
         }
 
         iterateThroughFontStyles(styleSpan,
-                [this, &shaper, textDirection, limitlessWidth, &advanceX](Block block) {
+                [this, &shaper, textDirection, limitlessWidth, &advanceX]
+                (Block block) {
             auto blockSpan = SkSpan<Block>(&block, 1);
 
             // Start from the beginning (hoping that it's a simple case one block - one run)
-            fHeight = block.fStyle.getHeight();
+            fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
             fAdvance = SkVector::Make(advanceX, 0);
             fCurrentText = block.fRange;
             fUnresolvedBlocks.emplace(RunBlock(block.fRange));
@@ -495,8 +497,9 @@
                     }
 
                     fCurrentText = unresolvedRange;
-                    shaper->shape(unresolvedText.begin(), unresolvedText.size(), fontIter, *bidi,
-                                  *script, lang, limitlessWidth, this);
+                    shaper->shape(unresolvedText.begin(), unresolvedText.size(),
+                            fontIter, *bidi,*script, lang,
+                            limitlessWidth, this);
 
                     // Take off the queue the block we tried to resolved -
                     // whatever happened, we have now smaller pieces of it to deal with
@@ -507,7 +510,7 @@
                 return !fUnresolvedBlocks.empty();
             });
 
-            this->finish(block.fRange, block.fStyle.getHeight(), advanceX);
+            this->finish(block.fRange, fHeight, advanceX);
         });
 
         return true;
diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp
index 7d4f08b..f8f287f 100644
--- a/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/modules/skparagraph/src/ParagraphImpl.cpp
@@ -887,7 +887,7 @@
                     return false;
                 }
 
-                if (dx >= context.clip.fRight) {
+                if (dx >= context.clip.fRight + offsetX) {
                     // We have to keep looking but just in case keep the last one as the closes
                     // so far
                     auto index = context.pos + context.size;
@@ -912,7 +912,7 @@
                     }
                     found = i;
                 }
-                auto glyphStart = context.run->positionX(found);
+                auto glyphStart = context.run->positionX(found) + context.fTextShift + offsetX;
                 auto glyphWidth = context.run->positionX(found + 1) - context.run->positionX(found);
                 auto clusterIndex8 = context.run->fClusterIndexes[found];
 
@@ -932,14 +932,11 @@
                     auto averageCodepoint = glyphWidth / graphemeSize;
                     auto codepointStart = glyphStart + averageCodepoint * (codepointIndex - codepoints.start);
                     auto codepointEnd = codepointStart + averageCodepoint;
-                    center = (codepointStart + codepointEnd) / 2 + context.fTextShift;
+                    center = (codepointStart + codepointEnd) / 2;
                 } else {
                     SkASSERT(graphemeSize == 1);
-                    auto codepointStart = glyphStart;
-                    auto codepointEnd = codepointStart + glyphWidth;
-                    center = (codepointStart + codepointEnd) / 2 + context.fTextShift;
+                    center = glyphStart + glyphWidth / 2;
                 }
-
                 if ((dx < center) == context.run->leftToRight()) {
                     result = { SkToS32(codepointIndex), kDownstream };
                 } else {
@@ -1092,15 +1089,25 @@
 }
 
 void ParagraphImpl::computeEmptyMetrics() {
-  auto defaultTextStyle = paragraphStyle().getTextStyle();
+    auto defaultTextStyle = paragraphStyle().getTextStyle();
 
-  auto typefaces = fontCollection()->findTypefaces(
+    auto typefaces = fontCollection()->findTypefaces(
       defaultTextStyle.getFontFamilies(), defaultTextStyle.getFontStyle());
-  auto typeface = typefaces.size() ? typefaces.front() : nullptr;
+    auto typeface = typefaces.size() ? typefaces.front() : nullptr;
 
-  SkFont font(typeface, defaultTextStyle.getFontSize());
-  fEmptyMetrics = InternalLineMetrics(font, paragraphStyle().getStrutStyle().getForceStrutHeight());
-  fStrutMetrics.updateLineMetrics(fEmptyMetrics);
+    SkFont font(typeface, defaultTextStyle.getFontSize());
+
+    fEmptyMetrics = InternalLineMetrics(font, paragraphStyle().getStrutStyle().getForceStrutHeight());
+    if (!paragraphStyle().getStrutStyle().getForceStrutHeight() &&
+        defaultTextStyle.getHeightOverride()) {
+        auto multiplier =
+                defaultTextStyle.getHeight() * defaultTextStyle.getFontSize() / fEmptyMetrics.height();
+        fEmptyMetrics = InternalLineMetrics(fEmptyMetrics.ascent() * multiplier,
+                                      fEmptyMetrics.descent() * multiplier,
+                                      fEmptyMetrics.leading() * multiplier);
+    }
+
+    fStrutMetrics.updateLineMetrics(fEmptyMetrics);
 }
 
 void ParagraphImpl::updateText(size_t from, SkString text) {
diff --git a/modules/skparagraph/src/ParagraphImpl.h b/modules/skparagraph/src/ParagraphImpl.h
index 25f6f13..30508c9 100644
--- a/modules/skparagraph/src/ParagraphImpl.h
+++ b/modules/skparagraph/src/ParagraphImpl.h
@@ -199,6 +199,8 @@
     InternalLineMetrics getEmptyMetrics() const { return fEmptyMetrics; }
     InternalLineMetrics getStrutMetrics() const { return fStrutMetrics; }
 
+    BlockRange findAllBlocks(TextRange textRange);
+
 private:
     friend class ParagraphBuilder;
     friend class ParagraphCacheKey;
@@ -209,7 +211,6 @@
     friend class OneLineShaper;
 
     void calculateBoundaries(ClusterRange clusters, SkVector offset, SkVector advance);
-    BlockRange findAllBlocks(TextRange textRange);
     void extractStyles();
 
     void markGraphemes16();
diff --git a/modules/skparagraph/src/TextStyle.cpp b/modules/skparagraph/src/TextStyle.cpp
index ce0cc40..4fc619a 100644
--- a/modules/skparagraph/src/TextStyle.cpp
+++ b/modules/skparagraph/src/TextStyle.cpp
@@ -84,8 +84,7 @@
     if (fTextShadows.size() != other.fTextShadows.size()) {
         return false;
     }
-
-    for (int32_t i = 0; i < (int32_t)fTextShadows.size(); ++i) {
+    for (size_t i = 0; i < fTextShadows.size(); ++i) {
         if (fTextShadows[i] != other.fTextShadows[i]) {
             return false;
         }
@@ -134,7 +133,6 @@
             // TODO: should not we take typefaces in account?
             return fFontStyle == other.fFontStyle && fFontFamilies == other.fFontFamilies &&
                    fFontSize == other.fFontSize && fHeight == other.fHeight;
-
         default:
             SkASSERT(false);
             return false;
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index 98fc5a4..4f69c46 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -35,9 +35,7 @@
                 break;
             }
             if (cluster->width() > maxWidth) {
-                // Break the cluster into parts by glyph position
-                auto delta = maxWidth - (fWords.width() + fClusters.width());
-                fClip.extend(cluster, cluster->roundPos(delta));
+                fClusters.extend(cluster);
                 fTooLongCluster = true;
                 fTooLongWord = true;
                 break;
@@ -53,11 +51,9 @@
             }
             if (nextWordLength > maxWidth) {
                 // If the word is too long we can break it right now and hope it's enough
+                fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, nextWordLength);
                 fTooLongWord = true;
             }
-
-            // TODO: this is the place when we use hyphenation
-            fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, fTooLongWord ? maxWidth : nextWordLength);
             break;
         }
 
@@ -83,8 +79,10 @@
         } else if (fClusters.width() > 0) {
             fEndLine.extend(fClusters);
             fTooLongWord = false;
+            fTooLongCluster = false;
         } else if (fClip.width() > 0 || (fTooLongWord && fTooLongCluster)) {
-            fEndLine.extend(fClip);
+            // Flutter: forget the clipped cluster but keep the metrics
+            fEndLine.metrics().add(fClip.metrics());
             fTooLongWord = false;
             fTooLongCluster = false;
         } else {
@@ -159,8 +157,8 @@
                                      SkScalar maxWidth,
                                      const AddLineToParagraph& addLine) {
     fHeight = 0;
-    fMinIntrinsicWidth = 0;
-    fMaxIntrinsicWidth = 0;
+    fMinIntrinsicWidth = std::numeric_limits<SkScalar>::min();
+    fMaxIntrinsicWidth = std::numeric_limits<SkScalar>::min();
 
     auto span = parent->clusters();
     if (span.size() == 0) {
@@ -268,16 +266,35 @@
     }
 
     // We finished formatting the text but we need to scan the rest for some numbers
-    auto cluster = fEndLine.endCluster();
-    while (cluster != end) {
-        fExceededMaxLines = true;
-        if (cluster->isHardBreak()) {
-            softLineMaxIntrinsicWidth = 0;
-        } else {
-            softLineMaxIntrinsicWidth += cluster->width();
-            fMaxIntrinsicWidth = SkTMax(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
+    if (fEndLine.breakCluster() != nullptr) {
+        auto lastWordLength = 0.0f;
+        auto cluster = fEndLine.breakCluster();
+        if (cluster != end) {
+            ++cluster;
         }
-        ++cluster;
+        while (cluster != end || cluster->endPos() < end->endPos()) {
+            fExceededMaxLines = true;
+            if (cluster->isHardBreak()) {
+                fMaxIntrinsicWidth = SkTMax(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
+                softLineMaxIntrinsicWidth = 0;
+
+                fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, lastWordLength);
+                lastWordLength = 0;
+            } else if (cluster->isWhitespaces()) {
+                SkASSERT(cluster->isWhitespaces());
+                softLineMaxIntrinsicWidth += cluster->width();
+                fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, lastWordLength);
+                lastWordLength = 0;
+            } else {
+                softLineMaxIntrinsicWidth += cluster->width();
+                lastWordLength += cluster->width();
+            }
+            ++cluster;
+        }
+        fMinIntrinsicWidth = SkTMax(fMinIntrinsicWidth, lastWordLength);
+        fMaxIntrinsicWidth = SkTMax(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
+        // In case we could not place a single cluster on the line
+        fHeight = SkTMax(fHeight, fEndLine.metrics().height());
     }
 
     if (fHardLineBreak) {
diff --git a/samplecode/SampleParagraph.cpp b/samplecode/SampleParagraph.cpp
index cc10e03..450f866 100644
--- a/samplecode/SampleParagraph.cpp
+++ b/samplecode/SampleParagraph.cpp
@@ -1647,15 +1647,13 @@
 
         auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
 
-        const char* text = "World domination is such an ugly phrase - I prefer to call it world optimisation";
+        std::u16string text = u"\u0068\u0301\u0350\u0312\u0357\u030C\u0369\u0305\u036C\u0304\u0310\u033F\u0366\u0350\u0343\u0364\u0369\u0311\u0309\u030E\u0365\u031B\u0340\u0337\u0335\u035E\u0334\u0328\u0360\u0360\u0315\u035F\u0340\u0340\u0362\u0360\u0322\u031B\u031B\u0337\u0340\u031E\u031F\u032A\u0331\u0345\u032F\u0332\u032E\u0333\u0353\u0320\u0345\u031C\u031F\u033C\u0325\u0355\u032C\u0325\u033Aa\u0307\u0312\u034B\u0308\u0312\u0346\u0313\u0346\u0304\u0307\u0344\u0305\u0342\u0368\u0346\u036A\u035B\u030F\u0365\u0307\u0340\u0328\u0322\u0361\u0489\u034F\u0328\u0334\u035F\u0335\u0362\u0489\u0360\u0358\u035E\u0360\u035D\u0341\u0337\u0337\u032E\u0326\u032D\u0359\u0318\u033C\u032F\u0333\u035A\u034D\u0319\u031C\u0353\u033C\u0345\u0359\u0331\u033B\u0331\u033C";
         ParagraphStyle paragraph_style;
-        paragraph_style.setMaxLines(7);
-        paragraph_style.setEllipsis(u"\u2026");
         ParagraphBuilderImpl builder(paragraph_style, fontCollection);
         TextStyle text_style;
         text_style.setColor(SK_ColorBLACK);
         text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(40);
+        text_style.setFontSize(20);
         builder.pushStyle(text_style);
         builder.addText(text);
         auto paragraph = builder.Build();
@@ -1677,19 +1675,20 @@
 
         auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
 
-        const char* text = "";
+        const char* text = "0";
         ParagraphStyle paragraph_style;
         paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
-        //paragraph_style.setEllipsis(u"\u2026");
         ParagraphBuilderImpl builder(paragraph_style, fontCollection);
         TextStyle text_style;
         text_style.setColor(SK_ColorBLACK);
-        text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(40);
+        text_style.setFontFamilies({SkString("Google Sans Display")});
+        text_style.setFontSize(160);
+        //text_style.setHeightOverride(true);
+        //text_style.setHeight(1.75);
         builder.pushStyle(text_style);
         builder.addText(text);
         auto paragraph = builder.Build();
-        paragraph->layout(this->width());
+        paragraph->layout(94);
 
         paragraph->paint(canvas, 0, 0);
     }
diff --git a/tests/SkParagraphTest.cpp b/tests/SkParagraphTest.cpp
index 20f5824..ffce0de 100644
--- a/tests/SkParagraphTest.cpp
+++ b/tests/SkParagraphTest.cpp
@@ -197,7 +197,6 @@
     }
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderParagraph.png");
@@ -297,7 +296,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.bottom(), 50, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBaselineParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBaselineParagraph.png");
@@ -354,7 +352,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44.694f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderAboveBaselineParagraph.png");
@@ -411,7 +408,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBelowBaselineParagraph.png");
@@ -468,7 +464,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.347f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBottomParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBottomParagraph.png");
@@ -523,7 +518,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderTopParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderTopParagraph.png");
@@ -578,7 +572,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.468f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderMiddleParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderMiddleParagraph.png");
@@ -633,7 +626,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 40.234f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderIdeographicBaselineParagraph.png");
@@ -687,7 +679,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 42.065f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBreakParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBreakParagraph.png");
@@ -823,7 +814,6 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.bottom(), 113.5f, EPSILON100));
 }
 
-// Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderGetRectsParagraph.png");
@@ -1671,6 +1661,124 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 156, EPSILON100));
 }
 
+DEF_TEST_DISABLED(SkParagraph_JustifyRTLNewLine, reporter) {
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
+    if (!fontCollection->fontsFound()) return;
+    TestCanvas canvas("SkParagraph_JustifyRTLNewLine.png");
+    const char* text =
+            "אאא בּבּבּבּ אאאא\nבּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ "
+            "אאאאא בּבּבּבּבּ אאאבּבּבּבּבּבּאאאאא בּבּבּבּבּבּאאאאאבּבּבּבּבּבּ אאאאא בּבּבּבּבּ "
+            "אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ";
+
+    auto icu_text = icu::UnicodeString::fromUTF8(text);
+    std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length());
+    const size_t len = strlen(text);
+
+    ParagraphStyle paragraph_style;
+    paragraph_style.setMaxLines(14);
+    paragraph_style.setTextAlign(TextAlign::kJustify);
+    paragraph_style.setTextDirection(TextDirection::kRtl);
+    paragraph_style.turnHintingOff();
+    ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("Ahem")});
+    text_style.setFontSize(26);
+    text_style.setColor(SK_ColorBLACK);
+    builder.pushStyle(text_style);
+    builder.addText(text, len);
+    builder.pop();
+
+    auto paragraph = builder.Build();
+    paragraph->layout(TestCanvasWidth - 100);
+    paragraph->paint(canvas.get(), 0, 0);
+
+    auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setAntiAlias(true);
+    paint.setStrokeWidth(1);
+
+    // Tests for GetRectsForRange()
+    RectHeightStyle rect_height_style = RectHeightStyle::kMax;
+    RectWidthStyle rect_width_style = RectWidthStyle::kTight;
+    paint.setColor(SK_ColorRED);
+    auto boxes = paragraph->getRectsForRange(0, 30, rect_height_style, rect_width_style);
+    for (size_t i = 0; i < boxes.size(); ++i) {
+        canvas.get()->drawRect(boxes[i].rect, paint);
+    }
+    REPORTER_ASSERT(reporter, boxes.size() == 2ull);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 562, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 900, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 26, EPSILON100));
+
+    paint.setColor(SK_ColorBLUE);
+    boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
+    for (size_t i = 0; i < boxes.size(); ++i) {
+        canvas.get()->drawRect(boxes[i].rect, paint);
+    }
+    REPORTER_ASSERT(reporter, boxes.size() == 1ull);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 68, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 130, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 120, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 156, EPSILON100));
+
+    // All lines should be justified to the width of the
+    // paragraph.
+    for (auto& line : impl->lines()) {
+        REPORTER_ASSERT(reporter,
+                        SkScalarNearlyEqual(line.width(), TestCanvasWidth - 100, EPSILON100));
+    }
+}
+
+DEF_TEST_DISABLED(SkParagraph_LeadingSpaceRTL, reporter) {
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
+    if (!fontCollection->fontsFound()) return;
+    TestCanvas canvas("SkParagraph_LeadingSpaceRTL.png");
+
+    const char* text = " leading space";
+
+    auto icu_text = icu::UnicodeString::fromUTF8(text);
+    std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length());
+    const size_t len = strlen(text);
+
+    ParagraphStyle paragraph_style;
+    paragraph_style.setMaxLines(14);
+    paragraph_style.setTextAlign(TextAlign::kJustify);
+    paragraph_style.setTextDirection(TextDirection::kRtl);
+    paragraph_style.turnHintingOff();
+    ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("Ahem")});
+    text_style.setFontSize(26);
+    text_style.setColor(SK_ColorBLACK);
+    builder.pushStyle(text_style);
+    builder.addText(text, len);
+    builder.pop();
+
+    auto paragraph = builder.Build();
+    paragraph->layout(TestCanvasWidth - 100);
+    paragraph->paint(canvas.get(), 0, 0);
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setAntiAlias(true);
+    paint.setStrokeWidth(1);
+
+    // Tests for GetRectsForRange()
+    RectHeightStyle rect_height_style = RectHeightStyle::kMax;
+    RectWidthStyle rect_width_style = RectWidthStyle::kTight;
+    paint.setColor(SK_ColorRED);
+    auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
+    for (size_t i = 0; i < boxes.size(); ++i) {
+        canvas.get()->drawRect(boxes[i].rect, paint);
+    }
+    REPORTER_ASSERT(reporter, boxes.size() == 2ull);
+}
+
 // Checked: NO DIFF (some minor decoration differences, probably)
 DEF_TEST(SkParagraph_DecorationsParagraph, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
@@ -1792,7 +1900,7 @@
 }
 
 DEF_TEST(SkParagraph_WavyDecorationParagraph, reporter) {
-    // SkDebugf("TODO: Fix decorations\n");
+    SkDebugf("TODO: Add test for wavy decorations\n");
 }
 
 // Checked: NO DIFF
@@ -4374,11 +4482,6 @@
     }
 }
 
-// TODO: Implement font features
-DEF_TEST(SkParagraph_FontFeaturesParagraph, reporter) {
-    // SkDebugf("TODO: Font features\n");
-}
-
 // Not in Minikin
 DEF_TEST(SkParagraph_WhitespacesInMultipleFonts, reporter) {
     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
@@ -5040,14 +5143,14 @@
     canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, 50, 500), paint);
 
     relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
-    relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
-    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
+    relayout(3, false, 50, 30,  90, 950, SK_ColorBLUE);
+    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  90, 950, SK_ColorGREEN);
 
     relayout(1, true, 50, 10, 950, 950, SK_ColorYELLOW);
-    relayout(3, true, 50, 30,  50, 950, SK_ColorMAGENTA);
+    relayout(3, true, 50, 30,  90, 950, SK_ColorMAGENTA);
     relayout(std::numeric_limits<size_t>::max(), true, 50, 20,  950, 950, SK_ColorCYAN);
 
     relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
-    relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
-    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
+    relayout(3, false, 50, 30,  90, 950, SK_ColorBLUE);
+    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  90, 950, SK_ColorGREEN);
 }