blob: db66583cb6eb1fb783ed8174747b988e4ea80b7e [file] [log] [blame]
Weston Tracey6d4577b2020-12-30 13:29:00 -05001/*
2 * Copyright 2020 Google Inc.
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
8#include "fuzz/Fuzz.h"
9#include "fuzz/FuzzCommon.h"
10#include "include/core/SkBitmap.h"
11#include "include/core/SkCanvas.h"
12#include "include/core/SkColor.h"
13#include "include/core/SkEncodedImageFormat.h"
14#include "include/core/SkFontMgr.h"
15#include "include/core/SkFontStyle.h"
16#include "include/core/SkImageEncoder.h"
17#include "include/core/SkPaint.h"
18#include "include/core/SkPoint.h"
19#include "include/core/SkRect.h"
20#include "include/core/SkRefCnt.h"
21#include "include/core/SkScalar.h"
Brian Osmana5842bc2021-05-11 13:41:46 -040022#include "include/core/SkSpan.h"
Weston Tracey6d4577b2020-12-30 13:29:00 -050023#include "include/core/SkStream.h"
24#include "include/core/SkString.h"
25#include "include/core/SkTypeface.h"
26#include "include/core/SkTypes.h"
27#include "modules/skparagraph/include/DartTypes.h"
28#include "modules/skparagraph/include/FontCollection.h"
29#include "modules/skparagraph/include/Paragraph.h"
30#include "modules/skparagraph/include/ParagraphCache.h"
31#include "modules/skparagraph/include/ParagraphStyle.h"
32#include "modules/skparagraph/include/TextShadow.h"
33#include "modules/skparagraph/include/TextStyle.h"
34#include "modules/skparagraph/include/TypefaceFontProvider.h"
35#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
36#include "modules/skparagraph/src/ParagraphImpl.h"
37#include "modules/skparagraph/src/Run.h"
38#include "modules/skparagraph/src/TextLine.h"
39#include "modules/skparagraph/utils/TestFontCollection.h"
40#include "src/core/SkOSFile.h"
Weston Tracey6d4577b2020-12-30 13:29:00 -050041#include "src/utils/SkOSPath.h"
42#include "src/utils/SkShaperJSONWriter.h"
43#include "tests/Test.h"
44#include "tools/Resources.h"
45
46
47#include <string.h>
48#include <algorithm>
49#include <limits>
50#include <memory>
51#include <string>
52#include <utility>
53#include <vector>
54
Robert Phillipsbe2c3ee2021-08-31 12:36:25 -040055#if defined(SK_ENABLE_PARAGRAPH)
56
Weston Tracey6d4577b2020-12-30 13:29:00 -050057using namespace skia::textlayout;
58namespace {
59const uint8_t MAX_TEXT_LENGTH = 255;
60const uint8_t MAX_TEXT_ADDITIONS = 4;
Weston Tracey3dd203d2021-02-08 14:02:53 -050061// Use 250 so uint8 can create text and layout width larger than the canvas.
62const uint16_t TEST_CANVAS_DIM = 250;
Weston Tracey6d4577b2020-12-30 13:29:00 -050063
64class ResourceFontCollection : public FontCollection {
65public:
66 ResourceFontCollection(bool testOnly = false)
67 : fFontsFound(false)
68 , fResolvedFonts(0)
69 , fResourceDir(GetResourcePath("fonts").c_str())
70 , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
71 std::vector<SkString> fonts;
72 SkOSFile::Iter iter(fResourceDir.c_str());
73
74 SkString path;
75 while (iter.next(&path)) {
76 if (path.endsWith("Roboto-Italic.ttf")) {
77 fFontsFound = true;
78 }
79 fonts.emplace_back(path);
80 }
81
82 if (!fFontsFound) {
83 // SkDebugf("Fonts not found, skipping all the tests\n");
84 return;
85 }
86 // Only register fonts if we have to
87 for (auto& font : fonts) {
88 SkString file_path;
89 file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
90 fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
91 }
92
93 if (testOnly) {
94 this->setTestFontManager(std::move(fFontProvider));
95 } else {
96 this->setAssetFontManager(std::move(fFontProvider));
97 }
98 this->disableFontFallback();
99 }
100
101 size_t resolvedFonts() const { return fResolvedFonts; }
102
103 // TODO: temp solution until we check in fonts
104 bool fontsFound() const { return fFontsFound; }
105
106private:
107 bool fFontsFound;
108 size_t fResolvedFonts;
109 std::string fResourceDir;
110 sk_sp<TypefaceFontProvider> fFontProvider;
111};
112
113// buffer must be at least MAX_TEXT_LENGTH in length.
114// Returns size of text placed in buffer.
115template <typename T>
116uint8_t RandomText(T* buffer, Fuzz* fuzz) {
117 uint8_t text_length;
118 fuzz->nextRange(&text_length, 0, MAX_TEXT_LENGTH);
119 fuzz->nextN(buffer, text_length);
120 return text_length;
121}
122
123// Add random bytes to the paragraph.
124void AddASCIIText(ParagraphBuilder* builder,Fuzz* fuzz) {
125 char text[MAX_TEXT_LENGTH];
126 const auto text_length = RandomText(text, fuzz);
127 builder->addText(text, text_length);
128}
129// Add random bytes to the paragraph.
130void AddUnicodeText(ParagraphBuilder* builder,Fuzz* fuzz) {
131 char16_t text[MAX_TEXT_LENGTH];
132 const auto text_length = RandomText(text, fuzz);
133 builder->addText(std::u16string(text, text_length));
134}
135
136// Combining characters to produce 'Zalgo' text.
137const std::u16string COMBINING_DOWN = u"\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u031f\u0320\u0324\u0325\u0326\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0339\u033a\u033b\u033c\u0345\u0347\u0348\u0349\u034d\u034e\u0353\u0354\u0355\u0356\u0359\u035a\u0323";
138const std::u16string COMBINING_UP = u"\u030d\u030e\u0304\u0305\u033f\u0311\u0306\u0310\u0352\u0357\u0351\u0307\u0308\u030a\u0342\u0343\u0344\u034a\u034b\u034c\u0303\u0302\u030c\u0350\u0300\u0301\u030b\u030f\u0312\u0313\u0314\u033d\u0309\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u035b\u0346\u031a";
139const std::u16string COMBINING_MIDDLE = u"\u0315\u031b\u0340\u0341\u0358\u0321\u0322\u0327\u0328\u0334\u0335\u0336\u034f\u035c\u035d\u035e\u035f\u0360\u0362\u0338\u0337\u0361\u0489";
140// Add random Zalgo text to the paragraph.
141void AddZalgoText(ParagraphBuilder* builder, Fuzz* fuzz) {
142 char text[MAX_TEXT_LENGTH];
143 const auto text_length = RandomText(text, fuzz);
144 std::u16string result;
145
146 for (auto& c : std::string(text, text_length)) {
147 result += c;
148 uint8_t mark_count;
149 fuzz->next(&mark_count);
150 for (int i = 0; i < mark_count; i++) {
151 uint8_t mark_type, mark_index;
152 fuzz->next(&mark_type, &mark_index);
153 switch (mark_type % 3) {
154 case 0:
155 result += COMBINING_UP[mark_index % COMBINING_UP.size()];
156 break;
157 case 1:
158 result += COMBINING_MIDDLE[mark_index % COMBINING_MIDDLE.size()];
159 break;
160 case 2:
161 default:
162 result += COMBINING_DOWN[mark_index % COMBINING_DOWN.size()];
163 break;
164 }
165 }
166 }
167 builder->addText(result);
168}
169
170void AddStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
171 // TODO(westont): Make this probabilistic, and fill in the remaining TextStyle fields.
172 TextStyle ts;
173 ts.setFontFamilies({SkString("Roboto")});
174 //ts.setColor(SK_ColorBLACK);
175 //ts.setForegroundColor
176 //ts.setBackgroundColor
177 //ts.setDecoration(TextDecoration decoration);
178 //ts.setDecorationMode(TextDecorationMode mode);
179 //ts.setDecorationStyle(TextDecorationStyle style);
180 //ts.setDecorationColor(SkColor color);
181 //ts.setDecorationThicknessMultiplier(SkScalar m);
182 //ts.setFontStyle
183 //ts.addShadow
184 //ts.addFontFeature
185 //ts.setFontSize
186 //ts.setHeight
187 //ts.setHeightOverride
188 //ts.setletterSpacing
189 //ts.setWordSpacing
190 //ts.setTypeface
191 //ts.setLocale
192 //ts.setTextBaseline
193 //ts.setPlaceholder
194
195 builder->pushStyle(ts);
196}
197void RemoveStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
198 bool pop;
199 fuzz->next(&pop);
200 if (pop) {
201 builder->pop();
202 }
203}
204
205void AddStyleAndText(ParagraphBuilder* builder, Fuzz* fuzz) {
206 AddStyle(builder, fuzz);
207 uint8_t text_type;
208 fuzz->next(&text_type);
209 switch (text_type % 3) {
210 case 0:
211 AddASCIIText(builder, fuzz);
212 break;
213 case 1:
214 AddUnicodeText(builder, fuzz);
215 break;
216 case 2:
217 AddZalgoText(builder, fuzz);
218 break;
219 }
220 RemoveStyle(builder, fuzz);
221
222}
223
224ParagraphStyle BuildParagraphStyle(Fuzz* fuzz) {
225 ParagraphStyle ps;
226 bool hinting;
227 fuzz->next(&hinting);
228 if (hinting) {
229 ps.turnHintingOff();
230 }
231 StrutStyle ss;
232 // TODO(westont): Fuzz this object.
233 ps.setStrutStyle(ss);
234 TextDirection td;
235 fuzz->nextEnum(&td, TextDirection::kRtl);
236 ps.setTextDirection(td);
237 TextAlign ta;
238 fuzz->nextEnum(&ta, TextAlign::kEnd);
239 ps.setTextAlign(ta);
240 size_t ml;
241 fuzz->next(&ml);
242 ps.setMaxLines(ml);
243 // TODO(westont): Randomize with other values and no value.
244 ps.setEllipsis(u"\u2026");
245 SkScalar h;
246 fuzz->next(&h);
247 ps.setHeight(h);
248 TextHeightBehavior thb = TextHeightBehavior::kAll;
249 // TODO(westont): This crashes our seed test case, why?
250 //fuzz->nextEnum(&thb, TextHeightBehavior::kDisableAll);
251 ps.setTextHeightBehavior(thb);
252
253 return ps;
254}
255
256} // namespace
257
Weston Tracey1a771fe2021-02-04 11:09:59 -0500258DEF_FUZZ(SkParagraph, fuzz) {
Weston Tracey6d4577b2020-12-30 13:29:00 -0500259 static sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
260 ParagraphStyle paragraph_style = BuildParagraphStyle(fuzz);
261 ParagraphBuilderImpl builder(paragraph_style, fontCollection);
262
263 uint8_t iterations;
264 fuzz->nextRange(&iterations, 1, MAX_TEXT_ADDITIONS);
265 for (int i = 0; i < iterations; i++) {
266 AddStyleAndText(&builder, fuzz);
267 }
268 // TODO(westont): Figure out if we can get more converage by having fontsFound, current
269 // they're not.
270 // if (!fontCollection->fontsFound()) return;
271
272 builder.pop();
273 auto paragraph = builder.Build();
274
Weston Tracey3dd203d2021-02-08 14:02:53 -0500275 SkBitmap bm;
276 if (!bm.tryAllocN32Pixels(TEST_CANVAS_DIM, TEST_CANVAS_DIM)) {
277 return;
278 }
279 SkCanvas canvas(bm);
280 uint8_t layout_width;
281 fuzz->next(&layout_width);
282 paragraph->layout(layout_width);
283 paragraph->paint(&canvas, 0, 0);
Weston Tracey6d4577b2020-12-30 13:29:00 -0500284}
Robert Phillipsbe2c3ee2021-08-31 12:36:25 -0400285
286#endif // SK_ENABLE_PARAGRAPH