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