From 90bfd1c1c33eb64e09f2dc34dbdb9bcf7cf51379 Mon Sep 17 00:00:00 2001 From: Julia Lavrova Date: Wed, 4 Dec 2019 11:43:32 -0500 Subject: [PATCH] Small changes Change-Id: Ic9c41a2dd11b4df8ab24037df0109e36536ec6c3 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257892 Reviewed-by: Ben Wagner Commit-Queue: Julia Lavrova --- modules/skparagraph/include/TextStyle.h | 19 ++- modules/skparagraph/src/OneLineShaper.cpp | 13 +- modules/skparagraph/src/ParagraphImpl.cpp | 33 +++-- modules/skparagraph/src/ParagraphImpl.h | 3 +- modules/skparagraph/src/TextStyle.cpp | 4 +- modules/skparagraph/src/TextWrapper.cpp | 53 +++++--- samplecode/SampleParagraph.cpp | 17 ++- tests/SkParagraphTest.cpp | 145 ++++++++++++++++++---- 8 files changed, 206 insertions(+), 81 deletions(-) diff --git a/modules/skparagraph/include/TextStyle.h b/modules/skparagraph/include/TextStyle.h index 85ab4288d3229..387bf82f474af 100644 --- a/modules/skparagraph/include/TextStyle.h +++ b/modules/skparagraph/include/TextStyle.h @@ -243,15 +243,13 @@ struct Block { 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 @@ struct Placeholder { , 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 2974e5e0d1d41..f777ac8987731 100644 --- a/modules/skparagraph/src/OneLineShaper.cpp +++ b/modules/skparagraph/src/OneLineShaper.cpp @@ -339,6 +339,7 @@ void OneLineShaper::sortOutGlyphs(std::function&& sortOutUnres void OneLineShaper::iterateThroughFontStyles(SkSpan 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 @@ bool OneLineShaper::shape() { } iterateThroughFontStyles(styleSpan, - [this, &shaper, textDirection, limitlessWidth, &advanceX](Block block) { + [this, &shaper, textDirection, limitlessWidth, &advanceX] + (Block block) { auto blockSpan = SkSpan(&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 @@ bool OneLineShaper::shape() { } 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 @@ bool OneLineShaper::shape() { 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 7d4f08b15f7a7..f8f287fc88261 100644 --- a/modules/skparagraph/src/ParagraphImpl.cpp +++ b/modules/skparagraph/src/ParagraphImpl.cpp @@ -887,7 +887,7 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk 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 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk } 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 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk 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::setState(InternalState state) { } 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()); + 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); + } - SkFont font(typeface, defaultTextStyle.getFontSize()); - fEmptyMetrics = InternalLineMetrics(font, paragraphStyle().getStrutStyle().getForceStrutHeight()); - fStrutMetrics.updateLineMetrics(fEmptyMetrics); + 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 25f6f134fc298..30508c90c83f0 100644 --- a/modules/skparagraph/src/ParagraphImpl.h +++ b/modules/skparagraph/src/ParagraphImpl.h @@ -199,6 +199,8 @@ class ParagraphImpl final : public Paragraph { 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 @@ class ParagraphImpl final : public Paragraph { 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 ce0cc40ec2e41..4fc619acfd7d5 100644 --- a/modules/skparagraph/src/TextStyle.cpp +++ b/modules/skparagraph/src/TextStyle.cpp @@ -84,8 +84,7 @@ bool TextStyle::equals(const TextStyle& other) const { 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 @@ bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) c // 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 98fc5a42a43ad..4f69c46b72262 100644 --- a/modules/skparagraph/src/TextWrapper.cpp +++ b/modules/skparagraph/src/TextWrapper.cpp @@ -35,9 +35,7 @@ void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters) { 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 @@ void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters) { } 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 @@ void TextWrapper::moveForward() { } 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 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent, SkScalar maxWidth, const AddLineToParagraph& addLine) { fHeight = 0; - fMinIntrinsicWidth = 0; - fMaxIntrinsicWidth = 0; + fMinIntrinsicWidth = std::numeric_limits::min(); + fMaxIntrinsicWidth = std::numeric_limits::min(); auto span = parent->clusters(); if (span.size() == 0) { @@ -268,16 +266,35 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent, } // 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 cc10e03223c0c..450f86626f416 100644 --- a/samplecode/SampleParagraph.cpp +++ b/samplecode/SampleParagraph.cpp @@ -1647,15 +1647,13 @@ class ParagraphView19 : public ParagraphView_Base { auto fontCollection = sk_make_sp(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 @@ class ParagraphView20 : public ParagraphView_Base { auto fontCollection = sk_make_sp(GetResourcePath("fonts").c_str(), false, true); - const char* text = ""; + const char* text = "0"; ParagraphStyle paragraph_style; paragraph_style.setMaxLines(std::numeric_limits::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 20f58248b3087..ffce0de0812d9 100644 --- a/tests/SkParagraphTest.cpp +++ b/tests/SkParagraphTest.cpp @@ -197,7 +197,6 @@ DEF_TEST(SkParagraph_SimpleParagraph, reporter) { } } -// Checked: DIFF+ (half of the letter spacing before the text???) DEF_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) { sk_sp fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderParagraph.png"); @@ -297,7 +296,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderBaselineParagraph.png"); @@ -354,7 +352,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderBaselineParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderAboveBaselineParagraph.png"); @@ -411,7 +408,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderBelowBaselineParagraph.png"); @@ -468,7 +464,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderBottomParagraph.png"); @@ -523,7 +518,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderBottomParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderTopParagraph.png"); @@ -578,7 +572,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderTopParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderMiddleParagraph.png"); @@ -633,7 +626,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderMiddleParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderIdeographicBaselineParagraph.png"); @@ -687,7 +679,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderBreakParagraph.png"); @@ -823,7 +814,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderBreakParagraph, reporter) { 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 fontCollection = sk_make_sp(); TestCanvas canvas("SkParagraph_InlinePlaceholderGetRectsParagraph.png"); @@ -1671,6 +1661,124 @@ DEF_TEST(SkParagraph_JustifyRTL, reporter) { REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 156, EPSILON100)); } +DEF_TEST_DISABLED(SkParagraph_JustifyRTLNewLine, reporter) { + sk_sp fontCollection = sk_make_sp(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(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 fontCollection = sk_make_sp(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 fontCollection = sk_make_sp(); @@ -1792,7 +1900,7 @@ DEF_TEST(SkParagraph_DecorationsParagraph, reporter) { } 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 @@ DEF_TEST(SkParagraph_StrutDefaultParagraph, reporter) { } } -// TODO: Implement font features -DEF_TEST(SkParagraph_FontFeaturesParagraph, reporter) { - // SkDebugf("TODO: Font features\n"); -} - // Not in Minikin DEF_TEST(SkParagraph_WhitespacesInMultipleFonts, reporter) { sk_sp fontCollection = sk_make_sp(); @@ -5040,14 +5143,14 @@ DEF_TEST(SkParagraph_Ellipsis, reporter) { 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::max(), false, 50, 200, 50, 950, SK_ColorGREEN); + relayout(3, false, 50, 30, 90, 950, SK_ColorBLUE); + relayout(std::numeric_limits::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::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::max(), false, 50, 200, 50, 950, SK_ColorGREEN); + relayout(3, false, 50, 30, 90, 950, SK_ColorBLUE); + relayout(std::numeric_limits::max(), false, 50, 200, 90, 950, SK_ColorGREEN); }