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