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