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