• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkBitmap.h"
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkColor.h"
10 #include "include/core/SkEncodedImageFormat.h"
11 #include "include/core/SkFontMgr.h"
12 #include "include/core/SkFontStyle.h"
13 #include "include/core/SkImageEncoder.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkSpan.h"
20 #include "include/core/SkStream.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkTypeface.h"
23 #include "include/core/SkTypes.h"
24 #include "modules/skparagraph/include/DartTypes.h"
25 #include "modules/skparagraph/include/FontCollection.h"
26 #include "modules/skparagraph/include/Paragraph.h"
27 #include "modules/skparagraph/include/ParagraphCache.h"
28 #include "modules/skparagraph/include/ParagraphStyle.h"
29 #include "modules/skparagraph/include/TextShadow.h"
30 #include "modules/skparagraph/include/TextStyle.h"
31 #include "modules/skparagraph/include/TypefaceFontProvider.h"
32 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
33 #include "modules/skparagraph/src/ParagraphImpl.h"
34 #include "modules/skparagraph/src/Run.h"
35 #include "modules/skparagraph/src/TextLine.h"
36 #include "modules/skparagraph/tests/SkShaperJSONWriter.h"
37 #include "modules/skparagraph/utils/TestFontCollection.h"
38 #include "src/core/SkOSFile.h"
39 #include "src/utils/SkOSPath.h"
40 #include "tests/Test.h"
41 #include "tools/Resources.h"
42 
43 #include <string.h>
44 #include <algorithm>
45 #include <limits>
46 #include <memory>
47 #include <string>
48 #include <utility>
49 #include <vector>
50 #include <thread>
51 
52 struct GrContextOptions;
53 
54 #define VeryLongCanvasWidth 1000000
55 #define TestCanvasWidth 1000
56 #define TestCanvasHeight 600
57 
58 using namespace skia::textlayout;
59 namespace {
60 
61 SkScalar EPSILON100 = 0.01f;
62 SkScalar EPSILON50 = 0.02f;
63 SkScalar EPSILON20 = 0.05f;
64 SkScalar EPSILON10 = 0.1f;
65 SkScalar EPSILON5 = 0.20f;
66 SkScalar EPSILON2 = 0.50f;
67 
equal(const char * base,TextRange a,const char * b)68 bool equal(const char* base, TextRange a, const char* b) {
69     return std::strncmp(b, base + a.start, a.width()) == 0;
70 }
71 
mirror(const std::string & text)72 std::u16string mirror(const std::string& text) {
73     std::u16string result;
74     result += u"\u202E";
75     for (auto i = text.size(); i > 0; --i) {
76         result += text[i - 1];
77     }
78     //result += u"\u202C";
79     return result;
80 }
81 
straight(const std::string & text)82 std::u16string straight(const std::string& text) {
83     std::u16string result;
84     result += u"\u202D";
85     for (auto ch : text) {
86         result += ch;
87     }
88     return result;
89 }
90 
91 class ResourceFontCollection : public FontCollection {
92 public:
ResourceFontCollection(bool testOnly=false)93     ResourceFontCollection(bool testOnly = false)
94             : fFontsFound(false)
95             , fResolvedFonts(0)
96             , fResourceDir(GetResourcePath("fonts").c_str())
97             , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
98         std::vector<SkString> fonts;
99         SkOSFile::Iter iter(fResourceDir.c_str());
100 
101         SkString path;
102         while (iter.next(&path)) {
103             if (path.endsWith("Roboto-Italic.ttf")) {
104                 fFontsFound = true;
105             }
106             fonts.emplace_back(path);
107         }
108 
109         if (!fFontsFound) {
110             // SkDebugf("Fonts not found, skipping all the tests\n");
111             return;
112         }
113         // Only register fonts if we have to
114         for (auto& font : fonts) {
115             SkString file_path;
116             file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
117             fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
118         }
119 
120         if (testOnly) {
121             this->setTestFontManager(std::move(fFontProvider));
122         } else {
123             this->setAssetFontManager(std::move(fFontProvider));
124         }
125         this->disableFontFallback();
126     }
127 
resolvedFonts() const128     size_t resolvedFonts() const { return fResolvedFonts; }
129 
130     // TODO: temp solution until we check in fonts
fontsFound() const131     bool fontsFound() const { return fFontsFound; }
132 
133 private:
134     bool fFontsFound;
135     size_t fResolvedFonts;
136     std::string fResourceDir;
137     sk_sp<TypefaceFontProvider> fFontProvider;
138 };
139 
140 class TestCanvas {
141 public:
TestCanvas(const char * testName)142     TestCanvas(const char* testName) : name(testName) {
143         bits.allocN32Pixels(TestCanvasWidth, TestCanvasHeight);
144         canvas = new SkCanvas(bits);
145         canvas->clear(SK_ColorWHITE);
146     }
147 
~TestCanvas()148     ~TestCanvas() {
149         SkString tmpDir = skiatest::GetTmpDir();
150         if (!tmpDir.isEmpty()) {
151             SkString path = SkOSPath::Join(tmpDir.c_str(), name);
152             SkFILEWStream file(path.c_str());
153             if (!SkEncodeImage(&file, bits, SkEncodedImageFormat::kPNG, 100)) {
154                 SkDebugf("Cannot write a picture %s\n", name);
155             }
156         }
157         delete canvas;
158     }
159 
drawRects(SkColor color,std::vector<TextBox> & result,bool fill=false)160     void drawRects(SkColor color, std::vector<TextBox>& result, bool fill = false) {
161 
162         SkPaint paint;
163         if (!fill) {
164             paint.setStyle(SkPaint::kStroke_Style);
165             paint.setAntiAlias(true);
166             paint.setStrokeWidth(1);
167         }
168         paint.setColor(color);
169         for (auto& r : result) {
170             canvas->drawRect(r.rect, paint);
171         }
172     }
173 
drawLine(SkColor color,SkRect rect,bool vertical=true)174     void drawLine(SkColor color, SkRect rect, bool vertical = true) {
175 
176         SkPaint paint;
177         paint.setStyle(SkPaint::kStroke_Style);
178         paint.setAntiAlias(true);
179         paint.setStrokeWidth(1);
180         paint.setColor(color);
181         if (vertical) {
182             canvas->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);
183         } else {
184             canvas->drawLine(rect.fLeft, rect.fTop, rect.fRight, rect.fTop, paint);
185         }
186     }
187 
drawLines(SkColor color,std::vector<TextBox> & result)188     void drawLines(SkColor color, std::vector<TextBox>& result) {
189 
190         for (auto& r : result) {
191             drawLine(color, r.rect);
192         }
193     }
194 
get()195     SkCanvas* get() { return canvas; }
196 private:
197     SkBitmap bits;
198     SkCanvas* canvas;
199     const char* name;
200 };
201 
202 #if defined(SK_UNICODE_ICU_IMPLEMENTATION) && defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
203 class SkUnicode_test : public SkUnicode {
204 public:
SkUnicode_test()205     SkUnicode_test() { }
206 
SkUnicode_test(SkSpan<char> text,const ParagraphStyle & style)207     SkUnicode_test(SkSpan<char> text, const ParagraphStyle& style) {
208 
209         fIcu = SkUnicode::Make();
210 
211         std::vector<SkUnicode::Position> words;
212         fIcu->getWords(text.data(), text.size(), nullptr, &words);
213 
214         SkTArray<SkUnicode::CodeUnitFlags, true> codeUnitFlags;
215         fIcu->computeCodeUnitFlags(
216                 text.data(), text.size(), false, &codeUnitFlags);
217 
218         std::vector<SkUnicode::Position> graphemeBreaks;
219         std::vector<SkUnicode::LineBreakBefore> lineBreaks;
220         Position pos = 0;
221         for (auto& flag : codeUnitFlags) {
222             if (SkUnicode::isGraphemeStart(flag)) {
223                 graphemeBreaks.emplace_back(pos);
224             }
225             if (SkUnicode::isHardLineBreak(flag)) {
226                 lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kHardLineBreak);
227             }
228             if (SkUnicode::isSoftLineBreak(flag)) {
229                 lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kSoftLineBreak);
230             }
231             ++pos;
232         }
233 
234         fClient = SkUnicode::MakeClientBasedUnicode(text, words, graphemeBreaks, lineBreaks);
235         SkTArray<SkUnicode::CodeUnitFlags, true> codeUnitFlags1;
236         fClient->computeCodeUnitFlags(
237                 text.data(), text.size(), style.getReplaceTabCharacters(), &codeUnitFlags1);
238         SkASSERT(codeUnitFlags.size() == codeUnitFlags1.size());
239         for (auto i = 0; i < codeUnitFlags.size(); ++i) {
240             // Deal with tabulation separately (to avoid all this trouble to copy the initial text)
241             if ((codeUnitFlags1[i] & CodeUnitFlags::kTabulation) == CodeUnitFlags::kTabulation) {
242                 SkASSERT((codeUnitFlags[i] | CodeUnitFlags::kTabulation) ==
243                          (codeUnitFlags1[i] | CodeUnitFlags::kControl));
244             } else {
245                 SkASSERT(codeUnitFlags[i] == codeUnitFlags1[i]);
246             }
247         }
248     }
249 
copy()250     std::unique_ptr<SkUnicode> copy() override {
251         auto unicode = std::make_unique<SkUnicode_test>();
252         unicode->fIcu = fIcu->copy();
253         unicode->fClient = fClient->copy();
254         return unicode;
255     }
256 
257     ~SkUnicode_test() override = default;
258 
259     // For SkShaper
260     std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t text[], int count,
261                                                      SkBidiIterator::Direction dir) override;
262     std::unique_ptr<SkBidiIterator> makeBidiIterator(const char text[],
263                                                      int count,
264                                                      SkBidiIterator::Direction dir) override;
265     std::unique_ptr<SkBreakIterator> makeBreakIterator(const char locale[],
266                                                        BreakType breakType) override;
267     std::unique_ptr<SkBreakIterator> makeBreakIterator(BreakType breakType) override;
268 
computeCodeUnitFlags(char utf8[],int utf8Units,bool replaceTabs,SkTArray<SkUnicode::CodeUnitFlags,true> * results)269     bool computeCodeUnitFlags(char utf8[], int utf8Units, bool replaceTabs,
270                           SkTArray<SkUnicode::CodeUnitFlags, true>* results) override {
271         return fClient->computeCodeUnitFlags(utf8, utf8Units, replaceTabs, results);
272     }
273 
computeCodeUnitFlags(char16_t utf16[],int utf16Units,bool replaceTabs,SkTArray<SkUnicode::CodeUnitFlags,true> * results)274     bool computeCodeUnitFlags(char16_t utf16[], int utf16Units, bool replaceTabs,
275                           SkTArray<SkUnicode::CodeUnitFlags, true>* results) override {
276         return fClient->computeCodeUnitFlags(utf16, utf16Units, replaceTabs, results);
277     }
278 
getWords(const char utf8[],int utf8Units,const char * locale,std::vector<Position> * results)279     bool getWords(const char utf8[], int utf8Units, const char* locale, std::vector<Position>* results) override {
280         return fClient->getWords(utf8, utf8Units, locale, results);
281     }
282 
getBidiRegions(const char utf8[],int utf8Units,TextDirection dir,std::vector<BidiRegion> * results)283     bool getBidiRegions(const char utf8[],
284                         int utf8Units,
285                         TextDirection dir,
286                         std::vector<BidiRegion>* results) override {
287         return fClient->getBidiRegions(utf8, utf8Units, dir, results);
288     }
289 
toUpper(const SkString & str)290     SkString toUpper(const SkString& str) override {
291         return fClient->toUpper(str);
292     }
293 
reorderVisual(const BidiLevel runLevels[],int levelsCount,int32_t logicalFromVisual[])294     void reorderVisual(const BidiLevel runLevels[],
295                        int levelsCount,
296                        int32_t logicalFromVisual[]) override {
297         fClient->reorderVisual(runLevels, levelsCount, logicalFromVisual);
298     }
299 private:
300     friend class SkBidiIterator_test;
301     friend class SkBreakIterator_test;
302 
303     std::unique_ptr<SkUnicode> fIcu;
304     std::unique_ptr<SkUnicode> fClient;
305 };
306 
307 class SkBreakIterator_test: public SkBreakIterator {
308     std::unique_ptr<SkBreakIterator> fIcuIter;
309     std::unique_ptr<SkBreakIterator> fClientIter;
310 public:
SkBreakIterator_test(SkUnicode * icu,SkUnicode * client,SkUnicode::BreakType breakType)311     explicit SkBreakIterator_test(SkUnicode* icu, SkUnicode* client, SkUnicode::BreakType breakType)
312             : fIcuIter(icu->makeBreakIterator(breakType))
313             , fClientIter(client->makeBreakIterator(breakType)) {}
SkBreakIterator_test(SkUnicode * icu,SkUnicode * client,const char locale[],SkUnicode::BreakType breakType)314     explicit SkBreakIterator_test(SkUnicode* icu,
315                                   SkUnicode* client,
316                                   const char locale[],
317                                   SkUnicode::BreakType breakType)
318             : fIcuIter(icu->makeBreakIterator(breakType))
319             , fClientIter(client->makeBreakIterator(locale, breakType)) {}
first()320     Position first() override {
321         SkASSERT(fIcuIter->first() == fClientIter->first());
322         return fClientIter->first();
323     }
current()324     Position current() override {
325         SkASSERT(fIcuIter->current() == fClientIter->current());
326         return fClientIter->current();
327     }
next()328     Position next() override {
329         SkASSERT(fIcuIter->next() == fClientIter->next());
330         return fClientIter->next();
331     }
status()332     Status status() override {
333         SkASSERT(fIcuIter->status() == fClientIter->status());
334         return fClientIter->status();
335     }
isDone()336     bool isDone() override {
337         SkASSERT(fIcuIter->isDone() == fClientIter->isDone());
338         return fClientIter->isDone();
339     }
setText(const char utftext8[],int utf8Units)340     bool setText(const char utftext8[], int utf8Units) override {
341         fIcuIter->setText(utftext8, utf8Units);
342         return fClientIter->setText(utftext8, utf8Units);
343     }
setText(const char16_t utftext16[],int utf16Units)344     bool setText(const char16_t utftext16[], int utf16Units) override {
345         fIcuIter->setText(utftext16, utf16Units);
346         return fClientIter->setText(utftext16, utf16Units);
347     }
348 };
349 
makeBidiIterator(const uint16_t text[],int count,SkBidiIterator::Direction dir)350 std::unique_ptr<SkBidiIterator> SkUnicode_test::makeBidiIterator(const uint16_t text[], int count,
351                                                  SkBidiIterator::Direction dir) {
352     return fIcu->makeBidiIterator(text, count, dir);
353 }
makeBidiIterator(const char text[],int count,SkBidiIterator::Direction dir)354 std::unique_ptr<SkBidiIterator> SkUnicode_test::makeBidiIterator(const char text[],
355                                                  int count,
356                                                  SkBidiIterator::Direction dir) {
357     return fIcu->makeBidiIterator(text, count, dir);
358 }
makeBreakIterator(const char locale[],BreakType breakType)359 std::unique_ptr<SkBreakIterator> SkUnicode_test::makeBreakIterator(const char locale[],
360                                                                    BreakType breakType) {
361     return std::make_unique<SkBreakIterator_test>(fIcu.get(), fClient.get(), locale, breakType);
362 }
makeBreakIterator(BreakType breakType)363 std::unique_ptr<SkBreakIterator> SkUnicode_test::makeBreakIterator(BreakType breakType) {
364     return std::make_unique<SkBreakIterator_test>(fIcu.get(), fClient.get(), breakType);
365 }
366 
367 class TestParagraphBuilderImpl : public ParagraphBuilderImpl {
368 public:
TestParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)369     TestParagraphBuilderImpl(const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
370         : ParagraphBuilderImpl(style, fontCollection) { }
Build()371     std::unique_ptr<Paragraph> Build() override {
372         this->SetUnicode(
373                 std::make_unique<SkUnicode_test>(this->getText(), this->getParagraphStyle()));
374         return ParagraphBuilderImpl::Build();
375     }
376 };
377 #else
378 typedef ParagraphBuilderImpl TestParagraphBuilderImpl;
379 #endif
380 
381 }  // namespace
382 
UNIX_ONLY_TEST(SkParagraph_SimpleParagraph,reporter)383 UNIX_ONLY_TEST(SkParagraph_SimpleParagraph, reporter) {
384     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
385     if (!fontCollection->fontsFound()) return;
386     const char* text = "Hello World Text Dialog";
387     const size_t len = strlen(text);
388 
389     ParagraphStyle paragraph_style;
390     paragraph_style.turnHintingOff();
391     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
392 
393     TextStyle text_style;
394     text_style.setFontFamilies({SkString("Roboto")});
395     text_style.setColor(SK_ColorBLACK);
396     builder.pushStyle(text_style);
397     builder.addText(text, len);
398     builder.pop();
399 
400     auto paragraph = builder.Build();
401     paragraph->layout(TestCanvasWidth);
402     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
403 
404     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
405     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
406     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
407     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
408 
409     size_t index = 0;
410     for (auto& line : impl->lines()) {
411         line.scanStyles(StyleType::kDecorations,
412                         [&index, reporter]
413                         (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
414                             REPORTER_ASSERT(reporter, index == 0);
415                             REPORTER_ASSERT(reporter, style.getColor() == SK_ColorBLACK);
416                             ++index;
417                         });
418     }
419 }
420 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderParagraph,reporter)421 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) {
422     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
423     TestCanvas canvas("SkParagraph_InlinePlaceholderParagraph.png");
424     if (!fontCollection->fontsFound()) return;
425 
426     const char* text = "012 34";
427     const size_t len = strlen(text);
428 
429     ParagraphStyle paragraph_style;
430     paragraph_style.turnHintingOff();
431     paragraph_style.setMaxLines(14);
432     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
433 
434     TextStyle text_style;
435     text_style.setFontFamilies({SkString("Roboto")});
436     text_style.setColor(SK_ColorBLACK);
437     text_style.setFontSize(26);
438     text_style.setWordSpacing(5);
439     text_style.setLetterSpacing(1);
440     text_style.setDecoration(TextDecoration::kUnderline);
441     text_style.setDecorationColor(SK_ColorBLACK);
442     builder.pushStyle(text_style);
443     builder.addText(text, len);
444 
445     PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 0);
446     builder.addPlaceholder(placeholder1);
447     builder.addText(text, len);
448     builder.addPlaceholder(placeholder1);
449 
450     PlaceholderStyle placeholder2(5, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
451     builder.addPlaceholder(placeholder2);
452     builder.addPlaceholder(placeholder1);
453     builder.addPlaceholder(placeholder2);
454     builder.addText(text, len);
455     builder.addPlaceholder(placeholder2);
456     builder.addText(text, len);
457     builder.addText(text, len);
458     builder.addPlaceholder(placeholder2);
459     builder.addPlaceholder(placeholder2);
460     builder.addPlaceholder(placeholder2);
461     builder.addPlaceholder(placeholder2);
462     builder.addPlaceholder(placeholder2);
463     builder.addPlaceholder(placeholder1);
464     builder.addText(text, len);
465     builder.addText(text, len);
466     builder.addText(text, len);
467     builder.addText(text, len);
468     builder.addText(text, len);
469     builder.addPlaceholder(placeholder2);
470     builder.addPlaceholder(placeholder1);
471     builder.addText(text, len);
472     builder.addText(text, len);
473 
474     builder.pop();
475 
476     auto paragraph = builder.Build();
477     paragraph->layout(TestCanvasWidth);
478     paragraph->paint(canvas.get(), 0, 0);
479 
480     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
481     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
482 
483     auto boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
484     canvas.drawRects(SK_ColorRED, boxes);
485     REPORTER_ASSERT(reporter, boxes.size() == 1);
486 
487     boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
488     canvas.drawRects(SK_ColorGREEN, boxes);
489     REPORTER_ASSERT(reporter, boxes.size() == 1);
490 
491     boxes = paragraph->getRectsForPlaceholders();
492     canvas.drawRects(SK_ColorRED, boxes);
493 
494     boxes = paragraph->getRectsForRange(4, 17, rect_height_style, rect_width_style);
495     canvas.drawRects(SK_ColorBLUE, boxes);
496 
497     REPORTER_ASSERT(reporter, boxes.size() == 7);
498 
499     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 90.921f, EPSILON2));
500     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 50, EPSILON100));
501     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 90.921f + 50, EPSILON2));
502     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 100, EPSILON100));
503 
504     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.left(), 231.343f, EPSILON2));
505     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.top(), 50, EPSILON100));
506     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.right(), 231.343f + 50, EPSILON2));
507     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.bottom(), 100, EPSILON100));
508 
509     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.left(), 281.343f, EPSILON2));
510     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.top(), 0, EPSILON100));
511     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.right(), 281.343f + 5, EPSILON2));
512     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.bottom(), 50, EPSILON100));
513 
514     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.left(), 336.343f, EPSILON2));
515     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.top(), 0, EPSILON100));
516     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.right(), 336.343f + 5, EPSILON2));
517     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.bottom(), 50, EPSILON100));
518 }
519 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBaselineParagraph,reporter)520 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBaselineParagraph, reporter) {
521     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
522     TestCanvas canvas("SkParagraph_InlinePlaceholderBaselineParagraph.png");
523     if (!fontCollection->fontsFound()) return;
524 
525     const char* text = "012 34";
526     const size_t len = strlen(text);
527 
528     ParagraphStyle paragraph_style;
529     paragraph_style.turnHintingOff();
530     paragraph_style.setMaxLines(14);
531     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
532 
533     TextStyle text_style;
534     text_style.setFontFamilies({SkString("Roboto")});
535     text_style.setColor(SK_ColorBLACK);
536     text_style.setFontSize(26);
537     text_style.setWordSpacing(5);
538     text_style.setLetterSpacing(1);
539     text_style.setDecoration(TextDecoration::kUnderline);
540     text_style.setDecorationColor(SK_ColorBLACK);
541     builder.pushStyle(text_style);
542     builder.addText(text, len);
543 
544     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 38.347f);
545     builder.addPlaceholder(placeholder);
546     builder.addText(text, len);
547 
548     builder.pop();
549 
550     auto paragraph = builder.Build();
551     paragraph->layout(TestCanvasWidth);
552     paragraph->paint(canvas.get(), 0, 0);
553 
554     auto boxes = paragraph->getRectsForPlaceholders();
555     canvas.drawRects(SK_ColorRED, boxes);
556 
557     REPORTER_ASSERT(reporter, boxes.size() == 1);
558     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
559     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
560     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
561     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
562 
563     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
564     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
565 
566     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
567     canvas.drawRects(SK_ColorBLUE, boxes);
568 
569     REPORTER_ASSERT(reporter, boxes.size() == 1);
570     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
571     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 14.226f, EPSILON100));
572     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
573     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44.694f, EPSILON100));
574 }
575 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph,reporter)576 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph, reporter) {
577     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
578     TestCanvas canvas("SkParagraph_InlinePlaceholderAboveBaselineParagraph.png");
579     if (!fontCollection->fontsFound()) return;
580 
581     const char* text = "012 34";
582     const size_t len = strlen(text);
583 
584     ParagraphStyle paragraph_style;
585     paragraph_style.turnHintingOff();
586     paragraph_style.setMaxLines(14);
587     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
588 
589     TextStyle text_style;
590     text_style.setFontFamilies({SkString("Roboto")});
591     text_style.setColor(SK_ColorBLACK);
592     text_style.setFontSize(26);
593     text_style.setWordSpacing(5);
594     text_style.setLetterSpacing(1);
595     text_style.setDecoration(TextDecoration::kUnderline);
596     text_style.setDecorationColor(SK_ColorBLACK);
597     builder.pushStyle(text_style);
598     builder.addText(text, len);
599 
600     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kAboveBaseline, TextBaseline::kAlphabetic, 903129.129308f);
601     builder.addPlaceholder(placeholder);
602     builder.addText(text, len);
603 
604     builder.pop();
605 
606     auto paragraph = builder.Build();
607     paragraph->layout(TestCanvasWidth);
608     paragraph->paint(canvas.get(), 0, 0);
609 
610     auto boxes = paragraph->getRectsForPlaceholders();
611     canvas.drawRects(SK_ColorRED, boxes);
612 
613     REPORTER_ASSERT(reporter, boxes.size() == 1);
614     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
615     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.347f, EPSILON100));
616     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
617     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 49.652f, EPSILON100));
618 
619     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
620     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
621 
622     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
623     canvas.drawRects(SK_ColorBLUE, boxes);
624 
625     REPORTER_ASSERT(reporter, boxes.size() == 1);
626     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
627     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 25.531f, EPSILON100));
628     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
629     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56, EPSILON100));
630 }
631 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph,reporter)632 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph, reporter) {
633     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
634     TestCanvas canvas("SkParagraph_InlinePlaceholderBelowBaselineParagraph.png");
635     if (!fontCollection->fontsFound()) return;
636 
637     const char* text = "012 34";
638     const size_t len = strlen(text);
639 
640     ParagraphStyle paragraph_style;
641     paragraph_style.turnHintingOff();
642     paragraph_style.setMaxLines(14);
643     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
644 
645     TextStyle text_style;
646     text_style.setFontFamilies({SkString("Roboto")});
647     text_style.setColor(SK_ColorBLACK);
648     text_style.setFontSize(26);
649     text_style.setWordSpacing(5);
650     text_style.setLetterSpacing(1);
651     text_style.setDecoration(TextDecoration::kUnderline);
652     text_style.setDecorationColor(SK_ColorBLACK);
653     builder.pushStyle(text_style);
654     builder.addText(text, len);
655 
656     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBelowBaseline, TextBaseline::kAlphabetic, 903129.129308f);
657     builder.addPlaceholder(placeholder);
658     builder.addText(text, len);
659 
660     builder.pop();
661 
662     auto paragraph = builder.Build();
663     paragraph->layout(TestCanvasWidth);
664     paragraph->paint(canvas.get(), 0, 0);
665 
666     auto boxes = paragraph->getRectsForPlaceholders();
667     canvas.drawRects(SK_ColorRED, boxes);
668 
669     REPORTER_ASSERT(reporter, boxes.size() == 1);
670     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
671     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
672     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
673     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
674 
675     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
676     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
677 
678     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
679     canvas.drawRects(SK_ColorBLUE, boxes);
680 
681     REPORTER_ASSERT(reporter, boxes.size() == 1);
682     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
683     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.121f, EPSILON100));
684     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
685     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.347f, EPSILON100));
686 }
687 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBottomParagraph,reporter)688 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBottomParagraph, reporter) {
689     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
690     TestCanvas canvas("SkParagraph_InlinePlaceholderBottomParagraph.png");
691     if (!fontCollection->fontsFound()) return;
692 
693     const char* text = "012 34";
694     const size_t len = strlen(text);
695 
696     ParagraphStyle paragraph_style;
697     paragraph_style.turnHintingOff();
698     paragraph_style.setMaxLines(14);
699     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
700 
701     TextStyle text_style;
702     text_style.setFontFamilies({SkString("Roboto")});
703     text_style.setColor(SK_ColorBLACK);
704     text_style.setFontSize(26);
705     text_style.setWordSpacing(5);
706     text_style.setLetterSpacing(1);
707     text_style.setDecoration(TextDecoration::kUnderline);
708     text_style.setDecorationColor(SK_ColorBLACK);
709     builder.pushStyle(text_style);
710     builder.addText(text, len);
711 
712     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBottom, TextBaseline::kAlphabetic, 0);
713     builder.addPlaceholder(placeholder);
714     builder.addText(text, len);
715 
716     builder.pop();
717 
718     auto paragraph = builder.Build();
719     paragraph->layout(TestCanvasWidth);
720     paragraph->paint(canvas.get(), 0, 0);
721 
722     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
723     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
724 
725     auto boxes = paragraph->getRectsForPlaceholders();
726     canvas.drawRects(SK_ColorRED, boxes);
727     REPORTER_ASSERT(reporter, boxes.size() == 1);
728     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
729     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
730     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
731     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
732 
733     boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
734     canvas.drawRects(SK_ColorBLUE, boxes);
735     REPORTER_ASSERT(reporter, boxes.size() == 1);
736     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
737     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 19.531f, EPSILON100));
738     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
739     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
740 }
741 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderTopParagraph,reporter)742 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderTopParagraph, reporter) {
743     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
744     TestCanvas canvas("SkParagraph_InlinePlaceholderTopParagraph.png");
745     if (!fontCollection->fontsFound()) return;
746 
747     const char* text = "012 34";
748     const size_t len = strlen(text);
749 
750     ParagraphStyle paragraph_style;
751     paragraph_style.turnHintingOff();
752     paragraph_style.setMaxLines(14);
753     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
754 
755     TextStyle text_style;
756     text_style.setFontFamilies({SkString("Roboto")});
757     text_style.setColor(SK_ColorBLACK);
758     text_style.setFontSize(26);
759     text_style.setWordSpacing(5);
760     text_style.setLetterSpacing(1);
761     text_style.setDecoration(TextDecoration::kUnderline);
762     text_style.setDecorationColor(SK_ColorBLACK);
763     builder.pushStyle(text_style);
764     builder.addText(text, len);
765 
766     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kTop, TextBaseline::kAlphabetic, 0);
767     builder.addPlaceholder(placeholder);
768     builder.addText(text, len);
769 
770     builder.pop();
771 
772     auto paragraph = builder.Build();
773     paragraph->layout(TestCanvasWidth);
774     paragraph->paint(canvas.get(), 0, 0);
775 
776     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
777     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
778 
779     auto boxes = paragraph->getRectsForPlaceholders();
780     canvas.drawRects(SK_ColorRED, boxes);
781     REPORTER_ASSERT(reporter, boxes.size() == 1);
782     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
783     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
784     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
785     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
786 
787     boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
788     canvas.drawRects(SK_ColorBLUE, boxes);
789     REPORTER_ASSERT(reporter, boxes.size() == 1);
790     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
791     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
792     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
793     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.468f, EPSILON100));
794 }
795 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderMiddleParagraph,reporter)796 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderMiddleParagraph, reporter) {
797     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
798     TestCanvas canvas("SkParagraph_InlinePlaceholderMiddleParagraph.png");
799     if (!fontCollection->fontsFound()) return;
800 
801     const char* text = "012 34";
802     const size_t len = strlen(text);
803 
804     ParagraphStyle paragraph_style;
805     paragraph_style.turnHintingOff();
806     paragraph_style.setMaxLines(14);
807     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
808 
809     TextStyle text_style;
810     text_style.setFontFamilies({SkString("Roboto")});
811     text_style.setColor(SK_ColorBLACK);
812     text_style.setFontSize(26);
813     text_style.setWordSpacing(5);
814     text_style.setLetterSpacing(1);
815     text_style.setDecoration(TextDecoration::kUnderline);
816     text_style.setDecorationColor(SK_ColorBLACK);
817     builder.pushStyle(text_style);
818     builder.addText(text, len);
819 
820     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kMiddle, TextBaseline::kAlphabetic, 0);
821     builder.addPlaceholder(placeholder);
822     builder.addText(text, len);
823 
824     builder.pop();
825 
826     auto paragraph = builder.Build();
827     paragraph->layout(TestCanvasWidth);
828     paragraph->paint(canvas.get(), 0, 0);
829 
830     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
831     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
832 
833     auto boxes = paragraph->getRectsForPlaceholders();
834     canvas.drawRects(SK_ColorRED, boxes);
835     REPORTER_ASSERT(reporter, boxes.size() == 1);
836     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
837     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
838     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
839     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
840 
841     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
842     canvas.drawRects(SK_ColorBLUE, boxes);
843     REPORTER_ASSERT(reporter, boxes.size() == 1);
844     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON50));
845     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 9.765f, EPSILON100));
846     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
847     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 40.234f, EPSILON100));
848 }
849 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph,reporter)850 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph, reporter) {
851     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
852     TestCanvas canvas("SkParagraph_InlinePlaceholderIdeographicBaselineParagraph.png");
853     if (!fontCollection->fontsFound()) return;
854 
855     const char* text = "給能上目秘使";
856     const size_t len = strlen(text);
857 
858     ParagraphStyle paragraph_style;
859     paragraph_style.turnHintingOff();
860     paragraph_style.setMaxLines(14);
861     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
862 
863     TextStyle text_style;
864     text_style.setFontFamilies({SkString("Source Han Serif CN")});
865     text_style.setColor(SK_ColorBLACK);
866     text_style.setFontSize(26);
867     text_style.setWordSpacing(5);
868     text_style.setLetterSpacing(1);
869     text_style.setDecoration(TextDecoration::kUnderline);
870     text_style.setDecorationColor(SK_ColorBLACK);
871     builder.pushStyle(text_style);
872     builder.addText(text, len);
873     PlaceholderStyle placeholder(55, 50, PlaceholderAlignment::kBaseline, TextBaseline::kIdeographic, 38.347f);
874     builder.addPlaceholder(placeholder);
875     builder.addText(text, len);
876 
877     builder.pop();
878 
879     auto paragraph = builder.Build();
880     paragraph->layout(TestCanvasWidth);
881     paragraph->paint(canvas.get(), 0, 0);
882 
883     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
884     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
885 
886     auto boxes = paragraph->getRectsForPlaceholders();
887     canvas.drawRects(SK_ColorRED, boxes);
888     REPORTER_ASSERT(reporter, boxes.size() == 1);
889     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 162.5f, EPSILON50));
890     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
891     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f + 55, EPSILON50));
892     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
893 
894     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
895     canvas.drawRects(SK_ColorBLUE, boxes);
896     REPORTER_ASSERT(reporter, boxes.size() == 1);
897     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 135.5f, EPSILON50));
898     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 4.703f, EPSILON100));
899     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f, EPSILON50));
900     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 42.065f, EPSILON100));
901 }
902 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBreakParagraph,reporter)903 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderBreakParagraph, reporter) {
904     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
905     TestCanvas canvas("SkParagraph_InlinePlaceholderBreakParagraph.png");
906     if (!fontCollection->fontsFound()) return;
907 
908     const char* text = "012 34";
909     const size_t len = strlen(text);
910 
911     ParagraphStyle paragraph_style;
912     paragraph_style.turnHintingOff();
913     paragraph_style.setMaxLines(14);
914     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
915 
916     TextStyle text_style;
917     text_style.setFontFamilies({SkString("Roboto")});
918     text_style.setColor(SK_ColorBLACK);
919     text_style.setFontSize(26);
920     text_style.setWordSpacing(5);
921     text_style.setLetterSpacing(1);
922     text_style.setDecoration(TextDecoration::kUnderline);
923     text_style.setDecorationColor(SK_ColorBLACK);
924     builder.pushStyle(text_style);
925     builder.addText(text, len);
926 
927     PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
928     PlaceholderStyle placeholder2(25, 25, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 12.5f);
929 
930     builder.addPlaceholder(placeholder1);
931     builder.addPlaceholder(placeholder1);
932     builder.addPlaceholder(placeholder1);
933     builder.addPlaceholder(placeholder2);
934     builder.addPlaceholder(placeholder1);
935     builder.addText(text, len);
936 
937     builder.addPlaceholder(placeholder1);
938     builder.addPlaceholder(placeholder1);
939     builder.addPlaceholder(placeholder1);
940     builder.addPlaceholder(placeholder1);
941     builder.addPlaceholder(placeholder2); // 4 + 1
942     builder.addPlaceholder(placeholder1);
943     builder.addPlaceholder(placeholder1);
944     builder.addPlaceholder(placeholder1);
945     builder.addPlaceholder(placeholder1);
946     builder.addPlaceholder(placeholder1);
947     builder.addPlaceholder(placeholder1);
948     builder.addPlaceholder(placeholder2); // 6 + 1
949     builder.addPlaceholder(placeholder1);
950     builder.addPlaceholder(placeholder1);
951     builder.addPlaceholder(placeholder1);
952     builder.addPlaceholder(placeholder1);
953     builder.addPlaceholder(placeholder1);
954     builder.addPlaceholder(placeholder1);
955     builder.addPlaceholder(placeholder1);
956     builder.addPlaceholder(placeholder2); // 7 + 1
957 
958     builder.addPlaceholder(placeholder1);
959     builder.addText(text, len);
960     builder.addPlaceholder(placeholder1);
961     builder.addPlaceholder(placeholder2);
962 
963     builder.addText(text, len);
964     builder.addText(text, len);
965     builder.addText(text, len);
966     builder.addText(text, len);
967 
968     builder.addPlaceholder(placeholder2);
969     builder.addPlaceholder(placeholder1);
970 
971     builder.addText(text, len);
972 
973     builder.addPlaceholder(placeholder2);
974 
975     builder.addText(text, len);
976     builder.addText(text, len);
977     builder.addText(text, len);
978     builder.addText(text, len);
979     builder.addText(text, len);
980     builder.addText(text, len);
981     builder.addText(text, len);
982     builder.addText(text, len);
983     builder.addText(text, len);
984     builder.addText(text, len);
985     builder.addText(text, len);
986     builder.addText(text, len);
987     builder.addText(text, len);
988     builder.addText(text, len);
989     builder.addText(text, len);
990     builder.addText(text, len);
991     builder.addText(text, len);
992     builder.addText(text, len);
993     builder.addText(text, len);
994 
995     builder.pop();
996 
997     auto paragraph = builder.Build();
998     paragraph->layout(TestCanvasWidth - 100);
999     paragraph->paint(canvas.get(), 0, 0);
1000 
1001     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
1002     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1003 
1004     auto boxes = paragraph->getRectsForRange(0, 3, rect_height_style, rect_width_style);
1005     canvas.drawRects(SK_ColorRED, boxes);
1006     REPORTER_ASSERT(reporter, boxes.size() == 1);
1007 
1008     boxes = paragraph->getRectsForRange(175, 176, rect_height_style, rect_width_style);
1009     canvas.drawRects(SK_ColorGREEN, boxes);
1010     REPORTER_ASSERT(reporter, boxes.size() == 1);
1011     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 31.695f, EPSILON50));
1012     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 218.531f, EPSILON100));
1013     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 47.292f, EPSILON50));
1014     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 249, EPSILON100));
1015 
1016     boxes = paragraph->getRectsForPlaceholders();
1017     canvas.drawRects(SK_ColorRED, boxes);
1018 
1019     boxes = paragraph->getRectsForRange(4, 45, rect_height_style, rect_width_style);
1020     canvas.drawRects(SK_ColorBLUE, boxes);
1021     REPORTER_ASSERT(reporter, boxes.size() == 30);
1022     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 59.726f, EPSILON50));
1023     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 26.378f, EPSILON100));
1024     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
1025     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56.847f, EPSILON100));
1026 
1027     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.left(), 606.343f, EPSILON20));
1028     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.top(), 38, EPSILON100));
1029     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.right(), 631.343f, EPSILON20));
1030     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.bottom(), 63, EPSILON100));
1031 
1032     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.left(), 0.5f, EPSILON50));
1033     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.top(), 63.5f, EPSILON100));
1034     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.right(), 50.5f, EPSILON50));
1035     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.bottom(), 113.5f, EPSILON100));
1036 }
1037 
UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph,reporter)1038 UNIX_ONLY_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph, reporter) {
1039     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1040     TestCanvas canvas("SkParagraph_InlinePlaceholderGetRectsParagraph.png");
1041     if (!fontCollection->fontsFound()) return;
1042 
1043     const char* text = "012 34";
1044     const size_t len = strlen(text);
1045 
1046     ParagraphStyle paragraph_style;
1047     paragraph_style.turnHintingOff();
1048     paragraph_style.setMaxLines(14);
1049     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1050 
1051     TextStyle text_style;
1052     text_style.setFontFamilies({SkString("Roboto")});
1053     text_style.setColor(SK_ColorBLACK);
1054     text_style.setFontSize(26);
1055     text_style.setWordSpacing(5);
1056     text_style.setLetterSpacing(1);
1057     text_style.setDecoration(TextDecoration::kUnderline);
1058     text_style.setDecorationColor(SK_ColorBLACK);
1059     builder.pushStyle(text_style);
1060     builder.addText(text, len);
1061 
1062     PlaceholderStyle placeholder1(50, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 50);
1063     PlaceholderStyle placeholder2(5, 20, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 10);
1064 
1065     builder.addPlaceholder(placeholder1);
1066     builder.addPlaceholder(placeholder1);
1067     builder.addPlaceholder(placeholder1);
1068     builder.addPlaceholder(placeholder1);
1069     builder.addPlaceholder(placeholder1);
1070     builder.addPlaceholder(placeholder1);
1071     builder.addPlaceholder(placeholder1);
1072     builder.addPlaceholder(placeholder1);
1073     builder.addPlaceholder(placeholder2); // 8 + 1
1074     builder.addPlaceholder(placeholder1);
1075     builder.addPlaceholder(placeholder1);
1076     builder.addPlaceholder(placeholder1);
1077     builder.addPlaceholder(placeholder1);
1078     builder.addPlaceholder(placeholder1);
1079     builder.addPlaceholder(placeholder2); // 5 + 1
1080     builder.addPlaceholder(placeholder1);
1081     builder.addPlaceholder(placeholder1);
1082     builder.addPlaceholder(placeholder1);
1083     builder.addPlaceholder(placeholder1);
1084     builder.addPlaceholder(placeholder1);
1085     builder.addPlaceholder(placeholder1);
1086     builder.addPlaceholder(placeholder1);
1087     builder.addPlaceholder(placeholder1); // 8 + 0
1088 
1089     builder.addText(text, len);
1090 
1091     builder.addPlaceholder(placeholder1);
1092     builder.addPlaceholder(placeholder2);
1093     builder.addPlaceholder(placeholder2); // 1 + 2
1094     builder.addPlaceholder(placeholder1);
1095     builder.addPlaceholder(placeholder2);
1096     builder.addPlaceholder(placeholder2); // 1 + 2
1097 
1098     builder.addText(text, len);
1099     builder.addText(text, len);
1100     builder.addText(text, len);
1101     builder.addText(text, len);
1102     builder.addText(text, len);
1103     builder.addText(text, len);
1104     builder.addText(text, len);
1105     builder.addText(text, len);
1106     builder.addText(text, len);
1107     builder.addText(text, len);
1108     builder.addText(text, len);  // 11
1109 
1110     builder.addPlaceholder(placeholder2);
1111     builder.addPlaceholder(placeholder1);
1112     builder.addPlaceholder(placeholder2);
1113     builder.addPlaceholder(placeholder1);
1114     builder.addPlaceholder(placeholder2);
1115 
1116     builder.addText(text, len);
1117 
1118     builder.pop();
1119 
1120     auto paragraph = builder.Build();
1121     paragraph->layout(TestCanvasWidth);
1122     paragraph->paint(canvas.get(), 0, 0);
1123 
1124     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
1125     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1126 
1127     auto boxes = paragraph->getRectsForPlaceholders();
1128     canvas.drawRects(SK_ColorRED, boxes);
1129 
1130     REPORTER_ASSERT(reporter, boxes.size() == 34);
1131     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
1132     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
1133     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 140.921f, EPSILON50));
1134     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
1135 
1136     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.left(), 800.921f, EPSILON20));
1137     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.top(), 0, EPSILON100));
1138     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.right(), 850.921f, EPSILON20));
1139     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.bottom(), 50, EPSILON100));
1140 
1141     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.left(), 503.382f, EPSILON10));
1142     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.top(), 160, EPSILON100));
1143     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.right(), 508.382f, EPSILON10));
1144     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.bottom(), 180, EPSILON100));
1145 
1146     boxes = paragraph->getRectsForRange(30, 50, rect_height_style, rect_width_style);
1147     canvas.drawRects(SK_ColorBLUE, boxes);
1148 
1149     REPORTER_ASSERT(reporter, boxes.size() == 8);
1150     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 216.097f, EPSILON50));
1151     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 60, EPSILON100));
1152     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 290.921f, EPSILON50));
1153     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 120, EPSILON100));
1154 
1155     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 290.921f, EPSILON20));
1156     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 60, EPSILON100));
1157     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 340.921f, EPSILON20));
1158     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 120, EPSILON100));
1159 
1160     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.left(), 340.921f, EPSILON50));
1161     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.top(), 60, EPSILON100));
1162     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.right(), 345.921f, EPSILON50));
1163     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.bottom(), 120, EPSILON100));
1164 }
1165 
UNIX_ONLY_TEST(SkParagraph_SimpleRedParagraph,reporter)1166 UNIX_ONLY_TEST(SkParagraph_SimpleRedParagraph, reporter) {
1167     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1168     if (!fontCollection->fontsFound()) return;
1169     const char* text = "I am RED";
1170     const size_t len = strlen(text);
1171 
1172     ParagraphStyle paragraph_style;
1173     paragraph_style.turnHintingOff();
1174     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1175 
1176     TextStyle text_style;
1177     text_style.setFontFamilies({SkString("Roboto")});
1178     text_style.setColor(SK_ColorRED);
1179     builder.pushStyle(text_style);
1180     builder.addText(text, len);
1181     builder.pop();
1182 
1183     auto paragraph = builder.Build();
1184     paragraph->layout(TestCanvasWidth);
1185     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
1186 
1187     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1188     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1189     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
1190     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1191 
1192     size_t index = 0;
1193     for (auto& line : impl->lines()) {
1194         line.scanStyles(StyleType::kDecorations,
1195             [reporter, &index](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
1196                 REPORTER_ASSERT(reporter, index == 0);
1197                 REPORTER_ASSERT(reporter, style.getColor() == SK_ColorRED);
1198                 ++index;
1199                 return true;
1200             });
1201     }
1202 }
1203 
1204 // Checked: DIFF+ (Space between 1 & 2 style blocks)
UNIX_ONLY_TEST(SkParagraph_RainbowParagraph,reporter)1205 UNIX_ONLY_TEST(SkParagraph_RainbowParagraph, reporter) {
1206     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1207     TestCanvas canvas("SkParagraph_RainbowParagraph.png");
1208     if (!fontCollection->fontsFound()) return;
1209     const char* text1 = "Red Roboto"; // [0:10)
1210     const char* text2 = "big Greeen Default"; // [10:28)
1211     const char* text3 = "Defcolor Homemade Apple"; // [28:51)
1212     const char* text4 = "Small Blue Roboto"; // [51:68)
1213     const char* text41 = "Small Blue ";
1214     const char* text5 =
1215             "Continue Last Style With lots of words to check if it overlaps "
1216             "properly or not"; // [68:)
1217     const char* text42 =
1218             "Roboto"
1219             "Continue Last Style With lots of words to check if it overlaps "
1220             "properly or not";
1221 
1222     ParagraphStyle paragraph_style;
1223     paragraph_style.turnHintingOff();
1224     paragraph_style.setTextAlign(TextAlign::kLeft);
1225     paragraph_style.setMaxLines(2);
1226     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1227 
1228     TextStyle text_style1;
1229     text_style1.setFontFamilies({SkString("Roboto")});
1230 
1231     text_style1.setColor(SK_ColorRED);
1232     builder.pushStyle(text_style1);
1233     builder.addText(text1, strlen(text1));
1234 
1235     TextStyle text_style2;
1236     text_style2.setFontFamilies({SkString("Roboto")});
1237     text_style2.setFontSize(50);
1238     text_style2.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
1239                                          SkFontStyle::kUpright_Slant));
1240     text_style2.setLetterSpacing(10);
1241     text_style2.setDecorationColor(SK_ColorBLACK);
1242     text_style2.setDecoration((TextDecoration)(
1243             TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
1244     text_style2.setWordSpacing(30);
1245     text_style2.setColor(SK_ColorGREEN);
1246     builder.pushStyle(text_style2);
1247     builder.addText(text2, strlen(text2));
1248 
1249     TextStyle text_style3;
1250     text_style3.setFontFamilies({SkString("Homemade Apple")});
1251     text_style3.setColor(SK_ColorBLACK);
1252     builder.pushStyle(text_style3);
1253     builder.addText(text3, strlen(text3));
1254 
1255     TextStyle text_style4;
1256     text_style4.setFontFamilies({SkString("Roboto")});
1257     text_style4.setFontSize(14);
1258     text_style4.setDecorationColor(SK_ColorBLACK);
1259     text_style4.setDecoration((TextDecoration)(
1260             TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
1261     text_style4.setColor(SK_ColorBLUE);
1262     builder.pushStyle(text_style4);
1263     builder.addText(text4, strlen(text4));
1264 
1265     builder.addText(text5, strlen(text5));
1266     builder.pop();
1267 
1268     auto paragraph = builder.Build();
1269     paragraph->layout(1000);
1270     paragraph->paint(canvas.get(), 0, 0);
1271 
1272     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
1273 
1274     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1275     REPORTER_ASSERT(reporter, impl->runs().size() == 4);
1276     REPORTER_ASSERT(reporter, impl->styles().size() == 4);
1277     REPORTER_ASSERT(reporter, impl->lines().size() == 2);
1278 
1279     auto rects = paragraph->getRectsForRange(0, impl->text().size(), RectHeightStyle::kMax, RectWidthStyle::kTight);
1280     canvas.drawRects(SK_ColorMAGENTA, rects);
1281 
1282     size_t index = 0;
1283     impl->lines()[0].scanStyles(
1284         StyleType::kAllAttributes,
1285            [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
1286             switch (index) {
1287                 case 0:
1288                     REPORTER_ASSERT(reporter, style.equals(text_style1));
1289                     REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text1));
1290                     break;
1291                 case 1:
1292                     REPORTER_ASSERT(reporter, style.equals(text_style2));
1293                     REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text2));
1294                     break;
1295                 case 2:
1296                     REPORTER_ASSERT(reporter, style.equals(text_style3));
1297                     REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text3));
1298                     break;
1299                 case 3:
1300                     REPORTER_ASSERT(reporter, style.equals(text_style4));
1301                     REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text41));
1302                     break;
1303                 default:
1304                     REPORTER_ASSERT(reporter, false);
1305                     break;
1306             }
1307             ++index;
1308             return true;
1309         });
1310     impl->lines()[1].scanStyles(
1311         StyleType::kAllAttributes,
1312         [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
1313         switch (index) {
1314             case 4:
1315                 REPORTER_ASSERT(reporter, style.equals(text_style4));
1316                 REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text42));
1317                 break;
1318             default:
1319                 REPORTER_ASSERT(reporter, false);
1320                 break;
1321         }
1322         ++index;
1323         return true;
1324     });
1325     REPORTER_ASSERT(reporter, index == 5);
1326 }
1327 
UNIX_ONLY_TEST(SkParagraph_DefaultStyleParagraph,reporter)1328 UNIX_ONLY_TEST(SkParagraph_DefaultStyleParagraph, reporter) {
1329     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1330     if (!fontCollection->fontsFound()) return;
1331     TestCanvas canvas("SkParagraph_DefaultStyleParagraph.png");
1332     const char* text = "No TextStyle! Uh Oh!";
1333     const size_t len = strlen(text);
1334 
1335     ParagraphStyle paragraph_style;
1336     TextStyle defaultStyle;
1337     defaultStyle.setFontFamilies({SkString("Roboto")});
1338     paragraph_style.setTextStyle(defaultStyle);
1339     paragraph_style.turnHintingOff();
1340     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1341     builder.addText(text, len);
1342 
1343     auto paragraph = builder.Build();
1344     paragraph->layout(TestCanvasWidth);
1345     paragraph->paint(canvas.get(), 10.0, 15.0);
1346 
1347     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
1348 
1349     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1350 
1351     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1352     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
1353     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
1354 
1355     size_t index = 0;
1356     impl->lines()[0].scanStyles(
1357             StyleType::kAllAttributes,
1358             [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
1359                 REPORTER_ASSERT(reporter, style.equals(paragraph_style.getTextStyle()));
1360                 REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text));
1361                 ++index;
1362                 return true;
1363             });
1364     REPORTER_ASSERT(reporter, index == 1);
1365 }
1366 
UNIX_ONLY_TEST(SkParagraph_BoldParagraph,reporter)1367 UNIX_ONLY_TEST(SkParagraph_BoldParagraph, reporter) {
1368     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1369     if (!fontCollection->fontsFound()) return;
1370     TestCanvas canvas("SkParagraph_BoldParagraph.png");
1371     const char* text = "This is Red max bold text!";
1372     const size_t len = strlen(text);
1373 
1374     ParagraphStyle paragraph_style;
1375     paragraph_style.turnHintingOff();
1376     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1377 
1378     TextStyle text_style;
1379     text_style.setFontFamilies({SkString("Roboto")});
1380     text_style.setColor(SK_ColorRED);
1381     text_style.setFontSize(60);
1382     text_style.setLetterSpacing(0);
1383     text_style.setFontStyle(SkFontStyle(SkFontStyle::kBlack_Weight, SkFontStyle::kNormal_Width,
1384                                         SkFontStyle::kUpright_Slant));
1385     builder.pushStyle(text_style);
1386     builder.addText(text, len);
1387     builder.pop();
1388 
1389     auto paragraph = builder.Build();
1390     paragraph->layout(VeryLongCanvasWidth);
1391     paragraph->paint(canvas.get(), 10.0, 60.0);
1392 
1393     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
1394 
1395     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1396 
1397     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1398     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
1399     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
1400 
1401     size_t index = 0;
1402     impl->lines()[0].scanStyles(
1403             StyleType::kAllAttributes,
1404             [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
1405                 REPORTER_ASSERT(reporter, style.equals(text_style));
1406                 REPORTER_ASSERT(reporter, equal(impl->text().begin(), textRange, text));
1407                 ++index;
1408                 return true;
1409             });
1410     REPORTER_ASSERT(reporter, index == 1);
1411 }
1412 
UNIX_ONLY_TEST(SkParagraph_HeightOverrideParagraph,reporter)1413 UNIX_ONLY_TEST(SkParagraph_HeightOverrideParagraph, reporter) {
1414     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1415     if (!fontCollection->fontsFound()) return;
1416     TestCanvas canvas("SkParagraph_HeightOverrideParagraph.png");
1417     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
1418     const size_t len = strlen(text);
1419 
1420     ParagraphStyle paragraph_style;
1421     paragraph_style.turnHintingOff();
1422     paragraph_style.setMaxLines(10);
1423     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1424 
1425     TextStyle text_style;
1426     text_style.setFontFamilies({SkString("Roboto")});
1427     text_style.setFontSize(20);
1428     text_style.setColor(SK_ColorBLACK);
1429     text_style.setHeight(3.6345f);
1430     text_style.setHeightOverride(true);
1431     builder.pushStyle(text_style);
1432     builder.addText(text, len);
1433     builder.pop();
1434 
1435     auto paragraph = builder.Build();
1436     paragraph->layout(550);
1437 
1438     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1439     REPORTER_ASSERT(reporter, impl->runs().size() == 5);
1440     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
1441     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1442 
1443     paragraph->paint(canvas.get(), 0, 0);
1444 
1445     SkPaint paint;
1446     paint.setStyle(SkPaint::kStroke_Style);
1447     paint.setAntiAlias(true);
1448     paint.setStrokeWidth(1);
1449 
1450     // Tests for GetRectsForRange()
1451     RectHeightStyle rect_height_style = RectHeightStyle::kIncludeLineSpacingMiddle;
1452     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1453     paint.setColor(SK_ColorRED);
1454     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
1455     canvas.drawRects(SK_ColorRED, boxes);
1456     REPORTER_ASSERT(reporter, boxes.size() == 0ull);
1457 
1458     boxes = paragraph->getRectsForRange(0, 40, rect_height_style, rect_width_style);
1459     canvas.drawRects(SK_ColorBLUE, boxes);
1460     REPORTER_ASSERT(reporter, boxes.size() == 3ull);
1461 
1462     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 0, EPSILON100));
1463     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 92.805f, EPSILON5));
1464     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 43.843f, EPSILON100));
1465     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 165.495f, EPSILON5));
1466 }
1467 
UNIX_ONLY_TEST(SkParagraph_BasicHalfLeading,reporter)1468 UNIX_ONLY_TEST(SkParagraph_BasicHalfLeading, reporter) {
1469     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1470 
1471     if (!fontCollection->fontsFound()) {
1472       return;
1473     }
1474 
1475     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
1476     const size_t len = strlen(text);
1477 
1478     TestCanvas canvas("SkParagraph_BasicHalfLeading.png");
1479 
1480     ParagraphStyle paragraph_style;
1481     TextStyle text_style;
1482     text_style.setFontFamilies({SkString("Roboto")});
1483     text_style.setFontSize(20.0f);
1484     text_style.setColor(SK_ColorBLACK);
1485     text_style.setLetterSpacing(0.0f);
1486     text_style.setWordSpacing(0.0f);
1487     text_style.setHeightOverride(true);
1488     text_style.setHeight(3.6345f);
1489     text_style.setHalfLeading(true);
1490 
1491     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1492 
1493     builder.pushStyle(text_style);
1494     builder.addText(text);
1495 
1496     auto paragraph = builder.Build();
1497     paragraph->layout(550);
1498 
1499     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1500     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
1501     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1502 
1503     paragraph->paint(canvas.get(), 0, 0);
1504 
1505     const RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1506     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kTight, rect_width_style);
1507     std::vector<TextBox> lineBoxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kMax, rect_width_style);
1508 
1509     canvas.drawRects(SK_ColorBLUE, boxes);
1510     REPORTER_ASSERT(reporter, boxes.size() == 3ull);
1511     REPORTER_ASSERT(reporter, lineBoxes.size() == boxes.size());
1512 
1513     const auto line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom();
1514     const auto line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom();
1515 
1516     // Uniform line spacing.
1517     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(line_spacing1, line_spacing2));
1518 
1519     // line spacing is distributed evenly over and under the text.
1520     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[0].rect.bottom() - boxes[0].rect.bottom(), boxes[0].rect.top() - lineBoxes[0].rect.top()));
1521     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[1].rect.bottom() - boxes[1].rect.bottom(), boxes[1].rect.top() - lineBoxes[1].rect.top()));
1522     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[2].rect.bottom() - boxes[2].rect.bottom(), boxes[2].rect.top() - lineBoxes[2].rect.top()));
1523 
1524     // Half leading does not move the text horizontally.
1525     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 0, EPSILON100));
1526     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 43.843f, EPSILON100));
1527 }
1528 
UNIX_ONLY_TEST(SkParagraph_NearZeroHeightMixedDistribution,reporter)1529 UNIX_ONLY_TEST(SkParagraph_NearZeroHeightMixedDistribution, reporter) {
1530     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1531 
1532     if (!fontCollection->fontsFound()) {
1533       return;
1534     }
1535 
1536     const char* text = "Cookies need love";
1537     const size_t len = strlen(text);
1538 
1539     TestCanvas canvas("SkParagraph_ZeroHeightHalfLeading.png");
1540 
1541     ParagraphStyle paragraph_style;
1542     paragraph_style.setTextHeightBehavior(TextHeightBehavior::kAll);
1543     TextStyle text_style;
1544     text_style.setFontFamilies({SkString("Roboto")});
1545     text_style.setFontSize(20.0f);
1546     text_style.setColor(SK_ColorBLACK);
1547     text_style.setLetterSpacing(0.0f);
1548     text_style.setWordSpacing(0.0f);
1549     text_style.setHeightOverride(true);
1550     text_style.setHeight(0.001f);
1551 
1552     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1553 
1554     // First run, half leading.
1555     text_style.setHalfLeading(true);
1556     builder.pushStyle(text_style);
1557     builder.addText(text);
1558 
1559     // Second run, no half leading.
1560     text_style.setHalfLeading(false);
1561     builder.pushStyle(text_style);
1562     builder.addText(text);
1563 
1564     auto paragraph = builder.Build();
1565     paragraph->layout(550);
1566     paragraph->paint(canvas.get(), 0, 0);
1567 
1568     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1569     REPORTER_ASSERT(reporter, impl->runs().size() == 2);
1570     REPORTER_ASSERT(reporter, impl->styles().size() == 2);  // paragraph style does not count
1571     REPORTER_ASSERT(reporter, impl->lines().size() == 1ull);
1572 
1573     const RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1574 
1575     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kTight, rect_width_style);
1576     std::vector<TextBox> lineBoxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kMax, rect_width_style);
1577 
1578     canvas.drawRects(SK_ColorBLUE, boxes);
1579     REPORTER_ASSERT(reporter, boxes.size() == 1ull);
1580     REPORTER_ASSERT(reporter, lineBoxes.size() == boxes.size());
1581 
1582     // From font metrics.
1583     const auto metricsAscent = -18.5546875f;
1584     const auto metricsDescent = 4.8828125f;
1585 
1586     // As the height multiplier converges to 0 (but not 0 since 0 is used as a
1587     // magic value to indicate there's no height multiplier), the `Run`s top
1588     // edge and bottom edge will converge to a horizontal line:
1589     // - When half leading is used the vertical line is roughly the center of
1590     //   of the glyphs in the run ((fontMetrics.descent - fontMetrics.ascent) / 2)
1591     // - When half leading is disabled the line is the alphabetic baseline.
1592 
1593     // Expected values in baseline coordinate space:
1594     const auto run1_ascent = (metricsAscent + metricsDescent) / 2;
1595     const auto run1_descent = (metricsAscent + metricsDescent) / 2;
1596     const auto run2_ascent = 0.0f;
1597     const auto run2_descent = 0.0f;
1598     const auto line_top = std::min(run1_ascent, run2_ascent);
1599     const auto line_bottom = std::max(run1_descent, run2_descent);
1600 
1601     // Expected glyph height in linebox coordinate space:
1602     const auto glyphs_top = metricsAscent - line_top;
1603     const auto glyphs_bottom = metricsDescent - line_top;
1604 
1605     // kTight reports the glyphs' bounding box in the linebox's coordinate
1606     // space.
1607     const auto actual_glyphs_top = boxes[0].rect.top() - lineBoxes[0].rect.top();
1608     const auto actual_glyphs_bottom = boxes[0].rect.bottom() - lineBoxes[0].rect.top();
1609 
1610     // Use a relatively large epsilon since the heightMultiplier is not actually
1611     // 0.
1612     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(glyphs_top, actual_glyphs_top, EPSILON20));
1613     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(glyphs_bottom, actual_glyphs_bottom, EPSILON20));
1614 
1615     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[0].rect.height(), line_bottom - line_top, EPSILON2));
1616     REPORTER_ASSERT(reporter, lineBoxes[0].rect.height() > 1);
1617 
1618     // Half leading does not move the text horizontally.
1619     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
1620 }
1621 
UNIX_ONLY_TEST(SkParagraph_StrutHalfLeading,reporter)1622 UNIX_ONLY_TEST(SkParagraph_StrutHalfLeading, reporter) {
1623     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1624 
1625     if (!fontCollection->fontsFound()) {
1626       return;
1627     }
1628 
1629     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
1630     const size_t len = strlen(text);
1631 
1632     TestCanvas canvas("SkParagraph_StrutHalfLeading.png");
1633 
1634     ParagraphStyle paragraph_style;
1635     // Tiny font and height multiplier to ensure the height is entirely decided
1636     // by the strut.
1637     TextStyle text_style;
1638     text_style.setFontFamilies({SkString("Roboto")});
1639     text_style.setFontSize(1.0f);
1640     text_style.setColor(SK_ColorBLACK);
1641     text_style.setLetterSpacing(0.0f);
1642     text_style.setWordSpacing(0.0f);
1643     text_style.setHeight(0.1f);
1644 
1645     StrutStyle strut_style;
1646     strut_style.setFontFamilies({SkString("Roboto")});
1647     strut_style.setFontSize(20.0f);
1648     strut_style.setHeight(3.6345f);
1649     strut_style.setHalfLeading(true);
1650     strut_style.setStrutEnabled(true);
1651     strut_style.setForceStrutHeight(true);
1652 
1653     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1654 
1655     builder.pushStyle(text_style);
1656     builder.addText(text);
1657 
1658     auto paragraph = builder.Build();
1659     paragraph->layout(550);
1660 
1661     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1662     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
1663 
1664     paragraph->paint(canvas.get(), 0, 0);
1665 
1666     const RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1667     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kTight, rect_width_style);
1668     std::vector<TextBox> lineBoxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kMax, rect_width_style);
1669 
1670     canvas.drawRects(SK_ColorBLUE, boxes);
1671     REPORTER_ASSERT(reporter, boxes.size() == 3ull);
1672     REPORTER_ASSERT(reporter, lineBoxes.size() == boxes.size());
1673 
1674     const auto line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom();
1675     const auto line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom();
1676 
1677     // Uniform line spacing.
1678     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(line_spacing1, line_spacing2));
1679 
1680     // line spacing is distributed evenly over and under the text.
1681     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[0].rect.bottom() - boxes[0].rect.bottom(), boxes[0].rect.top() - lineBoxes[0].rect.top()));
1682     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[1].rect.bottom() - boxes[1].rect.bottom(), boxes[1].rect.top() - lineBoxes[1].rect.top()));
1683     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[2].rect.bottom() - boxes[2].rect.bottom(), boxes[2].rect.top() - lineBoxes[2].rect.top()));
1684 
1685     // Half leading does not move the text horizontally.
1686     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 0, EPSILON100));
1687 }
1688 
UNIX_ONLY_TEST(SkParagraph_TrimLeadingDistribution,reporter)1689 UNIX_ONLY_TEST(SkParagraph_TrimLeadingDistribution, reporter) {
1690     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1691 
1692     if (!fontCollection->fontsFound()) {
1693       return;
1694     }
1695 
1696     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
1697     const size_t len = strlen(text);
1698 
1699     TestCanvas canvas("SkParagraph_TrimHalfLeading.png");
1700 
1701     ParagraphStyle paragraph_style;
1702     paragraph_style.setTextHeightBehavior(TextHeightBehavior::kDisableAll);
1703     TextStyle text_style;
1704     text_style.setFontFamilies({SkString("Roboto")});
1705     text_style.setFontSize(20.0f);
1706     text_style.setColor(SK_ColorBLACK);
1707     text_style.setLetterSpacing(0.0f);
1708     text_style.setWordSpacing(0.0f);
1709     text_style.setHeightOverride(true);
1710     text_style.setHeight(3.6345f);
1711     text_style.setHalfLeading(true);
1712 
1713     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1714 
1715     builder.pushStyle(text_style);
1716     builder.addText(text);
1717 
1718     auto paragraph = builder.Build();
1719     paragraph->layout(550);
1720     paragraph->paint(canvas.get(), 0, 0);
1721 
1722     const RectWidthStyle rect_width_style = RectWidthStyle::kTight;
1723 
1724     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kTight, rect_width_style);
1725     std::vector<TextBox> lineBoxes = paragraph->getRectsForRange(0, len, RectHeightStyle::kMax, rect_width_style);
1726 
1727     canvas.drawRects(SK_ColorBLUE, boxes);
1728     REPORTER_ASSERT(reporter, boxes.size() == 3ull);
1729     REPORTER_ASSERT(reporter, lineBoxes.size() == boxes.size());
1730 
1731     const auto line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom();
1732     const auto line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom();
1733 
1734     // Uniform line spacing. The delta is introduced by the height rounding.
1735     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(line_spacing1, line_spacing2, 1));
1736 
1737     // Trim the first line's top leading.
1738     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[0].rect.top(), boxes[0].rect.top()));
1739     // Trim the last line's bottom leading.
1740     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[2].rect.bottom(), boxes[2].rect.bottom()));
1741 
1742     const auto halfLeading =  lineBoxes[0].rect.bottom() - boxes[0].rect.bottom();
1743     // Large epsilon because of rounding.
1744     const auto epsilon = EPSILON10;
1745     // line spacing is distributed evenly over and under the text.
1746     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top() - lineBoxes[1].rect.top(), halfLeading, epsilon));
1747     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lineBoxes[1].rect.bottom() - boxes[1].rect.bottom(),  halfLeading));
1748     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.top() - lineBoxes[2].rect.top(), halfLeading, epsilon));
1749 
1750     // Half leading does not move the text horizontally.
1751     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 0, EPSILON100));
1752     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 43.843f, EPSILON100));
1753 }
1754 
UNIX_ONLY_TEST(SkParagraph_LeftAlignParagraph,reporter)1755 UNIX_ONLY_TEST(SkParagraph_LeftAlignParagraph, reporter) {
1756     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1757     if (!fontCollection->fontsFound()) return;
1758     TestCanvas canvas("SkParagraph_LeftAlignParagraph.png");
1759     const char* text =
1760             "This is a very long sentence to test if the text will properly wrap "
1761             "around and go to the next line. Sometimes, short sentence. Longer "
1762             "sentences are okay too because they are nessecary. Very short. "
1763             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1764             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1765             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1766             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1767             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1768             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1769             "mollit anim id est laborum. "
1770             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1771             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1772             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1773             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1774             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1775             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1776             "mollit anim id est laborum.";
1777     const size_t len = strlen(text);
1778 
1779     ParagraphStyle paragraph_style;
1780     paragraph_style.setMaxLines(14);
1781     paragraph_style.setTextAlign(TextAlign::kLeft);
1782     paragraph_style.turnHintingOff();
1783     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1784 
1785     TextStyle text_style;
1786     text_style.setFontFamilies({SkString("Roboto")});
1787     text_style.setFontSize(26);
1788     text_style.setLetterSpacing(1);
1789     text_style.setWordSpacing(5);
1790     text_style.setColor(SK_ColorBLACK);
1791     text_style.setHeight(1);
1792     text_style.setDecoration(TextDecoration::kUnderline);
1793     text_style.setDecorationColor(SK_ColorBLACK);
1794     builder.pushStyle(text_style);
1795     builder.addText(text, len);
1796     builder.pop();
1797 
1798     auto paragraph = builder.Build();
1799     paragraph->layout(TestCanvasWidth - 100);
1800     paragraph->paint(canvas.get(), 0, 0);
1801 
1802     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1803 
1804     REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
1805     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1806     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
1807     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1808     REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
1809 
1810     double expected_y = 0;
1811     double epsilon = 0.01f;
1812     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
1813     REPORTER_ASSERT(reporter,
1814                     SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
1815     expected_y += 30;
1816     REPORTER_ASSERT(reporter,
1817                     SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
1818     expected_y += 30;
1819     REPORTER_ASSERT(reporter,
1820                     SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
1821     expected_y += 30;
1822     REPORTER_ASSERT(reporter,
1823                     SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
1824     expected_y += 30 * 10;
1825     REPORTER_ASSERT(reporter,
1826                     SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
1827 
1828     REPORTER_ASSERT(reporter,
1829                     paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
1830 
1831     // Tests for GetGlyphPositionAtCoordinate()
1832     REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(0, 0).position == 0);
1833     REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 1).position == 0);
1834     REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 35).position == 68);
1835     REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(1, 70).position == 134);
1836     REPORTER_ASSERT(reporter, impl->getGlyphPositionAtCoordinate(2000, 35).position == 134);
1837 }
1838 
UNIX_ONLY_TEST(SkParagraph_RightAlignParagraph,reporter)1839 UNIX_ONLY_TEST(SkParagraph_RightAlignParagraph, reporter) {
1840     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1841     if (!fontCollection->fontsFound()) return;
1842     TestCanvas canvas("SkParagraph_RightAlignParagraph.png");
1843     const char* text =
1844             "This is a very long sentence to test if the text will properly wrap "
1845             "around and go to the next line. Sometimes, short sentence. Longer "
1846             "sentences are okay too because they are nessecary. Very short. "
1847             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1848             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1849             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1850             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1851             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1852             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1853             "mollit anim id est laborum. "
1854             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1855             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1856             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1857             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1858             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1859             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1860             "mollit anim id est laborum.";
1861     const size_t len = strlen(text);
1862 
1863     ParagraphStyle paragraph_style;
1864     paragraph_style.setMaxLines(14);
1865     paragraph_style.setTextAlign(TextAlign::kRight);
1866     paragraph_style.turnHintingOff();
1867     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1868 
1869     TextStyle text_style;
1870     text_style.setFontFamilies({SkString("Roboto")});
1871     text_style.setFontSize(26);
1872     text_style.setLetterSpacing(1);
1873     text_style.setWordSpacing(5);
1874     text_style.setColor(SK_ColorBLACK);
1875     text_style.setHeight(1);
1876     text_style.setDecoration(TextDecoration::kUnderline);
1877     text_style.setDecorationColor(SK_ColorBLACK);
1878     builder.pushStyle(text_style);
1879     builder.addText(text, len);
1880     builder.pop();
1881 
1882     auto paragraph = builder.Build();
1883     paragraph->layout(TestCanvasWidth - 100);
1884 
1885     paragraph->paint(canvas.get(), 0, 0);
1886 
1887     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1888 
1889     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1890     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
1891     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1892     REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
1893 
1894     double expected_y = 0;
1895     double epsilon = 0.01f;
1896     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
1897     REPORTER_ASSERT(reporter,
1898                     SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
1899     expected_y += 30;
1900     REPORTER_ASSERT(reporter,
1901                     SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
1902     expected_y += 30;
1903     REPORTER_ASSERT(reporter,
1904                     SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
1905     expected_y += 30;
1906     REPORTER_ASSERT(reporter,
1907                     SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
1908     expected_y += 30 * 10;
1909     REPORTER_ASSERT(reporter,
1910                     SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
1911 
1912     auto calculate = [](const TextLine& line) -> SkScalar {
1913         return TestCanvasWidth - 100 - line.offset().fX - line.width();
1914     };
1915 
1916     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, epsilon));
1917     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, epsilon));
1918     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, epsilon));
1919     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, epsilon));
1920     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[13]), 0, epsilon));
1921 
1922     REPORTER_ASSERT(reporter,
1923                     paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
1924 }
1925 
UNIX_ONLY_TEST(SkParagraph_CenterAlignParagraph,reporter)1926 UNIX_ONLY_TEST(SkParagraph_CenterAlignParagraph, reporter) {
1927     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
1928     if (!fontCollection->fontsFound()) return;
1929     TestCanvas canvas("SkParagraph_CenterAlignParagraph.png");
1930     const char* text =
1931             "This is a very long sentence to test if the text will properly wrap "
1932             "around and go to the next line. Sometimes, short sentence. Longer "
1933             "sentences are okay too because they are nessecary. Very short. "
1934             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1935             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1936             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1937             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1938             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1939             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1940             "mollit anim id est laborum. "
1941             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
1942             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
1943             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
1944             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
1945             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
1946             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
1947             "mollit anim id est laborum.";
1948     const size_t len = strlen(text);
1949 
1950     ParagraphStyle paragraph_style;
1951     paragraph_style.setMaxLines(14);
1952     paragraph_style.setTextAlign(TextAlign::kCenter);
1953     paragraph_style.turnHintingOff();
1954     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
1955 
1956     TextStyle text_style;
1957     text_style.setFontFamilies({SkString("Roboto")});
1958     text_style.setFontSize(26);
1959     text_style.setLetterSpacing(1);
1960     text_style.setWordSpacing(5);
1961     text_style.setColor(SK_ColorBLACK);
1962     text_style.setHeight(1);
1963     text_style.setDecoration(TextDecoration::kUnderline);
1964     text_style.setDecorationColor(SK_ColorBLACK);
1965     builder.pushStyle(text_style);
1966     builder.addText(text, len);
1967     builder.pop();
1968 
1969     auto paragraph = builder.Build();
1970     paragraph->layout(TestCanvasWidth - 100);
1971     paragraph->paint(canvas.get(), 0, 0);
1972 
1973     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
1974 
1975     REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
1976     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
1977     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
1978     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
1979     REPORTER_ASSERT(reporter, impl->lines().size() == paragraph_style.getMaxLines());
1980 
1981     double expected_y = 0;
1982     double epsilon = 0.01f;
1983     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, epsilon));
1984     REPORTER_ASSERT(reporter,
1985                     SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, epsilon));
1986     expected_y += 30;
1987     REPORTER_ASSERT(reporter,
1988                     SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, epsilon));
1989     expected_y += 30;
1990     REPORTER_ASSERT(reporter,
1991                     SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, epsilon));
1992     expected_y += 30;
1993     REPORTER_ASSERT(reporter,
1994                     SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, epsilon));
1995     expected_y += 30 * 10;
1996     REPORTER_ASSERT(reporter,
1997                     SkScalarNearlyEqual(impl->lines()[13].offset().fY, expected_y, epsilon));
1998 
1999     auto calculate = [](const TextLine& line) -> SkScalar {
2000         return TestCanvasWidth - 100 - (line.offset().fX * 2 + line.width());
2001     };
2002 
2003     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, epsilon));
2004     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, epsilon));
2005     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, epsilon));
2006     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, epsilon));
2007     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[13]), 0, epsilon));
2008 
2009     REPORTER_ASSERT(reporter,
2010                     paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
2011 }
2012 
UNIX_ONLY_TEST(SkParagraph_JustifyAlignParagraph,reporter)2013 UNIX_ONLY_TEST(SkParagraph_JustifyAlignParagraph, reporter) {
2014     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2015     if (!fontCollection->fontsFound()) return;
2016     TestCanvas canvas("SkParagraph_JustifyAlignParagraph.png");
2017     const char* text =
2018             "This is a very long sentence to test if the text will properly wrap "
2019             "around and go to the next line. Sometimes, short sentence. Longer "
2020             "sentences are okay too because they are nessecary. Very short. "
2021             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
2022             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
2023             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
2024             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
2025             "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
2026             "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
2027             "mollit anim id est laborum. "
2028             "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
2029             "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
2030             "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
2031             "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
2032             "velit esse cillum dolore eu fugiat.";
2033     const size_t len = strlen(text);
2034 
2035     ParagraphStyle paragraph_style;
2036     paragraph_style.setMaxLines(14);
2037     paragraph_style.setTextAlign(TextAlign::kJustify);
2038     paragraph_style.turnHintingOff();
2039     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2040 
2041     TextStyle text_style;
2042     text_style.setFontFamilies({SkString("Roboto")});
2043     text_style.setFontSize(26);
2044     text_style.setLetterSpacing(0);
2045     text_style.setWordSpacing(5);
2046     text_style.setColor(SK_ColorBLACK);
2047     text_style.setHeight(1);
2048     text_style.setDecoration(TextDecoration::kUnderline);
2049     text_style.setDecorationColor(SK_ColorBLACK);
2050     builder.pushStyle(text_style);
2051     builder.addText(text, len);
2052     builder.pop();
2053 
2054     auto paragraph = builder.Build();
2055     paragraph->layout(TestCanvasWidth - 100);
2056     paragraph->paint(canvas.get(), 0, 0);
2057 
2058     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2059     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2060     auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
2061     canvas.drawRects(SK_ColorRED, boxes);
2062 
2063     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2064 
2065     REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
2066     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
2067     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
2068     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
2069 
2070     double expected_y = 0;
2071     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].baseline(), 24.121f, EPSILON100));
2072     REPORTER_ASSERT(reporter,
2073                     SkScalarNearlyEqual(impl->lines()[0].offset().fY, expected_y, EPSILON100));
2074     expected_y += 30;
2075     REPORTER_ASSERT(reporter,
2076                     SkScalarNearlyEqual(impl->lines()[1].offset().fY, expected_y, EPSILON100));
2077     expected_y += 30;
2078     REPORTER_ASSERT(reporter,
2079                     SkScalarNearlyEqual(impl->lines()[2].offset().fY, expected_y, EPSILON100));
2080     expected_y += 30;
2081     REPORTER_ASSERT(reporter,
2082                     SkScalarNearlyEqual(impl->lines()[3].offset().fY, expected_y, EPSILON100));
2083     expected_y += 30 * 9;
2084     REPORTER_ASSERT(reporter,
2085                     SkScalarNearlyEqual(impl->lines()[12].offset().fY, expected_y, EPSILON100));
2086 
2087     auto calculate = [](const TextLine& line) -> SkScalar {
2088         return line.offset().fX;
2089     };
2090 
2091     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[0]), 0, EPSILON100));
2092     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[1]), 0, EPSILON100));
2093     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[2]), 0, EPSILON100));
2094     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(impl->lines()[3]), 0, EPSILON100));
2095 
2096     REPORTER_ASSERT(reporter,
2097                     paragraph_style.getTextAlign() == impl->paragraphStyle().getTextAlign());
2098 }
2099 
2100 // Checked: DIFF (ghost spaces as a separate box in TxtLib)
UNIX_ONLY_TEST(SkParagraph_JustifyRTL,reporter)2101 UNIX_ONLY_TEST(SkParagraph_JustifyRTL, reporter) {
2102     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
2103     if (!fontCollection->fontsFound()) return;
2104     TestCanvas canvas("SkParagraph_JustifyRTL.png");
2105     const char* text =
2106             "אאא בּבּבּבּ אאאא בּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ "
2107             "אאאאא בּבּבּבּבּ אאאבּבּבּבּבּבּאאאאא בּבּבּבּבּבּאאאאאבּבּבּבּבּבּ אאאאא בּבּבּבּבּ "
2108             "אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ";
2109     const size_t len = strlen(text);
2110 
2111     ParagraphStyle paragraph_style;
2112     paragraph_style.setMaxLines(14);
2113     paragraph_style.setTextAlign(TextAlign::kJustify);
2114     paragraph_style.setTextDirection(TextDirection::kRtl);
2115     paragraph_style.turnHintingOff();
2116     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2117 
2118     TextStyle text_style;
2119     text_style.setFontFamilies({SkString("Ahem")});
2120     text_style.setFontSize(26);
2121     text_style.setColor(SK_ColorBLACK);
2122     builder.pushStyle(text_style);
2123     builder.addText(text, len);
2124     builder.pop();
2125 
2126     auto paragraph = builder.Build();
2127     paragraph->layout(TestCanvasWidth - 100);
2128     paragraph->paint(canvas.get(), 0, 0);
2129 
2130     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2131 
2132     auto calculate = [](const TextLine& line) -> SkScalar {
2133         return TestCanvasWidth - 100 - line.width();
2134     };
2135     for (auto& line : impl->lines()) {
2136         if (&line == &impl->lines().back()) {
2137             REPORTER_ASSERT(reporter, calculate(line) > EPSILON100);
2138         } else {
2139             REPORTER_ASSERT(reporter, SkScalarNearlyEqual(calculate(line), 0, EPSILON100));
2140         }
2141     }
2142 
2143     // Just make sure the the text is actually RTL
2144     for (auto& run : impl->runs()) {
2145         REPORTER_ASSERT(reporter, !run.leftToRight());
2146     }
2147 
2148     // Tests for GetRectsForRange()
2149     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2150     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2151     auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
2152     canvas.drawRects(SK_ColorRED, boxes);
2153     REPORTER_ASSERT(reporter, boxes.size() == 3);
2154 
2155     boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
2156     canvas.drawRects(SK_ColorBLUE, boxes);
2157     REPORTER_ASSERT(reporter, boxes.size() == 1);
2158 
2159     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 588, EPSILON100));
2160     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 130, EPSILON100));
2161     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 640, EPSILON100));
2162     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 156, EPSILON100));
2163 }
2164 
UNIX_ONLY_TEST(SkParagraph_JustifyRTLNewLine,reporter)2165 UNIX_ONLY_TEST(SkParagraph_JustifyRTLNewLine, reporter) {
2166     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
2167     if (!fontCollection->fontsFound()) return;
2168     TestCanvas canvas("SkParagraph_JustifyRTLNewLine.png");
2169     const char* text =
2170             "אאא בּבּבּבּ אאאא\nבּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ "
2171             "אאאאא בּבּבּבּבּ אאאבּבּבּבּבּבּאאאאא בּבּבּבּבּבּאאאאאבּבּבּבּבּבּ אאאאא בּבּבּבּבּ "
2172             "אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ";
2173     const size_t len = strlen(text);
2174 
2175     ParagraphStyle paragraph_style;
2176     paragraph_style.setMaxLines(14);
2177     paragraph_style.setTextAlign(TextAlign::kJustify);
2178     paragraph_style.setTextDirection(TextDirection::kRtl);
2179     paragraph_style.turnHintingOff();
2180     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2181 
2182     TextStyle text_style;
2183     text_style.setFontFamilies({SkString("Ahem")});
2184     text_style.setFontSize(26);
2185     text_style.setColor(SK_ColorBLACK);
2186     builder.pushStyle(text_style);
2187     builder.addText(text, len);
2188     builder.pop();
2189 
2190     auto paragraph = builder.Build();
2191     paragraph->layout(TestCanvasWidth - 100);
2192     paragraph->paint(canvas.get(), 0, 0);
2193 
2194     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2195 
2196     SkPaint paint;
2197     paint.setStyle(SkPaint::kStroke_Style);
2198     paint.setAntiAlias(true);
2199     paint.setStrokeWidth(1);
2200 
2201     // Tests for GetRectsForRange()
2202     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2203     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2204     paint.setColor(SK_ColorRED);
2205     auto boxes = paragraph->getRectsForRange(0, 30, rect_height_style, rect_width_style);
2206     for (size_t i = 0; i < boxes.size(); ++i) {
2207         canvas.get()->drawRect(boxes[i].rect, paint);
2208     }
2209     REPORTER_ASSERT(reporter, boxes.size() == 2ull);
2210     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 562, EPSILON100));
2211     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
2212     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 900, EPSILON100));
2213     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 26, EPSILON100));
2214 
2215     paint.setColor(SK_ColorBLUE);
2216     boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
2217     for (size_t i = 0; i < boxes.size(); ++i) {
2218         canvas.get()->drawRect(boxes[i].rect, paint);
2219     }
2220     REPORTER_ASSERT(reporter, boxes.size() == 1ull);
2221     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 68, EPSILON100));
2222     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 130, EPSILON100));
2223     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 120, EPSILON100));
2224     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 156, EPSILON100));
2225 
2226     // All lines should be justified to the width of the paragraph
2227     // except for #0 (new line) and #5 (the last one)
2228     for (auto& line : impl->lines()) {
2229         ptrdiff_t num = &line - impl->lines().data();
2230         if (num == 0 || num == 5) {
2231             REPORTER_ASSERT(reporter, line.width() < TestCanvasWidth - 100);
2232         } else {
2233             REPORTER_ASSERT(reporter,
2234                             SkScalarNearlyEqual(line.width(), TestCanvasWidth - 100, EPSILON100),
2235                             "#%zd: %f <= %d\n", num, line.width(), TestCanvasWidth - 100);
2236         }
2237     }
2238 }
2239 
UNIX_ONLY_TEST(SkParagraph_LeadingSpaceRTL,reporter)2240 UNIX_ONLY_TEST(SkParagraph_LeadingSpaceRTL, reporter) {
2241     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
2242     if (!fontCollection->fontsFound()) return;
2243     TestCanvas canvas("SkParagraph_LeadingSpaceRTL.png");
2244 
2245     const char* text = " leading space";
2246     const size_t len = strlen(text);
2247 
2248     ParagraphStyle paragraph_style;
2249     paragraph_style.setMaxLines(14);
2250     paragraph_style.setTextAlign(TextAlign::kJustify);
2251     paragraph_style.setTextDirection(TextDirection::kRtl);
2252     paragraph_style.turnHintingOff();
2253     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2254 
2255     TextStyle text_style;
2256     text_style.setFontFamilies({SkString("Ahem")});
2257     text_style.setFontSize(26);
2258     text_style.setColor(SK_ColorBLACK);
2259     builder.pushStyle(text_style);
2260     builder.addText(text, len);
2261     builder.pop();
2262 
2263     auto paragraph = builder.Build();
2264     paragraph->layout(TestCanvasWidth - 100);
2265     paragraph->paint(canvas.get(), 0, 0);
2266 
2267     SkPaint paint;
2268     paint.setStyle(SkPaint::kStroke_Style);
2269     paint.setAntiAlias(true);
2270     paint.setStrokeWidth(1);
2271 
2272     // Tests for GetRectsForRange()
2273     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2274     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2275     paint.setColor(SK_ColorRED);
2276     auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
2277     for (size_t i = 0; i < boxes.size(); ++i) {
2278         canvas.get()->drawRect(boxes[i].rect, paint);
2279     }
2280     REPORTER_ASSERT(reporter, boxes.size() == 2ull);
2281 }
2282 
UNIX_ONLY_TEST(SkParagraph_DecorationsParagraph,reporter)2283 UNIX_ONLY_TEST(SkParagraph_DecorationsParagraph, reporter) {
2284     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2285     if (!fontCollection->fontsFound()) return;
2286     TestCanvas canvas("SkParagraph_DecorationsParagraph.png");
2287     const char* text1 = "This text should be";
2288     const char* text2 = " decorated even when";
2289     const char* text3 = " wrapped around to";
2290     const char* text4 = " the next line.";
2291     const char* text5 = " Otherwise, bad things happen.";
2292 
2293     ParagraphStyle paragraph_style;
2294     paragraph_style.setMaxLines(14);
2295     paragraph_style.setTextAlign(TextAlign::kLeft);
2296     paragraph_style.turnHintingOff();
2297     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2298 
2299     TextStyle text_style;
2300     text_style.setFontFamilies({SkString("Roboto")});
2301     text_style.setFontSize(26);
2302     text_style.setLetterSpacing(0);
2303     text_style.setWordSpacing(5);
2304     text_style.setColor(SK_ColorBLACK);
2305     text_style.setHeight(2);
2306     text_style.setDecoration(TextDecoration::kUnderline);
2307     text_style.setDecorationColor(SK_ColorBLACK);
2308     text_style.setDecoration((TextDecoration)(
2309             TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
2310     text_style.setDecorationStyle(TextDecorationStyle::kSolid);
2311     text_style.setDecorationColor(SK_ColorBLACK);
2312     text_style.setDecorationThicknessMultiplier(2.0);
2313     builder.pushStyle(text_style);
2314     builder.addText(text1, strlen(text1));
2315 
2316     text_style.setDecorationStyle(TextDecorationStyle::kDouble);
2317     text_style.setDecorationColor(SK_ColorBLUE);
2318     text_style.setDecorationThicknessMultiplier(1.0);
2319     builder.pushStyle(text_style);
2320     builder.addText(text2, strlen(text2));
2321 
2322     text_style.setDecorationStyle(TextDecorationStyle::kDotted);
2323     text_style.setDecorationColor(SK_ColorBLACK);
2324     builder.pushStyle(text_style);
2325     builder.addText(text3, strlen(text3));
2326 
2327     text_style.setDecorationStyle(TextDecorationStyle::kDashed);
2328     text_style.setDecorationColor(SK_ColorBLACK);
2329     text_style.setDecorationThicknessMultiplier(3.0);
2330     builder.pushStyle(text_style);
2331     builder.addText(text4, strlen(text4));
2332 
2333     text_style.setDecorationStyle(TextDecorationStyle::kWavy);
2334     text_style.setDecorationColor(SK_ColorRED);
2335     text_style.setDecorationThicknessMultiplier(1.0);
2336     builder.pushStyle(text_style);
2337     builder.addText(text5, strlen(text5));
2338     builder.pop();
2339 
2340     auto paragraph = builder.Build();
2341     paragraph->layout(TestCanvasWidth - 100);
2342     paragraph->paint(canvas.get(), 0, 0);
2343 
2344     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2345 
2346     size_t index = 0;
2347     for (auto& line : impl->lines()) {
2348         line.scanStyles(
2349             StyleType::kDecorations,
2350             [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
2351                     auto decoration = (TextDecoration)(TextDecoration::kUnderline |
2352                                                        TextDecoration::kOverline |
2353                                                        TextDecoration::kLineThrough);
2354                     REPORTER_ASSERT(reporter, style.getDecorationType() == decoration);
2355                     switch (index) {
2356                         case 0:
2357                             REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
2358                                                               TextDecorationStyle::kSolid);
2359                             REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
2360                             REPORTER_ASSERT(reporter,
2361                                             style.getDecorationThicknessMultiplier() == 2.0);
2362                             break;
2363                         case 1:  // The style appears on 2 lines so it has 2 pieces
2364                             REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
2365                                                               TextDecorationStyle::kDouble);
2366                             REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLUE);
2367                             REPORTER_ASSERT(reporter,
2368                                             style.getDecorationThicknessMultiplier() == 1.0);
2369                             break;
2370                         case 2:
2371                             REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
2372                                                               TextDecorationStyle::kDotted);
2373                             REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
2374                             REPORTER_ASSERT(reporter,
2375                                             style.getDecorationThicknessMultiplier() == 1.0);
2376                             break;
2377                         case 3:
2378                         case 4:
2379                             REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
2380                                                               TextDecorationStyle::kDashed);
2381                             REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorBLACK);
2382                             REPORTER_ASSERT(reporter,
2383                                             style.getDecorationThicknessMultiplier() == 3.0);
2384                             break;
2385                         case 5:
2386                             REPORTER_ASSERT(reporter, style.getDecorationStyle() ==
2387                                                               TextDecorationStyle::kWavy);
2388                             REPORTER_ASSERT(reporter, style.getDecorationColor() == SK_ColorRED);
2389                             REPORTER_ASSERT(reporter,
2390                                             style.getDecorationThicknessMultiplier() == 1.0);
2391                             break;
2392                         default:
2393                             REPORTER_ASSERT(reporter, false);
2394                             break;
2395                     }
2396                     ++index;
2397                     return true;
2398                 });
2399     }
2400 }
2401 
2402 // TODO: Add test for wavy decorations.
2403 
UNIX_ONLY_TEST(SkParagraph_ItalicsParagraph,reporter)2404 UNIX_ONLY_TEST(SkParagraph_ItalicsParagraph, reporter) {
2405     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2406     if (!fontCollection->fontsFound()) return;
2407     TestCanvas canvas("SkParagraph_ItalicsParagraph.png");
2408     const char* text1 = "No italic ";
2409     const char* text2 = "Yes Italic ";
2410     const char* text3 = "No Italic again.";
2411 
2412     ParagraphStyle paragraph_style;
2413     paragraph_style.turnHintingOff();
2414     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2415 
2416     TextStyle text_style;
2417     text_style.setFontFamilies({SkString("Roboto")});
2418     text_style.setFontSize(10);
2419     text_style.setColor(SK_ColorRED);
2420     builder.pushStyle(text_style);
2421     builder.addText(text1, strlen(text1));
2422 
2423     text_style.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
2424                                         SkFontStyle::kItalic_Slant));
2425     builder.pushStyle(text_style);
2426     builder.addText(text2, strlen(text2));
2427     builder.pop();
2428     builder.addText(text3, strlen(text3));
2429 
2430     auto paragraph = builder.Build();
2431     paragraph->layout(TestCanvasWidth);
2432     paragraph->paint(canvas.get(), 0, 0);
2433 
2434     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2435 
2436     REPORTER_ASSERT(reporter, impl->runs().size() == 3);
2437     REPORTER_ASSERT(reporter, impl->styles().size() == 3);
2438     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
2439     auto& line = impl->lines()[0];
2440     size_t index = 0;
2441     line.scanStyles(
2442         StyleType::kForeground,
2443         [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
2444             switch (index) {
2445                 case 0:
2446                     REPORTER_ASSERT(
2447                             reporter,
2448                             style.getFontStyle().slant() == SkFontStyle::kUpright_Slant);
2449                     break;
2450                 case 1:
2451                     REPORTER_ASSERT(reporter,
2452                                     style.getFontStyle().slant() == SkFontStyle::kItalic_Slant);
2453                     break;
2454                 case 2:
2455                     REPORTER_ASSERT(
2456                             reporter,
2457                             style.getFontStyle().slant() == SkFontStyle::kUpright_Slant);
2458                     break;
2459                 default:
2460                     REPORTER_ASSERT(reporter, false);
2461                     break;
2462             }
2463             ++index;
2464             return true;
2465         });
2466 }
2467 
UNIX_ONLY_TEST(SkParagraph_ChineseParagraph,reporter)2468 UNIX_ONLY_TEST(SkParagraph_ChineseParagraph, reporter) {
2469     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2470     if (!fontCollection->fontsFound()) return;
2471     TestCanvas canvas("SkParagraph_ChineseParagraph.png");
2472     const char* text =
2473             "左線読設重説切後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育"
2474             "図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得庭"
2475             "際輝求佐抗蒼提夜合逃表。注統天言件自謙雅載報紙喪。作画稿愛器灯女書利変探"
2476             "訃第金線朝開化建。子戦年帝励害表月幕株漠新期刊人秘。図的海力生禁挙保天戦"
2477             "聞条年所在口。";
2478     const size_t len = strlen(text);
2479 
2480     ParagraphStyle paragraph_style;
2481     paragraph_style.setMaxLines(14);
2482     paragraph_style.setTextAlign(TextAlign::kJustify);
2483     paragraph_style.turnHintingOff();
2484     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2485 
2486     auto decoration = (TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline |
2487                                        TextDecoration::kLineThrough);
2488 
2489     TextStyle text_style;
2490     text_style.setFontFamilies({SkString("Source Han Serif CN")});
2491     text_style.setFontSize(35);
2492     text_style.setColor(SK_ColorBLACK);
2493     text_style.setLetterSpacing(2);
2494     text_style.setHeight(1);
2495     text_style.setDecoration(decoration);
2496     text_style.setDecorationColor(SK_ColorBLACK);
2497     text_style.setDecorationStyle(TextDecorationStyle::kSolid);
2498     builder.pushStyle(text_style);
2499     builder.addText(text, len);
2500     builder.pop();
2501 
2502     auto paragraph = builder.Build();
2503     paragraph->layout(TestCanvasWidth - 100);
2504     paragraph->paint(canvas.get(), 0, 0);
2505 
2506     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
2507 
2508     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2509 
2510     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
2511     REPORTER_ASSERT(reporter, impl->lines().size() == 7);
2512     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
2513     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
2514 }
2515 
2516 // Checked: disabled for TxtLib
UNIX_ONLY_TEST(SkParagraph_ArabicParagraph,reporter)2517 UNIX_ONLY_TEST(SkParagraph_ArabicParagraph, reporter) {
2518     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2519     if (!fontCollection->fontsFound()) return;
2520     TestCanvas canvas("SkParagraph_ArabicParagraph.png");
2521     const char* text =
2522             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
2523             "بمباركة التقليدية قام عن. تصفح";
2524     const size_t len = strlen(text);
2525 
2526     ParagraphStyle paragraph_style;
2527     paragraph_style.setMaxLines(14);
2528     paragraph_style.setTextAlign(TextAlign::kJustify);
2529     paragraph_style.turnHintingOff();
2530     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2531 
2532     auto decoration = (TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline |
2533                                        TextDecoration::kLineThrough);
2534 
2535     TextStyle text_style;
2536     text_style.setFontFamilies({SkString("Katibeh")});
2537     text_style.setFontSize(35);
2538     text_style.setColor(SK_ColorBLACK);
2539     text_style.setLetterSpacing(2);
2540     text_style.setDecoration(decoration);
2541     text_style.setDecorationColor(SK_ColorBLACK);
2542     text_style.setDecorationStyle(TextDecorationStyle::kSolid);
2543     builder.pushStyle(text_style);
2544     builder.addText(text, len);
2545     builder.pop();
2546 
2547     auto paragraph = builder.Build();
2548     paragraph->layout(TestCanvasWidth - 100);
2549     paragraph->paint(canvas.get(), 0, 0);
2550 
2551     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
2552 
2553     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2554 
2555     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
2556     REPORTER_ASSERT(reporter, impl->lines().size() == 2);
2557     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
2558     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
2559 }
2560 
2561 // Checked: DIFF (2 boxes and each space is a word)
UNIX_ONLY_TEST(SkParagraph_ArabicRectsParagraph,reporter)2562 UNIX_ONLY_TEST(SkParagraph_ArabicRectsParagraph, reporter) {
2563 
2564     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2565     if (!fontCollection->fontsFound()) return;
2566     TestCanvas canvas("SkParagraph_ArabicRectsParagraph.png");
2567     const char* text = "بمباركة التقليدية قام عن. تصفح يد    ";
2568     const size_t len = strlen(text);
2569 
2570     ParagraphStyle paragraph_style;
2571     paragraph_style.turnHintingOff();
2572     paragraph_style.setMaxLines(14);
2573     paragraph_style.setTextAlign(TextAlign::kRight);
2574     paragraph_style.setTextDirection(TextDirection::kRtl);
2575     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2576 
2577     TextStyle text_style;
2578     text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
2579     text_style.setFontSize(26);
2580     text_style.setWordSpacing(5);
2581     text_style.setColor(SK_ColorBLACK);
2582     text_style.setDecoration(TextDecoration::kUnderline);
2583     text_style.setDecorationColor(SK_ColorBLACK);
2584     builder.pushStyle(text_style);
2585     builder.addText(text, len);
2586     builder.pop();
2587 
2588     auto paragraph = builder.Build();
2589     paragraph->layout(TestCanvasWidth - 100);
2590 
2591     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2592     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
2593 
2594     paragraph->paint(canvas.get(), 0, 0);
2595 
2596     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2597     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2598     std::vector<TextBox> boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
2599     canvas.drawRects(SK_ColorRED, boxes);
2600 
2601     REPORTER_ASSERT(reporter, boxes.size() == 1ull);
2602 
2603     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 538.548f, EPSILON100));  // DIFF: 510.09375
2604     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.268f, EPSILON100));
2605     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(),  900, EPSILON100));
2606     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
2607 }
2608 
2609 // Checked DIFF+
2610 // This test shows now 2 boxes for [36:40) range:
2611 // [36:38) for arabic text and [38:39) for the last space
2612 // that has default paragraph direction (LTR) and is placed at the end of the paragraph
UNIX_ONLY_TEST(SkParagraph_ArabicRectsLTRLeftAlignParagraph,reporter)2613 UNIX_ONLY_TEST(SkParagraph_ArabicRectsLTRLeftAlignParagraph, reporter) {
2614 
2615     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2616     if (!fontCollection->fontsFound()) return;
2617     TestCanvas canvas("SkParagraph_ArabicRectsLTRLeftAlignParagraph.png");
2618     const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
2619     const size_t len = strlen(text);
2620 
2621     ParagraphStyle paragraph_style;
2622     paragraph_style.turnHintingOff();
2623     paragraph_style.setMaxLines(14);
2624     paragraph_style.setTextAlign(TextAlign::kLeft);
2625     paragraph_style.setTextDirection(TextDirection::kLtr);
2626     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2627 
2628     TextStyle text_style;
2629     text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
2630     text_style.setFontSize(26);
2631     text_style.setWordSpacing(5);
2632     text_style.setColor(SK_ColorBLACK);
2633     text_style.setDecoration(TextDecoration::kUnderline);
2634     text_style.setDecorationColor(SK_ColorBLACK);
2635     builder.pushStyle(text_style);
2636     builder.addText(text, len);
2637     builder.pop();
2638 
2639     auto paragraph = builder.Build();
2640     paragraph->layout(TestCanvasWidth - 100);
2641 
2642     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2643     REPORTER_ASSERT(reporter, impl->runs().size() == 3);
2644 
2645     paragraph->paint(canvas.get(), 0, 0);
2646 
2647     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2648     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2649     // There are 39 codepoints: [0:39); asking for [36:40) would give the same as for [36:39)
2650     std::vector<TextBox> boxes = paragraph->getRectsForRange(36, 40, rect_height_style, rect_width_style);
2651     canvas.drawRects(SK_ColorRED, boxes);
2652 
2653     REPORTER_ASSERT(reporter, boxes.size() == 2ull);
2654     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 83.92f, EPSILON100));  // DIFF: 89.40625
2655     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.27f, EPSILON100));
2656     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 105.16f, EPSILON100)); // DIFF: 121.87891
2657     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
2658 }
2659 
2660 // Checked DIFF+
UNIX_ONLY_TEST(SkParagraph_ArabicRectsLTRRightAlignParagraph,reporter)2661 UNIX_ONLY_TEST(SkParagraph_ArabicRectsLTRRightAlignParagraph, reporter) {
2662 
2663     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2664     if (!fontCollection->fontsFound()) return;
2665     TestCanvas canvas("SkParagraph_ArabicRectsLTRRightAlignParagraph.png");
2666     const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
2667     const size_t len = strlen(text);
2668 
2669     ParagraphStyle paragraph_style;
2670     paragraph_style.turnHintingOff();
2671     paragraph_style.setMaxLines(14);
2672     paragraph_style.setTextAlign(TextAlign::kRight);
2673     paragraph_style.setTextDirection(TextDirection::kLtr);
2674     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
2675 
2676     TextStyle text_style;
2677     text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
2678     text_style.setFontSize(26);
2679     text_style.setWordSpacing(5);
2680     text_style.setColor(SK_ColorBLACK);
2681     text_style.setDecoration(TextDecoration::kUnderline);
2682     text_style.setDecorationColor(SK_ColorBLACK);
2683     builder.pushStyle(text_style);
2684     builder.addText(text, len);
2685     builder.pop();
2686 
2687     auto paragraph = builder.Build();
2688     paragraph->layout(TestCanvasWidth - 100);
2689 
2690     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
2691     REPORTER_ASSERT(reporter, impl->runs().size() == 3);
2692 
2693     paragraph->paint(canvas.get(), 0, 0);
2694 
2695     RectHeightStyle rect_height_style = RectHeightStyle::kMax;
2696     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
2697     std::vector<TextBox> boxes =
2698             paragraph->getRectsForRange(36, 40, rect_height_style, rect_width_style);
2699     canvas.drawRects(SK_ColorRED, boxes);
2700 
2701     REPORTER_ASSERT(reporter, boxes.size() == 2ull); // DIFF
2702     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 561.5f, EPSILON100));         // DIFF
2703     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.27f, EPSILON100));
2704     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 582.74f, EPSILON100));       // DIFF
2705     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44, EPSILON100));
2706 }
2707 
UNIX_ONLY_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph,reporter)2708 UNIX_ONLY_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph, reporter) {
2709     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2710     if (!fontCollection->fontsFound()) return;
2711     TestCanvas canvas("SkParagraph_GetGlyphPositionAtCoordinateParagraph.png");
2712     const char* text =
2713             "12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
2714             "67890 12345";
2715     const size_t len = strlen(text);
2716 
2717     ParagraphStyle paragraphStyle;
2718     paragraphStyle.setTextAlign(TextAlign::kLeft);
2719     paragraphStyle.setMaxLines(10);
2720     paragraphStyle.turnHintingOff();
2721     TextStyle textStyle;
2722     textStyle.setFontFamilies({SkString("Roboto")});
2723     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
2724                                    SkFontStyle::kUpright_Slant));
2725     textStyle.setFontSize(50);
2726     textStyle.setLetterSpacing(1);
2727     textStyle.setWordSpacing(5);
2728     textStyle.setHeight(1);
2729     textStyle.setColor(SK_ColorBLACK);
2730 
2731     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
2732     builder.pushStyle(textStyle);
2733     builder.addText(text, len);
2734     builder.pop();
2735 
2736     auto paragraph = builder.Build();
2737     paragraph->layout(550);
2738     paragraph->paint(canvas.get(), 0, 0);
2739 
2740     // Tests for getGlyphPositionAtCoordinate()
2741     // NOTE: resulting values can be a few off from their respective positions in
2742     // the original text because the final trailing whitespaces are sometimes not
2743     // drawn (namely, when using "justify" alignment) and therefore are not active
2744     // glyphs.
2745     REPORTER_ASSERT(reporter,
2746                     paragraph->getGlyphPositionAtCoordinate(-10000, -10000).position == 0);
2747     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(-1, -1).position == 0);
2748     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(0, 0).position == 0);
2749     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(3, 3).position == 0);
2750     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(35, 1).position == 1);
2751     REPORTER_ASSERT(reporter,
2752                     paragraph->getGlyphPositionAtCoordinate(300, 2).position == 11);
2753     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(301, 2.2f).position == 11);
2754     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(302, 2.6f).position == 11);
2755     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(301, 2.1f).position == 11);
2756     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(100000, 20).position == 18);
2757     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(450, 20).position == 16);
2758     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(100000, 90).position == 36);
2759     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(-100000, 90).position == 18);
2760     REPORTER_ASSERT(reporter,
2761                     paragraph->getGlyphPositionAtCoordinate(20, -80).position == 1);
2762     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 90).position == 18);
2763     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 170).position == 36);
2764     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(10000, 180).position == 72);
2765     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(70, 180).position == 56);
2766     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(1, 270).position == 72);
2767     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(35, 90).position == 19);
2768     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(10000, 10000).position == 77);
2769     REPORTER_ASSERT(reporter, paragraph->getGlyphPositionAtCoordinate(85, 10000).position == 75);
2770 }
2771 
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeParagraph,reporter)2772 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeParagraph, reporter) {
2773     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2774     if (!fontCollection->fontsFound()) return;
2775     TestCanvas canvas("SkParagraph_GetRectsForRangeParagraph.png");
2776     const char* text =
2777             "12345,  \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
2778             "67890 12345";
2779     const size_t len = strlen(text);
2780 
2781     ParagraphStyle paragraphStyle;
2782     paragraphStyle.setTextAlign(TextAlign::kLeft);
2783     paragraphStyle.setMaxLines(10);
2784     paragraphStyle.turnHintingOff();
2785     TextStyle textStyle;
2786     textStyle.setFontFamilies({SkString("Roboto")});
2787     textStyle.setFontSize(50);
2788     textStyle.setColor(SK_ColorBLACK);
2789     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
2790                                        SkFontStyle::kUpright_Slant));
2791 
2792     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
2793     builder.pushStyle(textStyle);
2794     builder.addText(text, len);
2795     builder.pop();
2796 
2797     auto paragraph = builder.Build();
2798     paragraph->layout(550);
2799     paragraph->paint(canvas.get(), 0, 0);
2800 
2801     RectHeightStyle heightStyle = RectHeightStyle::kMax;
2802     RectWidthStyle widthStyle = RectWidthStyle::kTight;
2803 
2804     SkPaint paint;
2805     paint.setStyle(SkPaint::kStroke_Style);
2806     paint.setAntiAlias(true);
2807     paint.setStrokeWidth(1);
2808 
2809     {
2810         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
2811         REPORTER_ASSERT(reporter, result.empty());
2812     }
2813     {
2814         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
2815         canvas.drawRects(SK_ColorRED, result);
2816         REPORTER_ASSERT(reporter, result.size() == 1);
2817         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
2818         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
2819         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 28.417f, EPSILON100));
2820         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
2821     }
2822     {
2823         auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
2824         canvas.drawRects(SK_ColorBLUE, result);
2825         REPORTER_ASSERT(reporter, result.size() == 1);
2826         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 56.835f, EPSILON100));
2827         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
2828         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 177.97f, EPSILON100));
2829         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
2830     }
2831     {
2832         auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
2833         canvas.drawRects(SK_ColorGREEN, result);
2834         REPORTER_ASSERT(reporter, result.size() == 1);
2835         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 177.97f, EPSILON100));
2836         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
2837         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 507.031f, EPSILON100));
2838         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
2839     }
2840     {
2841         auto result = paragraph->getRectsForRange(30, 100, heightStyle, widthStyle);
2842         canvas.drawRects(SK_ColorRED, result);
2843         REPORTER_ASSERT(reporter, result.size() == 4);
2844         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 211.375f, EPSILON100));
2845         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, EPSILON100));
2846         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 463.623f, EPSILON100));
2847         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, EPSILON100));
2848         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 0, EPSILON100));
2849         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 236.406f, EPSILON100));
2850         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 142.089f, EPSILON100));
2851         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 295, EPSILON100));
2852     }
2853     {
2854         auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
2855         canvas.drawRects(SK_ColorBLUE, result);
2856         REPORTER_ASSERT(reporter, result.size() == 1);
2857         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 450.1875f, EPSILON20));
2858         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
2859         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 519.47266f, EPSILON20));
2860         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
2861     }
2862     {
2863         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
2864         REPORTER_ASSERT(reporter, result.empty());
2865     }
2866 }
2867 
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeTight,reporter)2868 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeTight, reporter) {
2869     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2870     if (!fontCollection->fontsFound()) return;
2871     TestCanvas canvas("SkParagraph_GetRectsForRangeTight.png");
2872     const char* text =
2873             "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
2874             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
2875             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
2876     const size_t len = strlen(text);
2877 /*
2878 ( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)
2879     S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S   S    S     S   S
2880  G  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GGG  G G  G  G  G  GG
2881  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W                  W
2882 
2883  */
2884     ParagraphStyle paragraphStyle;
2885     paragraphStyle.setTextAlign(TextAlign::kLeft);
2886     paragraphStyle.setMaxLines(10);
2887     paragraphStyle.turnHintingOff();
2888     TextStyle textStyle;
2889     textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
2890     textStyle.setFontSize(50);
2891     textStyle.setColor(SK_ColorBLACK);
2892     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
2893                                        SkFontStyle::kUpright_Slant));
2894 
2895     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
2896     builder.pushStyle(textStyle);
2897     builder.addText(text, len);
2898     builder.pop();
2899 
2900     auto paragraph = builder.Build();
2901     paragraph->layout(550);
2902     paragraph->paint(canvas.get(), 0, 0);
2903 
2904     RectHeightStyle heightStyle = RectHeightStyle::kTight;
2905     RectWidthStyle widthStyle = RectWidthStyle::kTight;
2906     {
2907         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
2908         REPORTER_ASSERT(reporter, result.empty());
2909     }
2910     {
2911         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
2912         canvas.drawRects(SK_ColorRED, result);
2913         REPORTER_ASSERT(reporter, result.size() == 1);
2914         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
2915         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
2916         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 16.898f, EPSILON100));
2917         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
2918     }
2919     {
2920         auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
2921         canvas.drawRects(SK_ColorBLUE, result);
2922         REPORTER_ASSERT(reporter, result.size() == 1);
2923         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 66.899f, EPSILON100));
2924         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
2925         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 264.099f, EPSILON100));
2926         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
2927     }
2928     {
2929         auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
2930         canvas.drawRects(SK_ColorGREEN, result);
2931         REPORTER_ASSERT(reporter, result.size() == 2);
2932         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 264.099f, EPSILON100));
2933         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0, EPSILON100));
2934         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 595.085f, EPSILON50));
2935         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 74, EPSILON100));
2936     }
2937 }
2938 
2939 // Checked: DIFF+
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle,reporter)2940 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle, reporter) {
2941     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
2942     if (!fontCollection->fontsFound()) return;
2943     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle.png");
2944     const char* text =
2945             "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
2946             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
2947             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
2948     const size_t len = strlen(text);
2949 
2950     ParagraphStyle paragraphStyle;
2951     paragraphStyle.setTextAlign(TextAlign::kLeft);
2952     paragraphStyle.setMaxLines(10);
2953     paragraphStyle.turnHintingOff();
2954     TextStyle textStyle;
2955     textStyle.setFontFamilies({SkString("Roboto")});
2956     textStyle.setFontSize(50);
2957     textStyle.setHeight(1.6f);
2958     textStyle.setHeightOverride(true);
2959     textStyle.setColor(SK_ColorBLACK);
2960     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
2961                                        SkFontStyle::kUpright_Slant));
2962 
2963     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
2964     builder.pushStyle(textStyle);
2965     builder.addText(text, len);
2966     builder.pop();
2967 
2968     auto paragraph = builder.Build();
2969     paragraph->layout(550);
2970     paragraph->paint(canvas.get(), 0, 0);
2971 
2972     RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingMiddle;
2973     RectWidthStyle widthStyle = RectWidthStyle::kMax;
2974     {
2975         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
2976         REPORTER_ASSERT(reporter, result.empty());
2977     }
2978 
2979     {
2980         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
2981         canvas.drawRects(SK_ColorRED, result);
2982         REPORTER_ASSERT(reporter, result.size() == 1);
2983         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
2984         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
2985         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.4296889f, EPSILON100));
2986         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
2987     }
2988     {
2989         auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
2990         canvas.drawRects(SK_ColorBLUE, result);
2991         REPORTER_ASSERT(reporter, result.size() == 1);
2992         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.429688f, EPSILON100));
2993         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
2994         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.00781f, EPSILON100));
2995         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
2996     }
2997     {
2998         auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
2999         canvas.drawRects(SK_ColorGREEN, result);
3000         REPORTER_ASSERT(reporter, result.size() == 1);
3001         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON20));
3002         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3003         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.0625f, EPSILON20));
3004         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
3005     }
3006     {
3007         auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
3008         canvas.drawRects(SK_ColorRED, result);
3009         REPORTER_ASSERT(reporter, result.size() == 8);
3010 
3011         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON20));
3012         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 88.473305f, EPSILON100));
3013         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
3014         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 168.47331f, EPSILON100));
3015 
3016         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
3017         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 88.473305f, EPSILON100));
3018         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
3019         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 168.47331f, EPSILON100));
3020 
3021         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON100));
3022         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 168.47331f, EPSILON100));
3023         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
3024         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 248.47331f, EPSILON100));
3025 
3026         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
3027         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 168.47331f, EPSILON100));
3028         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.02344f, EPSILON20));
3029         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 248.47331f, EPSILON100));
3030 
3031         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON100));
3032         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 248.47331f, EPSILON100));
3033         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.02344f, EPSILON20));
3034         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 328.47333f, EPSILON100));
3035 
3036         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON100));
3037         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 328.47333f, EPSILON100));
3038         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.02344f, EPSILON20));
3039         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 408.4733f, EPSILON100));
3040     }
3041     {
3042         auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
3043         canvas.drawRects(SK_ColorBLUE, result);
3044         REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
3045         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.72656f, EPSILON20));
3046         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3047         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.23047f, EPSILON20));
3048         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 88.473305f, EPSILON100));
3049 
3050         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.23047f, EPSILON20));
3051         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946615f, EPSILON100));
3052         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
3053         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 88.473305f, EPSILON100));
3054     }
3055     {
3056         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
3057         REPORTER_ASSERT(reporter, result.empty());
3058     }
3059 }
3060 
3061 // Checked: NO DIFF+
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingTop,reporter)3062 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingTop, reporter) {
3063     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3064     if (!fontCollection->fontsFound()) return;
3065     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingTop.png");
3066     const char* text =
3067             "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
3068             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
3069             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
3070     const size_t len = strlen(text);
3071 
3072     ParagraphStyle paragraphStyle;
3073     paragraphStyle.setTextAlign(TextAlign::kLeft);
3074     paragraphStyle.setMaxLines(10);
3075     paragraphStyle.turnHintingOff();
3076     TextStyle textStyle;
3077     textStyle.setFontFamilies({SkString("Roboto")});
3078     textStyle.setFontSize(50);
3079     textStyle.setHeight(1.6f);
3080     textStyle.setHeightOverride(true);
3081     textStyle.setColor(SK_ColorBLACK);
3082     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
3083                                        SkFontStyle::kUpright_Slant));
3084 
3085     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3086     builder.pushStyle(textStyle);
3087     builder.addText(text, len);
3088     builder.pop();
3089 
3090     auto paragraph = builder.Build();
3091     paragraph->layout(550);
3092     paragraph->paint(canvas.get(), 0, 0);
3093 
3094     RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingTop;
3095     RectWidthStyle widthStyle = RectWidthStyle::kMax;
3096     {
3097         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3098         REPORTER_ASSERT(reporter, result.empty());
3099     }
3100 
3101     {
3102         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3103         canvas.drawRects(SK_ColorRED, result);
3104         REPORTER_ASSERT(reporter, result.size() == 1);
3105         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
3106         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3107         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.4296889f, EPSILON100));
3108         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
3109     }
3110     {
3111         auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
3112         canvas.drawRects(SK_ColorBLUE, result);
3113         REPORTER_ASSERT(reporter, result.size() == 1);
3114         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.429688f, EPSILON100));
3115         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3116         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.00781f, EPSILON100));
3117         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
3118     }
3119     {
3120         auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
3121         canvas.drawRects(SK_ColorGREEN, result);
3122         REPORTER_ASSERT(reporter, result.size() == 1);
3123         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON100));
3124         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3125         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.0625f, EPSILON50));
3126         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
3127     }
3128     {
3129         auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
3130         canvas.drawRects(SK_ColorMAGENTA, result);
3131         REPORTER_ASSERT(reporter, result.size() == 8);
3132 
3133         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.00781f, EPSILON100));
3134         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 80, EPSILON100));
3135         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
3136         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 160, EPSILON100));
3137 
3138         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
3139         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 80, EPSILON100));
3140         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
3141         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 160, EPSILON100));
3142 
3143         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON100));
3144         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 160, EPSILON100));
3145         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
3146         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 240, EPSILON100));
3147 
3148         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
3149         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 160, EPSILON100));
3150         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.02344f, EPSILON20));
3151         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 240, EPSILON100));
3152 
3153         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON100));
3154         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 240, EPSILON100));
3155         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.02344f, EPSILON20));
3156         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 320, EPSILON100));
3157 
3158         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON100));
3159         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 320, EPSILON100));
3160         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.02344f, EPSILON20));
3161         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 400, EPSILON100));
3162     }
3163     {
3164         auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
3165         canvas.drawRects(SK_ColorBLACK, result);
3166         REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
3167         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.72656f, EPSILON20));
3168         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946615f, EPSILON100));
3169         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.23047f, EPSILON20));
3170         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 80, EPSILON100));
3171 
3172         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.23047f, EPSILON50));
3173         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946615f, EPSILON100));
3174         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.02344f, EPSILON20));
3175         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 80, EPSILON100));
3176     }
3177     {
3178         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
3179         REPORTER_ASSERT(reporter, result.empty());
3180     }
3181 }
3182 
3183 // Checked: NO DIFF+
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingBottom,reporter)3184 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingBottom, reporter) {
3185     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3186     if (!fontCollection->fontsFound()) return;
3187     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingBottom.png");
3188     const char* text =
3189             "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
3190             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
3191             " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
3192     const size_t len = strlen(text);
3193 
3194     ParagraphStyle paragraphStyle;
3195     paragraphStyle.setTextAlign(TextAlign::kLeft);
3196     paragraphStyle.setMaxLines(10);
3197     paragraphStyle.turnHintingOff();
3198     TextStyle textStyle;
3199     textStyle.setFontFamilies({SkString("Roboto")});
3200     textStyle.setFontSize(50);
3201     textStyle.setHeight(1.6f);
3202     textStyle.setHeightOverride(true);
3203     textStyle.setColor(SK_ColorBLACK);
3204     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
3205                                        SkFontStyle::kUpright_Slant));
3206 
3207     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3208     builder.pushStyle(textStyle);
3209     builder.addText(text, len);
3210     builder.pop();
3211 
3212     auto paragraph = builder.Build();
3213     paragraph->layout(550);
3214     paragraph->paint(canvas.get(), 0, 0);
3215 
3216     RectHeightStyle heightStyle = RectHeightStyle::kIncludeLineSpacingBottom;
3217     RectWidthStyle widthStyle = RectWidthStyle::kMax;
3218     {
3219         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3220         REPORTER_ASSERT(reporter, result.empty());
3221     }
3222 
3223     {
3224         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3225         canvas.drawRects(SK_ColorRED, result);
3226         REPORTER_ASSERT(reporter, result.size() == 1);
3227         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
3228         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
3229         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 17.429f, EPSILON100));
3230         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
3231     }
3232     {
3233         auto result = paragraph->getRectsForRange(2, 8, heightStyle, widthStyle);
3234         canvas.drawRects(SK_ColorBLUE, result);
3235         REPORTER_ASSERT(reporter, result.size() == 1);
3236         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 67.4298f, EPSILON100));
3237         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
3238         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 190.007f, EPSILON100));
3239         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
3240     }
3241     {
3242         auto result = paragraph->getRectsForRange(8, 21, heightStyle, widthStyle);
3243         canvas.drawRects(SK_ColorGREEN, result);
3244         REPORTER_ASSERT(reporter, result.size() == 1);
3245         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.007f, EPSILON100));
3246         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
3247         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 508.062f, EPSILON50));
3248         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
3249     }
3250     {
3251         auto result = paragraph->getRectsForRange(30, 150, heightStyle, widthStyle);
3252         canvas.drawRects(SK_ColorMAGENTA, result);
3253         REPORTER_ASSERT(reporter, result.size() == 8);
3254 
3255         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 190.007f, EPSILON20));
3256         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 96.946f, EPSILON100));
3257         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 525.687f, EPSILON20));
3258         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 176.946f, EPSILON100));
3259 
3260         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 525.687f, EPSILON20));
3261         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 96.946f, EPSILON100));
3262         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.023f, EPSILON20));
3263         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 176.946f, EPSILON100));
3264 
3265         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.left(), 0, EPSILON20));
3266         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.top(), 176.946f, EPSILON100));
3267         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.right(), 531.574f, EPSILON20));
3268         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[2].rect.bottom(), 256.946f, EPSILON100));
3269 
3270         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.left(), 531.574f, EPSILON20));
3271         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.top(), 176.946f, EPSILON100));
3272         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.right(), 570.023f, EPSILON20));
3273         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[3].rect.bottom(), 256.946f, EPSILON100));
3274 
3275         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.left(), 0, EPSILON20));
3276         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.top(), 256.946f, EPSILON100));
3277         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.right(), 570.023f, EPSILON20));
3278         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[4].rect.bottom(), 336.946f, EPSILON100));
3279 
3280         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.left(), 0, EPSILON20));
3281         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.top(), 336.946f, EPSILON100));
3282         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.right(), 570.023f, EPSILON20));
3283         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[5].rect.bottom(), 416.946f, EPSILON100));
3284     }
3285     {
3286         auto result = paragraph->getRectsForRange(19, 22, heightStyle, widthStyle);
3287         canvas.drawRects(SK_ColorBLACK, result);
3288         REPORTER_ASSERT(reporter, result.size() == 2); // DIFF
3289         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 463.726f, EPSILON20));
3290         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 16.946f, EPSILON100));
3291         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 530.230f, EPSILON20));
3292         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 96.946f, EPSILON100));
3293 
3294         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.left(), 530.230f, EPSILON20));
3295         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.top(), 16.946f, EPSILON100));
3296         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.right(), 570.023f, EPSILON20));
3297         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[1].rect.bottom(), 96.946f, EPSILON100));
3298     }
3299     {
3300         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
3301         REPORTER_ASSERT(reporter, result.empty());
3302     }
3303 }
3304 
3305 // This is the test I cannot accommodate
3306 // Any text range gets a smallest glyph rectangle
DEF_TEST_DISABLED(SkParagraph_GetRectsForRangeIncludeCombiningCharacter,reporter)3307 DEF_TEST_DISABLED(SkParagraph_GetRectsForRangeIncludeCombiningCharacter, reporter) {
3308     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3309     if (!fontCollection->fontsFound()) return;
3310     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeCombiningCharacter.png");
3311     const char* text = "ดีสวัสดีชาวโลกที่น่ารัก";
3312     const size_t len = strlen(text);
3313 
3314     ParagraphStyle paragraphStyle;
3315     paragraphStyle.setTextAlign(TextAlign::kLeft);
3316     paragraphStyle.setMaxLines(10);
3317     paragraphStyle.turnHintingOff();
3318     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3319 
3320     TextStyle textStyle;
3321     textStyle.setFontFamilies({SkString("Roboto")});
3322     textStyle.setFontSize(50);
3323     textStyle.setLetterSpacing(1);
3324     textStyle.setWordSpacing(5);
3325     textStyle.setHeight(1);
3326     textStyle.setColor(SK_ColorBLACK);
3327 
3328     builder.pushStyle(textStyle);
3329     builder.addText(text, len);
3330     builder.pop();
3331 
3332     auto paragraph = builder.Build();
3333     paragraph->layout(TestCanvasWidth - 100);
3334     paragraph->paint(canvas.get(), 0, 0);
3335 
3336     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3337     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
3338 
3339     RectHeightStyle heightStyle = RectHeightStyle::kTight;
3340     RectWidthStyle widthStyle = RectWidthStyle::kTight;
3341     {
3342         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3343         REPORTER_ASSERT(reporter, result.empty());
3344     }
3345     {
3346         auto first = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3347         auto second = paragraph->getRectsForRange(1, 2, heightStyle, widthStyle);
3348         auto last = paragraph->getRectsForRange(0, 2, heightStyle, widthStyle);
3349         REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 1 && last.size() == 1);
3350         REPORTER_ASSERT(reporter, second[0].rect == last[0].rect);
3351     }
3352     {
3353         auto first = paragraph->getRectsForRange(3, 4, heightStyle, widthStyle);
3354         auto second = paragraph->getRectsForRange(4, 5, heightStyle, widthStyle);
3355         auto last = paragraph->getRectsForRange(3, 5, heightStyle, widthStyle);
3356         REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 1 && last.size() == 1);
3357         REPORTER_ASSERT(reporter, second[0].rect == last[0].rect);
3358     }
3359     {
3360         auto first = paragraph->getRectsForRange(14, 15, heightStyle, widthStyle);
3361         auto second = paragraph->getRectsForRange(15, 16, heightStyle, widthStyle);
3362         auto third = paragraph->getRectsForRange(16, 17, heightStyle, widthStyle);
3363         auto last = paragraph->getRectsForRange(14, 17, heightStyle, widthStyle);
3364         REPORTER_ASSERT(reporter, first.size() == 0 && second.size() == 0 && third.size() == 1 && last.size() == 1);
3365         REPORTER_ASSERT(reporter, third[0].rect == last[0].rect);
3366     }
3367 }
3368 
3369 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterParagraph,reporter)3370 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterParagraph, reporter) {
3371     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3372     if (!fontCollection->fontsFound()) return;
3373     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraph.png");
3374     // Minikin uses a hard coded list of unicode characters that he treats as invisible - as spaces.
3375     // It's absolutely wrong - invisibility is a glyph attribute, not character/grapheme.
3376     // Any attempt to substitute one for another leads to errors
3377     // (for instance, some fonts can use these hard coded characters for something that is visible)
3378     const char* text = "01234    ";   // includes ideographic space and english space.
3379     const size_t len = strlen(text);
3380 
3381     ParagraphStyle paragraphStyle;
3382     paragraphStyle.setTextAlign(TextAlign::kCenter);
3383     paragraphStyle.setMaxLines(10);
3384     paragraphStyle.turnHintingOff();
3385     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3386 
3387     TextStyle textStyle;
3388     textStyle.setFontFamilies({SkString("Roboto")});
3389     textStyle.setFontSize(50);
3390     textStyle.setHeight(1);
3391     textStyle.setColor(SK_ColorBLACK);
3392     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
3393                                        SkFontStyle::kUpright_Slant));
3394 
3395     builder.pushStyle(textStyle);
3396     builder.addText(text, len);
3397     builder.pop();
3398 
3399     auto paragraph = builder.Build();
3400     paragraph->layout(550);
3401     paragraph->paint(canvas.get(), 0, 0);
3402 
3403     // Some of the formatting lazily done on paint
3404     RectHeightStyle heightStyle = RectHeightStyle::kMax;
3405     RectWidthStyle widthStyle = RectWidthStyle::kTight;
3406     {
3407         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3408         REPORTER_ASSERT(reporter, result.empty());
3409     }
3410 
3411     {
3412         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3413         canvas.drawRects(SK_ColorRED, result);
3414         REPORTER_ASSERT(reporter, result.size() == 1);
3415         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, EPSILON100));
3416         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3417         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, EPSILON100));
3418         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3419     }
3420 
3421     {
3422         auto result = paragraph->getRectsForRange(2, 4, heightStyle, widthStyle);
3423         canvas.drawRects(SK_ColorBLUE, result);
3424         REPORTER_ASSERT(reporter, result.size() == 1);
3425         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 260.791f, EPSILON100));
3426         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3427         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 317.626f, EPSILON100));
3428         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3429     }
3430 
3431     {
3432         auto result = paragraph->getRectsForRange(4, 5, heightStyle, widthStyle);
3433         canvas.drawRects(SK_ColorGREEN, result);
3434         REPORTER_ASSERT(reporter, result.size() == 1);
3435         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, EPSILON100));
3436         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3437         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 346.044f, EPSILON100));
3438         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3439     }
3440 
3441     {
3442         auto result = paragraph->getRectsForRange(4, 6, heightStyle, widthStyle);
3443         canvas.drawRects(SK_ColorBLACK, result);
3444         REPORTER_ASSERT(reporter, result.size() == 1); // DIFF
3445         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, EPSILON100));
3446         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3447         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, EPSILON100));
3448         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3449     }
3450 
3451     {
3452         auto result = paragraph->getRectsForRange(5, 6, heightStyle, widthStyle);
3453         canvas.drawRects(SK_ColorRED, result);
3454         REPORTER_ASSERT(reporter, result.size() == 1);
3455         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 346.044f, EPSILON100));
3456         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3457         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, EPSILON100));
3458         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3459     }
3460 
3461     {
3462         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
3463         REPORTER_ASSERT(reporter, result.empty());
3464     }
3465 }
3466 
3467 // Checked DIFF+
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered,reporter)3468 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered, reporter) {
3469     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3470     if (!fontCollection->fontsFound()) return;
3471     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered.png");
3472     const char* text = "01234\n";
3473     const size_t len = strlen(text);
3474 
3475     ParagraphStyle paragraphStyle;
3476     paragraphStyle.setTextAlign(TextAlign::kCenter);
3477     paragraphStyle.setMaxLines(10);
3478     paragraphStyle.turnHintingOff();
3479     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3480 
3481     TextStyle textStyle;
3482     textStyle.setFontFamilies({SkString("Roboto")});
3483     textStyle.setFontSize(50);
3484     textStyle.setHeight(1);
3485     textStyle.setColor(SK_ColorBLACK);
3486     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
3487                                        SkFontStyle::kUpright_Slant));
3488 
3489     builder.pushStyle(textStyle);
3490     builder.addText(text, len);
3491     builder.pop();
3492 
3493     auto paragraph = builder.Build();
3494     paragraph->layout(550);
3495 
3496     paragraph->paint(canvas.get(), 0, 0);
3497 
3498     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3499     REPORTER_ASSERT(reporter, impl->lines().size() == 2);
3500 
3501     RectHeightStyle heightStyle = RectHeightStyle::kMax;
3502     RectWidthStyle widthStyle = RectWidthStyle::kTight;
3503     {
3504         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3505         REPORTER_ASSERT(reporter, result.empty());
3506     }
3507 
3508     {
3509         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3510         canvas.drawRects(SK_ColorRED, result);
3511         REPORTER_ASSERT(reporter, result.size() == 1);
3512         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, EPSILON100));
3513         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, EPSILON100));
3514         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, EPSILON100));
3515         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, EPSILON100));
3516     }
3517 
3518     {
3519         auto result = paragraph->getRectsForRange(6, 7, heightStyle, widthStyle);
3520         canvas.drawRects(SK_ColorBLUE, result);
3521         REPORTER_ASSERT(reporter, result.size() == 1);
3522         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 275.0f, EPSILON100));
3523         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.406f, EPSILON100));
3524         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 275.0f, EPSILON100));
3525         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, EPSILON100));
3526     }
3527 }
3528 
3529 // Checked NO DIFF
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterMultiLineParagraph,reporter)3530 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeCenterMultiLineParagraph, reporter) {
3531     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3532     if (!fontCollection->fontsFound()) return;
3533     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterMultiLineParagraph.png");
3534     const char* text = "01234    \n0123         "; // includes ideographic space and english space.
3535     const size_t len = strlen(text);
3536 
3537     ParagraphStyle paragraphStyle;
3538     paragraphStyle.setTextAlign(TextAlign::kCenter);
3539     paragraphStyle.setMaxLines(10);
3540     paragraphStyle.turnHintingOff();
3541     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3542 
3543     TextStyle textStyle;
3544     textStyle.setFontFamilies({SkString("Roboto")});
3545     textStyle.setFontSize(50);
3546     textStyle.setHeight(1);
3547     textStyle.setColor(SK_ColorBLACK);
3548     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
3549                                        SkFontStyle::kUpright_Slant));
3550 
3551     builder.pushStyle(textStyle);
3552     builder.addText(text, len);
3553     builder.pop();
3554 
3555     auto paragraph = builder.Build();
3556     paragraph->layout(550);
3557 
3558     paragraph->paint(canvas.get(), 0, 0);
3559 
3560     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3561 
3562     REPORTER_ASSERT(reporter, impl->lines().size() == 2);
3563 
3564     RectHeightStyle heightStyle = RectHeightStyle::kMax;
3565     RectWidthStyle widthStyle = RectWidthStyle::kTight;
3566     SkScalar epsilon = 0.01f;
3567     {
3568         auto result = paragraph->getRectsForRange(0, 0, heightStyle, widthStyle);
3569         REPORTER_ASSERT(reporter, result.empty());
3570     }
3571     {
3572         auto result = paragraph->getRectsForRange(0, 1, heightStyle, widthStyle);
3573         canvas.drawRects(SK_ColorRED, result);
3574         REPORTER_ASSERT(reporter, result.size() == 1);
3575         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 203.955f, epsilon));
3576         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
3577         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 232.373f, epsilon));
3578         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
3579     }
3580     {
3581         auto result = paragraph->getRectsForRange(2, 4, heightStyle, widthStyle);
3582         canvas.drawRects(SK_ColorBLUE, result);
3583         REPORTER_ASSERT(reporter, result.size() == 1);
3584         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 260.791f, epsilon));
3585         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
3586         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 317.626f, epsilon));
3587         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
3588     }
3589     {
3590         auto result = paragraph->getRectsForRange(4, 6, heightStyle, widthStyle);
3591         canvas.drawRects(SK_ColorGREEN, result);
3592         REPORTER_ASSERT(reporter, result.size() == 1);
3593         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 317.626f, epsilon));
3594         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
3595         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, epsilon));
3596         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
3597     }
3598     {
3599         auto result = paragraph->getRectsForRange(5, 6, heightStyle, widthStyle);
3600         canvas.drawRects(SK_ColorYELLOW, result);
3601         REPORTER_ASSERT(reporter, result.size() == 1);
3602         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 346.044f, epsilon));
3603         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 0.40625f, epsilon));
3604         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 358.494f, epsilon));
3605         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 59, epsilon));
3606     }
3607     {
3608         auto result = paragraph->getRectsForRange(10, 12, heightStyle, widthStyle);
3609         canvas.drawRects(SK_ColorCYAN, result);
3610         REPORTER_ASSERT(reporter, result.size() == 1);
3611         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 218.164f, epsilon));
3612         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, epsilon));
3613         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 275, epsilon));
3614         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, epsilon));
3615     }
3616     {
3617         auto result = paragraph->getRectsForRange(14, 18, heightStyle, widthStyle);
3618         canvas.drawRects(SK_ColorBLACK, result);
3619         REPORTER_ASSERT(reporter, result.size() == 1);
3620         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 331.835f, epsilon));
3621         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 59.40625f, epsilon));
3622         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 419.189f, epsilon));
3623         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 118, epsilon));
3624     }
3625     {
3626         auto result = paragraph->getRectsForRange(21, 21, heightStyle, widthStyle);
3627         REPORTER_ASSERT(reporter, result.empty());
3628     }
3629 }
3630 
3631 // Checked: DIFF (line height rounding error)
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeStrut,reporter)3632 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeStrut, reporter) {
3633     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3634     if (!fontCollection->fontsFound()) return;
3635     TestCanvas canvas("SkParagraph_GetRectsForRangeStrut.png");
3636     const char* text = "Chinese 字典";
3637     const size_t len = strlen(text);
3638 
3639     StrutStyle strutStyle;
3640     strutStyle.setStrutEnabled(true);
3641     strutStyle.setFontFamilies({SkString("Roboto")});
3642     strutStyle.setFontSize(14.0);
3643 
3644     ParagraphStyle paragraphStyle;
3645     paragraphStyle.setStrutStyle(strutStyle);
3646 
3647     TextStyle textStyle;
3648     textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
3649     textStyle.setFontSize(20);
3650     textStyle.setColor(SK_ColorBLACK);
3651 
3652     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3653     builder.pushStyle(textStyle);
3654     builder.addText(text, len);
3655     builder.pop();
3656 
3657     auto paragraph = builder.Build();
3658     paragraph->layout(550);
3659     paragraph->paint(canvas.get(), 0, 0);
3660 
3661     {
3662         auto result = paragraph->getRectsForRange(0, 10, RectHeightStyle::kTight, RectWidthStyle::kMax);
3663         canvas.drawRects(SK_ColorGREEN, result);
3664         REPORTER_ASSERT(reporter, result.size() == 1);
3665     }
3666 
3667     {
3668         auto result = paragraph->getRectsForRange(0, 10, RectHeightStyle::kStrut, RectWidthStyle::kMax);
3669         canvas.drawRects(SK_ColorRED, result);
3670         REPORTER_ASSERT(reporter, result.size() == 1);
3671         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.left(), 0, EPSILON100));
3672         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.top(), 10.611f, EPSILON2));
3673         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.right(), 118.605f, EPSILON50));
3674         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(result[0].rect.bottom(), 27.017f, EPSILON2));
3675     }
3676 }
3677 
3678 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeStrutFallback,reporter)3679 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeStrutFallback, reporter) {
3680     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3681     if (!fontCollection->fontsFound()) return;
3682     TestCanvas canvas("SkParagraph_GetRectsForRangeStrutFallback.png");
3683     const char* text = "Chinese 字典";
3684     const size_t len = strlen(text);
3685 
3686     StrutStyle strutStyle;
3687     strutStyle.setStrutEnabled(false);
3688 
3689     ParagraphStyle paragraphStyle;
3690     paragraphStyle.setStrutStyle(strutStyle);
3691 
3692     TextStyle textStyle;
3693     textStyle.setFontFamilies({SkString("Noto Sans CJK JP")});
3694     textStyle.setFontSize(20);
3695     textStyle.setColor(SK_ColorBLACK);
3696 
3697     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3698     builder.pushStyle(textStyle);
3699     builder.addText(text, len);
3700     builder.pop();
3701 
3702     auto paragraph = builder.Build();
3703     paragraph->layout(550);
3704     paragraph->paint(canvas.get(), 0, 0);
3705 
3706 
3707     auto result1 = paragraph->getRectsForRange(0, 10, RectHeightStyle::kTight, RectWidthStyle::kMax);
3708     canvas.drawRects(SK_ColorGREEN, result1);
3709     REPORTER_ASSERT(reporter, result1.size() == 1);
3710 
3711     auto result2 = paragraph->getRectsForRange(0, 10, RectHeightStyle::kStrut, RectWidthStyle::kMax);
3712     canvas.drawRects(SK_ColorRED, result2);
3713     REPORTER_ASSERT(reporter, result2.size() == 1);
3714 
3715     REPORTER_ASSERT(reporter, result1[0].rect == result2[0].rect);
3716 }
3717 
3718 // Checked: DIFF (small in numbers)
UNIX_ONLY_TEST(SkParagraph_GetWordBoundaryParagraph,reporter)3719 UNIX_ONLY_TEST(SkParagraph_GetWordBoundaryParagraph, reporter) {
3720     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3721     if (!fontCollection->fontsFound()) return;
3722     TestCanvas canvas("SkParagraph_GetWordBoundaryParagraph.png");
3723     const char* text = "12345  67890 12345 67890 12345 67890 12345 "
3724                        "67890 12345 67890 12345 67890 12345";
3725     const size_t len = strlen(text);
3726     ParagraphStyle paragraphStyle;
3727     paragraphStyle.setTextAlign(TextAlign::kLeft);
3728     paragraphStyle.setMaxLines(10);
3729     paragraphStyle.turnHintingOff();
3730     TextStyle textStyle;
3731     textStyle.setFontFamilies({SkString("Roboto")});
3732     textStyle.setFontSize(52);
3733     textStyle.setLetterSpacing(1.19039f);
3734     textStyle.setWordSpacing(5);
3735     textStyle.setHeight(1.5);
3736     textStyle.setHeightOverride(true);
3737     textStyle.setColor(SK_ColorBLACK);
3738 
3739     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
3740     builder.pushStyle(textStyle);
3741     builder.addText(text, len);
3742     builder.pop();
3743 
3744     auto paragraph = builder.Build();
3745     paragraph->layout(550);
3746     paragraph->paint(canvas.get(), 0, 0);
3747 
3748     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(0) == SkRange<size_t>(0, 5));
3749     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(1) == SkRange<size_t>(0, 5));
3750     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(2) == SkRange<size_t>(0, 5));
3751     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(3) == SkRange<size_t>(0, 5));
3752     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(4) == SkRange<size_t>(0, 5));
3753     auto boxes = paragraph->getRectsForRange(5, 6, RectHeightStyle::kMax, RectWidthStyle::kTight);
3754     canvas.drawLines(SK_ColorRED, boxes);
3755 
3756     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(5) == SkRange<size_t>(5, 7));
3757     boxes = paragraph->getRectsForRange(6, 7, RectHeightStyle::kMax, RectWidthStyle::kTight);
3758     canvas.drawLines(SK_ColorRED, boxes);
3759 
3760     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(6) == SkRange<size_t>(5, 7));
3761     boxes = paragraph->getRectsForRange(7, 8, RectHeightStyle::kMax, RectWidthStyle::kTight);
3762     canvas.drawLines(SK_ColorRED, boxes);
3763 
3764     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(7) == SkRange<size_t>(7, 12));
3765     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(8) == SkRange<size_t>(7, 12));
3766     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(9) == SkRange<size_t>(7, 12));
3767     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(10) == SkRange<size_t>(7, 12));
3768     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(11) == SkRange<size_t>(7, 12));
3769     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(12) == SkRange<size_t>(12, 13));
3770     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(13) == SkRange<size_t>(13, 18));
3771     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(30) == SkRange<size_t>(30, 31));
3772 
3773     boxes = paragraph->getRectsForRange(12, 13, RectHeightStyle::kMax, RectWidthStyle::kTight);
3774     canvas.drawLines(SK_ColorRED, boxes);
3775     boxes = paragraph->getRectsForRange(13, 14, RectHeightStyle::kMax, RectWidthStyle::kTight);
3776     canvas.drawLines(SK_ColorRED, boxes);
3777     boxes = paragraph->getRectsForRange(18, 19, RectHeightStyle::kMax, RectWidthStyle::kTight);
3778     canvas.drawLines(SK_ColorRED, boxes);
3779     boxes = paragraph->getRectsForRange(19, 20, RectHeightStyle::kMax, RectWidthStyle::kTight);
3780     canvas.drawLines(SK_ColorRED, boxes);
3781     boxes = paragraph->getRectsForRange(24, 25, RectHeightStyle::kMax, RectWidthStyle::kTight);
3782     canvas.drawLines(SK_ColorRED, boxes);
3783     boxes = paragraph->getRectsForRange(25, 26, RectHeightStyle::kMax, RectWidthStyle::kTight);
3784     canvas.drawLines(SK_ColorRED, boxes);
3785     boxes = paragraph->getRectsForRange(30, 31, RectHeightStyle::kMax, RectWidthStyle::kTight);
3786     canvas.drawLines(SK_ColorRED, boxes);
3787     boxes = paragraph->getRectsForRange(31, 32, RectHeightStyle::kMax, RectWidthStyle::kTight);
3788     canvas.drawLines(SK_ColorRED, boxes);
3789 
3790     auto outLen = static_cast<ParagraphImpl*>(paragraph.get())->text().size();
3791     REPORTER_ASSERT(reporter, paragraph->getWordBoundary(outLen - 1) == SkRange<size_t>(outLen - 5, outLen));
3792 }
3793 
3794 // Checked: DIFF (unclear)
UNIX_ONLY_TEST(SkParagraph_SpacingParagraph,reporter)3795 UNIX_ONLY_TEST(SkParagraph_SpacingParagraph, reporter) {
3796     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3797     if (!fontCollection->fontsFound()) return;
3798     TestCanvas canvas("SkParagraph_SpacingParagraph.png");
3799     ParagraphStyle paragraph_style;
3800     paragraph_style.setMaxLines(10);
3801     paragraph_style.setTextAlign(TextAlign::kLeft);
3802     paragraph_style.turnHintingOff();
3803     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
3804 
3805     TextStyle text_style;
3806     text_style.setFontFamilies({SkString("Roboto")});
3807     text_style.setFontSize(50);
3808     text_style.setLetterSpacing(20);
3809     text_style.setWordSpacing(0);
3810     text_style.setColor(SK_ColorBLACK);
3811     builder.pushStyle(text_style);
3812     builder.addText("H", 1);
3813     builder.pop();
3814 
3815     text_style.setLetterSpacing(10);
3816     text_style.setWordSpacing(0);
3817     builder.pushStyle(text_style);
3818     builder.addText("H", 1);
3819     builder.pop();
3820 
3821     text_style.setLetterSpacing(20);
3822     text_style.setWordSpacing(0);
3823     builder.pushStyle(text_style);
3824     builder.addText("H", 1);
3825     builder.pop();
3826 
3827     text_style.setLetterSpacing(0);
3828     text_style.setWordSpacing(0);
3829     builder.pushStyle(text_style);
3830     builder.addText("|", 1);
3831     builder.pop();
3832 
3833     const char* hSpace = "H ";
3834     const size_t len = strlen(hSpace);
3835 
3836     text_style.setLetterSpacing(0);
3837     text_style.setWordSpacing(20);
3838     builder.pushStyle(text_style);
3839     builder.addText(hSpace, len);
3840     builder.pop();
3841 
3842     text_style.setLetterSpacing(0);
3843     text_style.setWordSpacing(0);
3844     builder.pushStyle(text_style);
3845     builder.addText(hSpace, len);
3846     builder.pop();
3847 
3848     text_style.setLetterSpacing(0);
3849     text_style.setLetterSpacing(0);
3850     text_style.setWordSpacing(20);
3851     builder.pushStyle(text_style);
3852     builder.addText(hSpace, len);
3853     builder.pop();
3854 
3855     auto paragraph = builder.Build();
3856     paragraph->layout(550);
3857     paragraph->paint(canvas.get(), 0, 0);
3858 
3859     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3860     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
3861     size_t index = 0;
3862     impl->lines().begin()->scanStyles(StyleType::kLetterSpacing,
3863        [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
3864           ++index;
3865           return true;
3866         });
3867     REPORTER_ASSERT(reporter, index == 4);
3868     index = 0;
3869     impl->lines().begin()->scanStyles(StyleType::kWordSpacing,
3870         [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
3871           ++index;
3872           return true;
3873         });
3874     REPORTER_ASSERT(reporter, index == 4);
3875 }
3876 
3877 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_LongWordParagraph,reporter)3878 UNIX_ONLY_TEST(SkParagraph_LongWordParagraph, reporter) {
3879     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3880     if (!fontCollection->fontsFound()) return;
3881     TestCanvas canvas("SkParagraph_LongWordParagraph.png");
3882     const char* text =
3883             "A "
3884             "veryverylongwordtoseewherethiswillwraporifitwillatallandifitdoesthenthat"
3885             "wouldbeagoodthingbecausethebreakingisworking.";
3886     const size_t len = strlen(text);
3887 
3888     ParagraphStyle paragraph_style;
3889     paragraph_style.turnHintingOff();
3890     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
3891 
3892     TextStyle text_style;
3893     text_style.setFontFamilies({SkString("Roboto")});
3894     text_style.setColor(SK_ColorRED);
3895     text_style.setFontSize(31);
3896     text_style.setLetterSpacing(0);
3897     text_style.setWordSpacing(0);
3898     text_style.setColor(SK_ColorBLACK);
3899     text_style.setHeight(1);
3900     builder.pushStyle(text_style);
3901     builder.addText(text, len);
3902     builder.pop();
3903 
3904     auto paragraph = builder.Build();
3905     paragraph->layout(TestCanvasWidth / 2);
3906     paragraph->paint(canvas.get(), 0, 0);
3907 
3908     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3909     REPORTER_ASSERT(reporter, impl->text().size() == std::string{text}.length());
3910     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
3911     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
3912     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
3913     REPORTER_ASSERT(reporter, impl->lines().size() == 4);
3914 
3915     REPORTER_ASSERT(reporter, impl->lines()[0].width() > TestCanvasWidth / 2 - 20);
3916     REPORTER_ASSERT(reporter, impl->lines()[1].width() > TestCanvasWidth / 2 - 20);
3917     REPORTER_ASSERT(reporter, impl->lines()[2].width() > TestCanvasWidth / 2 - 20);
3918 }
3919 
3920 // Checked: DIFF?
UNIX_ONLY_TEST(SkParagraph_KernScaleParagraph,reporter)3921 UNIX_ONLY_TEST(SkParagraph_KernScaleParagraph, reporter) {
3922     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3923     if (!fontCollection->fontsFound()) return;
3924     TestCanvas canvas("SkParagraph_KernScaleParagraph.png");
3925 
3926     const char* text1 = "AVAVAWAH A0 V0 VA To The Lo";
3927     const char* text2 = " Dialog Text List lots of words to see "
3928                         "if kerning works on a bigger set of characters AVAVAW";
3929     float scale = 3.0f;
3930     ParagraphStyle paragraph_style;
3931     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
3932     TextStyle text_style;
3933     text_style.setFontFamilies({SkString("Droid Serif")});
3934     text_style.setFontSize(100 / scale);
3935     text_style.setColor(SK_ColorBLACK);
3936 
3937     builder.pushStyle(text_style);
3938     builder.addText(text1, strlen(text1));
3939     builder.pushStyle(text_style);
3940     builder.addText("A", 1);
3941     builder.pushStyle(text_style);
3942     builder.addText("V", 1);
3943     text_style.setFontSize(14 / scale);
3944     builder.pushStyle(text_style);
3945     builder.addText(text2, strlen(text2));
3946     builder.pop();
3947 
3948     auto paragraph = builder.Build();
3949     paragraph->layout(TestCanvasWidth / scale);
3950     canvas.get()->scale(scale, scale);
3951     paragraph->paint(canvas.get(), 0, 0);
3952     canvas.get()->scale(1, 1);
3953 
3954     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3955 
3956     // First and second lines must have the same width, the third one must be bigger
3957     REPORTER_ASSERT(reporter, impl->lines().size() == 3);
3958     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].width(), 285.858f, EPSILON100));
3959     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[1].width(), 329.709f, EPSILON100));
3960     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[2].width(), 120.619f, EPSILON100));
3961     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[0].height(), 39.00f, EPSILON100));
3962     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[1].height(), 39.00f, EPSILON100));
3963     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->lines()[2].height(), 05.00f, EPSILON100));
3964 }
3965 
3966 // Checked: DIFF+
UNIX_ONLY_TEST(SkParagraph_NewlineParagraph,reporter)3967 UNIX_ONLY_TEST(SkParagraph_NewlineParagraph, reporter) {
3968     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
3969     if (!fontCollection->fontsFound()) return;
3970     TestCanvas canvas("SkParagraph_NewlineParagraph.png");
3971     const char* text =
3972             "line1\nline2 test1 test2 test3 test4 test5 test6 test7\nline3\n\nline4 "
3973             "test1 test2 test3 test4";
3974     const size_t len = strlen(text);
3975 
3976     ParagraphStyle paragraph_style;
3977     paragraph_style.turnHintingOff();
3978     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
3979 
3980     TextStyle text_style;
3981     text_style.setFontFamilies({SkString("Roboto")});
3982     text_style.setColor(SK_ColorRED);
3983     text_style.setFontSize(60);
3984     text_style.setColor(SK_ColorBLACK);
3985     text_style.setHeight(1);
3986     builder.pushStyle(text_style);
3987     builder.addText(text, len);
3988     builder.pop();
3989 
3990     auto paragraph = builder.Build();
3991     paragraph->layout(TestCanvasWidth - 300);
3992     paragraph->paint(canvas.get(), 0, 0);
3993 
3994     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
3995     // Minikin does not count empty lines but SkParagraph does
3996     REPORTER_ASSERT(reporter, impl->lines().size() == 7);
3997 
3998     REPORTER_ASSERT(reporter, impl->lines()[0].offset().fY == 0);
3999     REPORTER_ASSERT(reporter, impl->lines()[1].offset().fY == 70);
4000     REPORTER_ASSERT(reporter, impl->lines()[2].offset().fY == 140);
4001     REPORTER_ASSERT(reporter, impl->lines()[3].offset().fY == 210);
4002     REPORTER_ASSERT(reporter, impl->lines()[4].offset().fY == 280);  // Empty line
4003     REPORTER_ASSERT(reporter, impl->lines()[5].offset().fY == 350);
4004     REPORTER_ASSERT(reporter, impl->lines()[6].offset().fY == 420);
4005 }
4006 
4007 // TODO: Fix underline
UNIX_ONLY_TEST(SkParagraph_EmojiParagraph,reporter)4008 UNIX_ONLY_TEST(SkParagraph_EmojiParagraph, reporter) {
4009     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4010     if (!fontCollection->fontsFound()) return;
4011     TestCanvas canvas("SkParagraph_EmojiParagraph.png");
4012   const char* text =
4013       "����������������☺��������������������������������������‍����‍����‍♂️����‍��‍��‍��\
4014       ������☂��������������������������������������������������������\
4015       ❄����������������⚽��‍♀️������������⚓������������⏰��������������\
4016       ������❤������♠♣��❗������️‍��������������������������";
4017     const size_t len = strlen(text);
4018 
4019     ParagraphStyle paragraph_style;
4020     paragraph_style.turnHintingOff();
4021     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4022 
4023     TextStyle text_style;
4024     text_style.setFontFamilies({SkString("Noto Color Emoji")});
4025     text_style.setFontSize(50);
4026     text_style.setDecoration(TextDecoration::kUnderline);
4027     text_style.setColor(SK_ColorBLACK);
4028     builder.pushStyle(text_style);
4029     builder.addText(text, len);
4030     builder.pop();
4031 
4032     auto paragraph = builder.Build();
4033     paragraph->layout(TestCanvasWidth);
4034     paragraph->paint(canvas.get(), 0, 0);
4035 
4036     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
4037 
4038     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4039 
4040     REPORTER_ASSERT(reporter, impl->lines().size() == 8);
4041     for (auto& line : impl->lines()) {
4042         if (&line != impl->lines().end() - 1) {
4043             REPORTER_ASSERT(reporter, line.width() == 998.25f);
4044         } else {
4045             REPORTER_ASSERT(reporter, line.width() < 998.25f);
4046         }
4047         REPORTER_ASSERT(reporter, line.height() == 59);
4048     }
4049 }
4050 
4051 // Checked: DIFF+
UNIX_ONLY_TEST(SkParagraph_EmojiMultiLineRectsParagraph,reporter)4052 UNIX_ONLY_TEST(SkParagraph_EmojiMultiLineRectsParagraph, reporter) {
4053     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4054     if (!fontCollection->fontsFound()) return;
4055     TestCanvas canvas("SkParagraph_EmojiMultiLineRectsParagraph.png");
4056   const char* text =
4057       "��‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��i������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍������"
4058       "��‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍������"
4059       "��‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍������"
4060       "��‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍��������‍��‍����‍��‍��‍������"
4061       "❄����������������⚽��‍♀️������������⚓������������⏰��������������"
4062       "������❤������♠♣��❗������️‍��������������������������";
4063     const size_t len = strlen(text);
4064 
4065     ParagraphStyle paragraph_style;
4066     paragraph_style.turnHintingOff();
4067     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4068 
4069     TextStyle text_style;
4070     text_style.setFontFamilies({SkString("Noto Color Emoji")});
4071     text_style.setFontSize(50);
4072     text_style.setColor(SK_ColorBLACK);
4073     builder.pushStyle(text_style);
4074     builder.addText(text, len);
4075     builder.pop();
4076 
4077     auto paragraph = builder.Build();
4078     paragraph->layout(TestCanvasWidth - 300);
4079     paragraph->paint(canvas.get(), 0, 0);
4080 
4081     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4082     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4083 
4084     auto result = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4085     REPORTER_ASSERT(reporter, result.size() == 0);
4086 
4087     result = paragraph->getRectsForRange(0, 119, rect_height_style, rect_width_style);
4088     REPORTER_ASSERT(reporter, result.size() == 2);
4089     canvas.drawRects(SK_ColorRED, result);
4090 
4091     result = paragraph->getRectsForRange(122, 132, rect_height_style, rect_width_style);
4092     REPORTER_ASSERT(reporter, result.size() == 0);
4093     // We changed the selection algorithm and now the selection is empty
4094     canvas.drawRects(SK_ColorBLUE, result);
4095 
4096     auto pos = paragraph->getGlyphPositionAtCoordinate(610, 100).position;
4097     result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
4098     REPORTER_ASSERT(reporter, result.size() == 2);
4099     canvas.drawRects(SK_ColorGREEN, result);
4100 
4101     pos = paragraph->getGlyphPositionAtCoordinate(580, 100).position;
4102     result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
4103     REPORTER_ASSERT(reporter, result.size() == 2);
4104     canvas.drawRects(SK_ColorGREEN, result);
4105 
4106     pos = paragraph->getGlyphPositionAtCoordinate(560, 100).position;
4107     result = paragraph->getRectsForRange(0, pos, rect_height_style, rect_width_style);
4108     REPORTER_ASSERT(reporter, result.size() == 2);
4109     canvas.drawRects(SK_ColorGREEN, result);
4110 }
4111 
4112 // Checked: DIFF (line breaking)
UNIX_ONLY_TEST(SkParagraph_RepeatLayoutParagraph,reporter)4113 UNIX_ONLY_TEST(SkParagraph_RepeatLayoutParagraph, reporter) {
4114     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4115     if (!fontCollection->fontsFound()) return;
4116     TestCanvas canvas("SkParagraph_RepeatLayoutParagraph.png");
4117     const char* text =
4118             "Sentence to layout at diff widths to get diff line counts. short words "
4119             "short words short words short words short words short words short words "
4120             "short words short words short words short words short words short words "
4121             "end";
4122     const size_t len = strlen(text);
4123 
4124     ParagraphStyle paragraph_style;
4125     paragraph_style.turnHintingOff();
4126     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4127 
4128     TextStyle text_style;
4129     text_style.setFontFamilies({SkString("Roboto")});
4130     text_style.setFontSize(31);
4131     text_style.setColor(SK_ColorBLACK);
4132     builder.pushStyle(text_style);
4133     builder.addText(text, len);
4134     builder.pop();
4135 
4136     auto paragraph = builder.Build();
4137     paragraph->layout(300);
4138 
4139     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4140     // Some of the formatting lazily done on paint
4141     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
4142     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
4143     REPORTER_ASSERT(reporter, impl->lines().size() == 12);
4144 
4145     paragraph->layout(600);
4146     paragraph->paint(canvas.get(), 0, 0);
4147     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
4148     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
4149     REPORTER_ASSERT(reporter, impl->lines().size() == 6);
4150 }
4151 
4152 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_Ellipsize,reporter)4153 UNIX_ONLY_TEST(SkParagraph_Ellipsize, reporter) {
4154     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4155     if (!fontCollection->fontsFound()) return;
4156     TestCanvas canvas("SkParagraph_Ellipsize.png");
4157     const char* text =
4158             "This is a very long sentence to test if the text will properly wrap "
4159             "around and go to the next line. Sometimes, short sentence. Longer "
4160             "sentences are okay too because they are nessecary. Very short. ";
4161     const size_t len = strlen(text);
4162 
4163     ParagraphStyle paragraph_style;
4164     paragraph_style.setMaxLines(1);
4165     std::u16string ellipsis = u"\u2026";
4166     paragraph_style.setEllipsis(ellipsis);
4167     std::u16string e = paragraph_style.getEllipsisUtf16();
4168     paragraph_style.turnHintingOff();
4169     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4170 
4171     TextStyle text_style;
4172     text_style.setFontFamilies({SkString("Roboto")});
4173     text_style.setColor(SK_ColorBLACK);
4174     builder.pushStyle(text_style);
4175     builder.addText(text, len);
4176     builder.pop();
4177 
4178     auto paragraph = builder.Build();
4179     paragraph->layout(TestCanvasWidth);
4180     paragraph->paint(canvas.get(), 0, 0);
4181 
4182     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4183 
4184     // Check that the ellipsizer limited the text to one line and did not wrap to a second line.
4185     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
4186 
4187     auto& line = impl->lines()[0];
4188     REPORTER_ASSERT(reporter, line.ellipsis() != nullptr);
4189     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
4190 }
4191 
4192 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_UnderlineShiftParagraph,reporter)4193 UNIX_ONLY_TEST(SkParagraph_UnderlineShiftParagraph, reporter) {
4194     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4195     if (!fontCollection->fontsFound()) return;
4196     TestCanvas canvas("SkParagraph_UnderlineShiftParagraph.png");
4197     const char* text1 = "fluttser ";
4198     const char* text2 = "mdje";
4199     const char* text3 = "fluttser mdje";
4200 
4201     ParagraphStyle paragraph_style;
4202     paragraph_style.turnHintingOff();
4203     paragraph_style.setTextAlign(TextAlign::kLeft);
4204     paragraph_style.setMaxLines(2);
4205     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4206 
4207     TextStyle text_style;
4208     text_style.setFontFamilies({SkString("Roboto")});
4209     text_style.setColor(SK_ColorBLACK);
4210     builder.pushStyle(text_style);
4211     builder.addText(text1, strlen(text1));
4212     text_style.setDecoration(TextDecoration::kUnderline);
4213     text_style.setDecorationColor(SK_ColorBLACK);
4214     builder.pushStyle(text_style);
4215     builder.addText(text2, strlen(text2));
4216     builder.pop();
4217 
4218     auto paragraph = builder.Build();
4219     paragraph->layout(TestCanvasWidth);
4220     paragraph->paint(canvas.get(), 0, 0);
4221 
4222     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4223 
4224     TestParagraphBuilderImpl builder1(paragraph_style, fontCollection);
4225     text_style.setDecoration(TextDecoration::kNoDecoration);
4226     builder1.pushStyle(text_style);
4227     builder1.addText(text3, strlen(text3));
4228     builder1.pop();
4229 
4230     auto paragraph1 = builder1.Build();
4231     paragraph1->layout(TestCanvasWidth);
4232     paragraph1->paint(canvas.get(), 0, 25);
4233 
4234     auto impl1 = static_cast<ParagraphImpl*>(paragraph1.get());
4235 
4236     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
4237     REPORTER_ASSERT(reporter, impl1->lines().size() == 1);
4238 
4239     auto rect = paragraph->getRectsForRange(0, 12, RectHeightStyle::kMax, RectWidthStyle::kTight)
4240                         .front()
4241                         .rect;
4242     auto rect1 = paragraph1->getRectsForRange(0, 12, RectHeightStyle::kMax, RectWidthStyle::kTight)
4243                          .front()
4244                          .rect;
4245     REPORTER_ASSERT(reporter, rect.fLeft == rect1.fLeft);
4246     REPORTER_ASSERT(reporter, rect.fRight == rect1.fRight);
4247 
4248     for (size_t i = 0; i < 12; ++i) {
4249         // Not all ranges produce a rectangle ("fl" goes into one cluster so [0:1) is empty)
4250         auto r1 = paragraph->getRectsForRange(i, i + 1, RectHeightStyle::kMax, RectWidthStyle::kTight);
4251         auto r2 = paragraph1->getRectsForRange(i, i + 1, RectHeightStyle::kMax, RectWidthStyle::kTight);
4252 
4253         REPORTER_ASSERT(reporter, r1.size() == r2.size());
4254         if (!r1.empty() && !r2.empty()) {
4255             REPORTER_ASSERT(reporter, r1.front().rect.fLeft == r2.front().rect.fLeft);
4256             REPORTER_ASSERT(reporter, r1.front().rect.fRight == r2.front().rect.fRight);
4257         }
4258     }
4259 }
4260 
4261 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_SimpleShadow,reporter)4262 UNIX_ONLY_TEST(SkParagraph_SimpleShadow, reporter) {
4263     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4264     if (!fontCollection->fontsFound()) return;
4265     TestCanvas canvas("SkParagraph_SimpleShadow.png");
4266     const char* text = "Hello World Text Dialog";
4267     const size_t len = strlen(text);
4268 
4269     ParagraphStyle paragraph_style;
4270     paragraph_style.turnHintingOff();
4271     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4272 
4273     TextStyle text_style;
4274     text_style.setFontFamilies({SkString("Roboto")});
4275     text_style.setColor(SK_ColorBLACK);
4276     text_style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(2.0f, 2.0f), 1.0));
4277     builder.pushStyle(text_style);
4278     builder.addText(text, len);
4279 
4280     auto paragraph = builder.Build();
4281     paragraph->layout(TestCanvasWidth);
4282     paragraph->paint(canvas.get(), 10.0, 15.0);
4283 
4284     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4285 
4286     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
4287     REPORTER_ASSERT(reporter, impl->styles().size() == 1);
4288     size_t index = 0;
4289     for (auto& line : impl->lines()) {
4290         line.scanStyles(StyleType::kShadow,
4291            [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
4292                 REPORTER_ASSERT(reporter, index == 0 && style.equals(text_style));
4293                 ++index;
4294                 return true;
4295             });
4296     }
4297 }
4298 
4299 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_ComplexShadow,reporter)4300 UNIX_ONLY_TEST(SkParagraph_ComplexShadow, reporter) {
4301     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4302     if (!fontCollection->fontsFound()) return;
4303     TestCanvas canvas("SkParagraph_ComplexShadow.png");
4304     const char* text = "Text Chunk ";
4305     const size_t len = strlen(text);
4306 
4307     ParagraphStyle paragraph_style;
4308     paragraph_style.turnHintingOff();
4309     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4310 
4311     TextStyle text_style;
4312     text_style.setFontFamilies({SkString("Roboto")});
4313     text_style.setColor(SK_ColorBLACK);
4314     text_style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(2.0f, 2.0f), 1.0f));
4315     builder.pushStyle(text_style);
4316     builder.addText(text, len);
4317 
4318     text_style.addShadow(TextShadow(SK_ColorRED, SkPoint::Make(2.0f, 2.0f), 5.0f));
4319     text_style.addShadow(TextShadow(SK_ColorGREEN, SkPoint::Make(10.0f, -5.0f), 3.0f));
4320     builder.pushStyle(text_style);
4321     builder.addText(text, len);
4322     builder.pop();
4323 
4324     builder.addText(text, len);
4325 
4326     text_style.addShadow(TextShadow(SK_ColorRED, SkPoint::Make(0.0f, 1.0f), 0.0f));
4327     builder.pushStyle(text_style);
4328     builder.addText(text, len);
4329     builder.pop();
4330 
4331     builder.addText(text, len);
4332 
4333     auto paragraph = builder.Build();
4334     paragraph->layout(TestCanvasWidth);
4335     paragraph->paint(canvas.get(), 10.0, 15.0);
4336 
4337     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4338 
4339     size_t index = 0;
4340     for (auto& line : impl->lines()) {
4341         line.scanStyles(StyleType::kShadow,
4342            [&](TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
4343                 ++index;
4344                 switch (index) {
4345                     case 1:
4346                         REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
4347                         break;
4348                     case 2:
4349                         REPORTER_ASSERT(reporter, style.getShadowNumber() == 3);
4350                         break;
4351                     case 3:
4352                         REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
4353                         break;
4354                     case 4:
4355                         REPORTER_ASSERT(reporter, style.getShadowNumber() == 4);
4356                         REPORTER_ASSERT(reporter, style.equals(text_style));
4357                         break;
4358                     case 5:
4359                         REPORTER_ASSERT(reporter, style.getShadowNumber() == 1);
4360                         break;
4361                     default:
4362                         REPORTER_ASSERT(reporter, false);
4363                 }
4364                 return true;
4365             });
4366     }
4367 }
4368 
4369 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_BaselineParagraph,reporter)4370 UNIX_ONLY_TEST(SkParagraph_BaselineParagraph, reporter) {
4371     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4372     if (!fontCollection->fontsFound()) return;
4373     TestCanvas canvas("SkParagraph_BaselineParagraph.png");
4374     const char* text =
4375             "左線読設Byg後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育"
4376             "図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得";
4377     const size_t len = strlen(text);
4378 
4379     ParagraphStyle paragraph_style;
4380     paragraph_style.turnHintingOff();
4381     paragraph_style.setMaxLines(14);
4382     paragraph_style.setTextAlign(TextAlign::kJustify);
4383     paragraph_style.setHeight(1.5);
4384     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4385 
4386     TextStyle text_style;
4387     text_style.setFontFamilies({SkString("Source Han Serif CN")});
4388     text_style.setColor(SK_ColorBLACK);
4389     text_style.setFontSize(55);
4390     text_style.setLetterSpacing(2);
4391     text_style.setDecorationStyle(TextDecorationStyle::kSolid);
4392     text_style.setDecorationColor(SK_ColorBLACK);
4393     builder.pushStyle(text_style);
4394     builder.addText(text, len);
4395     builder.pop();
4396 
4397     auto paragraph = builder.Build();
4398     paragraph->layout(TestCanvasWidth - 100);
4399     paragraph->paint(canvas.get(), 0, 0);
4400 
4401     SkRect rect1 = SkRect::MakeXYWH(0, paragraph->getIdeographicBaseline(),
4402                                        paragraph->getMaxWidth(),
4403                                        paragraph->getIdeographicBaseline());
4404     SkRect rect2 = SkRect::MakeXYWH(0, paragraph->getAlphabeticBaseline(),
4405                                        paragraph->getMaxWidth(),
4406                                        paragraph->getAlphabeticBaseline());
4407     canvas.drawLine(SK_ColorRED, rect1, false);
4408     canvas.drawLine(SK_ColorGREEN, rect2, false);
4409 
4410     REPORTER_ASSERT(reporter,
4411                     SkScalarNearlyEqual(paragraph->getIdeographicBaseline(), 79.035f, EPSILON100));
4412     REPORTER_ASSERT(reporter,
4413                     SkScalarNearlyEqual(paragraph->getAlphabeticBaseline(), 63.305f, EPSILON100));
4414 }
4415 
4416 // Checked: NO DIFF (number of runs only)
UNIX_ONLY_TEST(SkParagraph_FontFallbackParagraph,reporter)4417 UNIX_ONLY_TEST(SkParagraph_FontFallbackParagraph, reporter) {
4418     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4419     if (!fontCollection->fontsFound()) return;
4420     TestCanvas canvas("SkParagraph_FontFallbackParagraph.png");
4421 
4422     const char* text1 = "Roboto 字典 ";         // Roboto + unresolved
4423     const char* text2 = "Homemade Apple 字典";  // Homemade Apple + Noto Sans...
4424     const char* text3 = "Chinese 字典";         // Homemade Apple + Source Han
4425 
4426     ParagraphStyle paragraph_style;
4427     paragraph_style.turnHintingOff();
4428     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4429 
4430     TextStyle text_style;
4431     text_style.setFontFamilies({
4432             SkString("Not a real font"),
4433             SkString("Also a fake font"),
4434             SkString("So fake it is obvious"),
4435             SkString("Next one should be a real font..."),
4436             SkString("Roboto"),
4437             SkString("another fake one in between"),
4438             SkString("Homemade Apple"),
4439     });
4440     text_style.setColor(SK_ColorBLACK);
4441     builder.pushStyle(text_style);
4442     builder.addText(text1, strlen(text1));
4443 
4444     text_style.setFontFamilies({
4445             SkString("Not a real font"),
4446             SkString("Also a fake font"),
4447             SkString("So fake it is obvious"),
4448             SkString("Homemade Apple"),
4449             SkString("Next one should be a real font..."),
4450             SkString("Roboto"),
4451             SkString("another fake one in between"),
4452             SkString("Noto Sans CJK JP"),
4453             SkString("Source Han Serif CN"),
4454     });
4455     builder.pushStyle(text_style);
4456     builder.addText(text2, strlen(text2));
4457 
4458     text_style.setFontFamilies({
4459             SkString("Not a real font"),
4460             SkString("Also a fake font"),
4461             SkString("So fake it is obvious"),
4462             SkString("Homemade Apple"),
4463             SkString("Next one should be a real font..."),
4464             SkString("Roboto"),
4465             SkString("another fake one in between"),
4466             SkString("Source Han Serif CN"),
4467             SkString("Noto Sans CJK JP"),
4468     });
4469     builder.pushStyle(text_style);
4470     builder.addText(text3, strlen(text3));
4471 
4472     builder.pop();
4473 
4474     auto paragraph = builder.Build();
4475     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == -1); // Not shaped yet
4476     paragraph->layout(TestCanvasWidth);
4477     paragraph->paint(canvas.get(), 10.0, 15.0);
4478 
4479     size_t spaceRun = 1;
4480     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 2); // From the text1 ("字典" - excluding the last space)
4481 
4482     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4483 
4484     REPORTER_ASSERT(reporter, impl->runs().size() == 6 + spaceRun);
4485 
4486     // Font resolution in Skia produces 6 runs because 2 parts of "Roboto 字典 " have different
4487     // script (Minikin merges the first 2 into one because of unresolved)
4488     // [Apple + Unresolved + ' '] 0, 1, 2
4489     // [Apple + Noto] 3, 4
4490     // [Apple + Han] 5, 6
4491     auto robotoAdvance = impl->runs()[0].advance().fX +
4492                          impl->runs()[1].advance().fX;
4493     robotoAdvance += impl->runs()[2].advance().fX;
4494 
4495     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(robotoAdvance, 64.199f, EPSILON50));
4496     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[2 + spaceRun].advance().fX, 139.125f, EPSILON100));
4497     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[3 + spaceRun].advance().fX, 27.999f, EPSILON100));
4498     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[4 + spaceRun].advance().fX, 62.248f, EPSILON100));
4499     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[5 + spaceRun].advance().fX, 27.999f, EPSILON100));
4500 
4501     // When a different font is resolved, then the metrics are different.
4502     REPORTER_ASSERT(reporter, impl->runs()[3 + spaceRun].correctAscent() != impl->runs()[5 + spaceRun].correctAscent());
4503     REPORTER_ASSERT(reporter, impl->runs()[3 + spaceRun].correctDescent() != impl->runs()[5 + spaceRun].correctDescent());
4504 }
4505 
4506 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_StrutParagraph1,reporter)4507 UNIX_ONLY_TEST(SkParagraph_StrutParagraph1, reporter) {
4508     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4509     if (!fontCollection->fontsFound()) return;
4510     TestCanvas canvas("SkParagraph_StrutParagraph1.png");
4511     // The chinese extra height should be absorbed by the strut.
4512     const char* text = "01234満毎冠p来É本可\nabcd\n満毎É行p昼本可";
4513     const size_t len = strlen(text);
4514 
4515     ParagraphStyle paragraph_style;
4516     paragraph_style.setMaxLines(10);
4517     paragraph_style.setTextAlign(TextAlign::kLeft);
4518     paragraph_style.turnHintingOff();
4519 
4520     StrutStyle strut_style;
4521     strut_style.setStrutEnabled(true);
4522     strut_style.setFontFamilies({SkString("BlahFake"), SkString("Ahem")});
4523     strut_style.setFontSize(50);
4524     strut_style.setHeight(1.8f);
4525     strut_style.setHeightOverride(true);
4526     strut_style.setLeading(0.1f);
4527     paragraph_style.setStrutStyle(strut_style);
4528 
4529     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4530 
4531     TextStyle text_style;
4532     text_style.setFontFamilies({SkString("Ahem")});
4533     text_style.setFontSize(50);
4534     text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
4535     text_style.setColor(SK_ColorBLACK);
4536     text_style.setHeight(0.5f);
4537     builder.pushStyle(text_style);
4538     builder.addText(text, len);
4539     builder.pop();
4540 
4541     auto paragraph = builder.Build();
4542     paragraph->layout(550);
4543     paragraph->paint(canvas.get(), 0, 0);
4544 
4545     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4546     REPORTER_ASSERT(reporter, impl->lines().size() == 4);
4547 
4548     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4549     RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
4550     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4551     {
4552         auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4553         REPORTER_ASSERT(reporter, boxes.empty());
4554     }
4555     {
4556         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
4557         canvas.drawRects(SK_ColorRED, boxes);
4558         REPORTER_ASSERT(reporter, boxes.size() == 1);
4559         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4560         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 34.5f, EPSILON100));
4561         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
4562         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 84.5f, EPSILON100));
4563     }
4564     {
4565         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
4566         canvas.drawRects(SK_ColorRED, boxes);
4567         REPORTER_ASSERT(reporter, boxes.size() == 1);
4568         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4569         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
4570         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
4571         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
4572     }
4573     {
4574         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
4575         canvas.drawRects(SK_ColorRED, boxes);
4576         REPORTER_ASSERT(reporter, boxes.size() == 1);
4577         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
4578         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 34.5f, EPSILON100));
4579         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
4580         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 84.5f, EPSILON100));
4581     }
4582     {
4583         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
4584         canvas.drawRects(SK_ColorRED, boxes);
4585         REPORTER_ASSERT(reporter, boxes.size() == 1);
4586         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
4587         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
4588         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
4589         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 95, EPSILON100));
4590     }
4591     {
4592         auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
4593         canvas.drawRects(SK_ColorRED, boxes);
4594         REPORTER_ASSERT(reporter, boxes.size() == 1);
4595         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4596         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 190, EPSILON100));
4597         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, EPSILON100));
4598         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 285, EPSILON100));
4599     }
4600     {
4601         auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
4602         canvas.drawRects(SK_ColorRED, boxes);
4603         REPORTER_ASSERT(reporter, boxes.size() == 1);
4604         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, EPSILON100));
4605         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 285, EPSILON100));
4606         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, EPSILON100));
4607         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 380, EPSILON100));
4608     }
4609 }
4610 
4611 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_StrutParagraph2,reporter)4612 UNIX_ONLY_TEST(SkParagraph_StrutParagraph2, reporter) {
4613     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4614     if (!fontCollection->fontsFound()) return;
4615     TestCanvas canvas("SkParagraph_StrutParagraph2.png");
4616     // The chinese extra height should be absorbed by the strut.
4617     const char* text = "01234ABCDEFGH\nabcd\nABCDEFGH";
4618     const size_t len = strlen(text);
4619 
4620     ParagraphStyle paragraph_style;
4621     paragraph_style.setMaxLines(10);
4622     paragraph_style.setTextAlign(TextAlign::kLeft);
4623     paragraph_style.turnHintingOff();
4624 
4625     StrutStyle strut_style;
4626 
4627     strut_style.setStrutEnabled(true);
4628     strut_style.setFontFamilies({SkString("Ahem")});
4629     strut_style.setFontSize(50);
4630     strut_style.setHeight(1.6f);
4631     strut_style.setHeightOverride(true);
4632     paragraph_style.setStrutStyle(strut_style);
4633 
4634     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4635 
4636     TextStyle text_style;
4637     text_style.setFontFamilies({SkString("Ahem")});
4638     text_style.setFontSize(50);
4639     text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
4640     SkFontStyle::kUpright_Slant));
4641     text_style.setColor(SK_ColorBLACK);
4642     text_style.setHeight(1);
4643     builder.pushStyle(text_style);
4644     builder.addText(text, len);
4645     builder.pop();
4646 
4647     auto paragraph = builder.Build();
4648     paragraph->layout(550);
4649     paragraph->paint(canvas.get(), 0, 0);
4650 
4651     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4652     // Font is not resolved and the first line does not fit
4653     REPORTER_ASSERT(reporter, impl->lines().size() == 4);
4654 
4655     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4656     RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
4657     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4658     {
4659         auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4660         REPORTER_ASSERT(reporter, boxes.empty());
4661     }
4662     {
4663         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
4664         canvas.drawRects(SK_ColorRED, boxes);
4665         REPORTER_ASSERT(reporter, boxes.size() == 1);
4666         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4667         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
4668         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
4669         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
4670     }
4671     {
4672         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
4673         canvas.drawRects(SK_ColorRED, boxes);
4674         REPORTER_ASSERT(reporter, boxes.size() == 1);
4675         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4676         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
4677         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, EPSILON100));
4678         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
4679     }
4680     {
4681         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
4682         canvas.drawRects(SK_ColorRED, boxes);
4683         REPORTER_ASSERT(reporter, boxes.size() == 1);
4684         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
4685         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
4686         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
4687         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
4688     }
4689     {
4690         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
4691         canvas.drawRects(SK_ColorRED, boxes);
4692         REPORTER_ASSERT(reporter, boxes.size() == 1);
4693         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, EPSILON100));
4694         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
4695         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, EPSILON100));
4696         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 80, EPSILON100));
4697     }
4698     {
4699         auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
4700         canvas.drawRects(SK_ColorRED, boxes);
4701         REPORTER_ASSERT(reporter, boxes.size() == 1);
4702         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4703         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 160, EPSILON100));
4704         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, EPSILON100));
4705         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 240, EPSILON100));
4706     }
4707     {
4708         auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
4709         canvas.drawRects(SK_ColorRED, boxes);
4710         REPORTER_ASSERT(reporter, boxes.size() == 1);
4711         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, EPSILON100));
4712         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 240, EPSILON100));
4713         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, EPSILON100));
4714         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 320, EPSILON100));
4715     }
4716 }
4717 
4718 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_StrutParagraph3,reporter)4719 UNIX_ONLY_TEST(SkParagraph_StrutParagraph3, reporter) {
4720     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4721     if (!fontCollection->fontsFound()) return;
4722     TestCanvas canvas("SkParagraph_StrutParagraph3.png");
4723 
4724     // The chinese extra height should be absorbed by the strut.
4725     const char* text = "01234満毎p行来昼本可\nabcd\n満毎冠行来昼本可";
4726     const size_t len = strlen(text);
4727 
4728     ParagraphStyle paragraph_style;
4729     paragraph_style.setMaxLines(10);
4730     paragraph_style.setTextAlign(TextAlign::kLeft);
4731     paragraph_style.turnHintingOff();
4732 
4733     StrutStyle strut_style;
4734     strut_style.setStrutEnabled(true);
4735     strut_style.setFontFamilies({SkString("Ahem")});
4736     strut_style.setFontSize(50);
4737     strut_style.setHeight(1.2f);
4738     strut_style.setHeightOverride(true);
4739     paragraph_style.setStrutStyle(strut_style);
4740 
4741     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4742 
4743     TextStyle text_style;
4744     text_style.setFontFamilies({SkString("Ahem")});
4745     text_style.setFontSize(50);
4746     text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
4747     SkFontStyle::kUpright_Slant));
4748     text_style.setColor(SK_ColorBLACK);
4749     text_style.setHeight(1);
4750     builder.pushStyle(text_style);
4751     builder.addText(text, len);
4752     builder.pop();
4753 
4754     auto paragraph = builder.Build();
4755     paragraph->layout(550);
4756     paragraph->paint(canvas.get(), 0, 0);
4757 
4758     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4759     // Font is not resolved and the first line does not fit
4760     REPORTER_ASSERT(reporter, impl->lines().size() == 4);
4761 
4762     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4763     RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
4764     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4765     SkScalar epsilon = 0.001f;
4766     {
4767         auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4768         REPORTER_ASSERT(reporter, boxes.empty());
4769     }
4770     {
4771         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
4772         canvas.drawRects(SK_ColorRED, boxes);
4773         REPORTER_ASSERT(reporter, boxes.size() == 1);
4774         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
4775         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 8, epsilon));
4776         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, epsilon));
4777         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 58, epsilon));
4778     }
4779     {
4780         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
4781         canvas.drawRects(SK_ColorRED, boxes);
4782         REPORTER_ASSERT(reporter, boxes.size() == 1);
4783         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
4784         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, epsilon));
4785         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 50, epsilon));
4786         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
4787     }
4788     {
4789         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
4790         canvas.drawRects(SK_ColorRED, boxes);
4791         REPORTER_ASSERT(reporter, boxes.size() == 1);
4792         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, epsilon));
4793         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 8, epsilon));
4794         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, epsilon));
4795         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 58, epsilon));
4796     }
4797     {
4798         auto boxes = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
4799         canvas.drawRects(SK_ColorRED, boxes);
4800         REPORTER_ASSERT(reporter, boxes.size() == 1);
4801         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 300, epsilon));
4802         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, epsilon));
4803         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 500, epsilon));
4804         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 60, epsilon));
4805     }
4806     {
4807         auto boxes = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
4808         canvas.drawRects(SK_ColorRED, boxes);
4809         REPORTER_ASSERT(reporter, boxes.size() == 1);
4810         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, epsilon));
4811         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 120, epsilon));
4812         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 100, epsilon));
4813         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 180, epsilon));
4814     }
4815     {
4816         auto boxes = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
4817         canvas.drawRects(SK_ColorRED, boxes);
4818         REPORTER_ASSERT(reporter, boxes.size() == 1);
4819         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 50, epsilon));
4820         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 180, epsilon));
4821         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 300, epsilon));
4822         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 240, epsilon));
4823     }
4824 }
4825 
4826 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_StrutForceParagraph,reporter)4827 UNIX_ONLY_TEST(SkParagraph_StrutForceParagraph, reporter) {
4828     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4829     if (!fontCollection->fontsFound()) return;
4830     TestCanvas canvas("SkParagraph_StrutForceParagraph.png");
4831     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
4832     const size_t len = strlen(text);
4833 
4834     ParagraphStyle paragraph_style;
4835     paragraph_style.setMaxLines(10);
4836     paragraph_style.setTextAlign(TextAlign::kLeft);
4837     paragraph_style.turnHintingOff();
4838 
4839     StrutStyle strut_style;
4840     strut_style.setStrutEnabled(true);
4841     strut_style.setFontFamilies({SkString("Ahem")});
4842     strut_style.setFontSize(50);
4843     strut_style.setHeight(1.5f);
4844     strut_style.setHeightOverride(true);
4845     strut_style.setLeading(0.1f);
4846     strut_style.setForceStrutHeight(true);
4847     paragraph_style.setStrutStyle(strut_style);
4848 
4849     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4850 
4851     TextStyle text_style;
4852     text_style.setFontFamilies({SkString("Ahem")});
4853     text_style.setFontSize(50);
4854     text_style.setLetterSpacing(0);
4855     text_style.setColor(SK_ColorBLACK);
4856     text_style.setHeight(1);
4857     builder.pushStyle(text_style);
4858     builder.addText(text, len);
4859     builder.pop();
4860 
4861     auto paragraph = builder.Build();
4862     paragraph->layout(550);
4863     paragraph->paint(canvas.get(), 0, 0);
4864 
4865     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
4866     // Font is not resolved and the first line does not fit
4867     REPORTER_ASSERT(reporter, impl->lines().size() == 4);
4868 
4869     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4870     RectHeightStyle rect_height_max_style = RectHeightStyle::kMax;
4871     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4872 
4873     auto boxes1 = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4874     REPORTER_ASSERT(reporter, boxes1.empty());
4875 
4876     auto boxes2 = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
4877     canvas.drawRects(SK_ColorRED, boxes2);
4878     REPORTER_ASSERT(reporter, boxes2.size() == 1);
4879     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.left(), 0, EPSILON100));
4880     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.top(), 22.5f, EPSILON100));
4881     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.right(), 50, EPSILON100));
4882     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes2[0].rect.bottom(), 72.5f, EPSILON100));
4883 
4884     auto boxes3 = paragraph->getRectsForRange(0, 1, rect_height_max_style, rect_width_style);
4885     canvas.drawRects(SK_ColorRED, boxes3);
4886     REPORTER_ASSERT(reporter, boxes3.size() == 1);
4887     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.left(), 0, EPSILON100));
4888     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.top(), 0, EPSILON100));
4889     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.right(), 50, EPSILON100));
4890     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes3[0].rect.bottom(), 80, EPSILON100));
4891 
4892     auto boxes4 = paragraph->getRectsForRange(6, 10, rect_height_style, rect_width_style);
4893     canvas.drawRects(SK_ColorRED, boxes4);
4894     REPORTER_ASSERT(reporter, boxes4.size() == 1);
4895     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.left(), 300, EPSILON100));
4896     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.top(), 22.5f, EPSILON100));
4897     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.right(), 500, EPSILON100));
4898     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes4[0].rect.bottom(), 72.5f, EPSILON100));
4899 
4900     auto boxes5 = paragraph->getRectsForRange(6, 10, rect_height_max_style, rect_width_style);
4901     canvas.drawRects(SK_ColorRED, boxes5);
4902     REPORTER_ASSERT(reporter, boxes5.size() == 1);
4903     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.left(), 300, EPSILON100));
4904     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.top(), 0, EPSILON100));
4905     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.right(), 500, EPSILON100));
4906     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes5[0].rect.bottom(), 80, EPSILON100));
4907 
4908     auto boxes6 = paragraph->getRectsForRange(14, 16, rect_height_max_style, rect_width_style);
4909     canvas.drawRects(SK_ColorRED, boxes6);
4910     REPORTER_ASSERT(reporter, boxes6.size() == 1);
4911     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.left(), 0, EPSILON100));
4912     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.top(), 160, EPSILON100));
4913     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.right(), 100, EPSILON100));
4914     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes6[0].rect.bottom(), 240, EPSILON100));
4915 
4916     auto boxes7 = paragraph->getRectsForRange(20, 25, rect_height_max_style, rect_width_style);
4917     canvas.drawRects(SK_ColorRED, boxes7);
4918     REPORTER_ASSERT(reporter, boxes7.size() == 1);
4919     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.left(), 50, EPSILON100));
4920     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.top(), 240, EPSILON100));
4921     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.right(), 300, EPSILON100));
4922     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes7[0].rect.bottom(), 320, EPSILON100));
4923 }
4924 
4925 // Checked: NO DIFF
UNIX_ONLY_TEST(SkParagraph_StrutDefaultParagraph,reporter)4926 UNIX_ONLY_TEST(SkParagraph_StrutDefaultParagraph, reporter) {
4927     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4928     if (!fontCollection->fontsFound()) return;
4929     TestCanvas canvas("SkParagraph_StrutDefaultParagraph.png");
4930 
4931     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
4932     const size_t len = strlen(text);
4933 
4934     ParagraphStyle paragraph_style;
4935     paragraph_style.setMaxLines(10);
4936     paragraph_style.setTextAlign(TextAlign::kLeft);
4937     paragraph_style.turnHintingOff();
4938 
4939     StrutStyle strut_style;
4940     strut_style.setStrutEnabled(true);
4941     strut_style.setFontFamilies({SkString("Ahem")});
4942     strut_style.setFontSize(50);
4943     strut_style.setHeight(1.5f);
4944     strut_style.setLeading(0.1f);
4945     strut_style.setForceStrutHeight(false);
4946     paragraph_style.setStrutStyle(strut_style);
4947 
4948     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4949 
4950     TextStyle text_style;
4951     text_style.setFontFamilies({SkString("Ahem")});
4952     text_style.setFontSize(20);
4953     text_style.setColor(SK_ColorBLACK);
4954     builder.pushStyle(text_style);
4955     builder.addText(text, len);
4956     builder.pop();
4957 
4958     auto paragraph = builder.Build();
4959     paragraph->layout(550);
4960     paragraph->paint(canvas.get(), 0, 0);
4961 
4962     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
4963     RectHeightStyle rect_height_strut_style = RectHeightStyle::kStrut;
4964     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
4965     {
4966         auto boxes = paragraph->getRectsForRange(0, 0, rect_height_style, rect_width_style);
4967         REPORTER_ASSERT(reporter, boxes.empty());
4968     }
4969     {
4970         auto boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
4971         canvas.drawRects(SK_ColorRED, boxes);
4972         REPORTER_ASSERT(reporter, boxes.size() == 1);
4973         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4974         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 26.5f, EPSILON100));
4975         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 20, EPSILON100));
4976         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 46.5f, EPSILON100));
4977     }
4978     {
4979         auto boxes = paragraph->getRectsForRange(0, 2, rect_height_strut_style, rect_width_style);
4980         canvas.drawRects(SK_ColorRED, boxes);
4981         REPORTER_ASSERT(reporter, boxes.size() == 1);
4982         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0, EPSILON100));
4983         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 2.5f, EPSILON100));
4984         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 40, EPSILON100));
4985         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 52.5f, EPSILON100));
4986     }
4987 }
4988 
UNIX_ONLY_TEST(SkParagraph_FontFeaturesParagraph,reporter)4989 UNIX_ONLY_TEST(SkParagraph_FontFeaturesParagraph, reporter) {
4990     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
4991     if (!fontCollection->fontsFound()) return;
4992     TestCanvas canvas("SkParagraph_FontFeaturesParagraph.png");
4993 
4994     const char* text = "12ab\n";
4995 
4996     ParagraphStyle paragraph_style;
4997     paragraph_style.turnHintingOff();
4998     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
4999 
5000     TextStyle text_style;
5001     text_style.setFontStyle(SkFontStyle::Italic()); // Regular Roboto doesn't have font features
5002     text_style.setFontFamilies({SkString("Roboto")});
5003     text_style.setColor(SK_ColorBLACK);
5004 
5005     text_style.addFontFeature(SkString("tnum"), 1);
5006     builder.pushStyle(text_style);
5007     builder.addText(text);
5008 
5009     text_style.resetFontFeatures();
5010     text_style.addFontFeature(SkString("tnum"), 0);
5011     text_style.addFontFeature(SkString("pnum"), 1);
5012     builder.pushStyle(text_style);
5013     builder.addText(text);
5014 
5015     builder.pop();
5016     builder.pop();
5017 
5018     auto paragraph = builder.Build();
5019     paragraph->layout(TestCanvasWidth);
5020 
5021     paragraph->paint(canvas.get(), 10.0, 15.0);
5022 
5023     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5024     REPORTER_ASSERT(reporter, paragraph->lineNumber() == 3ull);
5025 
5026     auto& tnum_line = impl->lines()[0];
5027     auto& pnum_line = impl->lines()[1];
5028 
5029     REPORTER_ASSERT(reporter, tnum_line.clusters().width() == 4ull);
5030     REPORTER_ASSERT(reporter, pnum_line.clusters().width() == 4ull);
5031     // Tabular numbers should have equal widths.
5032     REPORTER_ASSERT(reporter, impl->clusters()[0].width() == impl->clusters()[1].width());
5033     // Proportional numbers should have variable widths.
5034     REPORTER_ASSERT(reporter, impl->clusters()[5].width() != impl->clusters()[6].width());
5035     // Alphabetic characters should be unaffected.
5036     REPORTER_ASSERT(reporter, impl->clusters()[2].width() == impl->clusters()[7].width());
5037 }
5038 
5039 // Not in Minikin
UNIX_ONLY_TEST(SkParagraph_WhitespacesInMultipleFonts,reporter)5040 UNIX_ONLY_TEST(SkParagraph_WhitespacesInMultipleFonts, reporter) {
5041     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5042     if (!fontCollection->fontsFound()) return;
5043     const char* text = "English English 字典 字典 ������ ������";
5044     const size_t len = strlen(text);
5045 
5046     ParagraphStyle paragraph_style;
5047     paragraph_style.turnHintingOff();
5048     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5049 
5050     TextStyle text_style;
5051     text_style.setFontFamilies(
5052             {SkString("Roboto"), SkString("Noto Color Emoji"), SkString("Source Han Serif CN")});
5053     text_style.setFontSize(60);
5054     builder.pushStyle(text_style);
5055     builder.addText(text, len);
5056     builder.pop();
5057 
5058     auto paragraph = builder.Build();
5059     paragraph->layout(TestCanvasWidth);
5060 
5061     REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
5062 
5063     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5064     for (size_t i = 0; i < impl->runs().size() - 1; ++i) {
5065         auto first = impl->runs()[i].textRange();
5066         auto next  = impl->runs()[i + 1].textRange();
5067         REPORTER_ASSERT(reporter, first.end == next.start);
5068     }
5069 }
5070 
5071 // Disable until I sort out fonts
DEF_TEST_DISABLED(SkParagraph_JSON1,reporter)5072 DEF_TEST_DISABLED(SkParagraph_JSON1, reporter) {
5073     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5074     if (!fontCollection->fontsFound()) return;
5075     const char* text = "��‍��‍��‍��";
5076     const size_t len = strlen(text);
5077 
5078     ParagraphStyle paragraph_style;
5079     paragraph_style.turnHintingOff();
5080     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5081 
5082     TextStyle text_style;
5083     text_style.setFontFamilies({SkString("Noto Color Emoji")});
5084     builder.pushStyle(text_style);
5085     builder.addText(text, len);
5086     builder.pop();
5087 
5088     auto paragraph = builder.Build();
5089     paragraph->layout(TestCanvasWidth);
5090 
5091     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5092     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
5093     auto& run = impl->runs().front();
5094 
5095     auto cluster = 0;
5096     SkShaperJSONWriter::VisualizeClusters(
5097             text, 0, std::strlen(text), run.glyphs(), run.clusterIndexes(),
5098             [&](int codePointCount, SkSpan<const char> utf1to1, SkSpan<const SkGlyphID> glyph1to1) {
5099                 if (cluster == 0) {
5100                     std::string toCheckUtf8{utf1to1.data(), utf1to1.size()};
5101                     SkASSERT(std::strcmp(text, utf1to1.data()) == 0);
5102                     SkASSERT(glyph1to1.size() == 1);
5103                     SkASSERT(*glyph1to1.begin() == 1611);
5104                 }
5105                 ++cluster;
5106             });
5107     REPORTER_ASSERT(reporter, cluster <= 2);
5108 }
5109 
5110 // Disable until I sort out fonts
DEF_TEST_DISABLED(SkParagraph_JSON2,reporter)5111 DEF_TEST_DISABLED(SkParagraph_JSON2, reporter) {
5112     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5113     if (!fontCollection->fontsFound()) return;
5114     const char* text = "p〠q";
5115     const size_t len = strlen(text);
5116 
5117     ParagraphStyle paragraph_style;
5118     paragraph_style.turnHintingOff();
5119     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5120 
5121     TextStyle text_style;
5122     text_style.setFontFamilies({SkString("Noto Sans CJK JP")});
5123     text_style.setColor(SK_ColorBLACK);
5124     text_style.setFontSize(50);
5125     builder.pushStyle(text_style);
5126     builder.addText(text, len);
5127     builder.pop();
5128 
5129     auto paragraph = builder.Build();
5130     paragraph->layout(TestCanvasWidth);
5131 
5132     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5133     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
5134 
5135     auto cluster = 0;
5136     for (auto& run : impl->runs()) {
5137         SkShaperJSONWriter::VisualizeClusters(
5138                 impl->text().begin() + run.textRange().start, 0, run.textRange().width(),
5139                 run.glyphs(), run.clusterIndexes(),
5140                 [&](int codePointCount, SkSpan<const char> utf1to1,
5141                     SkSpan<const SkGlyphID> glyph1to1) {
5142                     if (cluster == 0) {
5143                         std::string toCheckUtf8{utf1to1.data(), utf1to1.size()};
5144                         SkASSERT(std::strcmp(text, utf1to1.data()) == 0);
5145                         SkASSERT(glyph1to1.size() == 3);
5146                     }
5147                     ++cluster;
5148                 });
5149     }
5150 
5151     REPORTER_ASSERT(reporter, cluster <= 2);
5152 }
5153 
UNIX_ONLY_TEST(SkParagraph_CacheText,reporter)5154 UNIX_ONLY_TEST(SkParagraph_CacheText, reporter) {
5155     ParagraphCache cache;
5156     cache.turnOn(true);
5157     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5158     if (!fontCollection->fontsFound()) return;
5159 
5160     ParagraphStyle paragraph_style;
5161     paragraph_style.turnHintingOff();
5162 
5163     TextStyle text_style;
5164     text_style.setFontFamilies({SkString("Roboto")});
5165     text_style.setColor(SK_ColorBLACK);
5166 
5167     auto test = [&](const char* text, int count, bool expectedToBeFound) {
5168         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5169         builder.pushStyle(text_style);
5170         builder.addText(text, strlen(text));
5171         builder.pop();
5172         auto paragraph = builder.Build();
5173 
5174         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5175         REPORTER_ASSERT(reporter, count == cache.count());
5176         auto found = cache.findParagraph(impl);
5177         REPORTER_ASSERT(reporter, found == expectedToBeFound);
5178         auto added = cache.updateParagraph(impl);
5179         REPORTER_ASSERT(reporter, added != expectedToBeFound);
5180     };
5181 
5182     test("text1", 0, false);
5183     test("text1", 1, true);
5184     test("text2", 1, false);
5185     test("text2", 2, true);
5186     test("text3", 2, false);
5187 }
5188 
UNIX_ONLY_TEST(SkParagraph_CacheFonts,reporter)5189 UNIX_ONLY_TEST(SkParagraph_CacheFonts, reporter) {
5190     ParagraphCache cache;
5191     cache.turnOn(true);
5192     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5193     if (!fontCollection->fontsFound()) return;
5194 
5195     ParagraphStyle paragraph_style;
5196     paragraph_style.turnHintingOff();
5197 
5198     TextStyle text_style;
5199     text_style.setColor(SK_ColorBLACK);
5200 
5201     const char* text = "text";
5202     const size_t len = strlen(text);
5203 
5204     auto test = [&](int count, bool expectedToBeFound) {
5205         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5206         builder.pushStyle(text_style);
5207         builder.addText(text, len);
5208         builder.pop();
5209         auto paragraph = builder.Build();
5210         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5211 
5212         REPORTER_ASSERT(reporter, count == cache.count());
5213         auto found = cache.findParagraph(impl);
5214         REPORTER_ASSERT(reporter, found == expectedToBeFound);
5215         auto added = cache.updateParagraph(impl);
5216         REPORTER_ASSERT(reporter, added != expectedToBeFound);
5217     };
5218 
5219     text_style.setFontFamilies({SkString("Roboto")});
5220     test(0, false);
5221     test(1, true);
5222     text_style.setFontFamilies({SkString("Homemade Apple")});
5223     test(1, false);
5224     test(2, true);
5225     text_style.setFontFamilies({SkString("Noto Color Emoji")});
5226     test(2, false);
5227 }
5228 
UNIX_ONLY_TEST(SkParagraph_CacheFontRanges,reporter)5229 UNIX_ONLY_TEST(SkParagraph_CacheFontRanges, reporter) {
5230     ParagraphCache cache;
5231     cache.turnOn(true);
5232     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5233     if (!fontCollection->fontsFound()) return;
5234 
5235     ParagraphStyle paragraph_style;
5236     paragraph_style.turnHintingOff();
5237 
5238     TextStyle text_style;
5239     text_style.setFontFamilies({SkString("Roboto")});
5240     text_style.setColor(SK_ColorBLACK);
5241 
5242     auto test = [&](const char* text1,
5243                     const char* text2,
5244                     const char* font1,
5245                     const char* font2,
5246                     int count,
5247                     bool expectedToBeFound) {
5248         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5249         text_style.setFontFamilies({SkString(font1)});
5250         builder.pushStyle(text_style);
5251         builder.addText(text1, strlen(text1));
5252         builder.pop();
5253         text_style.setFontFamilies({SkString(font2)});
5254         builder.pushStyle(text_style);
5255         builder.addText(text2, strlen(text2));
5256         builder.pop();
5257         auto paragraph = builder.Build();
5258         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5259 
5260         REPORTER_ASSERT(reporter, count == cache.count());
5261         auto found = cache.findParagraph(impl);
5262         REPORTER_ASSERT(reporter, found == expectedToBeFound);
5263         auto added = cache.updateParagraph(impl);
5264         REPORTER_ASSERT(reporter, added != expectedToBeFound);
5265     };
5266 
5267     test("text", "", "Roboto", "Homemade Apple", 0, false);
5268     test("t", "ext", "Roboto", "Homemade Apple", 1, false);
5269     test("te", "xt", "Roboto", "Homemade Apple", 2, false);
5270     test("tex", "t", "Roboto", "Homemade Apple", 3, false);
5271     test("text", "", "Roboto", "Homemade Apple", 4, true);
5272 }
5273 
UNIX_ONLY_TEST(SkParagraph_CacheStyles,reporter)5274 UNIX_ONLY_TEST(SkParagraph_CacheStyles, reporter) {
5275     ParagraphCache cache;
5276     cache.turnOn(true);
5277     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5278     if (!fontCollection->fontsFound()) return;
5279 
5280     ParagraphStyle paragraph_style;
5281     paragraph_style.turnHintingOff();
5282 
5283     TextStyle text_style;
5284     text_style.setFontFamilies({SkString("Roboto")});
5285     text_style.setColor(SK_ColorBLACK);
5286 
5287     const char* text = "text";
5288     const size_t len = strlen(text);
5289 
5290     auto test = [&](int count, bool expectedToBeFound) {
5291         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5292         builder.pushStyle(text_style);
5293         builder.addText(text, len);
5294         builder.pop();
5295         auto paragraph = builder.Build();
5296         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5297 
5298         REPORTER_ASSERT(reporter, count == cache.count());
5299         auto found = cache.findParagraph(impl);
5300         REPORTER_ASSERT(reporter, found == expectedToBeFound);
5301         auto added = cache.updateParagraph(impl);
5302         REPORTER_ASSERT(reporter, added != expectedToBeFound);
5303     };
5304 
5305     test(0, false);
5306     test(1, true);
5307     text_style.setLetterSpacing(10);
5308     test(1, false);
5309     test(2, true);
5310     text_style.setWordSpacing(10);
5311     test(2, false);
5312 }
5313 
UNIX_ONLY_TEST(SkParagraph_EmptyParagraphWithLineBreak,reporter)5314 UNIX_ONLY_TEST(SkParagraph_EmptyParagraphWithLineBreak, reporter) {
5315     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5316     if (!fontCollection->fontsFound()) return;
5317     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5318     fontCollection->enableFontFallback();
5319 
5320     TestCanvas canvas("SkParagraph_EmptyParagraphWithLineBreak.png");
5321 
5322     ParagraphStyle paragraph_style;
5323     TextStyle text_style;
5324     text_style.setFontSize(16);
5325     text_style.setFontFamilies({SkString("Roboto")});
5326     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5327     builder.addText("abc\n\ndef");
5328 
5329     auto paragraph = builder.Build();
5330     paragraph->layout(TestCanvasWidth);
5331     paragraph->paint(canvas.get(), 0, 0);
5332 
5333     // Select a position at the second (empty) line
5334     auto pos = paragraph->getGlyphPositionAtCoordinate(0, 21);
5335     REPORTER_ASSERT(reporter, pos.affinity == Affinity::kDownstream && pos.position == 4);
5336     auto rect = paragraph->getRectsForRange(4, 5, RectHeightStyle::kTight, RectWidthStyle::kTight);
5337     REPORTER_ASSERT(reporter, rect.size() == 1 && rect[0].rect.width() == 0);
5338 }
5339 
UNIX_ONLY_TEST(SkParagraph_NullInMiddleOfText,reporter)5340 UNIX_ONLY_TEST(SkParagraph_NullInMiddleOfText, reporter) {
5341     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5342     if (!fontCollection->fontsFound()) return;
5343     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5344     TestCanvas canvas("SkParagraph_NullInMiddleOfText.png");
5345 
5346     const SkString text("null terminator ->\u0000<- on purpose did you see it?");
5347 
5348     ParagraphStyle paragraph_style;
5349     TextStyle text_style;
5350     text_style.setFontSize(16);
5351     text_style.setFontFamilies({SkString("Roboto")});
5352     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5353     builder.addText(text.c_str(), text.size());
5354 
5355     auto paragraph = builder.Build();
5356     paragraph->layout(TestCanvasWidth);
5357     paragraph->paint(canvas.get(), 0, 0);
5358 }
5359 
UNIX_ONLY_TEST(SkParagraph_PlaceholderOnly,reporter)5360 UNIX_ONLY_TEST(SkParagraph_PlaceholderOnly, reporter) {
5361     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5362     if (!fontCollection->fontsFound()) return;
5363     TestCanvas canvas("SkParagraph_PlaceholderOnly.png");
5364 
5365     ParagraphStyle paragraph_style;
5366     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5367 
5368     PlaceholderStyle placeholder(0, 0, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 0);
5369     builder.addPlaceholder(placeholder);
5370 
5371     auto paragraph = builder.Build();
5372     paragraph->layout(TestCanvasWidth);
5373     auto result = paragraph->getRectsForPlaceholders();
5374     paragraph->paint(canvas.get(), 0, 0);
5375 }
5376 
UNIX_ONLY_TEST(SkParagraph_Fallbacks,reporter)5377 UNIX_ONLY_TEST(SkParagraph_Fallbacks, reporter) {
5378     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5379     if (!fontCollection->fontsFound()) return;
5380     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault(), "Arial");
5381     TestCanvas canvas("SkParagraph_Fallbacks.png");
5382 
5383     const char* multiScript = "A1!aÀàĀāƁƀḂⱠꜲꬰəͲἀἏЀЖԠꙐꙮՁخ‎ࡔࠇܦআਉઐଘஇఘಧൺඣᭆᯔᮯ᳇ꠈᜅᩌꪈ༇ꥄꡙꫤ᧰៘꧁꧂ᜰᨏᯤᢆᣭᗗꗃⵞ��߷ጩꬤ��‡₩℻Ⅷ↹⋇⏳ⓖ╋▒◛⚧⑆שׁ��㊼龜ポ䷤��\n";
5384     const size_t len = strlen(multiScript);
5385 
5386     const char* androidFonts[] = {
5387         "sans-serif",
5388         "sans-serif-condensed",
5389         "serif",
5390         "monospace",
5391         "serif-monospace",
5392         "casual",
5393         "cursive",
5394         "sans-serif-smallcaps",
5395     };
5396 
5397     for (auto& font : androidFonts) {
5398 
5399         ParagraphStyle paragraph_style;
5400         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5401 
5402         TextStyle text_style;
5403         text_style.setColor(SK_ColorBLACK);
5404         text_style.setLocale(SkString("en_US"));
5405         text_style.setFontSize(20);
5406 
5407         text_style.setFontFamilies({ SkString(font) });
5408         builder.pushStyle(text_style);
5409         builder.addText(multiScript, len);
5410 
5411         builder.pop();
5412 
5413         auto paragraph = builder.Build();
5414         paragraph->layout(TestCanvasWidth);
5415         paragraph->paint(canvas.get(), 0, 0);
5416         canvas.get()->translate(0, paragraph->getHeight() + 10);
5417     }
5418 }
5419 
UNIX_ONLY_TEST(SkParagraph_Bidi1,reporter)5420 UNIX_ONLY_TEST(SkParagraph_Bidi1, reporter) {
5421     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5422     if (!fontCollection->fontsFound()) return;
5423     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5424     fontCollection->enableFontFallback();
5425     TestCanvas canvas("SkParagraph_Bidi1.png");
5426 
5427     std::u16string abc = u"\u202Dabc";
5428     std::u16string DEF = u"\u202EDEF";
5429     std::u16string ghi = u"\u202Dghi";
5430     std::u16string JKL = u"\u202EJKL";
5431     std::u16string mno = u"\u202Dmno";
5432 
5433     std::u16string abcDEFghiJKLmno = u"\u202Dabc\u202EDEF\u202Dghi\u202EJKL\u202Dmno";
5434 
5435     ParagraphStyle paragraph_style;
5436     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5437 
5438     TextStyle text_style;
5439     text_style.setFontFamilies({ SkString("sans-serif")});
5440     text_style.setFontSize(40);
5441 
5442     text_style.setColor(SK_ColorCYAN);
5443     text_style.setFontStyle(SkFontStyle(SkFontStyle::kThin_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
5444     builder.pushStyle(text_style);
5445     builder.addText(abc);
5446 
5447     text_style.setColor(SK_ColorGREEN);
5448     text_style.setFontStyle(SkFontStyle(SkFontStyle::kLight_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
5449     builder.pushStyle(text_style);
5450     builder.addText(DEF);
5451 
5452     text_style.setColor(SK_ColorYELLOW);
5453     text_style.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
5454     builder.pushStyle(text_style);
5455     builder.addText(ghi);
5456 
5457     text_style.setColor(SK_ColorMAGENTA);
5458     text_style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
5459     builder.pushStyle(text_style);
5460     builder.addText(JKL);
5461 
5462     text_style.setColor(SK_ColorBLUE);
5463     text_style.setFontStyle(SkFontStyle(SkFontStyle::kBlack_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant));
5464     builder.pushStyle(text_style);
5465     builder.addText(mno);
5466 
5467     auto paragraph = builder.Build();
5468     paragraph->layout(400);
5469     paragraph->paint(canvas.get(), 0, 0);
5470 }
5471 
UNIX_ONLY_TEST(SkParagraph_Bidi2,reporter)5472 UNIX_ONLY_TEST(SkParagraph_Bidi2, reporter) {
5473     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5474     if (!fontCollection->fontsFound()) return;
5475     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5476     fontCollection->enableFontFallback();
5477     TestCanvas canvas("SkParagraph_Bidi2.png");
5478 
5479     std::u16string abcD = u"\u202Dabc\u202ED";
5480     std::u16string EFgh = u"EF\u202Dgh";
5481     std::u16string iJKLmno = u"i\u202EJKL\u202Dmno";
5482 
5483     std::u16string abcDEFghiJKLmno = u"\u202Dabc\u202EDEF\u202Dghi\u202EJKL\u202Dmno";
5484 
5485     ParagraphStyle paragraph_style;
5486     TextStyle text_style;
5487     text_style.setFontFamilies({ SkString("sans-serif")});
5488     text_style.setFontSize(40);
5489 
5490     {
5491         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5492         builder.pushStyle(text_style);
5493         builder.addText(abcD);
5494         builder.pushStyle(text_style);
5495         builder.addText(EFgh);
5496         builder.pushStyle(text_style);
5497         builder.addText(iJKLmno);
5498         auto paragraph = builder.Build();
5499         paragraph->layout(360);
5500         paragraph->paint(canvas.get(), 0, 0);
5501     }
5502     canvas.get()->translate(0, 400);
5503     {
5504         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5505         builder.pushStyle(text_style);
5506         builder.addText(abcDEFghiJKLmno);
5507         auto paragraph = builder.Build();
5508         paragraph->layout(360);
5509         paragraph->paint(canvas.get(), 0, 0);
5510     }
5511 }
5512 
UNIX_ONLY_TEST(SkParagraph_NewlineOnly,reporter)5513 UNIX_ONLY_TEST(SkParagraph_NewlineOnly, reporter) {
5514     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5515     if (!fontCollection->fontsFound()) return;
5516     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5517     TestCanvas canvas("SkParagraph_Newline.png");
5518 
5519     TextStyle text_style;
5520     text_style.setFontFamilies({SkString("Ahem")});
5521     text_style.setColor(SK_ColorBLACK);
5522     StrutStyle strut_style;
5523     strut_style.setStrutEnabled(false);
5524     ParagraphStyle paragraph_style;
5525     paragraph_style.setStrutStyle(strut_style);
5526     paragraph_style.setTextStyle(text_style);
5527     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5528     builder.addText("\n");
5529     auto paragraph = builder.Build();
5530     paragraph->layout(1000);
5531     REPORTER_ASSERT(reporter, paragraph->getHeight() == 28);
5532 }
5533 
UNIX_ONLY_TEST(SkParagraph_FontResolutions,reporter)5534 UNIX_ONLY_TEST(SkParagraph_FontResolutions, reporter) {
5535     TestCanvas canvas("SkParagraph_FontResolutions.png");
5536 
5537     sk_sp<TestFontCollection> fontCollection =
5538             sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
5539     if (!fontCollection->fontsFound()) return;
5540 
5541     if (!fontCollection->addFontFromFile("abc/abc.ttf", "abc")) {
5542         return;
5543     }
5544     if (!fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave")) {
5545         return;
5546     }
5547     if (!fontCollection->addFontFromFile("abc/abc_agrave.ttf", "abc_agrave")) {
5548         return;
5549     }
5550 
5551     TextStyle text_style;
5552     text_style.setFontFamilies({SkString("abc")});
5553     text_style.setFontSize(50);
5554 
5555     ParagraphStyle paragraph_style;
5556     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5557 
5558     text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
5559     text_style.setColor(SK_ColorBLUE);
5560     builder.pushStyle(text_style);
5561     builder.addText(u"a\u0300");
5562     text_style.setColor(SK_ColorMAGENTA);
5563     builder.pushStyle(text_style);
5564     builder.addText(u"à");
5565 
5566     text_style.setFontFamilies({SkString("abc"), SkString("abc_agrave")});
5567 
5568     text_style.setColor(SK_ColorRED);
5569     builder.pushStyle(text_style);
5570     builder.addText(u"a\u0300");
5571     text_style.setColor(SK_ColorGREEN);
5572     builder.pushStyle(text_style);
5573     builder.addText(u"à");
5574 
5575     auto paragraph = builder.Build();
5576     paragraph->layout(TestCanvasWidth);
5577 
5578     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5579     REPORTER_ASSERT(reporter, impl->runs().size() == 2);
5580 
5581     REPORTER_ASSERT(reporter, impl->runs().front().size() == 4);
5582     REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[0] == impl->runs().front().glyphs()[2]);
5583     REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[1] == impl->runs().front().glyphs()[3]);
5584 
5585     REPORTER_ASSERT(reporter, impl->runs().back().size() == 2);
5586     REPORTER_ASSERT(reporter, impl->runs().back().glyphs()[0] == impl->runs().back().glyphs()[1]);
5587 
5588     paragraph->paint(canvas.get(), 100, 100);
5589 }
5590 
UNIX_ONLY_TEST(SkParagraph_FontStyle,reporter)5591 UNIX_ONLY_TEST(SkParagraph_FontStyle, reporter) {
5592     TestCanvas canvas("SkParagraph_FontStyle.png");
5593 
5594     sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
5595     if (!fontCollection->fontsFound()) return;
5596 
5597     TextStyle text_style;
5598     text_style.setFontFamilies({SkString("Roboto")});
5599     text_style.setColor(SK_ColorBLACK);
5600     text_style.setFontSize(20);
5601     SkFontStyle fs = SkFontStyle(
5602         SkFontStyle::Weight::kLight_Weight,
5603         SkFontStyle::Width::kNormal_Width,
5604         SkFontStyle::Slant::kUpright_Slant
5605     );
5606     text_style.setFontStyle(fs);
5607     ParagraphStyle paragraph_style;
5608     paragraph_style.setTextStyle(text_style);
5609     TextStyle boldItalic;
5610     boldItalic.setFontFamilies({SkString("Roboto")});
5611     boldItalic.setColor(SK_ColorRED);
5612     SkFontStyle bi = SkFontStyle(
5613         SkFontStyle::Weight::kBold_Weight,
5614         SkFontStyle::Width::kNormal_Width,
5615         SkFontStyle::Slant::kItalic_Slant
5616     );
5617     boldItalic.setFontStyle(bi);
5618     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5619     builder.addText("Default text\n");
5620     builder.pushStyle(boldItalic);
5621     builder.addText("Bold and Italic\n");
5622     builder.pop();
5623     builder.addText("back to normal");
5624     auto paragraph = builder.Build();
5625     paragraph->layout(250);
5626     paragraph->paint(canvas.get(), 0, 0);
5627 }
5628 
UNIX_ONLY_TEST(SkParagraph_Shaping,reporter)5629 UNIX_ONLY_TEST(SkParagraph_Shaping, reporter) {
5630     TestCanvas canvas("SkParagraph_Shaping.png");
5631 
5632     auto dir = "/usr/local/google/home/jlavrova/Sources/flutter/engine/src/out/host_debug_unopt_x86/gen/flutter/third_party/txt/assets";
5633     sk_sp<TestFontCollection> fontCollection =
5634             sk_make_sp<TestFontCollection>(dir, /*GetResourcePath("fonts").c_str(), */ false);
5635     if (!fontCollection->fontsFound()) return;
5636 
5637 
5638     TextStyle text_style;
5639     text_style.setFontFamilies({SkString("Roboto")});
5640     text_style.setColor(SK_ColorGRAY);
5641     text_style.setFontSize(14);
5642     SkFontStyle b = SkFontStyle(
5643         SkFontStyle::Weight::kNormal_Weight,
5644         SkFontStyle::Width::kNormal_Width,
5645         SkFontStyle::Slant::kUpright_Slant
5646     );
5647     text_style.setFontStyle(b);
5648     ParagraphStyle paragraph_style;
5649     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5650     builder.pushStyle(text_style);
5651     builder.addText("Eat0 apple0 pies0 | Eat1 apple1 pies1 | Eat2 apple2 pies2");
5652     auto paragraph = builder.Build();
5653     paragraph->layout(380);
5654     paragraph->paint(canvas.get(), 0, 0);
5655 }
5656 
UNIX_ONLY_TEST(SkParagraph_Ellipsis,reporter)5657 UNIX_ONLY_TEST(SkParagraph_Ellipsis, reporter) {
5658     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5659     if (!fontCollection->fontsFound()) return;
5660     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5661     TestCanvas canvas("SkParagraph_Ellipsis.png");
5662 
5663     const char* text = "This\n"
5664                        "is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.";
5665     TextStyle text_style;
5666     text_style.setFontFamilies({SkString("Ahem")});
5667     text_style.setColor(SK_ColorBLACK);
5668     text_style.setFontSize(10);
5669 
5670     auto relayout = [&](size_t lines, bool ellipsis,
5671             SkScalar width, SkScalar height, SkScalar minWidth, SkScalar maxWidth, SkColor bg) {
5672         ParagraphStyle paragraph_style;
5673         SkPaint paint;
5674         paint.setColor(bg);
5675         text_style.setForegroundColor(paint);
5676         paragraph_style.setTextStyle(text_style);
5677         paragraph_style.setMaxLines(lines);
5678         if (ellipsis) {
5679             paragraph_style.setEllipsis(u"\u2026");
5680         }
5681         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5682         builder.addText(text);
5683         auto paragraph = builder.Build();
5684         paragraph->layout(50);
5685         paragraph->paint(canvas.get(), 0, 0);
5686         canvas.get()->translate(50, paragraph->getHeight() + 10);
5687         auto result = paragraph->getRectsForRange(0, strlen(text), RectHeightStyle::kTight, RectWidthStyle::kTight);
5688         SkPaint background;
5689         background.setColor(SK_ColorRED);
5690         background.setStyle(SkPaint::kStroke_Style);
5691         background.setAntiAlias(true);
5692         background.setStrokeWidth(1);
5693         canvas.get()->drawRect(result.front().rect, background);
5694 
5695         SkASSERT(width == paragraph->getMaxWidth());
5696         SkASSERT(height == paragraph->getHeight());
5697         SkASSERT(minWidth == paragraph->getMinIntrinsicWidth());
5698         SkASSERT(maxWidth == paragraph->getMaxIntrinsicWidth());
5699     };
5700 
5701     SkPaint paint;
5702     paint.setColor(SK_ColorLTGRAY);
5703     canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, 50, 500), paint);
5704 
5705     relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
5706     relayout(3, false, 50, 30,  90, 950, SK_ColorBLUE);
5707     relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  90, 950, SK_ColorGREEN);
5708 
5709     relayout(1, true, 50, 10, 950, 950, SK_ColorYELLOW);
5710     relayout(3, true, 50, 30,  90, 950, SK_ColorMAGENTA);
5711     relayout(std::numeric_limits<size_t>::max(), true, 50, 20,  950, 950, SK_ColorCYAN);
5712 
5713     relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
5714     relayout(3, false, 50, 30,  90, 950, SK_ColorBLUE);
5715     relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  90, 950, SK_ColorGREEN);
5716 }
5717 
UNIX_ONLY_TEST(SkParagraph_MemoryLeak,reporter)5718 UNIX_ONLY_TEST(SkParagraph_MemoryLeak, reporter) {
5719     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5720     if (!fontCollection->fontsFound()) return;
5721     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5722 
5723     std::string text;
5724     for (size_t i = 0; i < 10; i++)
5725 	{
5726 		SkPaint paint;
5727 		paint.setAntiAlias(true);
5728 		paint.setColor(SK_ColorBLACK);
5729 
5730 		TextStyle textStyle;
5731 		textStyle.setForegroundColor(paint);
5732 		textStyle.setFontFamilies({ SkString("Roboto") });
5733 
5734 		ParagraphStyle paragraphStyle;
5735 		paragraphStyle.setTextStyle(textStyle);
5736 
5737 		TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
5738 		text += "Text ";
5739 		builder.addText(text.c_str());
5740 
5741 		auto paragraph = builder.Build();
5742 		paragraph->layout(100);
5743 
5744 		//used to add a delay so I can monitor memory usage
5745 		//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
5746 	}
5747 };
5748 
UNIX_ONLY_TEST(SkParagraph_FormattingInfinity,reporter)5749 UNIX_ONLY_TEST(SkParagraph_FormattingInfinity, reporter) {
5750     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5751     if (!fontCollection->fontsFound()) return;
5752     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
5753     TestCanvas canvas("SkParagraph_FormattingInfinity.png");
5754 
5755     const char* text = "Some text\nAnother line";
5756 
5757     SkPaint paint;
5758     paint.setAntiAlias(true);
5759     paint.setColor(SK_ColorBLACK);
5760 
5761     TextStyle textStyle;
5762     textStyle.setForegroundColor(paint);
5763     textStyle.setFontFamilies({ SkString("Roboto") });
5764     ParagraphStyle paragraphStyle;
5765     paragraphStyle.setTextStyle(textStyle);
5766 
5767     auto draw = [&](const char* prefix, TextAlign textAlign, TextDirection textDirection) {
5768         paragraphStyle.setTextAlign(textAlign);
5769         paragraphStyle.setTextDirection(textDirection);
5770         TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
5771         builder.addText(text);
5772         auto paragraph = builder.Build();
5773         paragraph->layout(SK_ScalarInfinity);
5774         paragraph->paint(canvas.get(), 0, 0);
5775         canvas.get()->translate(0, 100);
5776     };
5777 
5778     draw("left", TextAlign::kLeft, TextDirection::kLtr);
5779     draw("right", TextAlign::kRight, TextDirection::kLtr);
5780     draw("center", TextAlign::kCenter, TextDirection::kLtr);
5781     draw("justify LTR", TextAlign::kJustify, TextDirection::kLtr);
5782     draw("justify RTL", TextAlign::kJustify, TextDirection::kRtl);
5783 };
5784 
UNIX_ONLY_TEST(SkParagraph_Infinity,reporter)5785 UNIX_ONLY_TEST(SkParagraph_Infinity, reporter) {
5786     SkASSERT(nearlyEqual(1, SK_ScalarInfinity) == false);
5787     SkASSERT(nearlyEqual(1, SK_ScalarNegativeInfinity) == false);
5788     SkASSERT(nearlyEqual(1, SK_ScalarNaN) == false);
5789 
5790     SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarInfinity) == true);
5791     SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNegativeInfinity) == false);
5792     SkASSERT(nearlyEqual(SK_ScalarInfinity, SK_ScalarNaN) == false);
5793 
5794     SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarInfinity) == false);
5795     SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity) == true);
5796     SkASSERT(nearlyEqual(SK_ScalarNegativeInfinity, SK_ScalarNaN) == false);
5797 
5798     SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarInfinity) == false);
5799     SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNegativeInfinity) == false);
5800     SkASSERT(nearlyEqual(SK_ScalarNaN, SK_ScalarNaN) == false);
5801 };
5802 
UNIX_ONLY_TEST(SkParagraph_LineMetrics,reporter)5803 UNIX_ONLY_TEST(SkParagraph_LineMetrics, reporter) {
5804 
5805     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5806     if (!fontCollection->fontsFound()) return;
5807 
5808     TestCanvas canvas("SkParagraph_LineMetrics.png");
5809 
5810     const char* text = "One line of text\n";
5811     const size_t len = strlen(text);
5812 
5813     ParagraphStyle paragraph_style;
5814     paragraph_style.turnHintingOff();
5815     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5816 
5817     TextStyle text_style;
5818     text_style.setFontFamilies({SkString("Roboto")});
5819     text_style.setColor(SK_ColorBLACK);
5820 
5821     text_style.setFontSize(8);
5822     builder.pushStyle(text_style);
5823     builder.addText(text, len);
5824     builder.pop();
5825 
5826     text_style.setFontSize(12);
5827     builder.pushStyle(text_style);
5828     builder.addText(text, len);
5829     builder.pop();
5830 
5831     text_style.setFontSize(18);
5832     builder.pushStyle(text_style);
5833     builder.addText(text, len);
5834     builder.pop();
5835 
5836     text_style.setFontSize(30);
5837     builder.pushStyle(text_style);
5838     builder.addText(text, len - 1); // Skip the last \n
5839     builder.pop();
5840 
5841     auto paragraph = builder.Build();
5842     paragraph->layout(TestCanvasWidth);
5843 
5844     std::vector<LineMetrics> metrics;
5845     paragraph->getLineMetrics(metrics);
5846 
5847     SkDEBUGCODE(auto impl = static_cast<ParagraphImpl*>(paragraph.get());)
5848     SkASSERT(metrics.size() == impl->lines().size());
5849     for (size_t i = 0; i < metrics.size(); ++i) {
5850         SkDEBUGCODE(auto& line = impl->lines()[i];)
5851         SkDEBUGCODE(auto baseline = metrics[i].fBaseline;)
5852         SkDEBUGCODE(auto top = line.offset().fY;)
5853         SkDEBUGCODE(auto bottom = line.offset().fY + line.height();)
5854         SkASSERT( baseline > top && baseline <= bottom);
5855     }
5856 
5857     paragraph->paint(canvas.get(), 0, 0);
5858     auto rects = paragraph->getRectsForRange(0, len * 4, RectHeightStyle::kMax, RectWidthStyle::kTight);
5859 
5860     SkPaint red;
5861     red.setColor(SK_ColorRED);
5862     red.setStyle(SkPaint::kStroke_Style);
5863     red.setAntiAlias(true);
5864     red.setStrokeWidth(1);
5865 
5866     for (auto& rect : rects) {
5867         canvas.get()->drawRect(rect.rect, red);
5868     }
5869 
5870     SkPaint green;
5871     green.setColor(SK_ColorGREEN);
5872     green.setStyle(SkPaint::kStroke_Style);
5873     green.setAntiAlias(true);
5874     green.setStrokeWidth(1);
5875     for (auto& metric : metrics) {
5876         auto x0 = 0.0;
5877         auto x1 = metric.fWidth;
5878         auto y = metric.fBaseline;
5879         canvas.get()->drawLine(x0, y, x1, y, green);
5880     }
5881 };
5882 
DEF_TEST_DISABLED(SkParagraph_PlaceholderHeightInf,reporter)5883 DEF_TEST_DISABLED(SkParagraph_PlaceholderHeightInf, reporter) {
5884     TestCanvas canvas("SkParagraph_PlaceholderHeightInf.png");
5885 
5886     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5887     if (!fontCollection->fontsFound()) return;
5888 
5889     TextStyle text_style;
5890     text_style.setFontFamilies({SkString("Ahem")});
5891     text_style.setColor(SK_ColorBLACK);
5892     text_style.setFontSize(14);
5893 
5894     PlaceholderStyle placeholder_style;
5895     placeholder_style.fWidth = 16.0f;
5896     placeholder_style.fHeight = SK_ScalarInfinity;
5897     placeholder_style.fAlignment = PlaceholderAlignment::kBottom;
5898     placeholder_style.fBaseline = TextBaseline::kAlphabetic;
5899     placeholder_style.fBaselineOffset = SK_ScalarInfinity;
5900 
5901     ParagraphStyle paragraph_style;
5902     //paragraph_style.setDrawOptions(DrawOptions::kRecord);
5903     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5904     builder.pushStyle(text_style);
5905     builder.addText("Limited by budget");
5906     builder.addPlaceholder(placeholder_style);
5907     auto paragraph = builder.Build();
5908     paragraph->layout(SK_ScalarInfinity);
5909     paragraph->paint(canvas.get(), 0, 0);
5910 
5911     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5912     REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getPicture()->cullRect().height()));
5913     REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getPicture()->cullRect().width()));
5914 }
5915 
UNIX_ONLY_TEST(SkParagraph_LineMetricsTextAlign,reporter)5916 UNIX_ONLY_TEST(SkParagraph_LineMetricsTextAlign, reporter) {
5917 
5918     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
5919     if (!fontCollection->fontsFound()) return;
5920 
5921     TestCanvas canvas("SkParagraph_LineMetricsTextAlign.png");
5922 
5923     ParagraphStyle paragraph_style;
5924     paragraph_style.turnHintingOff();
5925     TextStyle text_style;
5926     text_style.setFontFamilies({SkString("Roboto")});
5927     text_style.setColor(SK_ColorBLACK);
5928 
5929     auto layout = [&](TextAlign text_align) -> std::pair<SkScalar, SkScalar> {
5930         paragraph_style.setTextAlign(text_align);
5931         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5932         builder.pushStyle(text_style);
5933         builder.addText("Some text that takes more than 200 px");
5934         auto paragraph = builder.Build();
5935         paragraph->layout(200);
5936 
5937         std::vector<LineMetrics> metrics;
5938         paragraph->getLineMetrics(metrics);
5939         REPORTER_ASSERT(reporter, metrics.size() > 0);
5940         return { metrics[0].fLeft, metrics[0].fWidth };
5941     };
5942 
5943     SkScalar left[4];
5944     SkScalar width[4];
5945     std::tie(left[0], width[0]) = layout(TextAlign::kLeft);
5946     std::tie(left[1], width[1]) = layout(TextAlign::kCenter);
5947     std::tie(left[2], width[2]) = layout(TextAlign::kRight);
5948     std::tie(left[3], width[3]) = layout(TextAlign::kJustify);
5949 
5950     // delta = line_width - text_width
5951     REPORTER_ASSERT(reporter, left[0] == 0);        // Starts from 0
5952     REPORTER_ASSERT(reporter, left[1] > left[0]);   // Starts from delta / 2
5953     REPORTER_ASSERT(reporter, left[2] > left[1]);   // Starts from delta
5954     REPORTER_ASSERT(reporter, left[3] == left[0]);  // Starts from 0
5955     REPORTER_ASSERT(reporter, width[1] == width[0]);
5956     REPORTER_ASSERT(reporter, width[2] == width[0]);
5957     REPORTER_ASSERT(reporter, width[3] > width[0]); // delta == 0
5958 }
5959 
UNIX_ONLY_TEST(SkParagraph_FontResolutionInRTL,reporter)5960 UNIX_ONLY_TEST(SkParagraph_FontResolutionInRTL, reporter) {
5961     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
5962     if (!fontCollection->fontsFound()) return;
5963     TestCanvas canvas("SkParagraph_FontResolutionInRTL.png");
5964     const char* text = " אאא בּבּבּבּ אאאא בּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ ";
5965     const size_t len = strlen(text);
5966 
5967     ParagraphStyle paragraph_style;
5968     paragraph_style.setMaxLines(14);
5969     paragraph_style.setTextAlign(TextAlign::kRight);
5970     paragraph_style.setTextDirection(TextDirection::kRtl);
5971     paragraph_style.turnHintingOff();
5972     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
5973 
5974     TextStyle text_style;
5975     text_style.setFontFamilies({SkString("Ahem")});
5976     text_style.setFontSize(26);
5977     text_style.setColor(SK_ColorBLACK);
5978     builder.pushStyle(text_style);
5979     builder.addText(text, len);
5980     builder.pop();
5981 
5982     auto paragraph = builder.Build();
5983     paragraph->layout(TestCanvasWidth);
5984     paragraph->paint(canvas.get(), 0, 0);
5985 
5986     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
5987     REPORTER_ASSERT(reporter, impl->runs().size() == (10 + 11));
5988 }
5989 
UNIX_ONLY_TEST(SkParagraph_FontResolutionInLTR,reporter)5990 UNIX_ONLY_TEST(SkParagraph_FontResolutionInLTR, reporter) {
5991     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
5992     if (!fontCollection->fontsFound()) return;
5993     TestCanvas canvas("SkParagraph_FontResolutionInLTR.png");
5994     auto text = u"abc \u01A2 \u01A2 def";
5995 
5996     ParagraphStyle paragraph_style;
5997     paragraph_style.setMaxLines(14);
5998     paragraph_style.turnHintingOff();
5999     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6000 
6001     TextStyle text_style;
6002     text_style.setFontFamilies({SkString("Roboto")});
6003     text_style.setFontSize(26);
6004     text_style.setColor(SK_ColorBLACK);
6005     builder.pushStyle(text_style);
6006     builder.addText(text);
6007     builder.pop();
6008 
6009     auto paragraph = builder.Build();
6010     paragraph->layout(TestCanvasWidth);
6011     paragraph->paint(canvas.get(), 0, 0);
6012 
6013     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
6014     REPORTER_ASSERT(reporter, impl->runs().size() == 5);
6015     REPORTER_ASSERT(reporter, impl->runs()[0].textRange().width() == 4); // "abc "
6016     REPORTER_ASSERT(reporter, impl->runs()[1].textRange().width() == 2); // "{unresolved}"
6017     REPORTER_ASSERT(reporter, impl->runs()[2].textRange().width() == 1); // " "
6018     REPORTER_ASSERT(reporter, impl->runs()[3].textRange().width() == 2); // "{unresolved}"
6019     REPORTER_ASSERT(reporter, impl->runs()[4].textRange().width() == 4); // " def"
6020 }
6021 
UNIX_ONLY_TEST(SkParagraph_Intrinsic,reporter)6022 UNIX_ONLY_TEST(SkParagraph_Intrinsic, reporter) {
6023     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6024     if (!fontCollection->fontsFound()) return;
6025     SkString text(std::string(3000, 'a'));
6026 
6027     ParagraphStyle paragraph_style;
6028     paragraph_style.turnHintingOff();
6029     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6030 
6031     TextStyle text_style;
6032     text_style.setFontFamilies({SkString("Google Sans")});
6033     text_style.setFontSize(12.0f);
6034     text_style.setColor(SK_ColorBLACK);
6035     builder.pushStyle(text_style);
6036     builder.addText(text.c_str());
6037 
6038     auto paragraph = builder.Build();
6039     paragraph->layout(300000.0f);
6040     REPORTER_ASSERT(reporter, paragraph->getMinIntrinsicWidth() <= paragraph->getMaxIntrinsicWidth());
6041 }
6042 
UNIX_ONLY_TEST(SkParagraph_NoCache1,reporter)6043 UNIX_ONLY_TEST(SkParagraph_NoCache1, reporter) {
6044 
6045     ParagraphCache cache;
6046     cache.turnOn(true);
6047 
6048     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
6049     if (!fontCollection->fontsFound()) return;
6050     TestCanvas canvas("SkParagraph_NoCache1.png");
6051     // Long arabic text with english spaces
6052     const char* text =
6053             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
6054             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
6055             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
6056             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
6057             "من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
6058             "عل بمباركة التقليدية قام عن. تصفح";
6059 
6060     SkString str;
6061 
6062     ParagraphStyle paragraph_style;
6063     paragraph_style.setTextDirection(TextDirection::kLtr);
6064     TextStyle text_style;
6065     text_style.setFontFamilies({SkString("Ahem")});
6066     text_style.setFontSize(14);
6067     text_style.setColor(SK_ColorBLACK);
6068 
6069 
6070     auto test = [&](const char* test, const char* text, bool editing) {
6071         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6072         //SkDebugf("test %s:\n", test);
6073         builder.pushStyle(text_style);
6074         builder.addText(text);
6075         builder.pop();
6076 
6077         auto cache = fontCollection->getParagraphCache();
6078         auto countBefore = cache->count();
6079         auto paragraph = builder.Build();
6080         paragraph->layout(TestCanvasWidth);
6081         auto countAfter = cache->count();
6082         //paragraph->paint(canvas.get(), 0, 0);
6083 
6084         if (test == nullptr) {
6085             return;
6086         }
6087 
6088         REPORTER_ASSERT(reporter, (countBefore == countAfter) == editing);
6089     };
6090 
6091     str.append(text);
6092     test("Long arabic text", str.c_str(), false);
6093 
6094     str.append("عل");
6095     test("+2 character at the end", str.c_str(), true);
6096 
6097     str = SkString(text);
6098     test("-2 characters from the end", str.c_str(), true);
6099 
6100     str.insert(0, "عل");
6101     test("+2 character at the start", str.c_str(), true);
6102 
6103     test("-2 characters from the start", text, true);
6104 
6105     // Make sure that different strings are not flagged as editing
6106     test("different strings", "0123456789 0123456789 0123456789 0123456789 0123456789", false);
6107 }
6108 
UNIX_ONLY_TEST(SkParagraph_HeightCalculations,reporter)6109 UNIX_ONLY_TEST(SkParagraph_HeightCalculations, reporter) {
6110     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6111     if (!fontCollection->fontsFound()) return;
6112 
6113     TestCanvas canvas("SkParagraph_HeightCalculations.png");
6114 
6115     auto draw = [&](TextHeightBehavior hb, const char* text, SkScalar height) {
6116         ParagraphStyle paragraph_style;
6117         paragraph_style.setTextHeightBehavior(hb);
6118 
6119         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6120         TextStyle text_style;
6121         text_style.setFontFamilies({SkString("Roboto")});
6122         text_style.setFontSize(14.0f);
6123         text_style.setHeight(5.0f);
6124         text_style.setHeightOverride(true);
6125         text_style.setColor(SK_ColorBLACK);
6126         builder.pushStyle(text_style);
6127         builder.addText(text);
6128 
6129         auto paragraph = builder.Build();
6130         paragraph->layout(500);
6131         paragraph->paint(canvas.get(), 0, 0);
6132         canvas.get()->translate(0, paragraph->getHeight());
6133         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(paragraph->getHeight(), height));
6134     };
6135 
6136     draw(TextHeightBehavior::kAll, "Hello\nLine 2\nLine 3", 210);
6137     draw(TextHeightBehavior::kDisableAll, "Hello\nLine 2\nLine 3", 157);
6138     draw(TextHeightBehavior::kDisableFirstAscent, "Hello", 28);
6139 }
6140 
UNIX_ONLY_TEST(SkParagraph_RTL_With_Styles,reporter)6141 UNIX_ONLY_TEST(SkParagraph_RTL_With_Styles, reporter) {
6142 
6143     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6144     if (!fontCollection->fontsFound()) return;
6145 
6146     TestCanvas canvas("SkParagraph_RTL_With_Styles.png");
6147 
6148     SkPaint whiteSpaces;
6149     whiteSpaces.setColor(SK_ColorLTGRAY);
6150 
6151     SkPaint breakingSpace;
6152     breakingSpace.setColor(SK_ColorYELLOW);
6153 
6154     SkPaint text;
6155     text.setColor(SK_ColorWHITE);
6156 
6157     const char* arabic = "قففغغغغقففغغغغقففغغغ";
6158 
6159     ParagraphStyle paragraph_style;
6160     paragraph_style.setTextAlign(TextAlign::kRight);
6161     TextStyle text_style;
6162     text_style.setColor(SK_ColorBLACK);
6163     text_style.setFontFamilies({SkString("Roboto")});
6164 
6165     paragraph_style.setTextDirection(TextDirection::kRtl);
6166     paragraph_style.setTextAlign(TextAlign::kRight);
6167     text_style.setFontSize(20);
6168     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6169     text_style.setBackgroundColor(whiteSpaces);
6170     builder.pushStyle(text_style);
6171     builder.addText("   ");
6172     text_style.setBackgroundColor(text);
6173     builder.pushStyle(text_style);
6174     builder.addText(arabic);
6175 
6176     auto paragraph = builder.Build();
6177     paragraph->layout(300);
6178     paragraph->paint(canvas.get(), 0, 0);
6179 }
6180 
UNIX_ONLY_TEST(SkParagraph_PositionInsideEmoji,reporter)6181 UNIX_ONLY_TEST(SkParagraph_PositionInsideEmoji, reporter) {
6182 
6183     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6184     if (!fontCollection->fontsFound()) return;
6185 
6186     TestCanvas canvas("SkParagraph_PositionInsideEmoji.png");
6187 
6188     std::u16string text = u"\U0001f469\u200D\U0001f469\u200D\U0001f467\u200D\U0001f467\U0001f469\U0001f469\U0001f467\U0001f467";
6189 
6190     ParagraphStyle paragraph_style;
6191     TextStyle text_style;
6192     text_style.setColor(SK_ColorBLACK);
6193     text_style.setFontFamilies({SkString("Noto Color Emoji")});
6194     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6195     builder.pushStyle(text_style);
6196     builder.addText(text);
6197 
6198     auto paragraph = builder.Build();
6199     paragraph->layout(TestCanvasWidth);
6200     paragraph->paint(canvas.get(), 0, 0);
6201 
6202     // UTF8       UTF16
6203     // 4          [0:2)
6204     // 3 + 4      [2:5)
6205     // 3 + 4      [5:8)
6206     // 3 + 4      [8:11)
6207     // 4          [11:13)
6208     // 4          [13:15)
6209     // 4          [15:17)
6210     // 4          [17:19)
6211 
6212     auto family = paragraph->getRectsForRange(0, 11, RectHeightStyle::kTight, RectWidthStyle::kTight);  // 00.0000000 + 17.4699993
6213     auto face01 = paragraph->getRectsForRange(11, 13, RectHeightStyle::kTight, RectWidthStyle::kTight); // 17.4699993 + 17.4699993
6214     auto face02 = paragraph->getRectsForRange(13, 15, RectHeightStyle::kTight, RectWidthStyle::kTight); // 34.9399986 + 17.4699993
6215     auto face03 = paragraph->getRectsForRange(15, 17, RectHeightStyle::kTight, RectWidthStyle::kTight); // 52.4099998 + 17.4699993
6216     auto face04 = paragraph->getRectsForRange(17, 19, RectHeightStyle::kTight, RectWidthStyle::kTight); // 69.8799973 + 17.4699993
6217 
6218     int32_t words[] = { 11, 13, 15, 17, 19, 21};
6219     auto j = 0;
6220     for (auto i :  words) {
6221         auto rects = paragraph->getRectsForRange(j, i, RectHeightStyle::kTight, RectWidthStyle::kTight);
6222         if (rects.empty()) {
6223             continue;
6224         }
6225         auto X = rects[0].rect.centerX();
6226         auto Y = rects[0].rect.centerY();
6227         auto res1 = paragraph->getGlyphPositionAtCoordinate(X - 5, Y);
6228         //SkDebugf("[%d:%d) @%f,%f: %d %s\n", j, i, X - 5, Y, res1.position, res1.affinity == Affinity::kDownstream ? "D" : "U");
6229         auto res2 = paragraph->getGlyphPositionAtCoordinate(X + 5, Y);
6230         //SkDebugf("[%d:%d) @%f,%f: %d %s\n\n", j, i, X + 5, Y, res2.position, res2.affinity == Affinity::kDownstream ? "D" : "U");
6231         REPORTER_ASSERT(reporter, i == res2.position && res1.position == j);
6232         j = i;
6233     }
6234 }
6235 
UNIX_ONLY_TEST(SkParagraph_SingleLineHeight1,reporter)6236 UNIX_ONLY_TEST(SkParagraph_SingleLineHeight1, reporter) {
6237     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6238     if (!fontCollection->fontsFound()) return;
6239 
6240     TestCanvas canvas("SkParagraph_SingleLineHeight1.png");
6241 
6242     auto paint = [&](const char* text) {
6243         ParagraphStyle paragraph_style;
6244         paragraph_style.setTextHeightBehavior(TextHeightBehavior::kDisableAll);
6245         paragraph_style.setMaxLines(1);
6246         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6247         TextStyle text_style;
6248         text_style.setColor(SK_ColorBLACK);
6249         text_style.setFontFamilies({SkString("Ahem")});
6250         text_style.setFontSize(14);
6251         text_style.setHeight(2);
6252         text_style.setHeightOverride(true);
6253         builder.pushStyle(text_style);
6254         builder.addText(text);
6255         auto paragraph = builder.Build();
6256         paragraph->layout(80);
6257         paragraph->paint(canvas.get(), 0, 0);
6258         REPORTER_ASSERT(reporter, paragraph->getHeight() == 14.0f);
6259     };
6260 
6261     paint("Loooooooooooooooooooooooooooooooooooong text");
6262     paint("");
6263 }
6264 
UNIX_ONLY_TEST(SkParagraph_SingleLineHeight2,reporter)6265 UNIX_ONLY_TEST(SkParagraph_SingleLineHeight2, reporter) {
6266     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6267     if (!fontCollection->fontsFound()) return;
6268 
6269     TestCanvas canvas("SkParagraph_SingleLineHeight2.png");
6270 
6271     auto paint = [&](const char* text) {
6272         ParagraphStyle paragraph_style;
6273         paragraph_style.setMaxLines(1);
6274         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6275         TextStyle text_style;
6276         text_style.setColor(SK_ColorBLACK);
6277         text_style.setFontFamilies({SkString("Ahem")});
6278         text_style.setFontSize(14);
6279         text_style.setHeight(2);
6280         text_style.setHeightOverride(true);
6281         builder.pushStyle(text_style);
6282         builder.addText(text);
6283         auto paragraph = builder.Build();
6284         paragraph->layout(80);
6285         paragraph->paint(canvas.get(), 0, 0);
6286         REPORTER_ASSERT(reporter, paragraph->getHeight() == 28.0f);
6287     };
6288 
6289     paint("Loooooooooooooooooooooooooooooooooooong text");
6290     paint("");
6291 }
6292 
UNIX_ONLY_TEST(SkParagraph_PlaceholderWidth,reporter)6293 UNIX_ONLY_TEST(SkParagraph_PlaceholderWidth, reporter) {
6294 
6295     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6296     if (!fontCollection->fontsFound()) return;
6297 
6298     TestCanvas canvas("SkParagraph_PlaceholderWidth.png");
6299 
6300     const char* text = "1 23 456 7890"; // 13 * 50 = 650
6301 
6302     ParagraphStyle paragraph_style;
6303     TextStyle text_style;
6304     text_style.setColor(SK_ColorBLACK);
6305     text_style.setFontSize(50);
6306     text_style.setFontFamilies({SkString("Ahem")});
6307     PlaceholderStyle placeholder(300, 50, PlaceholderAlignment::kBaseline, TextBaseline::kAlphabetic, 0);
6308 
6309     auto draw = [&](bool withPlaceholder) {
6310         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6311         builder.pushStyle(text_style);
6312         builder.addText(text);
6313         if (withPlaceholder) {
6314             SkPaint red;
6315             red.setColor(SK_ColorRED);
6316             text_style.setBackgroundColor(red);
6317             builder.pushStyle(text_style);
6318             builder.addPlaceholder(placeholder);
6319         }
6320         builder.addText(text);
6321 
6322         auto paragraph = builder.Build();
6323         paragraph->layout(950);
6324         paragraph->paint(canvas.get(), 0, 0);
6325         canvas.get()->translate(0, paragraph->getHeight());
6326         return paragraph->getMinIntrinsicWidth();
6327     };
6328 
6329     auto len1 = draw(true);
6330     auto len2 = draw(false);
6331 
6332     // placeholder: 300 "78901": 250
6333     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(len1, 300.0f));
6334     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(len2, 250.0f));
6335 }
6336 
UNIX_ONLY_TEST(SkParagraph_GlyphPositionsInEmptyLines,reporter)6337 UNIX_ONLY_TEST(SkParagraph_GlyphPositionsInEmptyLines, reporter) {
6338 
6339     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6340     if (!fontCollection->fontsFound()) return;
6341 
6342     TestCanvas canvas("SkParagraph_GlyphPositionsInEmptyLines");
6343     ParagraphStyle paragraph_style;
6344     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6345     TextStyle text_style;
6346     text_style.setFontFamilies({SkString("Roboto") });
6347     text_style.setFontSize(20);
6348     text_style.setColor(SK_ColorBLACK);
6349     builder.pushStyle(text_style);
6350     builder.addText("A\n\n");
6351     builder.pop();
6352     auto paragraph = builder.Build();
6353     paragraph->layout(300);
6354     paragraph->paint(canvas.get(), 0, 0);
6355 
6356     auto res1 = paragraph->
6357         getGlyphPositionAtCoordinate(paragraph->getMinIntrinsicWidth(),1);
6358     REPORTER_ASSERT(reporter, res1.position == 1 && res1.affinity == Affinity::kUpstream);
6359 
6360     auto res2 = paragraph->
6361         getGlyphPositionAtCoordinate(0,paragraph->getHeight() * 0.5);
6362     REPORTER_ASSERT(reporter, res2.position == 2 && res2.affinity == Affinity::kDownstream);
6363 
6364     auto res3 = paragraph->
6365         getGlyphPositionAtCoordinate(0,paragraph->getHeight() - 1);
6366     REPORTER_ASSERT(reporter, res3.position == 3 && res3.affinity == Affinity::kDownstream);
6367 }
6368 
UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositions,reporter)6369 UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositions, reporter) {
6370 
6371     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6372     if (!fontCollection->fontsFound()) return;
6373 
6374     TestCanvas canvas("SkParagraph_RTLGlyphPositions");
6375     ParagraphStyle paragraph_style;
6376     paragraph_style.setTextDirection(TextDirection::kRtl);
6377     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6378     TextStyle text_style;
6379     text_style.setFontFamilies({SkString("Roboto") });
6380     text_style.setFontSize(20);
6381     text_style.setColor(SK_ColorBLACK);
6382     builder.pushStyle(text_style);
6383     builder.addText("אאאא");
6384     builder.pop();
6385     auto paragraph = builder.Build();
6386     paragraph->layout(500);
6387     paragraph->paint(canvas.get(), 0, 0);
6388 
6389     std::vector<std::pair<SkScalar, PositionWithAffinity>> checks  = {
6390         std::make_pair(550, PositionWithAffinity(0, Affinity::kDownstream)),
6391         std::make_pair(500, PositionWithAffinity(0, Affinity::kDownstream)),
6392         std::make_pair(494, PositionWithAffinity(1, Affinity::kUpstream)),
6393         std::make_pair(488, PositionWithAffinity(1, Affinity::kDownstream)),
6394         std::make_pair(485, PositionWithAffinity(2, Affinity::kUpstream)),
6395         std::make_pair(480, PositionWithAffinity(2, Affinity::kDownstream)),
6396         std::make_pair(475, PositionWithAffinity(3, Affinity::kUpstream)),
6397         std::make_pair(471, PositionWithAffinity(3, Affinity::kDownstream)),
6398         std::make_pair(467, PositionWithAffinity(4, Affinity::kUpstream)),
6399         std::make_pair(  0, PositionWithAffinity(4, Affinity::kUpstream)),
6400     };
6401 
6402     for (auto check : checks) {
6403         auto pos = paragraph->getGlyphPositionAtCoordinate(check.first, 0);
6404         REPORTER_ASSERT(reporter, pos.affinity == check.second.affinity);
6405         REPORTER_ASSERT(reporter, pos.position == check.second.position);
6406     }
6407 }
6408 
UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositionsInEmptyLines,reporter)6409 UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositionsInEmptyLines, reporter) {
6410 
6411     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6412     if (!fontCollection->fontsFound()) return;
6413 
6414     TestCanvas canvas("SkParagraph_RTLGlyphPositionsInEmptyLines");
6415 
6416     ParagraphStyle paragraph_style;
6417     paragraph_style.setTextDirection(TextDirection::kRtl);
6418     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6419     TextStyle text_style;
6420     text_style.setFontFamilies({SkString("Roboto") });
6421     text_style.setFontSize(20);
6422     text_style.setColor(SK_ColorBLACK);
6423     builder.pushStyle(text_style);
6424     //builder.addText("בבבב\n\nאאאא");
6425     builder.addText("בבבב\n\nאאאא");
6426     builder.pop();
6427     auto paragraph = builder.Build();
6428     paragraph->layout(500);
6429     paragraph->paint(canvas.get(), 0, 0);
6430 
6431     auto height = paragraph->getHeight();
6432     auto res1 = paragraph->getGlyphPositionAtCoordinate(0, 0);
6433     REPORTER_ASSERT(reporter, res1.position == 4 && res1.affinity == Affinity::kUpstream);
6434     auto res2 = paragraph->getGlyphPositionAtCoordinate(0, height / 2);
6435     REPORTER_ASSERT(reporter, res2.position == 5 && res2.affinity == Affinity::kDownstream);
6436     auto res3 = paragraph->getGlyphPositionAtCoordinate(0, height);
6437     REPORTER_ASSERT(reporter, res3.position == 10 && res3.affinity == Affinity::kUpstream);
6438 }
6439 
UNIX_ONLY_TEST(SkParagraph_LTRGlyphPositionsForTrailingSpaces,reporter)6440 UNIX_ONLY_TEST(SkParagraph_LTRGlyphPositionsForTrailingSpaces, reporter) {
6441 
6442     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6443     if (!fontCollection->fontsFound()) return;
6444 
6445     TestCanvas canvas("SkParagraph_LTRGlyphPositionsForTrailingSpaces");
6446 
6447     ParagraphStyle paragraph_style;
6448     TextStyle text_style;
6449     text_style.setFontFamilies({SkString("Ahem") });
6450     text_style.setFontSize(10);
6451     text_style.setColor(SK_ColorBLACK);
6452 
6453     auto test = [&](const char* text) {
6454         auto str = straight(text);
6455         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6456         builder.pushStyle(text_style);
6457         builder.addText(str);
6458         builder.pop();
6459         SkPaint gray; gray.setColor(SK_ColorGRAY);
6460         auto paragraph = builder.Build();
6461         paragraph->layout(100);
6462         canvas.get()->translate(0, 20);
6463         canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, paragraph->getMaxIntrinsicWidth(), paragraph->getHeight()), gray);
6464         paragraph->paint(canvas.get(), 0, 0);
6465         canvas.get()->translate(0, paragraph->getHeight());
6466 
6467         for (size_t i = 0; i < str.size(); ++i) {
6468             auto res = paragraph->getGlyphPositionAtCoordinate(i * 10, 2);
6469             //SkDebugf("@%f[%d]: %d %s\n", i * 10.0f, i, res.position, res.affinity == Affinity::kDownstream ? "D" : "U");
6470             // There is a hidden codepoint at the beginning (to make it symmetric to RTL)
6471             REPORTER_ASSERT(reporter, res.position == SkToInt(i) + (i > 0 ? 1 : 0));
6472             // The ending looks slightly different...
6473             REPORTER_ASSERT(reporter, res.affinity == (res.position == SkToInt(str.size()) ? Affinity::kUpstream : Affinity::kDownstream));
6474         }
6475     };
6476 
6477     test("    ");
6478     test("hello               ");
6479 }
6480 
UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositionsForTrailingSpaces,reporter)6481 UNIX_ONLY_TEST(SkParagraph_RTLGlyphPositionsForTrailingSpaces, reporter) {
6482 
6483     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6484     if (!fontCollection->fontsFound()) return;
6485 
6486     TestCanvas canvas("SkParagraph_RTLGlyphPositionsForTrailingSpaces");
6487 
6488     ParagraphStyle paragraph_style;
6489     paragraph_style.setTextDirection(TextDirection::kRtl);
6490     paragraph_style.setTextAlign(TextAlign::kRight);
6491     TextStyle text_style;
6492     text_style.setFontFamilies({SkString("Ahem") });
6493     text_style.setFontSize(10);
6494     text_style.setColor(SK_ColorBLACK);
6495     canvas.get()->translate(200, 0);
6496 
6497     auto test = [&](const char* text, int whitespaces) {
6498         auto str = mirror(text);
6499         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6500         builder.pushStyle(text_style);
6501         builder.addText(str);
6502         builder.pop();
6503         SkPaint gray; gray.setColor(SK_ColorGRAY);
6504         auto paragraph = builder.Build();
6505         paragraph->layout(100);
6506         canvas.get()->translate(0, 20);
6507         auto res = paragraph->getRectsForRange(0, str.size(), RectHeightStyle::kTight, RectWidthStyle::kTight);
6508         bool even = true;
6509         for (auto& r : res) {
6510             if (even) {
6511                 gray.setColor(SK_ColorGRAY);
6512             } else {
6513                 gray.setColor(SK_ColorLTGRAY);
6514             }
6515             even = !even;
6516             canvas.get()->drawRect(r.rect, gray);
6517         }
6518         gray.setColor(SK_ColorRED);
6519         canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, 1, paragraph->getHeight()), gray);
6520         paragraph->paint(canvas.get(), 0, 0);
6521         canvas.get()->translate(0, paragraph->getHeight());
6522 
6523         for (int i = 0; i < SkToInt(str.size()); ++i) {
6524             auto pointX = (whitespaces + i) * 10.0f;
6525             auto pos = paragraph->getGlyphPositionAtCoordinate(pointX, 2);
6526             //SkDebugf("@%f[%d]: %d %s\n", pointX, i, pos.position, pos.affinity == Affinity::kDownstream ? "D" : "U");
6527             // At the beginning there is a control codepoint that makes the string RTL
6528             REPORTER_ASSERT(reporter, (pos.position + i) == SkToInt(str.size()) - (pos.affinity == Affinity::kDownstream ? 1 : 0));
6529         }
6530     };
6531 
6532     test("    ", 6);
6533     test("               hello", -10);
6534 }
6535 
UNIX_ONLY_TEST(SkParagraph_LTRLineMetricsDoesNotIncludeNewLine,reporter)6536 UNIX_ONLY_TEST(SkParagraph_LTRLineMetricsDoesNotIncludeNewLine, reporter) {
6537 
6538     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6539     if (!fontCollection->fontsFound()) return;
6540 
6541     TestCanvas canvas("SkParagraph_LTRLineMetricsDoesNotIncludeNewLine");
6542 
6543     ParagraphStyle paragraph_style;
6544     paragraph_style.setTextDirection(TextDirection::kRtl);
6545     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6546     TextStyle text_style;
6547     text_style.setFontFamilies({SkString("Roboto") });
6548     text_style.setFontSize(20);
6549     text_style.setColor(SK_ColorBLACK);
6550     builder.pushStyle(text_style);
6551     builder.addText("one two\n\nthree four\nwith spaces     \n    \n______________________");
6552     builder.pop();
6553     auto paragraph = builder.Build();
6554     paragraph->layout(190);
6555     paragraph->paint(canvas.get(), 0, 0);
6556 
6557     std::vector<std::tuple<size_t, size_t, size_t, size_t>> expected = {
6558             { 0, 7, 7, 8 },             // one two\n
6559             { 8, 8, 8, 9 },             // \n
6560             { 9, 19, 19, 20 },          // three four\n
6561             { 20, 31, 36, 37 },         // with spaces    \n
6562             { 37, 37, 41, 42 },         //      { just spaces }\n
6563             { 42, 63, 63, 63 },         // _____________________
6564             { 63, 64, 64, 64 },         // _
6565     };
6566     std::vector<LineMetrics> metrics;
6567     paragraph->getLineMetrics(metrics);
6568     for (auto& metric : metrics) {
6569         //SkDebugf("Line[%d:%d <= %d <=%d)\n", metric.fStartIndex, metric.fEndExcludingWhitespaces, metric.fEndIndex, metric.fEndIncludingNewline);
6570         auto result = expected[metric.fLineNumber];
6571         REPORTER_ASSERT(reporter, metric.fStartIndex ==std::get<0>(result));
6572         REPORTER_ASSERT(reporter, metric.fEndExcludingWhitespaces == std::get<1>(result));
6573         REPORTER_ASSERT(reporter, metric.fEndIndex == std::get<2>(result));
6574         REPORTER_ASSERT(reporter, metric.fEndIncludingNewline == std::get<3>(result));
6575     }
6576 }
6577 
UNIX_ONLY_TEST(SkParagraph_RTLLineMetricsDoesNotIncludeNewLine,reporter)6578 UNIX_ONLY_TEST(SkParagraph_RTLLineMetricsDoesNotIncludeNewLine, reporter) {
6579 
6580     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6581     if (!fontCollection->fontsFound()) return;
6582 
6583     TestCanvas canvas("SkParagraph_RTLLineMetricsDoesNotIncludeNewLine");
6584     canvas.get()->translate(100, 100);
6585 
6586     ParagraphStyle paragraph_style;
6587     paragraph_style.setTextDirection(TextDirection::kRtl);
6588     paragraph_style.setTextAlign(TextAlign::kRight);
6589     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6590     TextStyle text_style;
6591     text_style.setFontFamilies({SkString("Roboto") });
6592     text_style.setFontSize(20);
6593     text_style.setColor(SK_ColorBLACK);
6594     builder.pushStyle(text_style);
6595     builder.addText(mirror("______________________\none two\n\nthree four\nwith spaces     \n    "));
6596     builder.pop();
6597     auto paragraph = builder.Build();
6598     paragraph->layout(190);
6599     paragraph->paint(canvas.get(), 0, 0);
6600     //auto impl = static_cast<ParagraphImpl*>(paragraph.get());
6601 
6602     SkPaint gray;
6603     gray.setColor(SK_ColorGRAY);
6604     gray.setStyle(SkPaint::kStroke_Style);
6605     gray.setAntiAlias(true);
6606     gray.setStrokeWidth(1);
6607     canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, paragraph->getMaxWidth(), paragraph->getHeight()), gray);
6608 
6609     SkPaint red;
6610     red.setColor(SK_ColorRED);
6611     red.setStyle(SkPaint::kStroke_Style);
6612     red.setAntiAlias(true);
6613     red.setStrokeWidth(1);
6614 
6615     SkPaint blue;
6616     blue.setColor(SK_ColorRED);
6617     blue.setStyle(SkPaint::kStroke_Style);
6618     blue.setAntiAlias(true);
6619     blue.setStrokeWidth(1);
6620 
6621     auto boxes = paragraph->getRectsForRange(0, 100, RectHeightStyle::kTight, RectWidthStyle::kTight);
6622     bool even = false;
6623     for (auto& box : boxes) {
6624         canvas.get()->drawRect(box.rect, even ? red : blue);
6625         even = !even;
6626     }
6627 
6628     // RTL codepoint u"\u202E" messes everything up
6629     // (adds one invisible codepoint to the first line
6630     // and shift all the indexes by 1 right)
6631     std::vector<std::tuple<int, int, int, int>> expected = {
6632             { 0, 1, 5, 6 },                 //      { just spaces; the end of the text considered as a new line in libtxt?!? }
6633             { 6, 22, 22, 23  },             // with spaces    \n
6634             { 23, 33, 33, 34 },             // three four\n
6635             { 34, 34, 34, 35 },             // \n
6636             { 35, 42, 42, 43 },             // one two\n
6637             { 43, 64, 64, 64 },             // _____________________
6638             { 64, 65, 65, 65 }              // _
6639     };
6640 
6641     std::vector<LineMetrics> metrics;
6642     paragraph->getLineMetrics(metrics);
6643     for (auto& metric : metrics) {
6644         //SkDebugf("Line[%d:%d <= %d <=%d]\n", metric.fStartIndex, metric.fEndExcludingWhitespaces, metric.fEndIndex, metric.fEndIncludingNewline);
6645         auto result = expected[metric.fLineNumber];
6646         REPORTER_ASSERT(reporter, metric.fStartIndex == SkToU32(std::get<0>(result)));
6647         REPORTER_ASSERT(reporter, metric.fEndExcludingWhitespaces == SkToU32(std::get<1>(result)));
6648         REPORTER_ASSERT(reporter, metric.fEndIndex == SkToU32(std::get<2>(result)));
6649         REPORTER_ASSERT(reporter, metric.fEndIncludingNewline == SkToU32(std::get<3>(result)));
6650     }
6651 }
6652 
UNIX_ONLY_TEST(SkParagraph_PlaceholderPosition,reporter)6653 UNIX_ONLY_TEST(SkParagraph_PlaceholderPosition, reporter) {
6654     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6655     if (!fontCollection->fontsFound()) return;
6656 
6657     TestCanvas canvas("SkParagraph_PlaceholderPosition.png");
6658     canvas.get()->translate(100, 100);
6659 
6660     TextStyle text_style;
6661     text_style.setColor(SK_ColorBLACK);
6662     text_style.setFontFamilies({SkString("Ahem")});
6663     text_style.setFontSize(10.0f);
6664     ParagraphStyle paragraph_style;
6665     paragraph_style.setTextStyle(text_style);
6666     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6667     builder.pushStyle(text_style);
6668     builder.addText("abcd");
6669 
6670     PlaceholderStyle placeholder_style;
6671     placeholder_style.fHeight = 10;
6672     placeholder_style.fWidth = 10;
6673     placeholder_style.fBaseline = TextBaseline::kAlphabetic;
6674     placeholder_style.fAlignment = PlaceholderAlignment::kBottom;
6675     builder.addPlaceholder(placeholder_style);
6676 
6677     auto paragraph = builder.Build();
6678     paragraph->layout(500);
6679     paragraph->paint(canvas.get(), 0, 0);
6680 
6681     auto result = paragraph->getGlyphPositionAtCoordinate(41.0f, 0.0f);
6682     REPORTER_ASSERT(reporter, result.position == 4 && result.affinity == Affinity::kDownstream);
6683 }
6684 
UNIX_ONLY_TEST(SkParagraph_LineEnd,reporter)6685 UNIX_ONLY_TEST(SkParagraph_LineEnd, reporter) {
6686     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6687     if (!fontCollection->fontsFound()) return;
6688 
6689     TestCanvas canvas("SkParagraph_LineEnd.png");
6690     canvas.get()->translate(100, 100);
6691 
6692     TextStyle text_style;
6693     text_style.setColor(SK_ColorBLACK);
6694     text_style.setFontFamilies({SkString("Ahem")});
6695     text_style.setFontSize(10.0f);
6696     ParagraphStyle paragraph_style;
6697     paragraph_style.setTextStyle(text_style);
6698     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6699     builder.pushStyle(text_style);
6700     builder.addText("Hello ");
6701     builder.addText("hello   ");
6702     builder.addText("hello\n");
6703     builder.addText("hello   \n");
6704     builder.addText("world");
6705 
6706     auto paragraph = builder.Build();
6707     paragraph->layout(60.0f);
6708     paragraph->paint(canvas.get(), 0, 0);
6709 
6710     std::vector<LineMetrics> lm;
6711     paragraph->getLineMetrics(lm);
6712     /*
6713     for (auto& lm : lm) {
6714         SkDebugf("%d %d %d\n", (int)lm.fEndExcludingWhitespaces, (int)lm.fEndIndex, (int)lm.fEndIncludingNewline);
6715     }
6716     */
6717     REPORTER_ASSERT(reporter, lm[0].fEndExcludingWhitespaces == 05 && lm[0].fEndIndex == 06 && lm[0].fEndIncludingNewline == 06);
6718     REPORTER_ASSERT(reporter, lm[1].fEndExcludingWhitespaces == 11 && lm[1].fEndIndex == 14 && lm[1].fEndIncludingNewline == 14);
6719     REPORTER_ASSERT(reporter, lm[2].fEndExcludingWhitespaces == 19 && lm[2].fEndIndex == 19 && lm[2].fEndIncludingNewline == 20);
6720     REPORTER_ASSERT(reporter, lm[3].fEndExcludingWhitespaces == 25 && lm[3].fEndIndex == 28 && lm[3].fEndIncludingNewline == 29);
6721 }
6722 
UNIX_ONLY_TEST(SkParagraph_Utf16Indexes,reporter)6723 UNIX_ONLY_TEST(SkParagraph_Utf16Indexes, reporter) {
6724     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6725     if (!fontCollection->fontsFound()) return;
6726 
6727     TestCanvas canvas("SkParagraph_Utf16Indexes.png");
6728     canvas.get()->translate(100, 100);
6729 
6730     TextStyle text_style;
6731     text_style.setColor(SK_ColorBLACK);
6732     text_style.setFontFamilies({SkString("Ahem")});
6733     text_style.setFontSize(10.0f);
6734     ParagraphStyle paragraph_style;
6735     paragraph_style.setTextStyle(text_style);
6736     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6737     builder.pushStyle(text_style);
6738     builder.addText("áéíóú\nxxxx");
6739     auto paragraph = builder.Build();
6740     paragraph->layout(60.0f);
6741     paragraph->paint(canvas.get(), 0, 0);
6742     std::vector<LineMetrics> lm;
6743     paragraph->getLineMetrics(lm);
6744     //for (auto& lm : lm) {
6745     //    SkDebugf("%d %d %d\n", (int)lm.fEndExcludingWhitespaces, (int)lm.fEndIndex, (int)lm.fEndIncludingNewline);
6746     //}
6747     REPORTER_ASSERT(reporter, lm[0].fEndExcludingWhitespaces == 05 && lm[0].fEndIndex == 05 && lm[0].fEndIncludingNewline == 06);
6748     REPORTER_ASSERT(reporter, lm[1].fEndExcludingWhitespaces == 10 && lm[1].fEndIndex == 10 && lm[1].fEndIncludingNewline == 10);
6749 }
6750 
UNIX_ONLY_TEST(SkParagraph_RTLFollowedByLTR,reporter)6751 UNIX_ONLY_TEST(SkParagraph_RTLFollowedByLTR, reporter) {
6752     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6753     if (!fontCollection->fontsFound()) return;
6754 
6755     TestCanvas canvas("SkParagraph_RTLFollowedByLTR.png");
6756     canvas.get()->translate(100, 100);
6757 
6758     TextStyle text_style;
6759     text_style.setFontFamilies({SkString("Ahem")});
6760     text_style.setFontSize(10);
6761 
6762     ParagraphStyle paragraph_style;
6763     paragraph_style.setTextStyle(text_style);
6764     paragraph_style.setTextDirection(TextDirection::kLtr);
6765     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6766     builder.pushStyle(text_style);
6767     builder.addText(u"\u05D0\u05D0\u05D0ABC");
6768     auto paragraph = builder.Build();
6769     paragraph->layout(100);
6770     paragraph->paint(canvas.get(), 0, 0);
6771 
6772     auto boxes = paragraph->getRectsForRange(
6773             0, paragraph->getMaxWidth(), RectHeightStyle::kTight, RectWidthStyle::kTight);
6774     REPORTER_ASSERT(reporter, boxes.size() == 2);
6775     REPORTER_ASSERT(
6776             reporter,
6777             boxes[0].direction == TextDirection::kRtl && boxes[1].direction == TextDirection::kLtr);
6778     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fLeft, 0.0f));
6779     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fRight, boxes[1].rect.fLeft));
6780     REPORTER_ASSERT(reporter,
6781                     SkScalarNearlyEqual(boxes[1].rect.fRight, paragraph->getMaxIntrinsicWidth()));
6782 
6783     std::vector<std::pair<SkScalar, PositionWithAffinity>> checks  = {
6784         std::make_pair(-10, PositionWithAffinity(3, Affinity::kUpstream)),
6785         std::make_pair(  0, PositionWithAffinity(3, Affinity::kUpstream)),
6786         std::make_pair(  5, PositionWithAffinity(2, Affinity::kDownstream)),
6787         std::make_pair( 10, PositionWithAffinity(2, Affinity::kUpstream)),
6788         std::make_pair( 15, PositionWithAffinity(1, Affinity::kDownstream)),
6789         std::make_pair( 20, PositionWithAffinity(1, Affinity::kUpstream)),
6790         std::make_pair( 25, PositionWithAffinity(0, Affinity::kDownstream)),
6791         std::make_pair( 30, PositionWithAffinity(3, Affinity::kDownstream)),
6792         std::make_pair( 35, PositionWithAffinity(4, Affinity::kUpstream)),
6793         std::make_pair( 40, PositionWithAffinity(4, Affinity::kDownstream)),
6794         std::make_pair( 45, PositionWithAffinity(5, Affinity::kUpstream)),
6795         std::make_pair( 50, PositionWithAffinity(5, Affinity::kDownstream)),
6796         std::make_pair( 55, PositionWithAffinity(6, Affinity::kUpstream)),
6797         std::make_pair( 60, PositionWithAffinity(6, Affinity::kUpstream)),
6798     };
6799 
6800     for (auto check : checks) {
6801         auto pos = paragraph->getGlyphPositionAtCoordinate(check.first, 0);
6802         REPORTER_ASSERT(reporter, pos.affinity == check.second.affinity);
6803         REPORTER_ASSERT(reporter, pos.position == check.second.position);
6804     }
6805 }
6806 
UNIX_ONLY_TEST(SkParagraph_StrutTopLine,reporter)6807 UNIX_ONLY_TEST(SkParagraph_StrutTopLine, reporter) {
6808     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6809     if (!fontCollection->fontsFound()) return;
6810 
6811     TestCanvas canvas("SkParagraph_StrutTopLine.png");
6812 
6813     TextStyle text_style;
6814     text_style.setFontFamilies({SkString("Ahem")});
6815     text_style.setFontSize(10);
6816     SkPaint black;
6817     black.setColor(SK_ColorBLACK);
6818     text_style.setForegroundColor(black);
6819 
6820     ParagraphStyle paragraph_style;
6821     paragraph_style.setTextStyle(text_style);
6822     paragraph_style.setTextDirection(TextDirection::kLtr);
6823     StrutStyle strut_style;
6824     strut_style.setStrutEnabled(true);
6825     strut_style.setFontFamilies({SkString("Ahem")});
6826     strut_style.setFontSize(16);
6827     strut_style.setHeight(4.0f);
6828     strut_style.setHeightOverride(true);
6829     strut_style.setLeading(-1.0f);
6830     strut_style.setForceStrutHeight(true);
6831     paragraph_style.setStrutStyle(strut_style);
6832     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6833 
6834     builder.pushStyle(text_style);
6835     builder.addText(u"Atwater Peel Sherbrooke Bonaventure\nhi\nwasssup!");
6836 
6837     auto paragraph = builder.Build();
6838     paragraph->layout(797);
6839     paragraph->paint(canvas.get(), 0, 0);
6840     auto boxes = paragraph->getRectsForRange(0, 60, RectHeightStyle::kIncludeLineSpacingTop, RectWidthStyle::kMax);
6841     REPORTER_ASSERT(reporter, boxes.size() == 4);
6842     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fTop, 38.4f));
6843     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fBottom, 64.0f));
6844 
6845     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.fTop, 64.0f));
6846     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.fBottom, 128.0f));
6847 
6848     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.fTop, 64.0f));
6849     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.fBottom, 128.0f));
6850 
6851     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.fTop, 128.0f));
6852     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.fBottom, 192.0f));
6853 }
6854 
UNIX_ONLY_TEST(SkParagraph_DifferentFontsTopLine,reporter)6855 UNIX_ONLY_TEST(SkParagraph_DifferentFontsTopLine, reporter) {
6856     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6857     if (!fontCollection->fontsFound()) return;
6858 
6859     TestCanvas canvas("SkParagraph_DifferentFontsTopLine.png");
6860 
6861     TextStyle text_style;
6862     text_style.setFontFamilies({SkString("Ahem")});
6863     text_style.setFontSize(10);
6864     SkPaint black;
6865     black.setColor(SK_ColorBLACK);
6866     text_style.setForegroundColor(black);
6867 
6868     ParagraphStyle paragraph_style;
6869     paragraph_style.setTextStyle(text_style);
6870     paragraph_style.setTextDirection(TextDirection::kLtr);
6871     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6872 
6873     text_style.setFontSize(30.0);
6874     builder.pushStyle(text_style);
6875     builder.addText(u"Atwater Peel ");
6876     text_style.setFontSize(15.0);
6877     builder.pushStyle(text_style);
6878     builder.addText(u"Sherbrooke Bonaventure ");
6879     text_style.setFontSize(10.0);
6880     builder.pushStyle(text_style);
6881     builder.addText(u"hi wassup!");
6882 
6883     auto paragraph = builder.Build();
6884     paragraph->layout(797);
6885     paragraph->paint(canvas.get(), 0, 0);
6886     auto boxes = paragraph->getRectsForRange(0, 60, RectHeightStyle::kIncludeLineSpacingTop, RectWidthStyle::kMax);
6887     REPORTER_ASSERT(reporter, boxes.size() == 4);
6888     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fTop, 00.0f));
6889     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.fBottom, 30.0f));
6890 
6891     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.fTop, 00.0f));
6892     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.fBottom, 30.0f));
6893 
6894     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.fTop, 00.0f));
6895     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.fBottom, 30.0f));
6896 
6897     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.fTop, 30.0f));
6898     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.fBottom, 40.0f));
6899 }
6900 
UNIX_ONLY_TEST(SkParagraph_SimpleParagraphReset,reporter)6901 UNIX_ONLY_TEST(SkParagraph_SimpleParagraphReset, reporter) {
6902     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6903     if (!fontCollection->fontsFound()) return;
6904     const char* text = "Hello World Text Dialog";
6905     const size_t len = strlen(text);
6906 
6907     ParagraphStyle paragraph_style;
6908     paragraph_style.turnHintingOff();
6909     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6910 
6911     for (int iteration = 0; iteration < 2; iteration += 1) {
6912         builder.Reset();
6913         REPORTER_ASSERT(reporter, builder.peekStyle().equals(paragraph_style.getTextStyle()));
6914 
6915         TextStyle text_style;
6916         text_style.setFontFamilies({SkString("Roboto")});
6917         text_style.setColor(SK_ColorBLACK);
6918         builder.pushStyle(text_style);
6919         builder.addText(text, len);
6920         builder.pop();
6921 
6922         auto paragraph = builder.Build();
6923         paragraph->layout(TestCanvasWidth);
6924         REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
6925 
6926         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
6927         REPORTER_ASSERT(reporter, impl->runs().size() == 1);
6928         REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
6929         REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
6930 
6931         size_t index = 0;
6932         for (auto& line : impl->lines()) {
6933             line.scanStyles(StyleType::kDecorations,
6934                             [&index, reporter]
6935                             (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
6936                                 REPORTER_ASSERT(reporter, index == 0);
6937                                 REPORTER_ASSERT(reporter, style.getColor() == SK_ColorBLACK);
6938                                 ++index;
6939                             });
6940         }
6941     }
6942 }
6943 
UNIX_ONLY_TEST(SkParagraph_EllipsisGetRectForRange,reporter)6944 UNIX_ONLY_TEST(SkParagraph_EllipsisGetRectForRange, reporter) {
6945     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6946     if (!fontCollection->fontsFound()) return;
6947     TestCanvas canvas("SkParagraph_EllipsisGetRectForRange.png");
6948     const char* text =
6949             "This is a very long sentence to test if the text will properly wrap "
6950             "around and go to the next line. Sometimes, short sentence. Longer "
6951             "sentences are okay too because they are nessecary. Very short. ";
6952     const size_t len = strlen(text);
6953 
6954     ParagraphStyle paragraph_style;
6955     paragraph_style.setMaxLines(1);
6956     std::u16string ellipsis = u"\u2026";
6957     paragraph_style.setEllipsis(ellipsis);
6958     std::u16string e = paragraph_style.getEllipsisUtf16();
6959     paragraph_style.turnHintingOff();
6960     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
6961 
6962     TextStyle text_style;
6963     text_style.setFontFamilies({SkString("Roboto")});
6964     text_style.setColor(SK_ColorBLACK);
6965     builder.pushStyle(text_style);
6966     builder.addText(text, len);
6967     builder.pop();
6968 
6969     auto paragraph = builder.Build();
6970     paragraph->layout(TestCanvasWidth);
6971     paragraph->paint(canvas.get(), 0, 0);
6972 
6973     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
6974 
6975     // Check that the ellipsizer limited the text to one line and did not wrap to a second line.
6976     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
6977 
6978     auto boxes1 = impl->getRectsForRange(0, 2, RectHeightStyle::kTight, RectWidthStyle::kTight);
6979     REPORTER_ASSERT(reporter, boxes1.size() == 1);
6980 
6981     auto boxes2 = impl->getRectsForRange(0, 3, RectHeightStyle::kTight, RectWidthStyle::kTight);
6982     REPORTER_ASSERT(reporter, boxes2.size() == 1);
6983 
6984     canvas.drawRects(SK_ColorRED, boxes1);
6985     canvas.drawRects(SK_ColorRED, boxes2);
6986 }
6987 
UNIX_ONLY_TEST(SkParagraph_StrutAndTextBehavior,reporter)6988 UNIX_ONLY_TEST(SkParagraph_StrutAndTextBehavior, reporter) {
6989     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
6990     if (!fontCollection->fontsFound()) return;
6991     TestCanvas canvas("SkParagraph_StrutAndTextBehavior.png");
6992     const char* text = " ";
6993     const size_t len = strlen(text);
6994 
6995     TextStyle text_style;
6996     text_style.setFontFamilies({SkString("Ahem")});
6997     text_style.setFontSize(16.0);
6998     text_style.setColor(SK_ColorBLACK);
6999     StrutStyle strut_style;
7000     strut_style.setStrutEnabled(true);
7001     strut_style.setForceStrutHeight(true);
7002     strut_style.setHeight(1.5);
7003     strut_style.setHeightOverride(true);
7004     strut_style.setFontFamilies({SkString("Ahem")});
7005     strut_style.setFontSize(16.0);
7006     ParagraphStyle paragraph_style;
7007     paragraph_style.setStrutStyle(strut_style);
7008     paragraph_style.setTextStyle(text_style);
7009 
7010     auto draw = [&](TextHeightBehavior tb) {
7011         paragraph_style.setTextHeightBehavior(tb);
7012         TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
7013         builder.pushStyle(text_style);
7014         builder.addText(text, len);
7015         auto paragraph = builder.Build();
7016         paragraph->layout(SK_ScalarInfinity);
7017         return paragraph->getHeight();
7018     };
7019 
7020     auto height1 = draw(TextHeightBehavior::kDisableAll);
7021     auto height2 = draw(TextHeightBehavior::kAll);
7022 
7023     // Regardless of TextHeightBehavior strut sets the line height
7024     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(height1, 24.0f));
7025     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(height2, 24.0f));
7026 }
7027 
UNIX_ONLY_TEST(SkParagraph_NonMonotonicGlyphsLTR,reporter)7028 UNIX_ONLY_TEST(SkParagraph_NonMonotonicGlyphsLTR, reporter) {
7029     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7030     if (!fontCollection->fontsFound()) return;
7031     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
7032     fontCollection->enableFontFallback();
7033 
7034     TestCanvas canvas("SkParagraph_NonMonotonicGlyphsLTR.png");
7035     std::u16string text =
7036         u"\u0068\u0301\u0350\u0312\u0357\u030C\u0369\u0305\u036C\u0304\u0310\u033F\u0366\u0350 ";
7037             /*
7038             u"\u0343\u0364\u0369\u0311\u0309\u030E\u0365\u031B\u0340\u0337\u0335\u035E\u0334\u0328"
7039             u"\u0360\u0360\u0315\u035F\u0340\u0340\u0362\u0360\u0322\u031B\u031B\u0337\u0340\u031E"
7040             u"\u031F\u032A\u0331\u0345\u032F\u0332\u032E\u0333\u0353\u0320\u0345\u031C\u031F\u033C"
7041             u"\u0325\u0355\u032C\u0325\u033Aa\u0307\u0312\u034B\u0308\u0312\u0346\u0313\u0346\u0304"
7042             u"\u0307\u0344\u0305\u0342\u0368\u0346\u036A\u035B\u030F\u0365\u0307\u0340\u0328\u0322"
7043             u"\u0361\u0489\u034F\u0328\u0334\u035F\u0335\u0362\u0489\u0360\u0358\u035E\u0360\u035D"
7044             u"\u0341\u0337\u0337\u032E\u0326\u032D\u0359\u0318\u033C\u032F\u0333\u035A\u034D\u0319"
7045             u"\u031C\u0353\u033C\u0345\u0359\u0331\u033B\u0331\u033C";
7046             */
7047 
7048     TextStyle text_style;
7049     text_style.setFontSize(14);
7050     text_style.setFontFamilies({SkString("Roboto")});
7051     text_style.setColor(SK_ColorBLACK);
7052 
7053     ParagraphStyle paragraph_style;
7054     paragraph_style.setTextStyle(text_style);
7055     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
7056 
7057     builder.pushStyle(text_style);
7058     builder.addText(text);
7059     auto paragraph = builder.Build();
7060     paragraph->layout(SK_ScalarInfinity);
7061 
7062     paragraph->layout(paragraph->getMinIntrinsicWidth() + 1);
7063     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
7064     REPORTER_ASSERT(reporter, impl->runs().size() > 1); // It's not the simple case
7065     bool hasNonMonotonicPlacement = false;
7066     for (auto& run : impl->runs()) {
7067         for (auto& offset : run.offsets()) {
7068             if (offset.fX < 0) {
7069                 hasNonMonotonicPlacement = true;
7070             }
7071         }
7072         if (hasNonMonotonicPlacement) {
7073             break;
7074         }
7075     }
7076     REPORTER_ASSERT(reporter, hasNonMonotonicPlacement);    // There are non-monotonic placement
7077     REPORTER_ASSERT(reporter, impl->lineNumber() == 1);     // But it's still one line
7078     paragraph->paint(canvas.get(), 0, 0);
7079 }
7080 
UNIX_ONLY_TEST(SkParagraph_NonMonotonicGlyphsRTL,reporter)7081 UNIX_ONLY_TEST(SkParagraph_NonMonotonicGlyphsRTL, reporter) {
7082     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7083     if (!fontCollection->fontsFound()) return;
7084     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
7085     fontCollection->enableFontFallback();
7086 
7087     TestCanvas canvas("SkParagraph_NonMonotonicGlyphsRTL.png");
7088     const char* text = "ٱلْرَّحْمَـانُ";
7089     const size_t len = strlen(text);
7090 
7091     TextStyle text_style;
7092     text_style.setFontSize(14);
7093     text_style.setColor(SK_ColorBLACK);
7094 
7095     ParagraphStyle paragraph_style;
7096     paragraph_style.setTextStyle(text_style);
7097     TestParagraphBuilderImpl builder(paragraph_style, fontCollection);
7098 
7099     builder.pushStyle(text_style);
7100     builder.addText(text, len);
7101     auto paragraph = builder.Build();
7102     paragraph->layout(SK_ScalarInfinity);
7103 
7104     paragraph->layout(paragraph->getMinIntrinsicWidth() + 1);
7105     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
7106     REPORTER_ASSERT(reporter, impl->runs().size() > 1); // It's not the simple case
7107     bool hasNonMonotonicPlacement = false;
7108     for (auto& run : impl->runs()) {
7109         for (auto& offset : run.offsets()) {
7110             if (offset.fX < 0) {
7111                 hasNonMonotonicPlacement = true;
7112             }
7113         }
7114         if (hasNonMonotonicPlacement) {
7115             break;
7116         }
7117     }
7118     REPORTER_ASSERT(reporter, impl->lineNumber() == 1);  // But it's still one line
7119     paragraph->paint(canvas.get(), 0, 0);
7120 }
7121 
performGetRectsForRangeConcurrently(skiatest::Reporter * reporter)7122 void performGetRectsForRangeConcurrently(skiatest::Reporter* reporter) {
7123     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7124     if (!fontCollection->fontsFound()) {
7125         INFOF(reporter, "No fonts found\n");
7126         return;
7127     }
7128     auto const text = std::u16string(42000, 'x');
7129     ParagraphStyle paragraphStyle;
7130     TextStyle textStyle;
7131     textStyle.setFontFamilies({SkString("Roboto")});
7132     textStyle.setFontSize(14);
7133     textStyle.setColor(SK_ColorBLACK);
7134     textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
7135                                        SkFontStyle::kUpright_Slant));
7136 
7137     TestParagraphBuilderImpl builder(paragraphStyle, fontCollection);
7138     builder.pushStyle(textStyle);
7139     builder.addText(text);
7140     builder.pop();
7141 
7142     auto paragraph = builder.Build();
7143     paragraph->layout(std::numeric_limits<float>::max());
7144 
7145     RectHeightStyle heightStyle = RectHeightStyle::kMax;
7146     RectWidthStyle widthStyle = RectWidthStyle::kMax;
7147     auto t1 = std::thread([&] {
7148         auto result = paragraph->getRectsForRange(0, 2, heightStyle, widthStyle);
7149         REPORTER_ASSERT(reporter, !result.empty());
7150     });
7151     auto t2 = std::thread([&] {
7152         auto result = paragraph->getRectsForRange(5, 10, heightStyle, widthStyle);
7153         REPORTER_ASSERT(reporter, !result.empty());
7154     });
7155     t1.join();
7156     t2.join();
7157 }
7158 
UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeConcurrently,reporter)7159 UNIX_ONLY_TEST(SkParagraph_GetRectsForRangeConcurrently, reporter) {
7160     auto const threads_count = 100;
7161     std::thread threads[threads_count];
7162     for (auto& thread : threads) {
7163         thread = std::thread(performGetRectsForRangeConcurrently, reporter);
7164     }
7165     for (auto& thread : threads) {
7166         thread.join();
7167     }
7168 }
7169 
UNIX_ONLY_TEST(SkParagraph_TabSubstitution,reporter)7170 UNIX_ONLY_TEST(SkParagraph_TabSubstitution, reporter) {
7171     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7172     if (!fontCollection->fontsFound()) return;
7173 
7174     TestCanvas canvas("SkParagraph_TabSubstitution.png");
7175 
7176     ParagraphStyle paragraph_style;
7177     paragraph_style.setReplaceTabCharacters(true);
7178 
7179     TextStyle text_style;
7180     text_style.setColor(SK_ColorBLACK);
7181     text_style.setFontFamilies({SkString("Roboto")});
7182     text_style.setFontSize(100);
7183 
7184     TestParagraphBuilderImpl builder1(paragraph_style, fontCollection);
7185     builder1.pushStyle(text_style);
7186     builder1.addText("There is a tab>\t<right here");
7187     auto paragraph1 = builder1.Build();
7188     paragraph1->layout(TestCanvasWidth);
7189     paragraph1->paint(canvas.get(), 0, 0);
7190 
7191     paragraph_style.setReplaceTabCharacters(false);
7192     TestParagraphBuilderImpl builder2(paragraph_style, fontCollection);
7193     builder2.pushStyle(text_style);
7194     builder2.addText("There is a tab>\t<right here");
7195     auto paragraph2 = builder2.Build();
7196     paragraph2->layout(TestCanvasWidth);
7197     paragraph2->paint(canvas.get(), 0, 0);
7198 
7199     // Second paragraph has an unresolved \t (glyph == 0)
7200     REPORTER_ASSERT(reporter, ((ParagraphImpl*)paragraph1.get())->runs()[0].glyphs()[15] != 0);
7201     REPORTER_ASSERT(reporter, ((ParagraphImpl*)paragraph2.get())->runs()[0].glyphs()[15] == 0);
7202     // Notice, that the cache didn't work for the second paragraph - as it should not
7203     REPORTER_ASSERT(reporter, 2 == fontCollection->getParagraphCache()->count());
7204 }
7205 
DEF_TEST(SkParagraph_lineMetricsWithEllipsis,reporter)7206 DEF_TEST(SkParagraph_lineMetricsWithEllipsis, reporter) {
7207     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7208     if (!fontCollection->fontsFound()) return;
7209     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
7210     fontCollection->enableFontFallback();
7211 
7212     ParagraphStyle paragraph_style;
7213     paragraph_style.setMaxLines(1);
7214     std::u16string ellipsis = u"\u2026";
7215     paragraph_style.setEllipsis(ellipsis);
7216 
7217     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7218     builder.addText("hello");
7219 
7220     auto paragraph = builder.Build();
7221     paragraph->layout(1.);
7222 
7223     std::vector<LineMetrics> lm;
7224     paragraph->getLineMetrics(lm);
7225     REPORTER_ASSERT(reporter, lm.size() == 1);
7226 }
7227 
DEF_TEST(SkParagraph_lineMetricsAfterUpdate,reporter)7228 DEF_TEST(SkParagraph_lineMetricsAfterUpdate, reporter) {
7229     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7230     if (!fontCollection->fontsFound()) return;
7231     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
7232     fontCollection->enableFontFallback();
7233 
7234     auto text = std::u16string(u"hello world");
7235 
7236     ParagraphStyle paragraph_style;
7237 
7238     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7239     builder.addText(text);
7240 
7241     auto paragraph = builder.Build();
7242     paragraph->layout(200.);
7243 
7244     std::vector<LineMetrics> lm;
7245     paragraph->getLineMetrics(lm);
7246     REPORTER_ASSERT(reporter, lm.size() == 1);
7247 
7248     paragraph->updateFontSize(0, text.size(), 42);
7249     paragraph->layout(200.);
7250     paragraph->getLineMetrics(lm);
7251     REPORTER_ASSERT(reporter, lm.size() == 2);
7252 }
7253 
7254 // Google logo is shown in one style (the first one)
UNIX_ONLY_TEST(SkParagraph_MultiStyle_Logo,reporter)7255 UNIX_ONLY_TEST(SkParagraph_MultiStyle_Logo, reporter) {
7256     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7257     if (!fontCollection->fontsFound()) return;
7258 
7259     TestCanvas canvas("SkParagraph_MultiStyle_Logo.png");
7260 
7261     canvas.get()->drawColor(SK_ColorWHITE);
7262     SkScalar width = TestCanvasWidth;
7263     SkScalar height = TestCanvasHeight/2;
7264 
7265     SkAutoCanvasRestore acr(canvas.get(), true);
7266     canvas.get()->clipRect(SkRect::MakeWH(width, height));
7267 
7268     TextStyle style;
7269     style.setFontFamilies({SkString("Google Sans")});
7270     style.setFontSize(30);
7271 
7272     TextStyle style0(style);
7273     style0.setDecoration(TextDecoration::kUnderline);
7274     style0.setDecorationColor(SK_ColorBLACK);
7275 
7276     TextStyle style1(style);
7277     style1.setDecoration(TextDecoration::kOverline);
7278     style1.setDecorationColor(SK_ColorBLACK);
7279 
7280     ParagraphStyle paraStyle;
7281     paraStyle.setTextStyle(style);
7282     paraStyle.setMaxLines(std::numeric_limits<size_t>::max());
7283 
7284     const char* logo1 = "google_";
7285     const char* logo2 = "logo";
7286     const char* logo3 = "go";
7287     const char* logo4 = "ogle_logo";
7288     const char* logo5 = "google_lo";
7289     const char* logo6 = "go";
7290 
7291     ParagraphBuilderImpl builder(paraStyle, fontCollection);
7292     style0.setDecorationStyle(TextDecorationStyle::kDouble);
7293     style0.setForegroundColor(SkPaint(SkColors::kBlack));
7294     style0.setBackgroundColor(SkPaint(SkColors::kLtGray));
7295     builder.pushStyle(style0);
7296     builder.addText(logo1, strlen(logo1));
7297     style1.setDecorationStyle(TextDecorationStyle::kWavy);
7298     style1.setForegroundColor(SkPaint(SkColors::kBlue));
7299     style1.setBackgroundColor(SkPaint(SkColors::kYellow));
7300     builder.pushStyle(style1);
7301     builder.addText(logo2, strlen(logo2));
7302     builder.addText(" ", 1);
7303 
7304     style0.setDecorationStyle(TextDecorationStyle::kSolid);
7305     style0.setForegroundColor(SkPaint(SkColors::kBlue));
7306     style0.setBackgroundColor(SkPaint(SkColors::kWhite));
7307     builder.pushStyle(style0);
7308     builder.addText(logo3, strlen(logo3));
7309     style1.setDecorationStyle(TextDecorationStyle::kDotted);
7310     style1.setForegroundColor(SkPaint(SkColors::kBlack));
7311     style1.setBackgroundColor(SkPaint(SkColors::kMagenta));
7312     builder.pushStyle(style1);
7313     builder.addText(logo4, strlen(logo4));
7314     builder.addText(" ", 1);
7315 
7316     style0.setDecorationStyle(TextDecorationStyle::kDashed);
7317     style0.setForegroundColor(SkPaint(SkColors::kGreen));
7318     style0.setBackgroundColor(SkPaint(SkColors::kGray));
7319     builder.pushStyle(style0);
7320     builder.addText(logo5, strlen(logo5));
7321     style1.setDecorationStyle(TextDecorationStyle::kDouble);
7322     style1.setForegroundColor(SkPaint(SkColors::kBlue));
7323     style1.setBackgroundColor(SkPaint(SkColors::kCyan));
7324     builder.pushStyle(style1);
7325     builder.addText(logo6, strlen(logo6));
7326 
7327     auto paragraph = builder.Build();
7328     paragraph->layout(width - 40);
7329     paragraph->paint(canvas.get(), 20, 20);
7330 
7331     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
7332     REPORTER_ASSERT(reporter, impl->lines().size() == 1);
7333 
7334     size_t index = 0;
7335     SkScalar left = 0.0f;
7336     impl->lines().data()->scanStyles(StyleType::kDecorations,
7337         [&index, &left, reporter]
7338         (TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
7339             switch (index) {
7340                 case 0: REPORTER_ASSERT(reporter, context.pos == 0 && context.size == 1);
7341                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7342                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 62.6944885f));
7343                         break;
7344                 case 1: REPORTER_ASSERT(reporter, context.pos == 0 && context.size == 2);
7345                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7346                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 105.479904f));
7347                         break;
7348                 case 2: REPORTER_ASSERT(reporter, context.pos == 2 && context.size == 1);
7349                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7350                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 123.3926165f));
7351                         break;
7352                 case 3: REPORTER_ASSERT(reporter, context.pos == 2 && context.size == 2);
7353                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7354                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 210.959808f));
7355                         break;
7356                 case 4: REPORTER_ASSERT(reporter, context.pos == 4 && context.size == 1);
7357                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7358                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 291.567017f));
7359                         break;
7360                 case 5: REPORTER_ASSERT(reporter, context.pos == 4 && context.size == 1); // No space at the end
7361                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fLeft, left));
7362                         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(context.clip.fRight, 309.479736f));
7363                         break;
7364                 default: REPORTER_ASSERT(reporter, false); break;
7365             }
7366             left = context.clip.fRight;
7367             ++index;
7368         });
7369 }
7370 
7371 // Ligature FFI should allow painting and querying by codepoints
UNIX_ONLY_TEST(SkParagraph_MultiStyle_FFI,reporter)7372 UNIX_ONLY_TEST(SkParagraph_MultiStyle_FFI, reporter) {
7373     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7374     if (!fontCollection->fontsFound()) return;
7375 
7376     TestCanvas canvas("SkParagraph_MultiStyle_FFI.png");
7377 
7378     canvas.get()->drawColor(SK_ColorWHITE);
7379 
7380     ParagraphStyle paragraph_style;
7381     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7382     TextStyle text_style;
7383     text_style.setColor(SK_ColorBLACK);
7384     text_style.setFontFamilies({SkString("Roboto")});
7385     text_style.setFontSize(60);
7386     text_style.setBackgroundColor(SkPaint(SkColors::kGray));
7387     builder.pushStyle(text_style);
7388     builder.addText("f");
7389     text_style.setBackgroundColor(SkPaint(SkColors::kYellow));
7390     builder.pushStyle(text_style);
7391     builder.addText("f");
7392     text_style.setBackgroundColor(SkPaint(SkColors::kLtGray));
7393     builder.pushStyle(text_style);
7394     builder.addText("i");
7395     auto paragraph = builder.Build();
7396     paragraph->layout(TestCanvasWidth);
7397     paragraph->paint(canvas.get(), 0, 0);
7398     auto width = paragraph->getLongestLine();
7399     auto height = paragraph->getHeight();
7400 
7401     auto f1Pos = paragraph->getGlyphPositionAtCoordinate(width/6, height/2);
7402     auto f2Pos = paragraph->getGlyphPositionAtCoordinate(width/2, height/2);
7403     auto iPos = paragraph->getGlyphPositionAtCoordinate(width*5/6, height/2);
7404 
7405     // Positions are aligned with graphemes (no pointing inside ffi grapheme)
7406     REPORTER_ASSERT(reporter, f1Pos.position == 0 && f1Pos.affinity == Affinity::kDownstream);
7407     REPORTER_ASSERT(reporter, f2Pos.position == 3 && f2Pos.affinity == Affinity::kUpstream);
7408     REPORTER_ASSERT(reporter, iPos.position == 3 && iPos.affinity == Affinity::kUpstream);
7409 
7410     // Bounding boxes show the extact position (inside ffi grapheme)
7411     auto f1 = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight,
7412                                           RectWidthStyle::kTight);
7413     REPORTER_ASSERT(reporter, f1.size() == 1);
7414     REPORTER_ASSERT(reporter, f1[0].direction == TextDirection::kLtr);
7415     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f1[0].rect.fLeft, 0.000000f));
7416     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f1[0].rect.fRight, 17.070000f));
7417 
7418     auto f2 = paragraph->getRectsForRange(1, 2, RectHeightStyle::kTight,
7419                                           RectWidthStyle::kTight);
7420     REPORTER_ASSERT(reporter, f2.size() == 1);
7421     REPORTER_ASSERT(reporter, f2[0].direction == TextDirection::kLtr);
7422     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f2[0].rect.fLeft, 17.070000f));
7423     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f2[0].rect.fRight, 34.139999f));
7424 
7425     auto fi = paragraph->getRectsForRange(2, 3, RectHeightStyle::kTight,
7426                                           RectWidthStyle::kTight);
7427     REPORTER_ASSERT(reporter, fi.size() == 1);
7428     REPORTER_ASSERT(reporter, fi[0].direction == TextDirection::kLtr);
7429     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(fi[0].rect.fLeft, 34.139999f));
7430     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(fi[0].rect.fRight, 51.209999f));
7431 };
7432 
7433 // Multiple code points/single glyph emoji family should be treated as a single glyph
UNIX_ONLY_TEST(SkParagraph_MultiStyle_EmojiFamily,reporter)7434 UNIX_ONLY_TEST(SkParagraph_MultiStyle_EmojiFamily, reporter) {
7435     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7436     if (!fontCollection->fontsFound()) return;
7437 
7438     TestCanvas canvas("SkParagraph_MultiStyle_EmojiFamily.png");
7439 
7440     canvas.get()->drawColor(SK_ColorWHITE);
7441 
7442     ParagraphStyle paragraph_style;
7443     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7444     TextStyle text_style;
7445     text_style.setColor(SK_ColorBLACK);
7446     text_style.setFontFamilies({SkString("Noto Color Emoji")});
7447     text_style.setFontSize(40);
7448     builder.pushStyle(text_style);
7449     builder.addText(u"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466");
7450     auto paragraph = builder.Build();
7451     paragraph->layout(TestCanvasWidth);
7452     SkPaint paint;
7453     paint.setStyle(SkPaint::kStroke_Style);
7454     paint.setAntiAlias(true);
7455     paint.setStrokeWidth(1);
7456     paint.setColor(SK_ColorLTGRAY);
7457     canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, paragraph->getLongestLine(), paragraph->getHeight()), paint);
7458     paragraph->paint(canvas.get(), 0, 0);
7459     auto width = paragraph->getLongestLine();
7460     auto height = paragraph->getHeight();
7461 
7462     auto pos00 = paragraph->getGlyphPositionAtCoordinate(width/4, height/4);
7463     auto pos10 = paragraph->getGlyphPositionAtCoordinate(width*3/4, height/2);
7464     auto pos01 = paragraph->getGlyphPositionAtCoordinate(width/4, height/2);
7465     auto pos11 = paragraph->getGlyphPositionAtCoordinate(width*3/4, height*3/4);
7466 
7467     // Positioning is aligned with the closest grapheme edge (left or right)
7468     REPORTER_ASSERT(reporter, pos00.position == 0 && pos00.affinity == Affinity::kDownstream);
7469     REPORTER_ASSERT(reporter, pos01.position == 0 && pos01.affinity == Affinity::kDownstream);
7470     REPORTER_ASSERT(reporter, pos10.position == 11 && pos10.affinity == Affinity::kUpstream);
7471     REPORTER_ASSERT(reporter, pos11.position == 11 && pos11.affinity == Affinity::kUpstream);
7472 
7473     // Bounding boxes around a part of a grapheme are empty
7474     auto f1 = paragraph->getRectsForRange(0, 2, RectHeightStyle::kTight, RectWidthStyle::kTight);
7475     REPORTER_ASSERT(reporter, f1.size() == 0);
7476 
7477     auto f2 = paragraph->getRectsForRange(4, 6, RectHeightStyle::kTight, RectWidthStyle::kTight);
7478     REPORTER_ASSERT(reporter, f2.size() == 0);
7479 
7480     auto f3 = paragraph->getRectsForRange(8, 10, RectHeightStyle::kTight, RectWidthStyle::kTight);
7481     REPORTER_ASSERT(reporter, f3.size() == 0);
7482 
7483     auto f4 = paragraph->getRectsForRange(8, 10, RectHeightStyle::kTight, RectWidthStyle::kTight);
7484     REPORTER_ASSERT(reporter, f4.size() == 0);
7485 };
7486 
7487 // Arabic Ligature case should be painted into multi styles but queried as a single glyph
UNIX_ONLY_TEST(SkParagraph_MultiStyle_Arabic,reporter)7488 UNIX_ONLY_TEST(SkParagraph_MultiStyle_Arabic, reporter) {
7489     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7490     if (!fontCollection->fontsFound()) return;
7491 
7492     TestCanvas canvas("SkParagraph_MultiStyle_Arabic.png");
7493 
7494     canvas.get()->drawColor(SK_ColorWHITE);
7495 
7496     TextStyle text_style;
7497     text_style.setFontFamilies({SkString("Noto Naskh Arabic")});
7498     text_style.setFontSize(50);
7499     text_style.setColor(SK_ColorBLACK);
7500     ParagraphStyle paragraph_style;
7501     paragraph_style.setTextStyle(text_style);
7502     paragraph_style.setTextDirection(TextDirection::kRtl);
7503     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7504     text_style.setColor(SK_ColorBLUE);
7505     builder.pushStyle(text_style);
7506     builder.addText("ك");
7507     text_style.setColor(SK_ColorRED);
7508     builder.pushStyle(text_style);
7509     builder.addText("ِّ");
7510     text_style.setColor(SK_ColorBLUE);
7511     builder.pushStyle(text_style);
7512     builder.addText("ـ");
7513     auto paragraph = builder.Build();
7514     paragraph->layout(TestCanvasWidth);
7515     paragraph->paint(canvas.get(), 0, 0);
7516 
7517     auto width = paragraph->getLongestLine();
7518     auto height = paragraph->getHeight();
7519 
7520     // Positioning is align with the grapheme
7521     auto f1Pos = paragraph->getGlyphPositionAtCoordinate(TestCanvasWidth - width*5/6, height/2);
7522     auto f2Pos = paragraph->getGlyphPositionAtCoordinate(TestCanvasWidth - width/3, height/2);
7523     auto iPos = paragraph->getGlyphPositionAtCoordinate(TestCanvasWidth - width/6, height/2);
7524     REPORTER_ASSERT(reporter, f1Pos.position == 4 && f1Pos.affinity == Affinity::kUpstream);
7525     REPORTER_ASSERT(reporter, f2Pos.position == 1 && f2Pos.affinity == Affinity::kUpstream);
7526     REPORTER_ASSERT(reporter, iPos.position == 0 && iPos.affinity == Affinity::kDownstream);
7527 
7528     // Bounding boxes around a part of a grapheme are empty
7529     auto f1 = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight, RectWidthStyle::kTight);
7530     REPORTER_ASSERT(reporter, f1.size() == 0);
7531 
7532     auto f2 = paragraph->getRectsForRange(1, 2, RectHeightStyle::kTight, RectWidthStyle::kTight);
7533     REPORTER_ASSERT(reporter, f2.size() == 0);
7534 
7535     auto fi = paragraph->getRectsForRange(2, 3, RectHeightStyle::kTight, RectWidthStyle::kTight);
7536     REPORTER_ASSERT(reporter, fi.size() == 0);
7537 };
7538 
7539 // Zalgo text should be painted into multi styles but queried as a single glyph
UNIX_ONLY_TEST(SkParagraph_MultiStyle_Zalgo,reporter)7540 UNIX_ONLY_TEST(SkParagraph_MultiStyle_Zalgo, reporter) {
7541     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
7542     if (!fontCollection->fontsFound()) return;
7543     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
7544     fontCollection->enableFontFallback();
7545 
7546     TestCanvas canvas("SkParagraph_MultiStyle_Zalgo.png");
7547 
7548     canvas.get()->drawColor(SK_ColorWHITE);
7549 
7550     SkString text(">S͛ͭ̋͆̈̔̇͗̍͑̎ͪͮͧͣ̽ͫͣ́ͬ̀͌͑͂͗͒̍̔̄ͧ̏̉̌̊̊̿̀̌̃̄͐̓̓̚̚҉̵̡͜͟͝͠͏̸̵̡̧͜҉̷̡͇̜̘̻̺̘̟̝͙̬̘̩͇̭̼̥̖̤̦͎k͉̩̘͚̜̹̗̗͍̤̥̱͉̳͕͖̤̲̣͚̮̞̬̲͍͔̯̻̮̞̭͈̗̫͓̂ͨ̉ͪ̒͋͛̀̍͊ͧ̿̅͆̓̔̔ͬ̇̑̿ͩ͗ͮ̎͌̿̄ͅP̴̵̡̡̛̪͙̼̣̟̩̭̫̱͙̬͔͉͍̘̠͉̦̝̘̥̟̗͖̫̤͕̙̬̦͍̱̖̮̱͑͐̎̃̒͐͋̚͘͞a̶̶̵̵̵̶̶̡̧̢̢̺͔̣͖̭̺͍̤͚̱̜̰̥͕̬̥̲̞̥̘͇͚̺̰͚̪̺͔̤͍̓̿͆̎͋̓ͦ̈́ͦ̌́̄͗̌̓͌̕͜͜͟͢͝͡ŕ͎̝͕͉̻͎̤̭͚̗̳̖̙̘͚̫͖͓͚͉͔͈̟̰̟̬̗͓̟͚̱̕͡ͅͅͅa̸̶̢̛̛̽ͮͩ̅͒ͫ͗͂̎ͦ̈́̓̚͘͜͢͡҉̷̵̶̢̡̜̮̦̜̥̜̯̙͓͔̼̗̻͜͜ͅḡ̢̛͕̗͖̖̤̦̘͔ͨͨ̊͒ͩͭͤ̍̅̃ͪ̋̏̓̍̋͗̋ͨ̏̽̈́̔̀̋̉ͫ̅̂ͭͫ̏͒͋ͥ̚͜r̶̢̧̧̥̤̼̀̂̒ͪ͌̿͌̅͛ͨͪ͒̍ͥ̉ͤ̌̿̆́ͭ͆̃̒ͤ͛̊ͧ̽͘͝͠a̧̢̧̢͑͑̓͑ͮ̃͂̄͛́̈́͋̂͌̽̄͒̔́̇ͨͧͭ͐ͦ̋ͨ̍ͦ̍̋͆̔ͧ͑͋͌̈̓͛͛̚͢͜͜͏̴̢̧̛̳͍̹͚̰̹̻͔p̨̡͆ͦͣ͊̽̔͂̉ͣ̔ͣ̌̌̉̃̋̂͒ͫ̄̎̐͗̉̌̃̽̽́̀̚͘͜͟҉̱͉h̭̮̘̗͔̜̯͔͈̯̺͔̗̣̭͚̱̰̙̼̹͚̣̻̥̲̮͍̤͜͝<");
7551     auto K = text.find("k");
7552     auto P = text.find("P");
7553     auto h = text.find("h");
7554     ParagraphStyle paragraph_style;
7555     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7556     TextStyle text_style;
7557     text_style.setFontFamilies({SkString("Roboto")});
7558     text_style.setFontSize(20);
7559     text_style.setColor(SK_ColorRED);
7560     builder.pushStyle(text_style);
7561     builder.addText(text.data(), K + 3);
7562     text_style.setColor(SK_ColorBLUE);
7563     text_style.setBackgroundColor(SkPaint(SkColors::kYellow));
7564     builder.pushStyle(text_style);
7565     builder.addText(text.data() + K + 3, P - K - 3 + 6);
7566     text_style.setColor(SK_ColorGREEN);
7567     builder.pushStyle(text_style);
7568     builder.addText(text.data() + P + 6, h - P - 6);
7569     text_style.setColor(SK_ColorBLACK);
7570     text_style.setBackgroundColor(SkPaint(SkColors::kLtGray));
7571     builder.pushStyle(text_style);
7572     builder.addText(text.data() + h, text.size() - h);
7573     auto paragraph = builder.Build();
7574     paragraph->layout(TestCanvasWidth);
7575     paragraph->paint(canvas.get(), 0, paragraph->getHeight() / 2);
7576     //auto width = paragraph->getLongestLine();
7577     auto height = paragraph->getHeight();
7578 
7579     auto resSK = paragraph->getRectsForRange(0, K, RectHeightStyle::kTight,
7580                                           RectWidthStyle::kTight);
7581     REPORTER_ASSERT(reporter, resSK.size() != 0);
7582     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(resSK[0].rect.width(), 10.450000f));
7583 
7584     auto resKP = paragraph->getRectsForRange(K, P, RectHeightStyle::kTight,
7585                                           RectWidthStyle::kTight);
7586     REPORTER_ASSERT(reporter, resKP.size() != 0);
7587     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(resKP[0].rect.width(), 9.580002f));
7588 
7589     auto resPh = paragraph->getRectsForRange(P, h, RectHeightStyle::kTight,
7590                                           RectWidthStyle::kTight);
7591     REPORTER_ASSERT(reporter, resPh.size() != 0);
7592     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(resPh[0].rect.width(), 56.459999f));
7593 
7594     auto posK = paragraph->getGlyphPositionAtCoordinate(resSK.back().rect.fRight, height/2);
7595     auto posP = paragraph->getGlyphPositionAtCoordinate(resKP.back().rect.fRight, height/2);
7596     auto posH = paragraph->getGlyphPositionAtCoordinate(resPh.back().rect.fRight, height/2);
7597     REPORTER_ASSERT(reporter, posK.position == 148 && posP.position == 264 && posH.position == 572);
7598 };
7599 
7600 // RTL Ellipsis
UNIX_ONLY_TEST(SkParagraph_RtlEllipsis1,reporter)7601 UNIX_ONLY_TEST(SkParagraph_RtlEllipsis1, reporter) {
7602     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7603     if (!fontCollection->fontsFound()) return;
7604 
7605     TestCanvas canvas("SkParagraph_RtlEllipsis1.png");
7606 
7607     canvas.get()->drawColor(SK_ColorWHITE);
7608 
7609     TextStyle text_style;
7610     text_style.setFontFamilies({SkString("Noto Naskh Arabic"), SkString("Roboto")});
7611     text_style.setFontSize(100);
7612     text_style.setColor(SK_ColorBLACK);
7613     ParagraphStyle paragraph_style;
7614     paragraph_style.setTextStyle(text_style);
7615     paragraph_style.setTextDirection(TextDirection::kRtl);
7616     paragraph_style.setEllipsis(u"\u2026");
7617     paragraph_style.setTextAlign(TextAlign::kStart);
7618     paragraph_style.setMaxLines(1);
7619     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7620     builder.pushStyle(text_style);
7621     builder.addText(u"1  2  3  4  5  6  7  8  9");
7622     auto paragraph = builder.Build();
7623     paragraph->layout(474);
7624     paragraph->paint(canvas.get(), 0, 0);
7625     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
7626     REPORTER_ASSERT(reporter, paragraph->lineNumber() == 1);
7627     auto& line = impl->lines()[0];
7628     bool first = true;
7629     line.iterateThroughVisualRuns(true,
7630         [&]
7631         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
7632             REPORTER_ASSERT(reporter, first == (run->isEllipsis()));
7633             first = false;
7634             return true;
7635         });
7636 };
7637 
UNIX_ONLY_TEST(SkParagraph_RtlEllipsis2,reporter)7638 UNIX_ONLY_TEST(SkParagraph_RtlEllipsis2, reporter) {
7639     sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
7640     if (!fontCollection->fontsFound()) return;
7641 
7642     TestCanvas canvas("SkParagraph_RtlEllipsis2.png");
7643 
7644     canvas.get()->drawColor(SK_ColorWHITE);
7645 
7646     TextStyle text_style;
7647     text_style.setFontFamilies({SkString("Noto Naskh Arabic"), SkString("Roboto")});
7648     text_style.setFontSize(100);
7649     text_style.setColor(SK_ColorBLACK);
7650     ParagraphStyle paragraph_style;
7651     paragraph_style.setTextStyle(text_style);
7652     paragraph_style.setTextDirection(TextDirection::kRtl);
7653     paragraph_style.setEllipsis(u"\u2026");
7654     paragraph_style.setTextAlign(TextAlign::kStart);
7655     paragraph_style.setMaxLines(2);
7656     ParagraphBuilderImpl builder(paragraph_style, fontCollection);
7657     builder.pushStyle(text_style);
7658     builder.addText(u"تظاهرات و تجمعات اعتراضی در سراسر کشور ۲۳ مهر");
7659     auto paragraph = builder.Build();
7660     paragraph->layout(474);
7661     paragraph->paint(canvas.get(), 0, 0);
7662     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
7663     REPORTER_ASSERT(reporter, paragraph->lineNumber() == 2);
7664     auto& line = impl->lines()[1];
7665     bool first = true;
7666     line.iterateThroughVisualRuns(true,
7667         [&]
7668         (const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
7669             REPORTER_ASSERT(reporter, first == (run->isEllipsis()));
7670             first = false;
7671             return true;
7672         });
7673 };
7674