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