• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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/SkSpan.h"
23 #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"
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 
55 #if defined(SK_ENABLE_PARAGRAPH)
56 
57 using namespace skia::textlayout;
58 namespace {
59 const uint8_t MAX_TEXT_LENGTH = 255;
60 const uint8_t MAX_TEXT_ADDITIONS = 4;
61 // Use 250 so uint8 can create text and layout width larger than the canvas.
62 const uint16_t TEST_CANVAS_DIM = 250;
63 
64 class ResourceFontCollection : public FontCollection {
65 public:
ResourceFontCollection(bool testOnly=false)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 
resolvedFonts() const101     size_t resolvedFonts() const { return fResolvedFonts; }
102 
103     // TODO: temp solution until we check in fonts
fontsFound() const104     bool fontsFound() const { return fFontsFound; }
105 
106 private:
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.
115 template <typename T>
RandomText(T * buffer,Fuzz * fuzz)116 uint8_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.
AddASCIIText(ParagraphBuilder * builder,Fuzz * fuzz)124 void 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.
AddUnicodeText(ParagraphBuilder * builder,Fuzz * fuzz)130 void 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.
137 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";
138 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";
139 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";
140 // Add random Zalgo text to the paragraph.
AddZalgoText(ParagraphBuilder * builder,Fuzz * fuzz)141 void 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 
AddStyle(ParagraphBuilder * builder,Fuzz * fuzz)170 void 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 }
RemoveStyle(ParagraphBuilder * builder,Fuzz * fuzz)197 void RemoveStyle(ParagraphBuilder* builder, Fuzz*  fuzz) {
198     bool pop;
199     fuzz->next(&pop);
200     if (pop) {
201         builder->pop();
202     }
203 }
204 
AddStyleAndText(ParagraphBuilder * builder,Fuzz * fuzz)205 void 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 
BuildParagraphStyle(Fuzz * fuzz)224 ParagraphStyle 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 
DEF_FUZZ(SkParagraph,fuzz)258 DEF_FUZZ(SkParagraph, fuzz) {
259     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 
275     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);
284 }
285 
286 #endif // SK_ENABLE_PARAGRAPH
287