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