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