RTL fixes + few small bugs

1. Removed unnecessary iterators (use SkShaper iterators instead)
2. More careful hash function and comparison (ParagraphStyle)
3. computeEmptyMetrics should go after resolveStrut
4. longestLine for line with spaces only should not be 0
5. LTR/RTL * left/right align * latin/arabic * leading/trailing spaces positioning
6. Height for MaxHeight rect (to follow Gary's change)

Change-Id: I3507ff9fb93148e5ef882a2f514078fcea9cfef3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/268301
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
Julia Lavrova 2020-02-03 09:43:52 -05:00 committed by Skia Commit-Bot
parent c61d7e3d6e
commit d3a32c5425
10 changed files with 287 additions and 177 deletions

View File

@ -15,27 +15,6 @@
namespace skia {
namespace textlayout {
class SingleFontIterator final : public SkShaper::FontRunIterator {
public:
SingleFontIterator(SkSpan<const char> utf8, const SkFont& font)
: fText(utf8), fCurrentChar(utf8.begin()), fFont(font) { }
void consume() override {
SkASSERT(fCurrentChar < fText.end());
fCurrentChar = fText.end();
}
size_t endOfCurrentRun() const override { return fCurrentChar - fText.begin(); }
bool atEnd() const override { return fCurrentChar == fText.end(); }
const SkFont& currentFont() const override { return fFont; }
private:
SkSpan<const char> fText;
const char* fCurrentChar;
SkFont fFont;
};
class LangIterator final : public SkShaper::LanguageRunIterator {
public:
LangIterator(SkSpan<const char> utf8, SkSpan<Block> styles, const TextStyle& defaultStyle)

View File

@ -497,7 +497,7 @@ bool OneLineShaper::shape() {
auto result = iterateThroughShapingRegions(
[this, limitlessWidth]
(TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t textDirection) {
(TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t defaultBidiLevel) {
// Set up the shaper and shape the next
auto shaper = SkShaper::MakeShapeDontWrapOrReorder();
@ -507,7 +507,7 @@ bool OneLineShaper::shape() {
}
iterateThroughFontStyles(textRange, styleSpan,
[this, &shaper, textDirection, limitlessWidth, &advanceX]
[this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
(Block block, SkTArray<SkShaper::Feature> features) {
auto blockSpan = SkSpan<Block>(&block, 1);
@ -531,20 +531,15 @@ bool OneLineShaper::shape() {
auto unresolvedRange = fUnresolvedBlocks.front().fText;
auto unresolvedText = fParagraph->text(unresolvedRange);
SingleFontIterator fontIter(unresolvedText, font);
LangIterator lang(unresolvedText, blockSpan,
SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
LangIterator langIter(unresolvedText, blockSpan,
fParagraph->paragraphStyle().getTextStyle());
auto script = SkShaper::MakeHbIcuScriptRunIterator(unresolvedText.begin(),
unresolvedText.size());
auto bidi = SkShaper::MakeIcuBiDiRunIterator(
unresolvedText.begin(), unresolvedText.size(), textDirection);
if (bidi == nullptr) {
return false;
}
SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
auto scriptIter = SkShaper::MakeHbIcuScriptRunIterator
(unresolvedText.begin(), unresolvedText.size());
fCurrentText = unresolvedRange;
shaper->shape(unresolvedText.begin(), unresolvedText.size(),
fontIter, *bidi,*script, lang,
fontIter, bidiIter,*scriptIter, langIter,
features.data(), features.size(),
limitlessWidth, this);

View File

@ -83,11 +83,18 @@ uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const
for (auto& ff : ts.fStyle.getFontFamilies()) {
hash = mix(hash, SkGoodHash()(ff));
}
for (auto& ff : ts.fStyle.getFontFeatures()) {
hash = mix(hash, SkGoodHash()(ff));
}
hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(ts.fRange.start));
hash = mix(hash, SkGoodHash()(ts.fRange.end));
}
hash = mix(hash, SkGoodHash()(relax(key.fParagraphStyle.getHeight())));
hash = mix(hash, SkGoodHash()(key.fParagraphStyle.getTextDirection()));
hash = mix(hash, SkGoodHash()(key.fText));
return hash;
}
@ -107,7 +114,10 @@ bool operator==(const ParagraphCacheKey& a, const ParagraphCacheKey& b) {
}
// There is no need to compare default paragraph styles - they are included into fTextStyles
if (a.fParagraphStyle.getHeight() != b.fParagraphStyle.getHeight()) {
if (!SkScalarNearlyEqual(a.fParagraphStyle.getHeight(), b.fParagraphStyle.getHeight())) {
return false;
}
if (a.fParagraphStyle.getTextDirection() != b.fParagraphStyle.getTextDirection()) {
return false;
}

View File

@ -139,12 +139,13 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
fClusters.reset();
fGraphemes.reset();
this->markGraphemes();
this->computeEmptyMetrics();
if (!this->shapeTextIntoEndlessLine()) {
this->resetContext();
// TODO: merge the two next calls - they always come together
this->resolveStrut();
this->computeEmptyMetrics();
this->fLines.reset();
// Set the important values that are not zero
@ -190,6 +191,7 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
if (fState < kLineBroken) {
this->resetContext();
this->resolveStrut();
this->computeEmptyMetrics();
this->fLines.reset();
this->breakShapedTextIntoLines(floorWidth);
fState = kLineBroken;
@ -412,7 +414,7 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
}
}
fLongestLine = SkTMax(fLongestLine, advance.fX);
fLongestLine = SkTMax(fLongestLine, SkScalarNearlyZero(advance.fX) ? widthWithSpaces : advance.fX);
});
fHeight = textWrapper.height();
fWidth = maxWidth;
@ -654,7 +656,6 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
// Found a line that intersects with the text
auto firstBoxOnTheLine = results.size();
auto paragraphTextDirection = paragraphStyle().getTextDirection();
auto lineTextAlign = line.assumedTextAlign();
const Run* lastRun = nullptr;
line.iterateThroughVisualRuns(true,
[&](const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width) {
@ -683,35 +684,17 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
auto runInLineWidth = line.measureTextInsideOneRun(textRange, run, runOffset, 0, true, false).clip.width();
runOffset += *width;
*width = runInLineWidth;
// Found a run that intersects with the text
auto context = line.measureTextInsideOneRun(intersect, run, runOffset, 0, true, true);
//*width += context.clip.width();
*width = runInLineWidth;
SkRect clip = context.clip;
SkRect trailingSpaces = SkRect::MakeEmpty();
SkScalar ghostSpacesRight = context.run->leftToRight() ? clip.right() - line.width() : 0;
SkScalar ghostSpacesLeft = !context.run->leftToRight() ? clip.right() - line.width() : 0;
if (ghostSpacesRight + ghostSpacesLeft > 0) {
if (lineTextAlign == TextAlign::kLeft && ghostSpacesLeft > 0) {
clip.offset(-ghostSpacesLeft, 0);
} else if (lineTextAlign == TextAlign::kRight && ghostSpacesLeft > 0) {
clip.offset(-ghostSpacesLeft, 0);
} else if (lineTextAlign == TextAlign::kCenter) {
// TODO: What do we do for centering?
}
}
if (rectHeightStyle == RectHeightStyle::kMax) {
// TODO: Change it once flutter rolls into google3
// (probably will break things if changed before)
clip.fBottom = line.height();
clip.fTop = line.sizes().baseline() -
line.getMaxRunMetrics().baseline() +
line.getMaxRunMetrics().delta();
clip.fTop = line.sizes().delta();
} else if (rectHeightStyle == RectHeightStyle::kIncludeLineSpacingTop) {
if (&line != &fLines.front()) {
@ -734,37 +717,82 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
} else if (rectHeightStyle == RectHeightStyle::kStrut) {
auto strutStyle = this->paragraphStyle().getStrutStyle();
if (strutStyle.getStrutEnabled() && strutStyle.getFontSize() > 0) {
auto top = line.baseline() ; //+ line.sizes().runTop(run);
auto top = line.baseline();
clip.fTop = top + fStrutMetrics.ascent();
clip.fBottom = top + fStrutMetrics.descent();
}
}
clip.offset(line.offset());
// Check if we can merge two boxes
bool mergedBoxes = false;
if (!results.empty() &&
lastRun != nullptr && lastRun->placeholder() == nullptr && context.run->placeholder() == nullptr &&
lastRun->lineHeight() == context.run->lineHeight() &&
lastRun->font() == context.run->font()) {
auto& lastBox = results.back();
if (SkScalarNearlyEqual(lastBox.rect.fTop, clip.fTop) &&
SkScalarNearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
(SkScalarNearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
SkScalarNearlyEqual(lastBox.rect.fRight, clip.fLeft))) {
lastBox.rect.fLeft = SkTMin(lastBox.rect.fLeft, clip.fLeft);
lastBox.rect.fRight = SkTMax(lastBox.rect.fRight, clip.fRight);
mergedBoxes = true;
// Separate trailing spaces and move them in the default order of the paragraph
// in case the run order and the paragraph order don't match
SkRect trailingSpaces = SkRect::MakeEmpty();
if (line.trimmedText().end < line.textWithSpaces().end && // Line has trailing spaces
line.textWithSpaces().end == intersect.end && // Range is at the end of the line
line.trimmedText().end > intersect.start) // Range has more than just spaces
{
auto delta = line.spacesWidth();
trailingSpaces = SkRect::MakeXYWH(0, 0, 0, 0);
// There are trailing spaces in this run
if (this->paragraphStyle().getTextAlign() == TextAlign::kJustify &&
&line != &fLines.back())
{
// TODO: this is just a patch. Make it right later (when it's clear what and how)
clip.fLeft = 0;
clip.fRight = line.width();
} else if (this->fParagraphStyle.getTextDirection() == TextDirection::kRtl &&
!run->leftToRight())
{
// Split
trailingSpaces = clip;
trailingSpaces.fLeft = - delta;
trailingSpaces.fRight = 0;
clip.fLeft += delta;
} else if (this->fParagraphStyle.getTextDirection() == TextDirection::kLtr &&
run->leftToRight())
{
// Split
trailingSpaces = clip;
trailingSpaces.fLeft = line.width();
trailingSpaces.fRight = trailingSpaces.fLeft + delta;
clip.fRight -= delta;
}
}
lastRun = context.run;
if (!mergedBoxes) {
clip.offset(line.offset());
if (trailingSpaces.width() > 0) {
trailingSpaces.offset(line.offset());
}
// Check if we can merge two boxes instead of adding a new one
auto merge = [&lastRun, &context, &results](SkRect clip) {
bool mergedBoxes = false;
if (!results.empty() &&
lastRun != nullptr &&
lastRun->placeholder() == nullptr &&
context.run->placeholder() == nullptr &&
SkScalarNearlyEqual(lastRun->lineHeight(), context.run->lineHeight()) &&
lastRun->font() == context.run->font())
{
auto& lastBox = results.back();
if (SkScalarNearlyEqual(lastBox.rect.fTop, clip.fTop) &&
SkScalarNearlyEqual(lastBox.rect.fBottom, clip.fBottom) &&
(SkScalarNearlyEqual(lastBox.rect.fLeft, clip.fRight) ||
SkScalarNearlyEqual(lastBox.rect.fRight, clip.fLeft)))
{
lastBox.rect.fLeft = SkTMin(lastBox.rect.fLeft, clip.fLeft);
lastBox.rect.fRight = SkTMax(lastBox.rect.fRight, clip.fRight);
mergedBoxes = true;
}
}
lastRun = context.run;
return mergedBoxes;
};
if (!merge(clip)) {
results.emplace_back(
clip, context.run->leftToRight() ? TextDirection::kLtr : TextDirection::kRtl);
}
if (trailingSpaces.width() > 0) {
if (!SkScalarNearlyZero(trailingSpaces.width()) && !merge(trailingSpaces)) {
results.emplace_back(trailingSpaces, paragraphTextDirection);
}
@ -862,13 +890,13 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
auto offsetX = line.offset().fX;
auto context = line.measureTextInsideOneRun(textRange, run, 0, 0, true, false);
if (dx < context.clip.fLeft ) {
if (dx < context.clip.fLeft + offsetX) {
// All the other runs are placed right of this one
result = { SkToS32(context.run->fClusterIndexes[context.pos]), kDownstream };
return false;
}
if (dx >= context.clip.fRight) {
if (dx >= context.clip.fRight + offsetX) {
// We have to keep looking but just in case keep the last one as the closes
// so far
auto index = context.pos + context.size;
@ -887,12 +915,18 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
size_t found = context.pos;
for (size_t i = context.pos; i < context.pos + context.size; ++i) {
// TODO: this rounding is done to match Flutter tests. Must be removed..
auto end = littleRound(context.run->positionX(i) + context.fTextShift + offsetX);
if (end > dx) {
auto index = context.run->leftToRight() ? i : context.size - i;
auto end = littleRound(context.run->positionX(index) + context.fTextShift + offsetX);
if ((context.run->leftToRight() ? end > dx : dx > end)) {
break;
}
found = i;
found = index;
}
if (!context.run->leftToRight()) {
--found;
}
auto glyphStart = context.run->positionX(found) + context.fTextShift + offsetX;
auto glyphWidth = context.run->positionX(found + 1) - context.run->positionX(found);
auto clusterIndex8 = context.run->fClusterIndexes[found];
@ -1054,6 +1088,7 @@ void ParagraphImpl::setState(InternalState state) {
case kLineBroken:
this->resetContext();
this->resolveStrut();
this->computeEmptyMetrics();
this->fRunShifts.reset();
fLines.reset();
case kFormatted:

View File

@ -266,12 +266,9 @@ TextAlign TextLine::assumedTextAlign() const {
return this->fMaster->paragraphStyle().effective_align();
}
if (fClusterRange.empty()) {
return TextAlign::kLeft;
} else {
auto run = this->fMaster->cluster(fClusterRange.end - 1).run();
return run->leftToRight() ? TextAlign::kLeft : TextAlign::kRight;
}
return this->fMaster->paragraphStyle().getTextDirection() == TextDirection::kLtr
? TextAlign::kLeft
: TextAlign::kRight;
}
void TextLine::scanStyles(StyleType styleType, const RunStyleVisitor& visitor) {
@ -855,6 +852,7 @@ void TextLine::iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisi
// Walk through all the runs that intersect with the line in visual order
SkScalar width = 0;
SkScalar runOffset = 0;
SkScalar totalWidth = 0;
auto textRange = includingGhostSpaces ? this->textWithSpaces() : this->trimmedText();
for (auto& runIndex : fRunsInVisualOrder) {
@ -864,23 +862,37 @@ void TextLine::iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisi
// TODO: deal with empty runs in a better way
continue;
}
if (!run->leftToRight() && runOffset == 0 && includingGhostSpaces) {
// runOffset does not take in account a possibility
// that RTL run could start before the line (trailing spaces)
// so we need to do runOffset -= "trailing whitespaces length"
TextRange whitespaces = intersected(
TextRange(fTextRange.end, fTextWithWhitespacesRange.end), run->fTextRange);
if (whitespaces.width() > 0) {
auto whitespacesLen = measureTextInsideOneRun(whitespaces, run, runOffset, 0, true, false).clip.width();
runOffset -= whitespacesLen;
}
}
runOffset += width;
totalWidth += width;
if (!visitor(run, runOffset, lineIntersection, &width)) {
return;
}
}
runOffset += width;
totalWidth += width;
if (this->ellipsis() != nullptr) {
if (visitor(ellipsis(), runOffset, ellipsis()->textRange(), &width)) {
runOffset += width;
totalWidth += width;
}
}
// This is a very important assert!
// It asserts that 2 different ways of calculation come with the same results
if (!includingGhostSpaces && compareRound(runOffset, this->width()) != 0) {
SkDebugf("ASSERT: %f != %f\n", runOffset, this->width());
if (!includingGhostSpaces && compareRound(totalWidth, this->width()) != 0) {
SkDebugf("ASSERT: %f != %f\n", totalWidth, this->width());
SkASSERT(false);
}
}

View File

@ -50,6 +50,7 @@ public:
InternalLineMetrics sizes() const { return fSizes; }
bool empty() const { return fTextRange.empty(); }
SkScalar spacesWidth() { return fWidthWithSpaces - width(); }
SkScalar height() const { return fAdvance.fY; }
SkScalar width() const {
return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);

View File

@ -107,6 +107,7 @@ bool TextStyle::equalsByFonts(const TextStyle& that) const {
return !fIsPlaceholder && !that.fIsPlaceholder &&
fFontStyle == that.fFontStyle &&
fFontFamilies == that.fFontFamilies &&
fFontFeatures == that.fFontFeatures &&
SkScalarNearlyEqual(fLetterSpacing, that.fLetterSpacing) &&
SkScalarNearlyEqual(fWordSpacing, that.fWordSpacing) &&
SkScalarNearlyEqual(fHeight, that.fHeight) &&

View File

@ -103,19 +103,10 @@ void TextWrapper::trimEndSpaces(TextAlign align) {
// Remember the breaking position
fEndLine.saveBreak();
// Skip all space cluster at the end
//bool left = align == TextAlign::kStart || align == TextAlign::kLeft;
bool right = align == TextAlign::kRight || align == TextAlign::kEnd;
for (auto cluster = fEndLine.endCluster();
cluster >= fEndLine.startCluster() && cluster->isWhitespaces();
--cluster) {
if ((cluster->run()->leftToRight()) ||
(right && !cluster->run()->leftToRight()) ||
align == TextAlign::kJustify || align == TextAlign::kCenter) {
fEndLine.trim(cluster);
continue;
} else {
break;
}
fEndLine.trim(cluster);
}
fEndLine.trim();
}

View File

@ -1901,26 +1901,59 @@ protected:
SkString name() override { return SkString("Paragraph26"); }
void onDrawContent(SkCanvas* canvas) override {
canvas->drawColor(SK_ColorWHITE);
auto fontCollection = sk_make_sp<FontCollection>();
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
fontCollection->enableFontFallback();
fontCollection->getParagraphCache()->turnOn(false);
//const char* text = "ERROR\nSECOND ERROR";
const char* text = "ERROR\nSECOND ERROR\nTHIRD ERRORRRRRRRRR\nFOURTH ERROR";
ParagraphStyle paragraph_style;
paragraph_style.setEllipsis(u"\u2026");
paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setFontFamilies({SkString("Google Sans")});
text_style.setFontSize(20);
builder.pushStyle(text_style);
builder.addText(text);
auto paragraph = builder.Build();
paragraph->layout(std::numeric_limits<SkScalar>::max());
paragraph->paint(canvas, 0, 0);
canvas->translate(0, 300);
paragraph->layout(200);
paragraph->paint(canvas, 0, 0);
canvas->clear(SK_ColorWHITE);
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorBLACK);
TextStyle textStyle;
textStyle.setForegroundColor(paint);
textStyle.setFontFamilies({SkString("Roboto")});
textStyle.setFontSize(16);
ParagraphStyle paragraphStyle;
paragraphStyle.setTextStyle(textStyle);
paragraphStyle.setTextAlign(TextAlign::kLeft);
{
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.addText("");
auto paragraph = builder.Build();
paragraph->layout(SK_ScalarInfinity);
SkDebugf("layout: %f %f %f %f %f %f %f\n", paragraph->getMaxWidth(), paragraph->getHeight(),
paragraph->getMaxIntrinsicWidth(), paragraph->getMinIntrinsicWidth(),
paragraph->getLongestLine(), paragraph->getAlphabeticBaseline(),
paragraph->getIdeographicBaseline());
paragraph->layout(253);
SkDebugf("layout: %f %f %f %f %f %f %f\n", paragraph->getMaxWidth(), paragraph->getHeight(),
paragraph->getMaxIntrinsicWidth(), paragraph->getMinIntrinsicWidth(),
paragraph->getLongestLine(), paragraph->getAlphabeticBaseline(),
paragraph->getIdeographicBaseline());
}
{
ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
builder.addText(" ");
auto paragraph = builder.Build();
paragraph->layout(SK_ScalarInfinity);
SkDebugf("layout: %f %f %f %f %f %f %f\n", paragraph->getMaxWidth(), paragraph->getHeight(),
paragraph->getMaxIntrinsicWidth(), paragraph->getMinIntrinsicWidth(),
paragraph->getLongestLine(), paragraph->getAlphabeticBaseline(),
paragraph->getIdeographicBaseline());
paragraph->layout(253);
SkDebugf("layout: %f %f %f %f %f %f %f\n", paragraph->getMaxWidth(), paragraph->getHeight(),
paragraph->getMaxIntrinsicWidth(), paragraph->getMinIntrinsicWidth(),
paragraph->getLongestLine(), paragraph->getAlphabeticBaseline(),
paragraph->getIdeographicBaseline());
}
}
private:
@ -1932,37 +1965,104 @@ protected:
SkString name() override { return SkString("Paragraph27"); }
void onDrawContent(SkCanvas* canvas) override {
canvas->drawColor(SK_ColorWHITE);
auto fontCollection = sk_make_sp<FontCollection>();
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
fontCollection->enableFontFallback();
fontCollection->getParagraphCache()->turnOn(false);
SkPaint red;
red.setColor(SK_ColorRED);
red.setStyle(SkPaint::kStroke_Style);
red.setAntiAlias(true);
red.setStrokeWidth(1);
SkPaint blue;
blue.setColor(SK_ColorRED);
blue.setStyle(SkPaint::kStroke_Style);
blue.setAntiAlias(true);
blue.setStrokeWidth(1);
SkPaint black;
black.setColor(SK_ColorBLACK);
black.setStyle(SkPaint::kStroke_Style);
black.setAntiAlias(true);
black.setStrokeWidth(1);
ParagraphStyle paragraph_style;
paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
paragraph_style.setEllipsis(u"\u2026");
paragraph_style.setTextAlign(TextAlign::kRight);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setFontFamilies({SkString("Google Sans")});
text_style.setFontSize(24);
text_style.setFontFamilies({SkString("Roboto")});
auto draw = [&](const char* text, SkScalar height) {
ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
text_style.setHeightOverride(true);
text_style.setHeight(height);
// RTL + right align + arabic
// RTL + right align + latin
// LTR + right align + arabic
// LTR + right align + latin
// RTL + left align + arabic
// RTL + left align + latin
// arabic and latin should not differ at all
// check: line breaking and trailing spaces
canvas->drawColor(SK_ColorWHITE);
auto h = 60;
auto w = 300;
auto draw = [&](SkScalar width, SkScalar height, TextDirection td, TextAlign ta, const char* text) {
SkString str;
str.append(" ");
str.append(text);
str.append(" ");
str.append(text);
str.append(" ");
SkDebugf("draw '%s' dir:%s align:%s\n", str.c_str(),
td == TextDirection::kLtr ? "left" : "right",
ta == TextAlign::kLeft ? "left" : "right");
paragraph_style.setTextDirection(td);
paragraph_style.setTextAlign(ta);
text_style.setFontSize(20);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(text);
builder.addText(str.c_str());
auto paragraph = builder.Build();
paragraph->layout(153.33f);
SkDebugf("'%s': %f\n", text, paragraph->getHeight());
auto boxes1 = paragraph->getRectsForRange(0, strlen(text), RectHeightStyle::kTight, RectWidthStyle::kTight);
auto boxes2 = paragraph->getRectsForRange(0, strlen(text), RectHeightStyle::kMax, RectWidthStyle::kTight);
if (boxes1.size() > 0 && boxes2.size() > 0) {
SkDebugf("Heights: %f %f\\b", boxes1[0].rect.height() , boxes2[0].rect.height());
}
paragraph->layout(width);
paragraph->paint(canvas, 0, 0);
canvas->translate(0, 200);
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
for (auto& line : impl->lines()) {
SkDebugf("line[%d]: %f + %f\n", &line - impl->lines().begin(), line.offset().fX, line.shift());
line.iterateThroughVisualRuns(true,
[&](const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width) {
*width = line.measureTextInsideOneRun(textRange, run, runOffset, 0, true, false).clip.width();
SkDebugf("%d[%d: %d) @%f + %f %s\n", run->index(),
textRange.start, textRange.end, runOffset, *width, run->leftToRight() ? "left" : "right");
return true;
});
}
auto boxes = paragraph->getRectsForRange(0, 100, RectHeightStyle::kTight, RectWidthStyle::kTight);
bool even = true;
for (auto& box : boxes) {
SkDebugf("[%f:%f,%f:%f] %s\n",
box.rect.fLeft, box.rect.fRight, box.rect.fTop, box.rect.fBottom,
box.direction == TextDirection::kLtr ? "left" : "right");
canvas->drawRect(box.rect, even ? red : blue);
even = !even;
}
canvas->translate(0, height);
};
//draw("Artist Name with long text in active media bar to test word truncation", 1.333f);
draw("provider with long text in active media bar to test word truncation inside the subtitle", 1);
canvas->drawRect(SkRect::MakeXYWH(0, 0, w, h * 8), black);
draw(w, h, TextDirection::kRtl, TextAlign::kRight, "RTL+RIGHT#1234567890");
draw(w, h, TextDirection::kRtl, TextAlign::kRight, "قففغغغغقففغغغغقففغغغ");
draw(w, h, TextDirection::kLtr, TextAlign::kRight, "LTR+RIGHT#1234567890");
draw(w, h, TextDirection::kLtr, TextAlign::kRight, "قففغغغغقففغغغغقففغغغ");
draw(w, h, TextDirection::kRtl, TextAlign::kLeft, "RTL+LEFT##1234567890");
draw(w, h, TextDirection::kRtl, TextAlign::kLeft, "قففغغغغقففغغغغقففغغغ");
draw(w, h, TextDirection::kLtr, TextAlign::kLeft, "LTR+LEFT##1234567890");
draw(w, h, TextDirection::kLtr, TextAlign::kLeft, "قففغغغغقففغغغغقففغغغ");
}
private:

View File

@ -159,7 +159,6 @@ private:
} // namespace
// Checked: NO DIFF
DEF_TEST(SkParagraph_SimpleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -943,7 +942,6 @@ DEF_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph, reporter) {
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.bottom(), 120, EPSILON100));
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_SimpleRedParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1106,7 +1104,6 @@ DEF_TEST(SkParagraph_RainbowParagraph, reporter) {
REPORTER_ASSERT(reporter, index == 5);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_DefaultStyleParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1146,7 +1143,6 @@ DEF_TEST(SkParagraph_DefaultStyleParagraph, reporter) {
REPORTER_ASSERT(reporter, index == 1);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_BoldParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1193,7 +1189,6 @@ DEF_TEST(SkParagraph_BoldParagraph, reporter) {
REPORTER_ASSERT(reporter, index == 1);
}
// Checked: NO DIFF (line height rounding error)
DEF_TEST(SkParagraph_HeightOverrideParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1249,7 +1244,6 @@ DEF_TEST(SkParagraph_HeightOverrideParagraph, reporter) {
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 165.495f, EPSILON5));
}
// Checked: DIFF+
DEF_TEST(SkParagraph_LeftAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1331,10 +1325,9 @@ DEF_TEST(SkParagraph_LeftAlignParagraph, reporter) {
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);//
REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(2000, 35).position == 134);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_RightAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1422,7 +1415,6 @@ DEF_TEST(SkParagraph_RightAlignParagraph, reporter) {
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_CenterAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1510,7 +1502,6 @@ DEF_TEST(SkParagraph_CenterAlignParagraph, reporter) {
paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_JustifyAlignParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1612,6 +1603,7 @@ DEF_TEST(SkParagraph_JustifyRTL, reporter) {
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);
@ -1650,7 +1642,7 @@ DEF_TEST(SkParagraph_JustifyRTL, reporter) {
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() == 5);
REPORTER_ASSERT(reporter, boxes.size() == 3);
boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
canvas.drawRects(SK_ColorBLUE, boxes);
@ -1780,7 +1772,6 @@ DEF_TEST_DISABLED(SkParagraph_LeadingSpaceRTL, reporter) {
REPORTER_ASSERT(reporter, boxes.size() == 2ull);
}
// Checked: NO DIFF (some minor decoration differences, probably)
DEF_TEST(SkParagraph_DecorationsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1904,7 +1895,6 @@ DEF_TEST(SkParagraph_WavyDecorationParagraph, reporter) {
SkDebugf("TODO: Add test for wavy decorations\n");
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_ItalicsParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -1969,7 +1959,6 @@ DEF_TEST(SkParagraph_ItalicsParagraph, reporter) {
});
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_ChineseParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -2018,7 +2007,7 @@ DEF_TEST(SkParagraph_ChineseParagraph, reporter) {
REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
}
// Checked: NO DIFF (disabled)
// Checked: disabled for TxtLib
DEF_TEST(SkParagraph_ArabicParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -2210,7 +2199,6 @@ DEF_TEST(SkParagraph_ArabicRectsLTRRightAlignParagraph, reporter) {
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -2275,7 +2263,6 @@ DEF_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph, reporter) {
REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(85, 10000).position == 75);
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_GetRectsForRangeParagraph, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -2372,7 +2359,6 @@ DEF_TEST(SkParagraph_GetRectsForRangeParagraph, reporter) {
}
}
// Checked: NO DIFF
DEF_TEST(SkParagraph_GetRectsForRangeTight, reporter) {
sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
if (!fontCollection->fontsFound()) return;
@ -4067,7 +4053,7 @@ DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
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.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
}
@ -4085,7 +4071,7 @@ DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
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.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
}
@ -4094,7 +4080,7 @@ DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
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(), 224.5f, 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));
}
@ -4103,7 +4089,7 @@ DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
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(), 319.5f, 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));
}
@ -4174,7 +4160,7 @@ DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
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.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
}
@ -4192,7 +4178,7 @@ DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
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.top(), 0, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
}
@ -4201,7 +4187,7 @@ DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
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(), 184, 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));
}
@ -4210,7 +4196,7 @@ DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
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(), 264, 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));
}
@ -4282,7 +4268,7 @@ DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
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.top(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
}
@ -4300,7 +4286,7 @@ DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
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.top(), 0, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, epsilon));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
}
@ -4309,7 +4295,7 @@ DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
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(), 128, 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));
}
@ -4318,7 +4304,7 @@ DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
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(), 188, 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));
}
@ -4386,7 +4372,7 @@ DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
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(), 22.5f, 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));
@ -4402,7 +4388,7 @@ DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
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(), 22.5f, 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));
@ -4410,7 +4396,7 @@ DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
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(), 182.5f, 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));
@ -4418,7 +4404,7 @@ DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
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(), 262.5f, 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));
}
@ -4499,7 +4485,7 @@ DEF_TEST(SkParagraph_FontFeaturesParagraph, reporter) {
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setFontStyle(SkFontStyle::Italic());
text_style.setFontStyle(SkFontStyle::Italic()); // Regular Roboto doesn't have font features
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);