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