Coverage Report

Created: 2024-05-20 07:14

/src/skia/fuzz/FuzzSkParagraph.cpp
Line
Count
Source (jump to first uncovered line)
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:
64
    ResourceFontCollection(bool testOnly = false)
65
            : fFontsFound(false)
66
            , fResolvedFonts(0)
67
            , fResourceDir(GetResourcePath("fonts").c_str())
68
0
            , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
69
0
        std::vector<SkString> fonts;
70
0
        SkOSFile::Iter iter(fResourceDir.c_str());
71
72
0
        SkString path;
73
0
        sk_sp<SkFontMgr> mgr = ToolUtils::TestFontMgr();
74
0
        while (iter.next(&path)) {
75
0
            if (path.endsWith("Roboto-Italic.ttf")) {
76
0
                fFontsFound = true;
77
0
            }
78
0
            fonts.emplace_back(path);
79
0
        }
80
81
0
        if (!fFontsFound) {
82
            // SkDebugf("Fonts not found, skipping all the tests\n");
83
0
            return;
84
0
        }
85
        // Only register fonts if we have to
86
0
        for (auto& font : fonts) {
87
0
            SkString file_path;
88
0
            file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
89
0
            fFontProvider->registerTypeface(mgr->makeFromFile(file_path.c_str(), 0));
90
0
        }
91
92
0
        if (testOnly) {
93
0
            this->setTestFontManager(std::move(fFontProvider));
94
0
        } else {
95
0
            this->setAssetFontManager(std::move(fFontProvider));
96
0
        }
97
0
        this->disableFontFallback();
98
0
    }
99
100
0
    size_t resolvedFonts() const { return fResolvedFonts; }
101
102
    // TODO: temp solution until we check in fonts
103
0
    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>
115
0
uint8_t RandomText(T* buffer, Fuzz* fuzz) {
116
0
    uint8_t text_length;
117
0
    fuzz->nextRange(&text_length, 0, MAX_TEXT_LENGTH);
118
0
    fuzz->nextN(buffer, text_length);
119
0
    return text_length;
120
0
}
Unexecuted instantiation: FuzzSkParagraph.cpp:unsigned char (anonymous namespace)::RandomText<char>(char*, Fuzz*)
Unexecuted instantiation: FuzzSkParagraph.cpp:unsigned char (anonymous namespace)::RandomText<char16_t>(char16_t*, Fuzz*)
121
122
// Add random bytes to the paragraph.
123
0
void AddASCIIText(ParagraphBuilder* builder,Fuzz* fuzz) {
124
0
    char text[MAX_TEXT_LENGTH];
125
0
    const auto text_length = RandomText(text, fuzz);
126
0
    builder->addText(text, text_length);
127
0
}
128
// Add random bytes to the paragraph.
129
0
void AddUnicodeText(ParagraphBuilder* builder,Fuzz* fuzz) {
130
0
    char16_t text[MAX_TEXT_LENGTH];
131
0
    const auto text_length = RandomText(text, fuzz);
132
0
    builder->addText(std::u16string(text, text_length));
133
0
}
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.
140
0
void AddZalgoText(ParagraphBuilder* builder, Fuzz* fuzz) {
141
0
    char text[MAX_TEXT_LENGTH];
142
0
    const auto text_length = RandomText(text, fuzz);
143
0
    std::u16string result;
144
145
0
    for (auto& c : std::string(text, text_length)) {
146
0
        result += c;
147
0
        uint8_t mark_count;
148
0
        fuzz->next(&mark_count);
149
0
        for (int i = 0; i < mark_count; i++) {
150
0
            uint8_t mark_type, mark_index;
151
0
            fuzz->next(&mark_type, &mark_index);
152
0
            switch (mark_type % 3) {
153
0
                case 0:
154
0
                    result += COMBINING_UP[mark_index % COMBINING_UP.size()];
155
0
                    break;
156
0
                case 1:
157
0
                    result += COMBINING_MIDDLE[mark_index % COMBINING_MIDDLE.size()];
158
0
                    break;
159
0
                case 2:
160
0
                default:
161
0
                    result += COMBINING_DOWN[mark_index % COMBINING_DOWN.size()];
162
0
                    break;
163
0
            }
164
0
        }
165
0
    }
166
0
    builder->addText(result);
167
0
}
168
169
0
void AddStyle(ParagraphBuilder* builder, Fuzz*  fuzz) {
170
    // TODO(westont): Make this probabilistic, and fill in the remaining TextStyle fields.
171
0
    TextStyle ts;
172
0
    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
0
    builder->pushStyle(ts);
195
0
}
196
0
void RemoveStyle(ParagraphBuilder* builder, Fuzz*  fuzz) {
197
0
    bool pop;
198
0
    fuzz->next(&pop);
199
0
    if (pop) {
200
0
        builder->pop();
201
0
    }
202
0
}
203
204
0
void AddStyleAndText(ParagraphBuilder* builder, Fuzz*  fuzz) {
205
0
    AddStyle(builder, fuzz);
206
0
    uint8_t text_type;
207
0
    fuzz->next(&text_type);
208
0
    switch (text_type % 3) {
209
0
        case 0:
210
0
            AddASCIIText(builder, fuzz);
211
0
            break;
212
0
        case 1:
213
0
            AddUnicodeText(builder, fuzz);
214
0
            break;
215
0
        case 2:
216
0
            AddZalgoText(builder, fuzz);
217
0
            break;
218
0
    }
219
0
    RemoveStyle(builder, fuzz);
220
221
0
}
222
223
0
ParagraphStyle BuildParagraphStyle(Fuzz* fuzz) {
224
0
    ParagraphStyle ps;
225
0
    bool hinting;
226
0
    fuzz->next(&hinting);
227
0
    if (hinting) {
228
0
        ps.turnHintingOff();
229
0
    }
230
0
    StrutStyle ss;
231
    // TODO(westont): Fuzz this object.
232
0
    ps.setStrutStyle(ss);
233
0
    TextDirection td;
234
0
    fuzz->nextEnum(&td, TextDirection::kRtl);
235
0
    ps.setTextDirection(td);
236
0
    TextAlign ta;
237
0
    fuzz->nextEnum(&ta, TextAlign::kEnd);
238
0
    ps.setTextAlign(ta);
239
0
    size_t ml;
240
0
    fuzz->next(&ml);
241
0
    ps.setMaxLines(ml);
242
    // TODO(westont): Randomize with other values and no value.
243
0
    ps.setEllipsis(u"\u2026");
244
0
    SkScalar h;
245
0
    fuzz->next(&h);
246
0
    ps.setHeight(h);
247
0
    TextHeightBehavior thb = TextHeightBehavior::kAll;
248
    // TODO(westont): This crashes our seed test case, why?
249
    //fuzz->nextEnum(&thb, TextHeightBehavior::kDisableAll);
250
0
    ps.setTextHeightBehavior(thb);
251
252
0
    return ps;
253
0
}
254
255
0
static sk_sp<SkUnicode> get_unicode() {
256
0
    auto factory = SkShapers::BestAvailable();
257
0
    return sk_ref_sp<SkUnicode>(factory->getUnicode());
258
0
}
259
260
}  // namespace
261
262
0
DEF_FUZZ(SkParagraph, fuzz) {
263
0
    static sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
264
0
    ParagraphStyle paragraph_style = BuildParagraphStyle(fuzz);
265
0
    ParagraphBuilderImpl builder(paragraph_style, fontCollection, get_unicode());
266
267
0
    uint8_t iterations;
268
0
    fuzz->nextRange(&iterations, 1, MAX_TEXT_ADDITIONS);
269
0
    for (int i = 0; i < iterations; i++) {
270
0
        AddStyleAndText(&builder, fuzz);
271
0
    }
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
0
    builder.pop();
277
0
    auto paragraph = builder.Build();
278
279
0
    SkBitmap bm;
280
0
    if (!bm.tryAllocN32Pixels(TEST_CANVAS_DIM, TEST_CANVAS_DIM)) {
281
0
        return;
282
0
    }
283
0
    SkCanvas canvas(bm);
284
0
    uint8_t layout_width;
285
0
    fuzz->next(&layout_width);
286
0
    paragraph->layout(layout_width);
287
0
    paragraph->paint(&canvas, 0, 0);
288
0
}
289
290
#endif // SK_ENABLE_PARAGRAPH