blob: 0c662255a3f0b04ff99daef40a6052366a2bf07c [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"
22#include "include/core/SkStream.h"
23#include "include/core/SkString.h"
24#include "include/core/SkTypeface.h"
25#include "include/core/SkTypes.h"
26#include "modules/skparagraph/include/DartTypes.h"
27#include "modules/skparagraph/include/FontCollection.h"
28#include "modules/skparagraph/include/Paragraph.h"
29#include "modules/skparagraph/include/ParagraphCache.h"
30#include "modules/skparagraph/include/ParagraphStyle.h"
31#include "modules/skparagraph/include/TextShadow.h"
32#include "modules/skparagraph/include/TextStyle.h"
33#include "modules/skparagraph/include/TypefaceFontProvider.h"
34#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
35#include "modules/skparagraph/src/ParagraphImpl.h"
36#include "modules/skparagraph/src/Run.h"
37#include "modules/skparagraph/src/TextLine.h"
38#include "modules/skparagraph/utils/TestFontCollection.h"
39#include "src/core/SkOSFile.h"
40#include "src/core/SkSpan.h"
41#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
55using namespace skia::textlayout;
56namespace {
57const uint8_t MAX_TEXT_LENGTH = 255;
58const uint8_t MAX_TEXT_ADDITIONS = 4;
Weston Tracey3dd203d2021-02-08 14:02:53 -050059// Use 250 so uint8 can create text and layout width larger than the canvas.
60const uint16_t TEST_CANVAS_DIM = 250;
Weston Tracey6d4577b2020-12-30 13:29:00 -050061
62class ResourceFontCollection : public FontCollection {
63public:
64 ResourceFontCollection(bool testOnly = false)
65 : fFontsFound(false)
66 , fResolvedFonts(0)
67 , fResourceDir(GetResourcePath("fonts").c_str())
68 , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
69 std::vector<SkString> fonts;
70 SkOSFile::Iter iter(fResourceDir.c_str());
71
72 SkString path;
73 while (iter.next(&path)) {
74 if (path.endsWith("Roboto-Italic.ttf")) {
75 fFontsFound = true;
76 }
77 fonts.emplace_back(path);
78 }
79
80 if (!fFontsFound) {
81 // SkDebugf("Fonts not found, skipping all the tests\n");
82 return;
83 }
84 // Only register fonts if we have to
85 for (auto& font : fonts) {
86 SkString file_path;
87 file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
88 fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
89 }
90
91 if (testOnly) {
92 this->setTestFontManager(std::move(fFontProvider));
93 } else {
94 this->setAssetFontManager(std::move(fFontProvider));
95 }
96 this->disableFontFallback();
97 }
98
99 size_t resolvedFonts() const { return fResolvedFonts; }
100
101 // TODO: temp solution until we check in fonts
102 bool fontsFound() const { return fFontsFound; }
103
104private:
105 bool fFontsFound;
106 size_t fResolvedFonts;
107 std::string fResourceDir;
108 sk_sp<TypefaceFontProvider> fFontProvider;
109};
110
111// buffer must be at least MAX_TEXT_LENGTH in length.
112// Returns size of text placed in buffer.
113template <typename T>
114uint8_t RandomText(T* buffer, Fuzz* fuzz) {
115 uint8_t text_length;
116 fuzz->nextRange(&text_length, 0, MAX_TEXT_LENGTH);
117 fuzz->nextN(buffer, text_length);
118 return text_length;
119}
120
121// Add random bytes to the paragraph.
122void AddASCIIText(ParagraphBuilder* builder,Fuzz* fuzz) {
123 char text[MAX_TEXT_LENGTH];
124 const auto text_length = RandomText(text, fuzz);
125 builder->addText(text, text_length);
126}
127// Add random bytes to the paragraph.
128void AddUnicodeText(ParagraphBuilder* builder,Fuzz* fuzz) {
129 char16_t text[MAX_TEXT_LENGTH];
130 const auto text_length = RandomText(text, fuzz);
131 builder->addText(std::u16string(text, text_length));
132}
133
134// Combining characters to produce 'Zalgo' text.
135const 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";
136const 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";
137const 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";
138// Add random Zalgo text to the paragraph.
139void AddZalgoText(ParagraphBuilder* builder, Fuzz* fuzz) {
140 char text[MAX_TEXT_LENGTH];
141 const auto text_length = RandomText(text, fuzz);
142 std::u16string result;
143
144 for (auto& c : std::string(text, text_length)) {
145 result += c;
146 uint8_t mark_count;
147 fuzz->next(&mark_count);
148 for (int i = 0; i < mark_count; i++) {
149 uint8_t mark_type, mark_index;
150 fuzz->next(&mark_type, &mark_index);
151 switch (mark_type % 3) {
152 case 0:
153 result += COMBINING_UP[mark_index % COMBINING_UP.size()];
154 break;
155 case 1:
156 result += COMBINING_MIDDLE[mark_index % COMBINING_MIDDLE.size()];
157 break;
158 case 2:
159 default:
160 result += COMBINING_DOWN[mark_index % COMBINING_DOWN.size()];
161 break;
162 }
163 }
164 }
165 builder->addText(result);
166}
167
168void AddStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
169 // TODO(westont): Make this probabilistic, and fill in the remaining TextStyle fields.
170 TextStyle ts;
171 ts.setFontFamilies({SkString("Roboto")});
172 //ts.setColor(SK_ColorBLACK);
173 //ts.setForegroundColor
174 //ts.setBackgroundColor
175 //ts.setDecoration(TextDecoration decoration);
176 //ts.setDecorationMode(TextDecorationMode mode);
177 //ts.setDecorationStyle(TextDecorationStyle style);
178 //ts.setDecorationColor(SkColor color);
179 //ts.setDecorationThicknessMultiplier(SkScalar m);
180 //ts.setFontStyle
181 //ts.addShadow
182 //ts.addFontFeature
183 //ts.setFontSize
184 //ts.setHeight
185 //ts.setHeightOverride
186 //ts.setletterSpacing
187 //ts.setWordSpacing
188 //ts.setTypeface
189 //ts.setLocale
190 //ts.setTextBaseline
191 //ts.setPlaceholder
192
193 builder->pushStyle(ts);
194}
195void RemoveStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
196 bool pop;
197 fuzz->next(&pop);
198 if (pop) {
199 builder->pop();
200 }
201}
202
203void AddStyleAndText(ParagraphBuilder* builder, Fuzz* fuzz) {
204 AddStyle(builder, fuzz);
205 uint8_t text_type;
206 fuzz->next(&text_type);
207 switch (text_type % 3) {
208 case 0:
209 AddASCIIText(builder, fuzz);
210 break;
211 case 1:
212 AddUnicodeText(builder, fuzz);
213 break;
214 case 2:
215 AddZalgoText(builder, fuzz);
216 break;
217 }
218 RemoveStyle(builder, fuzz);
219
220}
221
222ParagraphStyle BuildParagraphStyle(Fuzz* fuzz) {
223 ParagraphStyle ps;
224 bool hinting;
225 fuzz->next(&hinting);
226 if (hinting) {
227 ps.turnHintingOff();
228 }
229 StrutStyle ss;
230 // TODO(westont): Fuzz this object.
231 ps.setStrutStyle(ss);
232 TextDirection td;
233 fuzz->nextEnum(&td, TextDirection::kRtl);
234 ps.setTextDirection(td);
235 TextAlign ta;
236 fuzz->nextEnum(&ta, TextAlign::kEnd);
237 ps.setTextAlign(ta);
238 size_t ml;
239 fuzz->next(&ml);
240 ps.setMaxLines(ml);
241 // TODO(westont): Randomize with other values and no value.
242 ps.setEllipsis(u"\u2026");
243 SkScalar h;
244 fuzz->next(&h);
245 ps.setHeight(h);
246 TextHeightBehavior thb = TextHeightBehavior::kAll;
247 // TODO(westont): This crashes our seed test case, why?
248 //fuzz->nextEnum(&thb, TextHeightBehavior::kDisableAll);
249 ps.setTextHeightBehavior(thb);
250
251 return ps;
252}
253
254} // namespace
255
Weston Tracey1a771fe2021-02-04 11:09:59 -0500256DEF_FUZZ(SkParagraph, fuzz) {
Weston Tracey6d4577b2020-12-30 13:29:00 -0500257 static sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
258 ParagraphStyle paragraph_style = BuildParagraphStyle(fuzz);
259 ParagraphBuilderImpl builder(paragraph_style, fontCollection);
260
261 uint8_t iterations;
262 fuzz->nextRange(&iterations, 1, MAX_TEXT_ADDITIONS);
263 for (int i = 0; i < iterations; i++) {
264 AddStyleAndText(&builder, fuzz);
265 }
266 // TODO(westont): Figure out if we can get more converage by having fontsFound, current
267 // they're not.
268 // if (!fontCollection->fontsFound()) return;
269
270 builder.pop();
271 auto paragraph = builder.Build();
272
Weston Tracey3dd203d2021-02-08 14:02:53 -0500273 SkBitmap bm;
274 if (!bm.tryAllocN32Pixels(TEST_CANVAS_DIM, TEST_CANVAS_DIM)) {
275 return;
276 }
277 SkCanvas canvas(bm);
278 uint8_t layout_width;
279 fuzz->next(&layout_width);
280 paragraph->layout(layout_width);
281 paragraph->paint(&canvas, 0, 0);
Weston Tracey6d4577b2020-12-30 13:29:00 -0500282}