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