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