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