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