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