skia2/tests/SkParagraphTest.cpp
Julia Lavrova cd2d4e4835 Mixed languages text crashes
Extending text to grapheme edges should correct glyph range
Bug: skia:10087
Change-Id: I254901aaaa40c2782d1afbd5d5390599bdd7c922

Change-Id: I1d51076656d09e4d2e35e3ddad28bfd60fc87081
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/281756
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
2020-04-07 17:43:57 +00:00

5277 lines
235 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2019 Google LLC.
#include <sstream>
#include <thread>
#include "include/core/SkFontMgr.h"
#include "modules/skparagraph/include/TypefaceFontProvider.h"
#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
#include "modules/skparagraph/utils/TestFontCollection.h"
#include "src/core/SkFontMgrPriv.h"
#include "src/core/SkOSFile.h"
#include "src/utils/SkOSPath.h"
#include "src/utils/SkShaperJSONWriter.h"
#include "tests/CodecPriv.h"
#include "tests/Test.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
#include "third_party/icu/SkLoadICU.h"
#define VeryLongCanvasWidth 1000000
#define TestCanvasWidth 1000
#define TestCanvasHeight 600
#define DEF_TEST_DISABLED(name, reporter) \
static void test_##name(skiatest::Reporter* reporter, const GrContextOptions&); \
skiatest::TestRegistry name##TestRegistry(skiatest::Test(#name, false, test_##name)); \
void test_##name(skiatest::Reporter* reporter, const GrContextOptions&) { /* SkDebugf("Disabled:"#name "\n"); */ } \
void disabled_##name(skiatest::Reporter* reporter, const GrContextOptions&)
using namespace skia::textlayout;
namespace {
SkScalar EPSILON100 = 0.01f;
SkScalar EPSILON50 = 0.02f;
SkScalar EPSILON20 = 0.05f;
SkScalar EPSILON10 = 0.1f;
SkScalar EPSILON5 = 0.20f;
SkScalar EPSILON2 = 0.50f;
bool equal(const char* base, TextRange a, const char* b) {
return std::strncmp(b, base + a.start, a.width()) == 0;
}
class ResourceFontCollection : public FontCollection {
public:
ResourceFontCollection(bool testOnly = false)
: fFontsFound(false)
, fResolvedFonts(0)
, fResourceDir(GetResourcePath("fonts").c_str())
, fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
std::vector<SkString> fonts;
SkOSFile::Iter iter(fResourceDir.c_str());
if (!SkLoadICU()) {
SkDebugf("ICU not loaded, skipping all the tests\n");
return;
}
SkString path;
while (iter.next(&path)) {
if (path.endsWith("Roboto-Italic.ttf")) {
fFontsFound = true;
}
fonts.emplace_back(path);
}
if (!fFontsFound) {
// SkDebugf("Fonts not found, skipping all the tests\n");
return;
}
// Only register fonts if we have to
for (auto& font : fonts) {
SkString file_path;
file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
}
if (testOnly) {
this->setTestFontManager(std::move(fFontProvider));
} else {
this->setAssetFontManager(std::move(fFontProvider));
}
this->disableFontFallback();
}
~ResourceFontCollection() = default;
size_t resolvedFonts() const { return fResolvedFonts; }
// TODO: temp solution until we check in fonts
bool fontsFound() const { return fFontsFound; }
private:
bool fFontsFound;
size_t fResolvedFonts;
std::string fResourceDir;
sk_sp<TypefaceFontProvider> fFontProvider;
};
class TestCanvas {
public:
TestCanvas(const char* testName) : name(testName) {
bits.allocN32Pixels(TestCanvasWidth, TestCanvasHeight);
canvas = new SkCanvas(bits);
canvas->clear(SK_ColorWHITE);
}
~TestCanvas() {
SkString tmpDir = skiatest::GetTmpDir();
if (!tmpDir.isEmpty()) {
SkString path = SkOSPath::Join(tmpDir.c_str(), name);
SkFILEWStream file(path.c_str());
if (!SkEncodeImage(&file, bits, SkEncodedImageFormat::kPNG, 100)) {
SkDebugf("Cannot write a picture %s\n", name);
}
}
delete canvas;
}
void drawRects(SkColor color, std::vector<TextBox>& result, bool fill = false) {
SkPaint paint;
if (!fill) {
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);
}
paint.setColor(color);
for (auto& r : result) {
canvas->drawRect(r.rect, paint);
}
}
void drawLine(SkColor color, SkRect rect, bool vertical = true) {
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);
paint.setColor(color);
if (vertical) {
canvas->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);
} else {
canvas->drawLine(rect.fLeft, rect.fTop, rect.fRight, rect.fTop, paint);
}
}
void drawLines(SkColor color, std::vector<TextBox>& result) {
for (auto& r : result) {
drawLine(color, r.rect);
}
}
SkCanvas* get() { return canvas; }
private:
SkBitmap bits;
SkCanvas* canvas;
const char* name;
};
} // namespace
DEF_TEST(SkParagraph_SimpleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
const char* text = "Hello World Text Dialog";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1); // paragraph style does not count
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
size_t index = 0;
for (auto& line : impl->lines()) {
line.scanStyles(StyleType::kDecorations,
[&index, reporter]
(TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
REPORTER_ASSERT(reporter, index == 0);
REPORTER_ASSERT(reporter, style.getColor() == SK_ColorBLACK);
++index;
});
}
}
DEF_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 0);
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addPlaceholder(placeholder1);
PlaceholderStyle placeholder2(5, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addText(text, len);
builder.addPlaceholder(placeholder2);
builder.addText(text, len);
builder.addText(text, len);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorGREEN, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(4, 17, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 7);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 90.921f + 50, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 100, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.left(), 231.343f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.top(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.right(), 231.343f + 50, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.bottom(), 100, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.left(), 281.343f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.right(), 281.343f + 5, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.bottom(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.left(), 336.343f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.right(), 336.343f + 5, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.bottom(), 50, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderBaselineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderBaselineParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 38.347f);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 14.226f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44.694f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderAboveBaselineParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kAboveBaseline, TextBaseline::kAlphabetic, 903129.129308f);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.347f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 49.652f, EPSILON100));
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 25.531f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderBelowBaselineParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBelowBaseline, TextBaseline::kAlphabetic, 903129.129308f);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.121f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.347f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderBottomParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderBottomParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBottom, TextBaseline::kAlphabetic, 0);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 19.531f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderTopParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderTopParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kTop, TextBaseline::kAlphabetic, 0);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.468f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderMiddleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderMiddleParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kMiddle, TextBaseline::kAlphabetic, 0);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 9.765f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 40.234f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderIdeographicBaselineParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "給能上目秘使";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Source Han Serif CN")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBaseline, TextBaseline::kIdeographic, 38.347f);
builder.addPlaceholder(placeholder);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 162.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f + 55, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 135.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 4.703f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 42.065f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderBreakParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderBreakParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
PlaceholderStyle placeholder2(25, 25, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 12.5f);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2); // 4 + 1
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2); // 6 + 1
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2); // 7 + 1
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addText(text, len);
builder.addPlaceholder(placeholder2);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
boxes = paragraph->getRectsForRange(175, 176, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorGREEN, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 31.695f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 218.531f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 47.292f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 249, EPSILON100));
boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(4, 45, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 30);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 59.726f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 26.378f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56.847f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.left(), 606.343f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.top(), 38, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.right(), 631.343f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.bottom(), 63, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.left(), 0.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.top(), 63.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.right(), 50.5f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.bottom(), 113.5f, EPSILON100));
}
DEF_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_InlinePlaceholderGetRectsParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text = "012 34";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setLetterSpacing(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
PlaceholderStyle placeholder2(5, 20, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 10);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2); // 8 + 1
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2); // 5 + 1
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder1); // 8 + 0
builder.addText(text, len);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2); // 1 + 2
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder2); // 1 + 2
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len);
builder.addText(text, len); // 11
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addPlaceholder(placeholder1);
builder.addPlaceholder(placeholder2);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForPlaceholders();
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 34);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 140.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.left(), 800.921f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.right(), 850.921f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.bottom(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.left(), 503.382f, EPSILON10));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.top(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.right(), 508.382f, EPSILON10));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.bottom(), 180, EPSILON100));
boxes = paragraph->getRectsForRange(30, 50, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 8);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 216.097f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 60, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 290.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 120, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 290.921f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 60, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 340.921f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 120, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.left(), 340.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.top(), 60, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.right(), 345.921f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.bottom(), 120, EPSILON100));
}
DEF_TEST(SkParagraph_SimpleRedParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
const char* text = "I am RED";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorRED);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1); // paragraph style does not count
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
size_t index = 0;
for (auto& line : impl->lines()) {
line.scanStyles(StyleType::kDecorations,
[reporter, &index](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
REPORTER_ASSERT(reporter, index == 0);
REPORTER_ASSERT(reporter, style.getColor() == SK_ColorRED);
++index;
return true;
});
}
}
// Checked: DIFF+ (Space between 1 & 2 style blocks)
DEF_TEST(SkParagraph_RainbowParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
TestCanvas canvas("SkParagraph_RainbowParagraph.png");
if (!fontCollection->fontsFound()) return;
const char* text1 = "Red Roboto"; // [0:10)
const char* text2 = "big Greeen Default"; // [10:28)
const char* text3 = "Defcolor Homemade Apple"; // [28:51)
const char* text4 = "Small Blue Roboto"; // [51:68)
const char* text41 = "Small Blue ";
const char* text5 =
"Continue Last Style With lots of words to check if it overlaps "
"properly or not"; // [68:)
const char* text42 =
"Roboto"
"Continue Last Style With lots of words to check if it overlaps "
"properly or not";
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.setMaxLines(2);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style1;
text_style1.setFontFamilies({SkString("Roboto")});
text_style1.setColor(SK_ColorRED);
builder.pushStyle(text_style1);
builder.addText(text1, strlen(text1));
TextStyle text_style2;
text_style2.setFontFamilies({SkString("Roboto")});
text_style2.setFontSize(50);
text_style2.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
text_style2.setLetterSpacing(10);
text_style2.setDecorationColor(SK_ColorBLACK);
text_style2.setDecoration((TextDecoration)(
TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
text_style2.setWordSpacing(30);
text_style2.setColor(SK_ColorGREEN);
builder.pushStyle(text_style2);
builder.addText(text2, strlen(text2));
TextStyle text_style3;
text_style3.setFontFamilies({SkString("Homemade Apple")});
text_style3.setColor(SK_ColorBLACK);
builder.pushStyle(text_style3);
builder.addText(text3, strlen(text3));
TextStyle text_style4;
text_style4.setFontFamilies({SkString("Roboto")});
text_style4.setFontSize(14);
text_style4.setDecorationColor(SK_ColorBLACK);
text_style4.setDecoration((TextDecoration)(
TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
text_style4.setColor(SK_ColorBLUE);
builder.pushStyle(text_style4);
builder.addText(text4, strlen(text4));
builder.addText(text5, strlen(text5));
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(1000);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 4);
REPORTER_ASSERT(reporter, impl->styles().size() == 4);
REPORTER_ASSERT(reporter, impl->lines().size() == 2);
auto rects = paragraph->getRectsForRange(0, impl->text().size(), RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawRects(SK_ColorMAGENTA, rects);
size_t index = 0;
impl->lines()[0].scanStyles(
StyleType::kAllAttributes,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
switch (index) {
case 0:
REPORTER_ASSERT(reporter, style.equals(text_style1));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text1));
break;
case 1:
REPORTER_ASSERT(reporter, style.equals(text_style2));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text2));
break;
case 2:
REPORTER_ASSERT(reporter, style.equals(text_style3));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text3));
break;
case 3:
REPORTER_ASSERT(reporter, style.equals(text_style4));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text41));
break;
default:
REPORTER_ASSERT(reporter, false);
break;
}
++index;
return true;
});
impl->lines()[1].scanStyles(
StyleType::kAllAttributes,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
switch (index) {
case 4:
REPORTER_ASSERT(reporter, style.equals(text_style4));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text42));
break;
default:
REPORTER_ASSERT(reporter, false);
break;
}
++index;
return true;
});
REPORTER_ASSERT(reporter, index == 5);
}
DEF_TEST(SkParagraph_DefaultStyleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_DefaultStyleParagraph.png");
const char* text = "No TextStyle! Uh Oh!";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
TextStyle defaultStyle;
defaultStyle.setFontFamilies({SkString("Roboto")});
paragraph_style.setTextStyle(defaultStyle);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText(text, len);
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 15.0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
size_t index = 0;
impl->lines()[0].scanStyles(
StyleType::kAllAttributes,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
REPORTER_ASSERT(reporter, style.equals(paragraph_style.getTextStyle()));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text));
++index;
return true;
});
REPORTER_ASSERT(reporter, index == 1);
}
DEF_TEST(SkParagraph_BoldParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_BoldParagraph.png");
const char* text = "This is Red max bold text!";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorRED);
text_style.setFontSize(60);
text_style.setLetterSpacing(0);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kBlack_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(VeryLongCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 60.0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
size_t index = 0;
impl->lines()[0].scanStyles(
StyleType::kAllAttributes,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
REPORTER_ASSERT(reporter, style.equals(text_style));
REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text));
++index;
return true;
});
REPORTER_ASSERT(reporter, index == 1);
}
DEF_TEST(SkParagraph_HeightOverrideParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_HeightOverrideParagraph.png");
const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(10);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(20);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(3.6345f);
text_style.setHeightOverride(true);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 5);
REPORTER_ASSERT(reporter, impl->styles().size() == 1); // paragraph style does not count
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
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::kIncludeLineSpacingMiddle;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
paint.setColor(SK_ColorRED);
std::vector<TextBox> boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 0ull);
boxes = paragraph->getRectsForRange(0, 40, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 3ull);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 92.805f, EPSILON5));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 43.843f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 165.495f, EPSILON5));
}
DEF_TEST(SkParagraph_LeftAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_LeftAlignParagraph.png");
const char* text =
"This is a very long sentence to test if the text will properly wrap "
"around and go to the next line. Sometimes, short sentence. Longer "
"sentences are okay too because they are nessecary. Very short. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum.";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(26);
text_style.setLetterSpacing(1);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(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());
REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
double expected_y = 0;
double epsilon = 0.01f;
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
expected_y += 30 * 10;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
REPORTER_ASSERT(reporter,
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
// Tests for GetGlyphPositionAtCoordinate()
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(0, 0).position == 0);
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 1).position == 0);
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 35).position == 68);
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 70).position == 134);
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(2000, 35).position == 134);
}
DEF_TEST(SkParagraph_RightAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_RightAlignParagraph.png");
const char* text =
"This is a very long sentence to test if the text will properly wrap "
"around and go to the next line. Sometimes, short sentence. Longer "
"sentences are okay too because they are nessecary. Very short. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum.";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kRight);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(26);
text_style.setLetterSpacing(1);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(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());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
double expected_y = 0;
double epsilon = 0.01f;
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
expected_y += 30 * 10;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
auto calculate = [](const TextLine& line) -> SkScalar {
return TestCanvasWidth - 100 - line.offset().fX - line.width();
};
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[13]), 0, epsilon));
REPORTER_ASSERT(reporter,
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
}
DEF_TEST(SkParagraph_CenterAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_CenterAlignParagraph.png");
const char* text =
"This is a very long sentence to test if the text will properly wrap "
"around and go to the next line. Sometimes, short sentence. Longer "
"sentences are okay too because they are nessecary. Very short. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum.";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kCenter);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(26);
text_style.setLetterSpacing(1);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(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());
REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
double expected_y = 0;
double epsilon = 0.01f;
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
expected_y += 30 * 10;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
auto calculate = [](const TextLine& line) -> SkScalar {
return TestCanvasWidth - 100 - (line.offset().fX * 2 + line.width());
};
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[13]), 0, epsilon));
REPORTER_ASSERT(reporter,
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
}
DEF_TEST(SkParagraph_JustifyAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_JustifyAlignParagraph.png");
const char* text =
"This is a very long sentence to test if the text will properly wrap "
"around and go to the next line. Sometimes, short sentence. Longer "
"sentences are okay too because they are nessecary. Very short. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
"mollit anim id est laborum. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
"velit esse cillum dolore eu fugiat.";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kJustify);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(26);
text_style.setLetterSpacing(0);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(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);
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
double expected_y = 0;
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, EPSILON100));
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, EPSILON100));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, EPSILON100));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, EPSILON100));
expected_y += 30;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, EPSILON100));
expected_y += 30 * 9;
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(impl->lines()[12].offset().fY, expected_y, EPSILON100));
auto calculate = [](const TextLine& line) -> SkScalar {
return line.offset().fX;
};
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, EPSILON100));
REPORTER_ASSERT(reporter,
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
}
// Checked: DIFF (ghost spaces as a separate box in TxtLib)
DEF_TEST(SkParagraph_JustifyRTL, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_JustifyRTL.png");
const char* text =
"אאא בּבּבּבּ אאאא בּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ "
"אאאאא בּבּבּבּבּ אאאבּבּבּבּבּבּאאאאא בּבּבּבּבּבּאאאאאבּבּבּבּבּבּ אאאאא בּבּבּבּבּ "
"אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ";
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());
auto calculate = [](const TextLine& line) -> SkScalar {
return TestCanvasWidth - 100 - line.width();
};
for (auto& line : impl->lines()) {
if (&line == &impl->lines().back()) {
REPORTER_ASSERT(reporter, calculate(line) > EPSILON100);
} else {
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(line), 0, EPSILON100));
}
}
// Just make sure the the text is actually RTL
for (auto& run : impl->runs()) {
REPORTER_ASSERT(reporter, !run.leftToRight());
}
// Tests for GetRectsForRange()
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 3);
boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 588, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 130, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 640, EPSILON100));
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);
}
DEF_TEST(SkParagraph_DecorationsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_DecorationsParagraph.png");
const char* text1 = "This text should be";
const char* text2 = " decorated even when";
const char* text3 = " wrapped around to";
const char* text4 = " the next line.";
const char* text5 = " Otherwise, bad things happen.";
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(26);
text_style.setLetterSpacing(0);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(2);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
text_style.setDecoration((TextDecoration)(
TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
text_style.setDecorationStyle(TextDecorationStyle::kSolid);
text_style.setDecorationColor(SK_ColorBLACK);
text_style.setDecorationThicknessMultiplier(2.0);
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
text_style.setDecorationStyle(TextDecorationStyle::kDouble);
text_style.setDecorationColor(SK_ColorBLUE);
text_style.setDecorationThicknessMultiplier(1.0);
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
text_style.setDecorationStyle(TextDecorationStyle::kDotted);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text3, strlen(text3));
text_style.setDecorationStyle(TextDecorationStyle::kDashed);
text_style.setDecorationColor(SK_ColorBLACK);
text_style.setDecorationThicknessMultiplier(3.0);
builder.pushStyle(text_style);
builder.addText(text4, strlen(text4));
text_style.setDecorationStyle(TextDecorationStyle::kWavy);
text_style.setDecorationColor(SK_ColorRED);
text_style.setDecorationThicknessMultiplier(1.0);
builder.pushStyle(text_style);
builder.addText(text5, strlen(text5));
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
size_t index = 0;
for (auto& line : impl->lines()) {
line.scanStyles(
StyleType::kDecorations,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
auto decoration = (TextDecoration)(TextDecoration::kUnderline |
TextDecoration::kOverline |
TextDecoration::kLineThrough);
REPORTER_ASSERT(reporter, style.getDecorationType() == decoration);
switch (index) {
case 0:
REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
TextDecorationStyle::kSolid);
REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
REPORTER_ASSERT(reporter,
style.getDecorationThicknessMultiplier() == 2.0);
break;
case 1: // The style appears on 2 lines so it has 2 pieces
REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
TextDecorationStyle::kDouble);
REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLUE);
REPORTER_ASSERT(reporter,
style.getDecorationThicknessMultiplier() == 1.0);
break;
case 2:
REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
TextDecorationStyle::kDotted);
REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
REPORTER_ASSERT(reporter,
style.getDecorationThicknessMultiplier() == 1.0);
break;
case 3:
case 4:
REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
TextDecorationStyle::kDashed);
REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
REPORTER_ASSERT(reporter,
style.getDecorationThicknessMultiplier() == 3.0);
break;
case 5:
REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
TextDecorationStyle::kWavy);
REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorRED);
REPORTER_ASSERT(reporter,
style.getDecorationThicknessMultiplier() == 1.0);
break;
default:
REPORTER_ASSERT(reporter, false);
break;
}
++index;
return true;
});
}
}
DEF_TEST(SkParagraph_WavyDecorationParagraph, reporter) {
SkDebugf("TODO: Add test for wavy decorations\n");
}
DEF_TEST(SkParagraph_ItalicsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ItalicsParagraph.png");
const char* text1 = "No italic ";
const char* text2 = "Yes Italic ";
const char* text3 = "No Italic again.";
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(10);
text_style.setColor(SK_ColorRED);
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
text_style.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kItalic_Slant));
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
builder.pop();
builder.addText(text3, strlen(text3));
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 3);
REPORTER_ASSERT(reporter, impl->styles().size() == 3);
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
auto& line = impl->lines()[0];
size_t index = 0;
line.scanStyles(
StyleType::kForeground,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
switch (index) {
case 0:
REPORTER_ASSERT(
reporter,
style.getFontStyle().slant() == SkFontStyle::kUpright_Slant);
break;
case 1:
REPORTER_ASSERT(reporter,
style.getFontStyle().slant() == SkFontStyle::kItalic_Slant);
break;
case 2:
REPORTER_ASSERT(
reporter,
style.getFontStyle().slant() == SkFontStyle::kUpright_Slant);
break;
default:
REPORTER_ASSERT(reporter, false);
break;
}
++index;
return true;
});
}
DEF_TEST(SkParagraph_ChineseParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ChineseParagraph.png");
const char* text =
"左線読設重説切後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育"
"図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得庭"
"際輝求佐抗蒼提夜合逃表。注統天言件自謙雅載報紙喪。作画稿愛器灯女書利変探"
"訃第金線朝開化建。子戦年帝励害表月幕株漠新期刊人秘。図的海力生禁挙保天戦"
"聞条年所在口。";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kJustify);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
auto decoration = (TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline |
TextDecoration::kLineThrough);
TextStyle text_style;
text_style.setFontFamilies({SkString("Source Han Serif CN")});
text_style.setFontSize(35);
text_style.setColor(SK_ColorBLACK);
text_style.setLetterSpacing(2);
text_style.setHeight(1);
text_style.setDecoration(decoration);
text_style.setDecorationColor(SK_ColorBLACK);
text_style.setDecorationStyle(TextDecorationStyle::kSolid);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 7);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
}
// Checked: disabled for TxtLib
DEF_TEST(SkParagraph_ArabicParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ArabicParagraph.png");
const char* text =
"من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
"بمباركة التقليدية قام عن. تصفح";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kJustify);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
auto decoration = (TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline |
TextDecoration::kLineThrough);
TextStyle text_style;
text_style.setFontFamilies({SkString("Katibeh")});
text_style.setFontSize(35);
text_style.setColor(SK_ColorBLACK);
text_style.setLetterSpacing(2);
text_style.setDecoration(decoration);
text_style.setDecorationColor(SK_ColorBLACK);
text_style.setDecorationStyle(TextDecorationStyle::kSolid);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 2);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
}
// Checked: DIFF (2 boxes and each space is a word)
DEF_TEST(SkParagraph_ArabicRectsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ArabicRectsParagraph.png");
const char* text = "بمباركة التقليدية قام عن. تصفح يد ";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kRight);
paragraph_style.setTextDirection(TextDirection::kRtl);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
std::vector<TextBox> boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1ull);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 538.548f, EPSILON100)); // DIFF: 510.09375
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.268f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 900, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
}
// Checked DIFF+
// This test shows now 2 boxes for [36:40) range:
// [36:38) for arabic text and [38:39) for the last space
// that has default paragraph direction (LTR) and is placed at the end of the paragraph
DEF_TEST(SkParagraph_ArabicRectsLTRLeftAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ArabicRectsLTRLeftAlignParagraph.png");
const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.setTextDirection(TextDirection::kLtr);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 3);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
// There are 39 codepoints: [0:39); asking for [36:40) would give the same as for [36:39)
std::vector<TextBox> boxes = paragraph->getRectsForRange(36, 40, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 2ull);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 83.92f, EPSILON100)); // DIFF: 89.40625
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.27f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 110.16f, EPSILON100)); // DIFF: 121.87891
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
}
// Checked DIFF+
DEF_TEST(SkParagraph_ArabicRectsLTRRightAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ArabicRectsLTRRightAlignParagraph.png");
const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kRight);
paragraph_style.setTextDirection(TextDirection::kLtr);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
text_style.setFontSize(26);
text_style.setWordSpacing(5);
text_style.setColor(SK_ColorBLACK);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 100);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 3);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
std::vector<TextBox> boxes =
paragraph->getRectsForRange(36, 40, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 2ull); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 561.5f, EPSILON100)); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.27f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 587.74f, EPSILON100)); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
}
DEF_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetGlyphPositionAtCoordinateParagraph.png");
const char* text =
"12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
"67890 12345";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
textStyle.setFontSize(50);
textStyle.setLetterSpacing(1);
textStyle.setWordSpacing(5);
textStyle.setHeight(1);
textStyle.setColor(SK_ColorBLACK);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
// Tests for getGlyphPositionAtCoordinate()
// NOTE: resulting values can be a few off from their respective positions in
// the original text because the final trailing whitespaces are sometimes not
// drawn (namely, when using "justify" alignment) and therefore are not active
// glyphs.
REPORTER_ASSERT(reporter,
paragraph->getGlyphPositionAtCoordinate(-10000, -10000).position == 0);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(-1, -1).position == 0);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(0, 0).position == 0);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(3, 3).position == 0);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(35, 1).position == 1);
REPORTER_ASSERT(reporter,
paragraph->getGlyphPositionAtCoordinate(300, 2).position == 11);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(301, 2.2f).position == 11);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(302, 2.6f).position == 11);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(301, 2.1f).position == 11);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(100000, 20).position == 18);//
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(450, 20).position == 16);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(100000, 90).position == 36);//
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(-100000, 90).position == 18);
REPORTER_ASSERT(reporter,
paragraph->getGlyphPositionAtCoordinate(20, -80).position == 1);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 90).position == 18);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 170).position == 36);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(10000, 180).position == 72);//
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(70, 180).position == 56);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 270).position == 72);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(35, 90).position == 19);
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(10000, 10000).position == 77);//
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(85, 10000).position == 75);
}
DEF_TEST(SkParagraph_GetRectsForRangeParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeParagraph.png");
const char* text =
"12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
"67890 12345";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle heightStyle = RectHeightStyle::kMax;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 28.417f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 56.835f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 177.97f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 177.97f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 507.031f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(30, 100, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 4);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 211.375f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 463.623f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 236.406f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 142.089f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 295, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 450.1875f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 519.47266f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
DEF_TEST(SkParagraph_GetRectsForRangeTight, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeTight.png");
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
textStyle.setFontSize(50);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle heightStyle = RectHeightStyle::kTight;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 16.898f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 66.899f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 264.099f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 2);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 264.099f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 595.085f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
}
}
// Checked: DIFF+
DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle.png");
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1.6f);
textStyle.setHeightOverride(true);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingMiddle;
RectWidthStyle widthStyle = RectWidthStyle::kMax;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.4296889f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.429688f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.00781f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.0625f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 8);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 88.473305f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 168.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 88.473305f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 168.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 168.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 248.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 168.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 248.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 248.47331f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 328.47333f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 328.47333f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 408.4733f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.72656f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.23047f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.23047f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 88.473305f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
// Checked: NO DIFF+
DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingTop, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingTop.png");
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1.6f);
textStyle.setHeightOverride(true);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingTop;
RectWidthStyle widthStyle = RectWidthStyle::kMax;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.4296889f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.429688f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.00781f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.0625f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
canvas.drawRects(SK_ColorMAGENTA, result);
REPORTER_ASSERT(reporter, result.size() == 8);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 80, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 80, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 240, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 240, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 240, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 320, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 320, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 400, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLACK, result);
REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.72656f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.23047f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.23047f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946615f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 80, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
// Checked: NO DIFF+
DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingBottom, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingBottom.png");
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1.6f);
textStyle.setHeightOverride(true);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingBottom;
RectWidthStyle widthStyle = RectWidthStyle::kMax;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.429f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.4298f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.007f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.007f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.062f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
canvas.drawRects(SK_ColorMAGENTA, result);
REPORTER_ASSERT(reporter, result.size() == 8);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.007f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 96.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 176.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 96.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.023f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 176.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 176.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 256.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 176.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.023f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 256.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 256.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.023f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 336.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 336.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.023f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 416.946f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLACK, result);
REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.726f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.230f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.230f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.023f, EPSILON20));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 96.946f, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
// This is the test I cannot accommodate
// Any text range gets a smallest glyph rectangle
DEF_TEST_DISABLED(SkParagraph_GetRectsForRangeIncludeCombiningCharacter, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeCombiningCharacter.png");
const char* text = "ดีสวัสดีชาวโลกที่น่ารัก";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setLetterSpacing(1);
textStyle.setWordSpacing(5);
textStyle.setHeight(1);
textStyle.setColor(SK_ColorBLACK);
builder.pushStyle(textStyle);
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());
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
RectHeightStyle heightStyle = RectHeightStyle::kTight;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto first = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
auto second = paragraph->getRectsForRange(1, 2, heightStyle, widthStyle);
auto last = paragraph->getRectsForRange(0, 2, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 1 && last.size() == 1);
REPORTER_ASSERT(reporter, second[0].rect == last[0].rect);
}
{
auto first = paragraph->getRectsForRange(3, 4, heightStyle, widthStyle);
auto second = paragraph->getRectsForRange(4, 5, heightStyle, widthStyle);
auto last = paragraph->getRectsForRange(3, 5, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 1 && last.size() == 1);
REPORTER_ASSERT(reporter, second[0].rect == last[0].rect);
}
{
auto first = paragraph->getRectsForRange(14, 15, heightStyle, widthStyle);
auto second = paragraph->getRectsForRange(15, 16, heightStyle, widthStyle);
auto third = paragraph->getRectsForRange(16, 17, heightStyle, widthStyle);
auto last = paragraph->getRectsForRange(14, 17, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 0 && third.size() == 1 && last.size() == 1);
REPORTER_ASSERT(reporter, third[0].rect == last[0].rect);
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_GetRectsForRangeCenterParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraph.png");
// Minikin uses a hard coded list of unicode characters that he treats as invisible - as spaces.
// It's absolutely wrong - invisibility is a glyph attribute, not character/grapheme.
// Any attempt to substitute one for another leads to errors
// (for instance, some fonts can use these hard coded characters for something that is visible)
const char* text = "01234   "; // includes ideographic space and english space.
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kCenter);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
// Some of the formatting lazily done on paint
RectHeightStyle heightStyle = RectHeightStyle::kMax;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(2, 4, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 260.791f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 317.626f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(4, 5, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 346.044f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(4, 6, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLACK, result);
REPORTER_ASSERT(reporter, result.size() == 1); // DIFF
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(5, 6, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 346.044f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
// Checked DIFF+
DEF_TEST(SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered.png");
const char* text = "01234\n";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kCenter);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 2);
RectHeightStyle heightStyle = RectHeightStyle::kMax;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
}
{
auto result = paragraph->getRectsForRange(6, 7, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 275.0f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.406f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 275.0f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, EPSILON100));
}
}
// Checked NO DIFF
DEF_TEST(SkParagraph_GetRectsForRangeCenterMultiLineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeCenterMultiLineParagraph.png");
const char* text = "01234   \n0123  "; // includes ideographic space and english space.
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kCenter);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(50);
textStyle.setHeight(1);
textStyle.setColor(SK_ColorBLACK);
textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 2);
RectHeightStyle heightStyle = RectHeightStyle::kMax;
RectWidthStyle widthStyle = RectWidthStyle::kTight;
SkScalar epsilon = 0.01f;
{
auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
{
auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
}
{
auto result = paragraph->getRectsForRange(2, 4, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLUE, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 260.791f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 317.626f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
}
{
auto result = paragraph->getRectsForRange(4, 6, heightStyle, widthStyle);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
}
{
auto result = paragraph->getRectsForRange(5, 6, heightStyle, widthStyle);
canvas.drawRects(SK_ColorYELLOW, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 346.044f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
}
{
auto result = paragraph->getRectsForRange(10, 12, heightStyle, widthStyle);
canvas.drawRects(SK_ColorCYAN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 218.164f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 275, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, epsilon));
}
{
auto result = paragraph->getRectsForRange(14, 18, heightStyle, widthStyle);
canvas.drawRects(SK_ColorBLACK, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 331.835f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 419.189f, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, epsilon));
}
{
auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
REPORTER_ASSERT(reporter, result.empty());
}
}
// Checked: DIFF (line height rounding error)
DEF_TEST(SkParagraph_GetRectsForRangeStrut, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeStrut.png");
const char* text = "Chinese 字典";
const size_t len = strlen(text);
StrutStyle strutStyle;
strutStyle.setStrutEnabled(true);
strutStyle.setFontFamilies({SkString("Roboto")});
strutStyle.setFontSize(14.0);
ParagraphStyle paragraphStyle;
paragraphStyle.setStrutStyle(strutStyle);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
textStyle.setFontSize(20);
textStyle.setColor(SK_ColorBLACK);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
{
auto result = paragraph->getRectsForRange(0, 10, RectHeightStyle::kTight, RectWidthStyle::kMax);
canvas.drawRects(SK_ColorGREEN, result);
REPORTER_ASSERT(reporter, result.size() == 1);
}
{
auto result = paragraph->getRectsForRange(0, 10, RectHeightStyle::kStrut, RectWidthStyle::kMax);
canvas.drawRects(SK_ColorRED, result);
REPORTER_ASSERT(reporter, result.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 10.611f, EPSILON2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 118.605f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 27.017f, EPSILON2));
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_GetRectsForRangeStrutFallback, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetRectsForRangeStrutFallback.png");
const char* text = "Chinese 字典";
const size_t len = strlen(text);
StrutStyle strutStyle;
strutStyle.setStrutEnabled(false);
ParagraphStyle paragraphStyle;
paragraphStyle.setStrutStyle(strutStyle);
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
textStyle.setFontSize(20);
textStyle.setColor(SK_ColorBLACK);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto result1 = paragraph->getRectsForRange(0, 10, RectHeightStyle::kTight, RectWidthStyle::kMax);
canvas.drawRects(SK_ColorGREEN, result1);
REPORTER_ASSERT(reporter, result1.size() == 1);
auto result2 = paragraph->getRectsForRange(0, 10, RectHeightStyle::kStrut, RectWidthStyle::kMax);
canvas.drawRects(SK_ColorRED, result2);
REPORTER_ASSERT(reporter, result2.size() == 1);
REPORTER_ASSERT(reporter, result1[0].rect == result2[0].rect);
}
// Checked: DIFF (small in numbers)
DEF_TEST(SkParagraph_GetWordBoundaryParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_GetWordBoundaryParagraph.png");
const char* text = "12345 67890 12345 67890 12345 67890 12345 "
"67890 12345 67890 12345 67890 12345";
const size_t len = strlen(text);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextAlign(TextAlign::kLeft);
paragraphStyle.setMaxLines(10);
paragraphStyle.turnHintingOff();
TextStyle textStyle;
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(52);
textStyle.setLetterSpacing(1.19039f);
textStyle.setWordSpacing(5);
textStyle.setHeight(1.5);
textStyle.setHeightOverride(true);
textStyle.setColor(SK_ColorBLACK);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.pushStyle(textStyle);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(0) == SkRange<size_t>(0, 5));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(1) == SkRange<size_t>(0, 5));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(2) == SkRange<size_t>(0, 5));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(3) == SkRange<size_t>(0, 5));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(4) == SkRange<size_t>(0, 5));
auto boxes = paragraph->getRectsForRange(5, 6, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(5) == SkRange<size_t>(5, 7));
boxes = paragraph->getRectsForRange(6, 7, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(6) == SkRange<size_t>(5, 7));
boxes = paragraph->getRectsForRange(7, 8, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(7) == SkRange<size_t>(7, 12));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(8) == SkRange<size_t>(7, 12));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(9) == SkRange<size_t>(7, 12));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(10) == SkRange<size_t>(7, 12));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(11) == SkRange<size_t>(7, 12));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(12) == SkRange<size_t>(12, 13));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(13) == SkRange<size_t>(13, 18));
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(30) == SkRange<size_t>(30, 31));
boxes = paragraph->getRectsForRange(12, 13, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(13, 14, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(18, 19, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(19, 20, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(24, 25, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(25, 26, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(30, 31, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
boxes = paragraph->getRectsForRange(31, 32, RectHeightStyle::kMax, RectWidthStyle::kTight);
canvas.drawLines(SK_ColorRED, boxes);
auto outLen = static_cast<ParagraphImpl*>(paragraph.get())->text().size();
REPORTER_ASSERT(reporter, paragraph->getWordBoundary(outLen - 1) == SkRange<size_t>(outLen - 5, outLen));
}
// Checked: DIFF (unclear)
DEF_TEST(SkParagraph_SpacingParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_SpacingParagraph.png");
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(50);
text_style.setLetterSpacing(20);
text_style.setWordSpacing(0);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText("H", 1);
builder.pop();
text_style.setLetterSpacing(10);
text_style.setWordSpacing(0);
builder.pushStyle(text_style);
builder.addText("H", 1);
builder.pop();
text_style.setLetterSpacing(20);
text_style.setWordSpacing(0);
builder.pushStyle(text_style);
builder.addText("H", 1);
builder.pop();
text_style.setLetterSpacing(0);
text_style.setWordSpacing(0);
builder.pushStyle(text_style);
builder.addText("|", 1);
builder.pop();
const char* hSpace = "H ";
const size_t len = strlen(hSpace);
text_style.setLetterSpacing(0);
text_style.setWordSpacing(20);
builder.pushStyle(text_style);
builder.addText(hSpace, len);
builder.pop();
text_style.setLetterSpacing(0);
text_style.setWordSpacing(0);
builder.pushStyle(text_style);
builder.addText(hSpace, len);
builder.pop();
text_style.setLetterSpacing(0);
text_style.setLetterSpacing(0);
text_style.setWordSpacing(20);
builder.pushStyle(text_style);
builder.addText(hSpace, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
size_t index = 0;
impl->lines().begin()->scanStyles(StyleType::kLetterSpacing,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
++index;
return true;
});
REPORTER_ASSERT(reporter, index == 4);
index = 0;
impl->lines().begin()->scanStyles(StyleType::kWordSpacing,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
++index;
return true;
});
REPORTER_ASSERT(reporter, index == 4);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_LongWordParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_LongWordParagraph.png");
const char* text =
"A "
"veryverylongwordtoseewherethiswillwraporifitwillatallandifitdoesthenthat"
"wouldbeagoodthingbecausethebreakingisworking.";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorRED);
text_style.setFontSize(31);
text_style.setLetterSpacing(0);
text_style.setWordSpacing(0);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth / 2);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
REPORTER_ASSERT(reporter, impl->lines().size() == 4);
REPORTER_ASSERT(reporter, impl->lines()[0].width() > TestCanvasWidth / 2 - 20);
REPORTER_ASSERT(reporter, impl->lines()[1].width() > TestCanvasWidth / 2 - 20);
REPORTER_ASSERT(reporter, impl->lines()[2].width() > TestCanvasWidth / 2 - 20);
}
// Checked: DIFF?
DEF_TEST(SkParagraph_KernScaleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_KernScaleParagraph.png");
const char* text1 = "AVAVAWAH A0 V0 VA To The Lo";
const char* text2 = " Dialog Text List lots of words to see "
"if kerning works on a bigger set of characters AVAVAW";
float scale = 3.0f;
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Droid Serif")});
text_style.setFontSize(100 / scale);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
builder.pushStyle(text_style);
builder.addText("A", 1);
builder.pushStyle(text_style);
builder.addText("V", 1);
text_style.setFontSize(14 / scale);
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth / scale);
canvas.get()->scale(scale, scale);
paragraph->paint(canvas.get(), 0, 0);
canvas.get()->scale(1, 1);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// First and second lines must have the same width, the third one must be bigger
REPORTER_ASSERT(reporter, impl->lines().size() == 3);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].width(), 285.858f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[1].width(), 329.709f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[2].width(), 120.619f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].height(), 39.00f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[1].height(), 39.00f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[2].height(), 05.00f, EPSILON100));
}
// Checked: DIFF+
DEF_TEST(SkParagraph_NewlineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_NewlineParagraph.png");
const char* text =
"line1\nline2 test1 test2 test3 test4 test5 test6 test7\nline3\n\nline4 "
"test1 test2 test3 test4";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorRED);
text_style.setFontSize(60);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 300);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Minikin does not count empty lines but SkParagraph does
REPORTER_ASSERT(reporter, impl->lines().size() == 7);
REPORTER_ASSERT(reporter, impl->lines()[0].offset().fY == 0);
REPORTER_ASSERT(reporter, impl->lines()[1].offset().fY == 70);
REPORTER_ASSERT(reporter, impl->lines()[2].offset().fY == 140);
REPORTER_ASSERT(reporter, impl->lines()[3].offset().fY == 210);
REPORTER_ASSERT(reporter, impl->lines()[4].offset().fY == 280); // Empty line
REPORTER_ASSERT(reporter, impl->lines()[5].offset().fY == 296);
REPORTER_ASSERT(reporter, impl->lines()[6].offset().fY == 366);
}
// TODO: Fix underline
DEF_TEST(SkParagraph_EmojiParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_EmojiParagraph.png");
const char* text =
"😀😃😄😁😆😅😂🤣☺😇🙂😍😡😟😢😻👽💩👍👎🙏👌👋👄👁👦👼👨‍🚀👨‍🚒🙋‍♂️👳👨‍👨‍👧‍👧\
💼👡👠☂🐶🐰🐻🐼🐷🐒🐵🐔🐧🐦🐋🐟🐡🕸🐌🐴🐊🐄🐪🐘🌸🌏🔥🌟🌚🌝💦💧\
❄🍕🍔🍟🥝🍱🕶🎩🏈⚽🚴‍♀️🎻🎼🎹🚨🚎🚐⚓🛳🚀🚁🏪🏢🖱⏰📱💾💉📉🛏🔑🔓\
📁🗓📊❤💯🚫🔻♠♣🕓❗🏳🏁🏳️‍🌈🇮🇹🇱🇷🇺🇸🇬🇧🇨🇳🇧🇴";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Color Emoji")});
text_style.setFontSize(50);
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 8);
for (auto& line : impl->lines()) {
if (&line != impl->lines().end() - 1) {
REPORTER_ASSERT(reporter, line.width() == 998.25f);
} else {
REPORTER_ASSERT(reporter, line.width() < 998.25f);
}
REPORTER_ASSERT(reporter, line.height() == 59);
}
}
// Checked: DIFF+
DEF_TEST(SkParagraph_EmojiMultiLineRectsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_EmojiMultiLineRectsParagraph.png");
const char* text =
"👩👩👦👩👩👧👧🇺🇸👩👩👦👩👩👧👧i🇺🇸👩👩👦👩👩👧👧🇺🇸👩👩👦👩👩👧👧🇺🇸"
"👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸"
"👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸"
"👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸👩‍👩‍👦👩‍👩‍👧‍👧🇺🇸"
"❄🍕🍔🍟🥝🍱🕶🎩🏈⚽🚴‍♀️🎻🎼🎹🚨🚎🚐⚓🛳🚀🚁🏪🏢🖱⏰📱💾💉📉🛏🔑🔓"
"📁🗓📊❤💯🚫🔻♠♣🕓❗🏳🏁🏳️‍🌈🇮🇹🇱🇷🇺🇸🇬🇧🇨🇳🇧🇴";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Color Emoji")});
text_style.setFontSize(50);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth - 300);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto result = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 0);
result = paragraph->getRectsForRange(0, 119, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 2);
canvas.drawRects(SK_ColorRED, result);
result = paragraph->getRectsForRange(122, 132, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 1);
canvas.drawRects(SK_ColorBLUE, result);
auto pos = paragraph->getGlyphPositionAtCoordinate(610, 100).position;
result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 2);
canvas.drawRects(SK_ColorGREEN, result);
pos = paragraph->getGlyphPositionAtCoordinate(580, 100).position;
result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 2);
canvas.drawRects(SK_ColorGREEN, result);
pos = paragraph->getGlyphPositionAtCoordinate(560, 100).position;
result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, result.size() == 2);
canvas.drawRects(SK_ColorGREEN, result);
}
DEF_TEST(SkParagraph_HyphenBreakParagraph, reporter) {
SkDebugf("Hyphens are not implemented, and will not be implemented soon.\n");
}
// Checked: DIFF (line breaking)
DEF_TEST(SkParagraph_RepeatLayoutParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_RepeatLayoutParagraph.png");
const char* text =
"Sentence to layout at diff widths to get diff line counts. short words "
"short words short words short words short words short words short words "
"short words short words short words short words short words short words "
"end";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(31);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(300);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Some of the formatting lazily done on paint
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 12);
paragraph->layout(600);
paragraph->paint(canvas.get(), 0, 0);
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
REPORTER_ASSERT(reporter, impl->lines().size() == 6);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_Ellipsize, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_Ellipsize.png");
const char* text =
"This is a very long sentence to test if the text will properly wrap "
"around and go to the next line. Sometimes, short sentence. Longer "
"sentences are okay too because they are nessecary. Very short. ";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(1);
paragraph_style.setEllipsis(u"\u2026");
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Check that the ellipsizer limited the text to one line and did not wrap to a second line.
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
auto& line = impl->lines()[0];
REPORTER_ASSERT(reporter, line.ellipsis() != nullptr);
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_UnderlineShiftParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_UnderlineShiftParagraph.png");
const char* text1 = "fluttser ";
const char* text2 = "mdje";
const char* text3 = "fluttser mdje";
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.setMaxLines(2);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
text_style.setDecoration(TextDecoration::kUnderline);
text_style.setDecorationColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
ParagraphBuilderImpl builder1(paragraph_style, fontCollection);
text_style.setDecoration(TextDecoration::kNoDecoration);
builder1.pushStyle(text_style);
builder1.addText(text3, strlen(text3));
builder1.pop();
auto paragraph1 = builder1.Build();
paragraph1->layout(TestCanvasWidth);
paragraph1->paint(canvas.get(), 0, 25);
auto impl1 = static_cast<ParagraphImpl*>(paragraph1.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 1);
REPORTER_ASSERT(reporter, impl1->lines().size() == 1);
auto rect = paragraph->getRectsForRange(0, 12, RectHeightStyle::kMax, RectWidthStyle::kTight)
.front()
.rect;
auto rect1 = paragraph1->getRectsForRange(0, 12, RectHeightStyle::kMax, RectWidthStyle::kTight)
.front()
.rect;
REPORTER_ASSERT(reporter, rect.fLeft == rect1.fLeft);
REPORTER_ASSERT(reporter, rect.fRight == rect1.fRight);
for (size_t i = 0; i < 12; ++i) {
// Not all ranges produce a rectangle ("fl" goes into one cluster so [0:1) is empty)
auto r1 = paragraph->getRectsForRange(i, i + 1, RectHeightStyle::kMax, RectWidthStyle::kTight);
auto r2 = paragraph1->getRectsForRange(i, i + 1, RectHeightStyle::kMax, RectWidthStyle::kTight);
REPORTER_ASSERT(reporter, r1.size() == r2.size());
if (!r1.empty() && !r2.empty()) {
REPORTER_ASSERT(reporter, r1.front().rect.fLeft == r2.front().rect.fLeft);
REPORTER_ASSERT(reporter, r1.front().rect.fRight == r2.front().rect.fRight);
}
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_SimpleShadow, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_SimpleShadow.png");
const char* text = "Hello World Text Dialog";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(2.0f, 2.0f), 1.0));
builder.pushStyle(text_style);
builder.addText(text, len);
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 15.0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
REPORTER_ASSERT(reporter, impl->styles().size() == 1);
size_t index = 0;
for (auto& line : impl->lines()) {
line.scanStyles(StyleType::kShadow,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
REPORTER_ASSERT(reporter, index == 0 && style.equals(text_style));
++index;
return true;
});
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_ComplexShadow, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_ComplexShadow.png");
const char* text = "Text Chunk ";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(2.0f, 2.0f), 1.0f));
builder.pushStyle(text_style);
builder.addText(text, len);
text_style.addShadow(TextShadow(SK_ColorRED, SkPoint::Make(2.0f, 2.0f), 5.0f));
text_style.addShadow(TextShadow(SK_ColorGREEN, SkPoint::Make(10.0f, -5.0f), 3.0f));
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
builder.addText(text, len);
text_style.addShadow(TextShadow(SK_ColorRED, SkPoint::Make(0.0f, 1.0f), 0.0f));
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
builder.addText(text, len);
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 15.0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
size_t index = 0;
for (auto& line : impl->lines()) {
line.scanStyles(StyleType::kShadow,
[&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
++index;
switch (index) {
case 1:
REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
break;
case 2:
REPORTER_ASSERT(reporter, style.getShadowNumber() == 3);
break;
case 3:
REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
break;
case 4:
REPORTER_ASSERT(reporter, style.getShadowNumber() == 4);
REPORTER_ASSERT(reporter, style.equals(text_style));
break;
case 5:
REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
break;
default:
REPORTER_ASSERT(reporter, false);
}
return true;
});
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_BaselineParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_BaselineParagraph.png");
const char* text =
"左線読設Byg後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育"
"図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setMaxLines(14);
paragraph_style.setTextAlign(TextAlign::kJustify);
paragraph_style.setHeight(1.5);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Source Han Serif CN")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(55);
text_style.setLetterSpacing(2);
text_style.setDecorationStyle(TextDecorationStyle::kSolid);
text_style.setDecorationColor(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);
SkRect rect1 = SkRect::MakeXYWH(0, paragraph->getIdeographicBaseline(),
paragraph->getMaxWidth(),
paragraph->getIdeographicBaseline());
SkRect rect2 = SkRect::MakeXYWH(0, paragraph->getAlphabeticBaseline(),
paragraph->getMaxWidth(),
paragraph->getAlphabeticBaseline());
canvas.drawLine(SK_ColorRED, rect1, false);
canvas.drawLine(SK_ColorGREEN, rect2, false);
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(paragraph->getIdeographicBaseline(), 79.035f, EPSILON100));
REPORTER_ASSERT(reporter,
SkScalarNearlyEqual(paragraph->getAlphabeticBaseline(), 63.305f, EPSILON100));
}
// Checked: NO DIFF (number of runs only)
DEF_TEST(SkParagraph_FontFallbackParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_FontFallbackParagraph.png");
const char* text1 = "Roboto 字典 "; // Roboto + unresolved
const char* text2 = "Homemade Apple 字典"; // Homemade Apple + Noto Sans...
const char* text3 = "Chinese 字典"; // Homemade Apple + Source Han
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({
SkString("Not a real font"),
SkString("Also a fake font"),
SkString("So fake it is obvious"),
SkString("Next one should be a real font..."),
SkString("Roboto"),
SkString("another fake one in between"),
SkString("Homemade Apple"),
});
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
text_style.setFontFamilies({
SkString("Not a real font"),
SkString("Also a fake font"),
SkString("So fake it is obvious"),
SkString("Homemade Apple"),
SkString("Next one should be a real font..."),
SkString("Roboto"),
SkString("another fake one in between"),
SkString("Noto Sans CJK JP"),
SkString("Source Han Serif CN"),
});
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
text_style.setFontFamilies({
SkString("Not a real font"),
SkString("Also a fake font"),
SkString("So fake it is obvious"),
SkString("Homemade Apple"),
SkString("Next one should be a real font..."),
SkString("Roboto"),
SkString("another fake one in between"),
SkString("Source Han Serif CN"),
SkString("Noto Sans CJK JP"),
});
builder.pushStyle(text_style);
builder.addText(text3, strlen(text3));
builder.pop();
auto paragraph = builder.Build();
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == -1); // Not shaped yet
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 15.0);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 2); // From the text1
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Font resolution in Skia produces 6 runs because 2 parts of "Roboto 字典 " have different
// script (Minikin merges the first 2 into one because of unresolved) [Apple + Unresolved ]
// [Apple + Noto] [Apple + Han]
REPORTER_ASSERT(reporter, impl->runs().size() == 7);
auto robotoAdvance = impl->runs()[0].advance().fX +
impl->runs()[1].advance().fX +
impl->runs()[2].advance().fX;
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(robotoAdvance, 64.199f, EPSILON50));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[3].advance().fX, 139.125f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[4].advance().fX, 27.999f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[5].advance().fX, 62.248f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[6].advance().fX, 27.999f, EPSILON100));
// When a different font is resolved, then the metrics are different.
REPORTER_ASSERT(reporter, impl->runs()[4].correctAscent() != impl->runs()[6].correctAscent());
REPORTER_ASSERT(reporter, impl->runs()[4].correctDescent() != impl->runs()[6].correctDescent());
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_StrutParagraph1.png");
// The chinese extra height should be absorbed by the strut.
const char* text = "01234満毎冠p来É本可\nabcd\n満毎É行p昼本可";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
StrutStyle strut_style;
strut_style.setStrutEnabled(true);
strut_style.setFontFamilies({SkString("BlahFake"), SkString("Ahem")});
strut_style.setFontSize(50);
strut_style.setHeight(1.8f);
strut_style.setHeightOverride(true);
strut_style.setLeading(0.1f);
paragraph_style.setStrutStyle(strut_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(50);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(0.5f);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->lines().size() == 4);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
{
auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, boxes.empty());
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 34.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 84.5f, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 34.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 84.5f, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 190, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 285, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 285, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 380, EPSILON100));
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_StrutParagraph2.png");
// The chinese extra height should be absorbed by the strut.
const char* text = "01234ABCDEFGH\nabcd\nABCDEFGH";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
StrutStyle strut_style;
strut_style.setStrutEnabled(true);
strut_style.setFontFamilies({SkString("Ahem")});
strut_style.setFontSize(50);
strut_style.setHeight(1.6f);
strut_style.setHeightOverride(true);
paragraph_style.setStrutStyle(strut_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(50);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Font is not resolved and the first line does not fit
REPORTER_ASSERT(reporter, impl->lines().size() == 4);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
{
auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, boxes.empty());
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 240, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 240, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 320, EPSILON100));
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_StrutParagraph3.png");
// The chinese extra height should be absorbed by the strut.
const char* text = "01234満毎p行来昼本可\nabcd\n満毎冠行来昼本可";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
StrutStyle strut_style;
strut_style.setStrutEnabled(true);
strut_style.setFontFamilies({SkString("Ahem")});
strut_style.setFontSize(50);
strut_style.setHeight(1.2f);
strut_style.setHeightOverride(true);
paragraph_style.setStrutStyle(strut_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(50);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
SkFontStyle::kUpright_Slant));
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Font is not resolved and the first line does not fit
REPORTER_ASSERT(reporter, impl->lines().size() == 4);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
SkScalar epsilon = 0.001f;
{
auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, boxes.empty());
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 8, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 58, epsilon));
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 8, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 58, epsilon));
}
{
auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
}
{
auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 120, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 180, epsilon));
}
{
auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 180, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 240, epsilon));
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_StrutForceParagraph.png");
const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
StrutStyle strut_style;
strut_style.setStrutEnabled(true);
strut_style.setFontFamilies({SkString("Ahem")});
strut_style.setFontSize(50);
strut_style.setHeight(1.5f);
strut_style.setHeightOverride(true);
strut_style.setLeading(0.1f);
strut_style.setForceStrutHeight(true);
paragraph_style.setStrutStyle(strut_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(50);
text_style.setLetterSpacing(0);
text_style.setColor(SK_ColorBLACK);
text_style.setHeight(1);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
// Font is not resolved and the first line does not fit
REPORTER_ASSERT(reporter, impl->lines().size() == 4);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
auto boxes1 = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, boxes1.empty());
auto boxes2 = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes2);
REPORTER_ASSERT(reporter, boxes2.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.top(), 22.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.bottom(), 72.5f, EPSILON100));
auto boxes3 = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes3);
REPORTER_ASSERT(reporter, boxes3.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.bottom(), 80, EPSILON100));
auto boxes4 = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes4);
REPORTER_ASSERT(reporter, boxes4.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.top(), 22.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.bottom(), 72.5f, EPSILON100));
auto boxes5 = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes5);
REPORTER_ASSERT(reporter, boxes5.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.left(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.bottom(), 80, EPSILON100));
auto boxes6 = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes6);
REPORTER_ASSERT(reporter, boxes6.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.top(), 160, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.right(), 100, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.bottom(), 240, EPSILON100));
auto boxes7 = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes7);
REPORTER_ASSERT(reporter, boxes7.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.left(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.top(), 240, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.right(), 300, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.bottom(), 320, EPSILON100));
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_StrutDefaultParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_StrutDefaultParagraph.png");
const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(10);
paragraph_style.setTextAlign(TextAlign::kLeft);
paragraph_style.turnHintingOff();
StrutStyle strut_style;
strut_style.setStrutEnabled(true);
strut_style.setFontFamilies({SkString("Ahem")});
strut_style.setFontSize(50);
strut_style.setHeight(1.5f);
strut_style.setLeading(0.1f);
strut_style.setForceStrutHeight(false);
paragraph_style.setStrutStyle(strut_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(20);
text_style.setColor(SK_ColorBLACK);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(550);
paragraph->paint(canvas.get(), 0, 0);
RectHeightStyle rect_height_style = RectHeightStyle::kTight;
RectHeightStyle rect_height_strut_style = RectHeightStyle::kStrut;
RectWidthStyle rect_width_style = RectWidthStyle::kTight;
{
auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
REPORTER_ASSERT(reporter, boxes.empty());
}
{
auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 26.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 20, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 46.5f, EPSILON100));
}
{
auto boxes = paragraph->getRectsForRange(0, 2, rect_height_strut_style, rect_width_style);
canvas.drawRects(SK_ColorRED, boxes);
REPORTER_ASSERT(reporter, boxes.size() == 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 2.5f, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 40, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 52.5f, EPSILON100));
}
}
DEF_TEST(SkParagraph_FontFeaturesParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_FontFeaturesParagraph.png");
const char* text = "12ab\n";
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontStyle(SkFontStyle::Italic()); // Regular Roboto doesn't have font features
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.addFontFeature(SkString("tnum"), 1);
builder.pushStyle(text_style);
builder.addText(text);
text_style.resetFontFeatures();
text_style.addFontFeature(SkString("tnum"), 0);
text_style.addFontFeature(SkString("pnum"), 1);
builder.pushStyle(text_style);
builder.addText(text);
builder.pop();
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 10.0, 15.0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, paragraph->lineNumber() == 3ull);
auto& tnum_line = impl->lines()[0];
auto& pnum_line = impl->lines()[1];
REPORTER_ASSERT(reporter, tnum_line.clusters().width() == 4ull);
REPORTER_ASSERT(reporter, pnum_line.clusters().width() == 4ull);
// Tabular numbers should have equal widths.
REPORTER_ASSERT(reporter, impl->clusters()[0].width() == impl->clusters()[1].width());
// Proportional numbers should have variable widths.
REPORTER_ASSERT(reporter, impl->clusters()[5].width() != impl->clusters()[6].width());
// Alphabetic characters should be unaffected.
REPORTER_ASSERT(reporter, impl->clusters()[2].width() == impl->clusters()[7].width());
}
// Not in Minikin
DEF_TEST(SkParagraph_WhitespacesInMultipleFonts, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
const char* text = "English English 字典 字典 😀😃😄 😀😃😄";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies(
{SkString("Roboto"), SkString("Noto Color Emoji"), SkString("Source Han Serif CN")});
text_style.setFontSize(60);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
for (size_t i = 0; i < impl->runs().size() - 1; ++i) {
auto first = impl->runs()[i].textRange();
auto next = impl->runs()[i + 1].textRange();
REPORTER_ASSERT(reporter, first.end == next.start);
}
}
DEF_TEST(SkParagraph_JSON1, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
const char* text = "👨‍👩‍👧‍👦";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Color Emoji")});
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
auto& run = impl->runs().front();
auto cluster = 0;
SkShaperJSONWriter::VisualizeClusters(
text, 0, std::strlen(text), run.glyphs(), run.clusterIndexes(),
[&](int codePointCount, SkSpan<const char> utf1to1, SkSpan<const SkGlyphID> glyph1to1) {
if (cluster == 0) {
std::string toCheckUtf8{utf1to1.data(), utf1to1.size()};
SkASSERT(std::strcmp(text, utf1to1.data()) == 0);
SkASSERT(glyph1to1.size() == 1);
SkASSERT(*glyph1to1.begin() == 1611);
}
++cluster;
});
REPORTER_ASSERT(reporter, cluster <= 2);
}
DEF_TEST(SkParagraph_JSON2, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
const char* text = "p〠q";
const size_t len = strlen(text);
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({SkString("Noto Sans CJK JP")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(50);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 1);
auto cluster = 0;
for (auto& run : impl->runs()) {
SkShaperJSONWriter::VisualizeClusters(
impl->text().begin() + run.textRange().start, 0, run.textRange().width(),
run.glyphs(), run.clusterIndexes(),
[&](int codePointCount, SkSpan<const char> utf1to1,
SkSpan<const SkGlyphID> glyph1to1) {
if (cluster == 0) {
std::string toCheckUtf8{utf1to1.data(), utf1to1.size()};
SkASSERT(std::strcmp(text, utf1to1.data()) == 0);
SkASSERT(glyph1to1.size() == 3);
}
++cluster;
});
}
REPORTER_ASSERT(reporter, cluster <= 2);
}
DEF_TEST(SkParagraph_CacheText, reporter) {
ParagraphCache cache;
cache.turnOn(true);
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
auto test = [&](const char* text, int count, bool expectedToBeFound) {
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(text, strlen(text));
builder.pop();
auto paragraph = builder.Build();
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, count == cache.count());
auto found = cache.findParagraph(impl);
REPORTER_ASSERT(reporter, found == expectedToBeFound);
auto added = cache.updateParagraph(impl);
REPORTER_ASSERT(reporter, added != expectedToBeFound);
};
test("text1", 0, false);
test("text1", 1, true);
test("text2", 1, false);
test("text2", 2, true);
test("text3", 2, false);
}
DEF_TEST(SkParagraph_CacheFonts, reporter) {
ParagraphCache cache;
cache.turnOn(true);
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
const char* text = "text";
const size_t len = strlen(text);
auto test = [&](int count, bool expectedToBeFound) {
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, count == cache.count());
auto found = cache.findParagraph(impl);
REPORTER_ASSERT(reporter, found == expectedToBeFound);
auto added = cache.updateParagraph(impl);
REPORTER_ASSERT(reporter, added != expectedToBeFound);
};
text_style.setFontFamilies({SkString("Roboto")});
test(0, false);
test(1, true);
text_style.setFontFamilies({SkString("Homemade Apple")});
test(1, false);
test(2, true);
text_style.setFontFamilies({SkString("Noto Color Emoji")});
test(2, false);
}
DEF_TEST(SkParagraph_CacheFontRanges, reporter) {
ParagraphCache cache;
cache.turnOn(true);
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
auto test = [&](const char* text1,
const char* text2,
const char* font1,
const char* font2,
int count,
bool expectedToBeFound) {
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
text_style.setFontFamilies({SkString(font1)});
builder.pushStyle(text_style);
builder.addText(text1, strlen(text1));
builder.pop();
text_style.setFontFamilies({SkString(font2)});
builder.pushStyle(text_style);
builder.addText(text2, strlen(text2));
builder.pop();
auto paragraph = builder.Build();
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, count == cache.count());
auto found = cache.findParagraph(impl);
REPORTER_ASSERT(reporter, found == expectedToBeFound);
auto added = cache.updateParagraph(impl);
REPORTER_ASSERT(reporter, added != expectedToBeFound);
};
test("text", "", "Roboto", "Homemade Apple", 0, false);
test("t", "ext", "Roboto", "Homemade Apple", 1, false);
test("te", "xt", "Roboto", "Homemade Apple", 2, false);
test("tex", "t", "Roboto", "Homemade Apple", 3, false);
test("text", "", "Roboto", "Homemade Apple", 4, true);
}
DEF_TEST(SkParagraph_CacheStyles, reporter) {
ParagraphCache cache;
cache.turnOn(true);
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
const char* text = "text";
const size_t len = strlen(text);
auto test = [&](int count, bool expectedToBeFound) {
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(text, len);
builder.pop();
auto paragraph = builder.Build();
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, count == cache.count());
auto found = cache.findParagraph(impl);
REPORTER_ASSERT(reporter, found == expectedToBeFound);
auto added = cache.updateParagraph(impl);
REPORTER_ASSERT(reporter, added != expectedToBeFound);
};
test(0, false);
test(1, true);
text_style.setLetterSpacing(10);
test(1, false);
test(2, true);
text_style.setWordSpacing(10);
test(2, false);
}
DEF_TEST(SkParagraph_EmptyParagraphWithLineBreak, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_EmptyParagraphWithLineBreak.png");
ParagraphStyle paragraph_style;
TextStyle text_style;
text_style.setFontSize(16);
text_style.setFontFamilies({SkString("Roboto")});
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText("\n", 1);
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
auto result = paragraph->getRectsForPlaceholders();
}
DEF_TEST(SkParagraph_NullInMiddleOfText, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_NullInMiddleOfText.png");
const SkString text("null terminator ->\u0000<- on purpose did you see it?");
ParagraphStyle paragraph_style;
TextStyle text_style;
text_style.setFontSize(16);
text_style.setFontFamilies({SkString("Roboto")});
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText(text.c_str(), text.size());
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
}
DEF_TEST(SkParagraph_PlaceholderOnly, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
TestCanvas canvas("SkParagraph_PlaceholderOnly.png");
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
PlaceholderStyle placeholder(0, 0, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 0);
builder.addPlaceholder(placeholder);
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
auto result = paragraph->getRectsForPlaceholders();
paragraph->paint(canvas.get(), 0, 0);
}
DEF_TEST(SkParagraph_Fallbacks, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault(), "Arial");
TestCanvas canvas("SkParagraph_Fallbacks.png");
const char* multiScript = "A1!aÀàĀāƁƀḂⱠꜲꬰəͲἀἏЀЖԠꙐꙮՁخࡔࠇܦআਉઐଘஇఘಧൺඣᭆᯔᮯ᳇ꠈᜅᩌꪈ༇ꥄꡙꫤ᧰៘꧁꧂ᜰᨏᯤᢆᣭᗗꗃⵞ𐒎߷ጩꬤ𖠺‡₩℻Ⅷ↹⋇⏳ⓖ╋▒◛⚧⑆שׁ🅕㊼龜ポ䷤🂡\n";
const size_t len = strlen(multiScript);
const char* androidFonts[] = {
"sans-serif",
"sans-serif-condensed",
"serif",
"monospace",
"serif-monospace",
"casual",
"cursive",
"sans-serif-smallcaps",
};
for (auto& font : androidFonts) {
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setLocale(SkString("en_US"));
text_style.setFontSize(20);
text_style.setFontFamilies({ SkString(font) });
builder.pushStyle(text_style);
builder.addText(multiScript, len);
builder.pop();
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
paragraph->paint(canvas.get(), 0, 0);
canvas.get()->translate(0, paragraph.get()->getHeight() + 10);
}
}
DEF_TEST(SkParagraph_Bidi1, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
fontCollection->enableFontFallback();
TestCanvas canvas("SkParagraph_Bidi1.png");
std::u16string abc = u"\u202Dabc";
std::u16string DEF = u"\u202EDEF";
std::u16string ghi = u"\u202Dghi";
std::u16string JKL = u"\u202EJKL";
std::u16string mno = u"\u202Dmno";
std::u16string abcDEFghiJKLmno = u"\u202Dabc\u202EDEF\u202Dghi\u202EJKL\u202Dmno";
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontFamilies({ SkString("sans-serif")});
text_style.setFontSize(40);
text_style.setColor(SK_ColorCYAN);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kThin_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(abc);
text_style.setColor(SK_ColorGREEN);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kLight_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(DEF);
text_style.setColor(SK_ColorYELLOW);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(ghi);
text_style.setColor(SK_ColorMAGENTA);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(JKL);
text_style.setColor(SK_ColorBLUE);
text_style.setFontStyle(SkFontStyle(SkFontStyle::kBlack_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
builder.pushStyle(text_style);
builder.addText(mno);
auto paragraph = builder.Build();
paragraph->layout(400);
paragraph->paint(canvas.get(), 0, 0);
}
DEF_TEST(SkParagraph_Bidi2, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
fontCollection->enableFontFallback();
TestCanvas canvas("SkParagraph_Bidi2.png");
std::u16string abcD = u"\u202Dabc\u202ED";
std::u16string EFgh = u"EF\u202Dgh";
std::u16string iJKLmno = u"i\u202EJKL\u202Dmno";
std::u16string abcDEFghiJKLmno = u"\u202Dabc\u202EDEF\u202Dghi\u202EJKL\u202Dmno";
ParagraphStyle paragraph_style;
TextStyle text_style;
text_style.setFontFamilies({ SkString("sans-serif")});
text_style.setFontSize(40);
{
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(abcD);
builder.pushStyle(text_style);
builder.addText(EFgh);
builder.pushStyle(text_style);
builder.addText(iJKLmno);
auto paragraph = builder.Build();
paragraph->layout(360);
paragraph->paint(canvas.get(), 0, 0);
}
canvas.get()->translate(0, 400);
{
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(abcDEFghiJKLmno);
auto paragraph = builder.Build();
paragraph->layout(360);
paragraph->paint(canvas.get(), 0, 0);
}
}
DEF_TEST(SkParagraph_NewlineOnly, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_Newline.png");
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setColor(SK_ColorBLACK);
StrutStyle strut_style;
strut_style.setStrutEnabled(false);
ParagraphStyle paragraph_style;
paragraph_style.setStrutStyle(strut_style);
paragraph_style.setTextStyle(text_style);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText("\n");
auto paragraph = builder.Build();
paragraph->layout(1000);
REPORTER_ASSERT(reporter, paragraph->getHeight() == 28);
}
DEF_TEST_DISABLED(SkParagraph_FontResolutions, reporter) {
TestCanvas canvas("SkParagraph_FontResolutions.png");
sk_sp<TestFontCollection> fontCollection =
sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
if (!fontCollection->fontsFound()) return;
if (!fontCollection->addFontFromFile("abc/abc.ttf", "abc")) {
return;
}
if (!fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave")) {
return;
}
if (!fontCollection->addFontFromFile("abc/abc_agrave.ttf", "abc_agrave")) {
return;
}
TextStyle text_style;
text_style.setFontFamilies({SkString("abc")});
text_style.setFontSize(50);
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
text_style.setColor(SK_ColorBLUE);
builder.pushStyle(text_style);
builder.addText(u"a\u0300");
text_style.setColor(SK_ColorMAGENTA);
builder.pushStyle(text_style);
builder.addText(u"à");
text_style.setFontFamilies({SkString("abc"), SkString("abc_agrave")});
text_style.setColor(SK_ColorRED);
builder.pushStyle(text_style);
builder.addText(u"a\u0300");
text_style.setColor(SK_ColorGREEN);
builder.pushStyle(text_style);
builder.addText(u"à");
auto paragraph = builder.Build();
paragraph->layout(TestCanvasWidth);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
REPORTER_ASSERT(reporter, impl->runs().size() == 2);
REPORTER_ASSERT(reporter, impl->runs().front().size() == 4);
REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[0] == impl->runs().front().glyphs()[2]);
REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[1] == impl->runs().front().glyphs()[3]);
REPORTER_ASSERT(reporter, impl->runs().back().size() == 2);
REPORTER_ASSERT(reporter, impl->runs().back().glyphs()[0] == impl->runs().back().glyphs()[1]);
paragraph->paint(canvas.get(), 100, 100);
}
DEF_TEST_DISABLED(SkParagraph_FontStyle, reporter) {
TestCanvas canvas("SkParagraph_FontStyle.png");
sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
if (!fontCollection->fontsFound()) return;
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(20);
SkFontStyle fs = SkFontStyle(
SkFontStyle::Weight::kLight_Weight,
SkFontStyle::Width::kNormal_Width,
SkFontStyle::Slant::kUpright_Slant
);
text_style.setFontStyle(fs);
ParagraphStyle paragraph_style;
paragraph_style.setTextStyle(text_style);
TextStyle boldItalic;
boldItalic.setFontFamilies({SkString("Roboto")});
boldItalic.setColor(SK_ColorRED);
SkFontStyle bi = SkFontStyle(
SkFontStyle::Weight::kBold_Weight,
SkFontStyle::Width::kNormal_Width,
SkFontStyle::Slant::kItalic_Slant
);
boldItalic.setFontStyle(bi);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText("Default text\n");
builder.pushStyle(boldItalic);
builder.addText("Bold and Italic\n");
builder.pop();
builder.addText("back to normal");
auto paragraph = builder.Build();
paragraph->layout(250);
paragraph->paint(canvas.get(), 0, 0);
}
DEF_TEST_DISABLED(SkParagraph_Shaping, reporter) {
TestCanvas canvas("SkParagraph_Shaping.png");
auto dir = "/usr/local/google/home/jlavrova/Sources/flutter/engine/src/out/host_debug_unopt_x86/gen/flutter/third_party/txt/assets";
sk_sp<TestFontCollection> fontCollection =
sk_make_sp<TestFontCollection>(dir, /*GetResourcePath("fonts").c_str(), */ false);
if (!fontCollection->fontsFound()) return;
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorGRAY);
text_style.setFontSize(14);
SkFontStyle b = SkFontStyle(
SkFontStyle::Weight::kNormal_Weight,
SkFontStyle::Width::kNormal_Width,
SkFontStyle::Slant::kUpright_Slant
);
text_style.setFontStyle(b);
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText("Eat0 apple0 pies0 | Eat1 apple1 pies1 | Eat2 apple2 pies2");
auto paragraph = builder.Build();
paragraph->layout(380);
paragraph->paint(canvas.get(), 0, 0);
}
DEF_TEST(SkParagraph_Ellipsis, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_Ellipsis.png");
const char* text = "This\n"
"is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.";
TextStyle text_style;
text_style.setFontFamilies({SkString("Ahem")});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(10);
auto relayout = [&](size_t lines, bool ellipsis,
SkScalar width, SkScalar height, SkScalar minWidth, SkScalar maxWidth, SkColor bg) {
ParagraphStyle paragraph_style;
SkPaint paint;
paint.setColor(bg);
text_style.setForegroundColor(paint);
paragraph_style.setTextStyle(text_style);
paragraph_style.setMaxLines(lines);
if (ellipsis) {
paragraph_style.setEllipsis(u"\u2026");
}
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.addText(text);
auto paragraph = builder.Build();
paragraph->layout(50);
paragraph->paint(canvas.get(), 0, 0);
canvas.get()->translate(50, paragraph->getHeight() + 10);
auto result = paragraph->getRectsForRange(0, strlen(text), RectHeightStyle::kTight, RectWidthStyle::kTight);
SkPaint background;
background.setColor(SK_ColorRED);
background.setStyle(SkPaint::kStroke_Style);
background.setAntiAlias(true);
background.setStrokeWidth(1);
canvas.get()->drawRect(result.front().rect, background);
SkASSERT(width == paragraph->getMaxWidth());
SkASSERT(height == paragraph->getHeight());
SkASSERT(minWidth == paragraph->getMinIntrinsicWidth());
SkASSERT(maxWidth == paragraph->getMaxIntrinsicWidth());
};
SkPaint paint;
paint.setColor(SK_ColorLTGRAY);
canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, 50, 500), paint);
relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
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, 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, 90, 950, SK_ColorBLUE);
relayout(std::numeric_limits<size_t>::max(), false, 50, 200, 90, 950, SK_ColorGREEN);
}
DEF_TEST(SkParagraph_MemoryLeak, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
std::string text;
for (size_t i = 0; i < 10; i++)
{
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorBLACK);
TextStyle textStyle;
textStyle.setForegroundColor(paint);
textStyle.setFontFamilies({ SkString("Roboto") });
ParagraphStyle paragraphStyle;
paragraphStyle.setTextStyle(textStyle);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
text += "Text ";
builder.addText(text.c_str());
auto paragraph = builder.Build();
paragraph->layout(100);
//used to add a delay so I can monitor memory usage
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
};
DEF_TEST(SkParagraph_FormattingInfinity, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
TestCanvas canvas("SkParagraph_FormattingInfinity.png");
const char* text = "Some text\nAnother line";
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorBLACK);
TextStyle textStyle;
textStyle.setForegroundColor(paint);
textStyle.setFontFamilies({ SkString("Roboto") });
ParagraphStyle paragraphStyle;
paragraphStyle.setTextStyle(textStyle);
auto draw = [&](const char* prefix, TextAlign textAlign) {
paragraphStyle.setTextAlign(textAlign);
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.addText(text);
auto paragraph = builder.Build();
paragraph->layout(SK_ScalarInfinity);
paragraph->paint(canvas.get(), 0, 0);
canvas.get()->translate(0, 100);
};
draw("left", TextAlign::kLeft);
draw("right", TextAlign::kRight);
draw("center", TextAlign::kCenter);
draw("justify", TextAlign::kJustify);
};
DEF_TEST(SkParagraph_Infinity, reporter) {
SkASSERT(nearlyEqual(1, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(1, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(1, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarInfinity) == true);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity) == true);
SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNaN) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNegativeInfinity) == false);
SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNaN) == false);
};