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