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