• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 The Android Open Source Project
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 "tests/Test.h"
9 
10 #ifdef SK_SUPPORT_PDF
11 
12 #include "include/core/SkBlendMode.h"
13 #include "include/core/SkCanvas.h"
14 #include "include/core/SkColor.h"
15 #include "include/core/SkDocument.h"
16 #include "include/core/SkFont.h"
17 #include "include/core/SkFontStyle.h"
18 #include "include/core/SkFontTypes.h"
19 #include "include/core/SkImageFilter.h"
20 #include "include/core/SkPaint.h"
21 #include "include/core/SkPath.h"
22 #include "include/core/SkPoint.h"
23 #include "include/core/SkRect.h"
24 #include "include/core/SkRefCnt.h"
25 #include "include/core/SkScalar.h"
26 #include "include/core/SkSpan.h"
27 #include "include/core/SkStream.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkTypes.h"
30 #include "include/docs/SkPDFDocument.h"
31 #include "include/docs/SkPDFJpegHelpers.h"
32 #include "include/effects/SkImageFilters.h"
33 #include "include/effects/SkPerlinNoiseShader.h"
34 #include "include/private/base/SkDebug.h"
35 #include "include/private/base/SkFloatingPoint.h"
36 #include "include/private/base/SkTo.h"
37 #include "src/base/SkRandom.h"
38 #include "src/core/SkImageFilterTypes.h"
39 #include "src/core/SkImageFilter_Base.h"
40 #include "src/pdf/SkClusterator.h"
41 #include "src/pdf/SkPDFDocumentPriv.h"
42 #include "src/pdf/SkPDFFont.h"
43 #include "src/pdf/SkPDFTypes.h"
44 #include "src/pdf/SkPDFUnion.h"
45 #include "src/pdf/SkPDFUtils.h"
46 #include "src/text/GlyphRun.h"
47 #include "src/utils/SkFloatToDecimal.h"
48 #include "tools/Resources.h"
49 #include "tools/ToolUtils.h"
50 #include "tools/fonts/FontToolUtils.h"
51 
52 #include <cfloat>
53 #include <cmath>
54 #include <cstdint>
55 #include <cstdio>
56 #include <cstdlib>
57 #include <cstring>
58 #include <memory>
59 #include <optional>
60 #include <utility>
61 
62 class SkTypeface;
63 
64 template <typename T>
emit_to_string(T & obj)65 static SkString emit_to_string(T& obj) {
66     SkDynamicMemoryWStream buffer;
67     obj.emitObject(&buffer);
68     SkString tmp(buffer.bytesWritten());
69     buffer.copyTo(tmp.data());
70     return tmp;
71 }
72 
eq(const SkString & str,const char * strPtr,size_t len)73 static bool eq(const SkString& str, const char* strPtr, size_t len) {
74     return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
75 }
76 
assert_eql(skiatest::Reporter * reporter,const SkString & skString,const char * str,size_t len)77 static void assert_eql(skiatest::Reporter* reporter,
78                        const SkString& skString,
79                        const char* str,
80                        size_t len) {
81     if (!eq(skString, str, len)) {
82         REPORT_FAILURE(reporter, "", SkStringPrintf(
83                 "'%*s' != '%s'", (int)len, str, skString.c_str()));
84     }
85 }
86 
assert_eq(skiatest::Reporter * reporter,const SkString & skString,const char * str)87 static void assert_eq(skiatest::Reporter* reporter,
88                       const SkString& skString,
89                       const char* str) {
90     assert_eql(reporter, skString, str, strlen(str));
91 }
92 
93 
94 template <typename T>
assert_emit_eq(skiatest::Reporter * reporter,T & object,const char * string)95 static void assert_emit_eq(skiatest::Reporter* reporter,
96                            T& object,
97                            const char* string) {
98     SkString result = emit_to_string(object);
99     assert_eq(reporter, result, string);
100 }
101 
102 // This test used to assert without the fix submitted for
103 // http://code.google.com/p/skia/issues/detail?id=1083.
104 // SKP files might have invalid glyph ids. This test ensures they are ignored,
105 // and there is no assert on input data in Debug mode.
test_issue1083()106 static void test_issue1083() {
107     SkDynamicMemoryWStream outStream;
108     auto doc = SkPDF::MakeDocument(&outStream, SkPDF::JPEG::MetadataWithCallbacks());
109     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
110 
111     uint16_t glyphID = 65000;
112     SkFont font = ToolUtils::DefaultFont();
113     canvas->drawSimpleText(&glyphID, 2, SkTextEncoding::kGlyphID, 0, 0, font, SkPaint());
114 
115     doc->close();
116 }
117 
assert_emit_eq_number(skiatest::Reporter * reporter,float number)118 static void assert_emit_eq_number(skiatest::Reporter* reporter, float number) {
119     SkPDFUnion pdfUnion = SkPDFUnion::Scalar(number);
120     SkString result = emit_to_string(pdfUnion);
121     float value = static_cast<float>(std::atof(result.c_str()));
122     if (value != number) {
123         ERRORF(reporter, "%.9g != %s", number, result.c_str());
124     }
125 }
126 
127 
TestPDFUnion(skiatest::Reporter * reporter)128 static void TestPDFUnion(skiatest::Reporter* reporter) {
129     SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
130     assert_emit_eq(reporter, boolTrue, "true");
131 
132     SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
133     assert_emit_eq(reporter, boolFalse, "false");
134 
135     SkPDFUnion int42 = SkPDFUnion::Int(42);
136     assert_emit_eq(reporter, int42, "42");
137 
138     assert_emit_eq_number(reporter, SK_ScalarHalf);
139     assert_emit_eq_number(reporter, 110999.75f);  // bigScalar
140     assert_emit_eq_number(reporter, 50000000.1f);  // biggerScalar
141     assert_emit_eq_number(reporter, 1.0f / 65536);  // smallScalar
142 
143     SkPDFUnion stringSimple = SkPDFUnion::TextString("test ) string ( foo");
144     assert_emit_eq(reporter, stringSimple, "(test \\) string \\( foo)");
145 
146     SkString stringComplexInput("\ttest ) string ( foo");
147     SkPDFUnion stringComplex = SkPDFUnion::TextString(stringComplexInput);
148     assert_emit_eq(reporter, stringComplex, "(\\011test \\) string \\( foo)");
149 
150     SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20");
151     SkPDFUnion binaryString = SkPDFUnion::ByteString(binaryStringInput);
152     assert_emit_eq(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
153 
154     SkString nameInput("Test name\twith#tab");
155     SkPDFUnion name = SkPDFUnion::Name(nameInput);
156     assert_emit_eq(reporter, name, "/Test#20name#09with#23tab");
157 
158     SkString nameInput2("A#/%()<>[]{}B");
159     SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
160     assert_emit_eq(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
161 
162     SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
163     assert_emit_eq(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
164 
165     // Test that we correctly handle characters with the high-bit set.
166     SkString highBitString("\xDE\xAD" "be\xEF");
167     SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
168     assert_emit_eq(reporter, highBitName, "/#DE#ADbe#EF");
169 
170     // https://bugs.skia.org/9508
171     // https://crbug.com/494913
172     // Trailing '\0' characters must be removed.
173     const char nameInput4[] = "Test name with nil\0";
174     SkPDFUnion name4 = SkPDFUnion::Name(SkString(nameInput4, strlen(nameInput4) + 1));
175     assert_emit_eq(reporter, name4, "/Test#20name#20with#20nil");
176 }
177 
TestPDFArray(skiatest::Reporter * reporter)178 static void TestPDFArray(skiatest::Reporter* reporter) {
179     std::unique_ptr<SkPDFArray> array(new SkPDFArray);
180     assert_emit_eq(reporter, *array, "[]");
181 
182     array->appendInt(42);
183     assert_emit_eq(reporter, *array, "[42]");
184 
185     array->appendScalar(SK_ScalarHalf);
186     assert_emit_eq(reporter, *array, "[42 .5]");
187 
188     array->appendInt(0);
189     assert_emit_eq(reporter, *array, "[42 .5 0]");
190 
191     array->appendBool(true);
192     assert_emit_eq(reporter, *array, "[42 .5 0 true]");
193 
194     array->appendName("ThisName");
195     assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName]");
196 
197     array->appendName(SkString("AnotherName"));
198     assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
199 
200     array->appendTextString("This String");
201     assert_emit_eq(reporter, *array,
202                    "[42 .5 0 true /ThisName /AnotherName (This String)]");
203 
204     array->appendByteString(SkString("Another String"));
205     assert_emit_eq(reporter, *array,
206                    "[42 .5 0 true /ThisName /AnotherName (This String) "
207                    "(Another String)]");
208 
209     std::unique_ptr<SkPDFArray> innerArray(new SkPDFArray);
210     innerArray->appendInt(-1);
211     array->appendObject(std::move(innerArray));
212     assert_emit_eq(reporter, *array,
213                    "[42 .5 0 true /ThisName /AnotherName (This String) "
214                    "(Another String) [-1]]");
215 }
216 
TestPDFDict(skiatest::Reporter * reporter)217 static void TestPDFDict(skiatest::Reporter* reporter) {
218     std::unique_ptr<SkPDFDict> dict(new SkPDFDict);
219     assert_emit_eq(reporter, *dict, "<<>>");
220 
221     dict->insertInt("n1", SkToSizeT(42));
222     assert_emit_eq(reporter, *dict, "<</n1 42>>");
223 
224     dict = std::make_unique<SkPDFDict>();
225     assert_emit_eq(reporter, *dict, "<<>>");
226 
227     dict->insertInt("n1", 42);
228     assert_emit_eq(reporter, *dict, "<</n1 42>>");
229 
230     dict->insertScalar("n2", SK_ScalarHalf);
231 
232     SkString n3("n3");
233     std::unique_ptr<SkPDFArray> innerArray(new SkPDFArray);
234     innerArray->appendInt(-100);
235     dict->insertObject(n3, std::move(innerArray));
236     assert_emit_eq(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
237 
238     dict = std::make_unique<SkPDFDict>();
239     assert_emit_eq(reporter, *dict, "<<>>");
240 
241     dict->insertInt("n1", 24);
242     assert_emit_eq(reporter, *dict, "<</n1 24>>");
243 
244     dict->insertInt("n2", SkToSizeT(99));
245     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99>>");
246 
247     dict->insertScalar("n3", SK_ScalarHalf);
248     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
249 
250     dict->insertName("n4", "AName");
251     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
252 
253     dict->insertName("n5", SkString("AnotherName"));
254     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
255                    "/n5 /AnotherName>>");
256 
257     dict->insertTextString("n6", "A String");
258     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
259                    "/n5 /AnotherName\n/n6 (A String)>>");
260 
261     dict->insertByteString("n7", SkString("Another String"));
262     assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
263                    "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
264 
265     dict = std::make_unique<SkPDFDict>("DType");
266     assert_emit_eq(reporter, *dict, "<</Type /DType>>");
267 }
268 
DEF_TEST(SkPDF_Primitives,reporter)269 DEF_TEST(SkPDF_Primitives, reporter) {
270     TestPDFUnion(reporter);
271     TestPDFArray(reporter);
272     TestPDFDict(reporter);
273     test_issue1083();
274 }
275 
276 namespace {
277 
278 class TestImageFilter : public SkImageFilter_Base {
279 public:
TestImageFilter()280     TestImageFilter() : SkImageFilter_Base(nullptr, 0), fVisited(false) {}
281 
visited() const282     bool visited() const { return fVisited; }
283 
284 private:
getFactory() const285     Factory getFactory() const override {
286         SK_ABORT("Does not participate in serialization");
287         return nullptr;
288     }
getTypeName() const289     const char* getTypeName() const override { return "TestImageFilter"; }
290 
onFilterImage(const skif::Context & ctx) const291     skif::FilterResult onFilterImage(const skif::Context& ctx) const override {
292         fVisited = true;
293         return ctx.source();
294     }
295 
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const296     skif::LayerSpace<SkIRect> onGetInputLayerBounds(
297             const skif::Mapping& mapping,
298             const skif::LayerSpace<SkIRect>& desiredOutput,
299             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
300         return desiredOutput;
301     }
302 
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const303     std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
304             const skif::Mapping& mapping,
305             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
306         return contentBounds;
307     }
308 
309     mutable bool fVisited;
310 };
311 
312 }  // namespace
313 
314 // Check that PDF rendering of image filters successfully falls back to
315 // CPU rasterization.
DEF_TEST(SkPDF_ImageFilter,reporter)316 DEF_TEST(SkPDF_ImageFilter, reporter) {
317     REQUIRE_PDF_DOCUMENT(SkPDF_ImageFilter, reporter);
318     SkDynamicMemoryWStream stream;
319     auto doc = SkPDF::MakeDocument(&stream, SkPDF::JPEG::MetadataWithCallbacks());
320     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
321 
322     sk_sp<TestImageFilter> filter(new TestImageFilter());
323 
324     // Filter just created; should be unvisited.
325     REPORTER_ASSERT(reporter, !filter->visited());
326     SkPaint paint;
327     paint.setImageFilter(filter);
328     canvas->drawRect(SkRect::MakeWH(100, 100), paint);
329     doc->close();
330 
331     // Filter was used in rendering; should be visited.
332     REPORTER_ASSERT(reporter, filter->visited());
333 }
334 
335 // Check that PDF rendering of image filters successfully falls back to
336 // CPU rasterization.
DEF_TEST(SkPDF_FontCanEmbedTypeface,reporter)337 DEF_TEST(SkPDF_FontCanEmbedTypeface, reporter) {
338     SkNullWStream nullWStream;
339     SkPDFDocument doc(&nullWStream, SkPDF::Metadata());
340 
341     const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
342     sk_sp<SkTypeface> noEmbedTypeface(ToolUtils::CreateTypefaceFromResource(resource));
343     if (noEmbedTypeface) {
344         REPORTER_ASSERT(reporter,
345                         !SkPDFFont::CanEmbedTypeface(*noEmbedTypeface, &doc));
346     }
347     sk_sp<SkTypeface> portableTypeface(ToolUtils::DefaultTypeface());
348     REPORTER_ASSERT(reporter,
349                     SkPDFFont::CanEmbedTypeface(*portableTypeface, &doc));
350 }
351 
352 
353 // test to see that all finite scalars round trip via scanf().
check_pdf_scalar_serialization(skiatest::Reporter * reporter,float inputFloat)354 static void check_pdf_scalar_serialization(
355         skiatest::Reporter* reporter, float inputFloat) {
356     char floatString[kMaximumSkFloatToDecimalLength];
357     size_t len = SkFloatToDecimal(inputFloat, floatString);
358     if (len >= sizeof(floatString)) {
359         ERRORF(reporter, "string too long: %u", (unsigned)len);
360         return;
361     }
362     if (floatString[len] != '\0' || strlen(floatString) != len) {
363         ERRORF(reporter, "terminator misplaced.");
364         return;  // The terminator is needed for sscanf().
365     }
366     if (reporter->verbose()) {
367         SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString);
368     }
369     float roundTripFloat;
370     if (1 != sscanf(floatString, "%f", &roundTripFloat)) {
371         ERRORF(reporter, "unscannable result: %s", floatString);
372         return;
373     }
374     if (SkIsFinite(inputFloat) && roundTripFloat != inputFloat) {
375         ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)",
376                roundTripFloat, inputFloat);
377     }
378 }
379 
380 // Test SkPDFUtils::AppendScalar for accuracy.
DEF_TEST(SkPDF_Primitives_Scalar,reporter)381 DEF_TEST(SkPDF_Primitives_Scalar, reporter) {
382     SkRandom random(0x5EED);
383     int iterationCount = 512;
384     while (iterationCount-- > 0) {
385         union { uint32_t u; float f; };
386         u = random.nextU();
387         static_assert(sizeof(float) == sizeof(uint32_t), "");
388         check_pdf_scalar_serialization(reporter, f);
389     }
390     float alwaysCheck[] = {
391         0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX,
392         -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f,
393         SK_FloatNaN, SK_FloatInfinity, SK_FloatNegativeInfinity,
394         -FLT_MIN / 8388608.0
395     };
396     for (float inputFloat: alwaysCheck) {
397         check_pdf_scalar_serialization(reporter, inputFloat);
398     }
399 }
400 
401 // Test SkPDFUtils:: for accuracy.
DEF_TEST(SkPDF_Primitives_Color,reporter)402 DEF_TEST(SkPDF_Primitives_Color, reporter) {
403     char buffer[5];
404     for (int i = 0; i < 256; ++i) {
405         size_t len = SkPDFUtils::ColorToDecimal(i, buffer);
406         REPORTER_ASSERT(reporter, len == strlen(buffer));
407         float f;
408         REPORTER_ASSERT(reporter, 1 == sscanf(buffer, "%f", &f));
409         int roundTrip = (int)(0.5 + f * 255);
410         REPORTER_ASSERT(reporter, roundTrip == i);
411     }
412 }
413 
make_run(size_t len,const SkGlyphID * glyphs,SkPoint * pos,const SkFont & font,const uint32_t * clusters,size_t utf8TextByteLength,const char * utf8Text)414 static sktext::GlyphRun make_run(size_t len, const SkGlyphID* glyphs, SkPoint* pos,
415                            const SkFont& font, const uint32_t* clusters,
416                            size_t utf8TextByteLength, const char* utf8Text) {
417     return sktext::GlyphRun(font,
418                             SkSpan<const SkPoint>{pos, len},
419                             SkSpan<const SkGlyphID>{glyphs, len},
420                             SkSpan<const char>{utf8Text, utf8TextByteLength},
421                             SkSpan<const uint32_t>{clusters, len},
422                             SkSpan<const SkVector>{});
423 }
424 
DEF_TEST(SkPDF_Clusterator,reporter)425 DEF_TEST(SkPDF_Clusterator, reporter) {
426     SkFont font = ToolUtils::DefaultFont();
427     {
428         constexpr unsigned len = 11;
429         const uint32_t clusters[len] = { 3, 2, 2, 1, 0, 4, 4, 7, 6, 6, 5 };
430         const SkGlyphID glyphs[len] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
431         SkPoint pos[len] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
432                                   {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
433         const char text[] = "abcdefgh";
434         sktext::GlyphRun run = make_run(len, glyphs, pos, font, clusters, strlen(text), text);
435         SkClusterator clusterator(run);
436         SkClusterator::Cluster expectations[] = {
437             {&text[3], 1, 0, 1},
438             {&text[2], 1, 1, 2},
439             {&text[1], 1, 3, 1},
440             {&text[0], 1, 4, 1},
441             {&text[4], 1, 5, 2},
442             {&text[7], 1, 7, 1},
443             {&text[6], 1, 8, 2},
444             {&text[5], 1, 10, 1},
445             {nullptr, 0, 0, 0},
446         };
447         for (const auto& expectation : expectations) {
448             REPORTER_ASSERT(reporter, clusterator.next() == expectation);
449         }
450     }
451     {
452         constexpr unsigned len = 5;
453         const uint32_t clusters[len] = { 0, 1, 4, 5, 6 };
454         const SkGlyphID glyphs[len] = { 43, 167, 79, 79, 82, };
455         SkPoint pos[len] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
456         const char text[] = "Ha\xCC\x8A" "llo";
457         sktext::GlyphRun run = make_run(len, glyphs, pos, font, clusters, strlen(text), text);
458         SkClusterator clusterator(run);
459         SkClusterator::Cluster expectations[] = {
460             {&text[0], 1, 0, 1},
461             {&text[1], 3, 1, 1},
462             {&text[4], 1, 2, 1},
463             {&text[5], 1, 3, 1},
464             {&text[6], 1, 4, 1},
465             {nullptr, 0, 0, 0},
466         };
467         for (const auto& expectation : expectations) {
468             REPORTER_ASSERT(reporter, clusterator.next() == expectation);
469         }
470     }
471 }
472 
DEF_TEST(fuzz875632f0,reporter)473 DEF_TEST(fuzz875632f0, reporter) {
474     SkNullWStream stream;
475     auto doc = SkPDF::MakeDocument(&stream, SkPDF::JPEG::MetadataWithCallbacks());
476     REPORTER_ASSERT(reporter, doc);
477     SkCanvas* canvas = doc->beginPage(128, 160);
478 
479     SkAutoCanvasRestore autoCanvasRestore(canvas, false);
480 
481     SkPaint layerPaint({0, 0, 0, 0});
482     layerPaint.setImageFilter(SkImageFilters::Dilate(536870912, 0, nullptr, nullptr));
483     layerPaint.setBlendMode(SkBlendMode::kClear);
484 
485     canvas->saveLayer(nullptr, &layerPaint);
486     canvas->saveLayer(nullptr, nullptr);
487 
488     SkPaint paint;
489     paint.setBlendMode(SkBlendMode::kDarken);
490     paint.setShader(SkShaders::MakeFractalNoise(0, 0, 2, 0, nullptr));
491     paint.setColor4f(SkColor4f{0, 0, 0 ,0});
492 
493     canvas->drawPath(SkPath(), paint);
494 }
495 #endif
496