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