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