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