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 "Resources.h"
9 #include "SkBitmap.h"
10 #include "SkCanvas.h"
11 #include "SkData.h"
12 #include "SkDocument.h"
13 #include "SkDeflate.h"
14 #include "SkImageEncoder.h"
15 #include "SkMatrix.h"
16 #include "SkPDFCanon.h"
17 #include "SkPDFDevice.h"
18 #include "SkPDFFont.h"
19 #include "SkPDFStream.h"
20 #include "SkPDFTypes.h"
21 #include "SkPDFUtils.h"
22 #include "SkReadBuffer.h"
23 #include "SkScalar.h"
24 #include "SkStream.h"
25 #include "SkTypes.h"
26 #include "Test.h"
27 #include "sk_tool_utils.h"
28 
29 #define DUMMY_TEXT "DCT compessed stream."
30 
31 namespace {
32 struct Catalog {
33     SkPDFSubstituteMap substitutes;
34     SkPDFObjNumMap numbers;
35 };
36 }  // namespace
37 
38 template <typename T>
emit_to_string(T & obj,Catalog * catPtr=nullptr)39 static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) {
40     Catalog catalog;
41     SkDynamicMemoryWStream buffer;
42     if (!catPtr) {
43         catPtr = &catalog;
44     }
45     obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes);
46     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
47     SkString tmp(asset->getLength());
48     asset->read(tmp.writable_str(), asset->getLength());
49     return tmp;
50 }
51 
eq(const SkString & str,const char * strPtr,size_t len)52 static bool eq(const SkString& str, const char* strPtr, size_t len) {
53     return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
54 }
55 
56 #define ASSERT_EQL(REPORTER, SKSTRING, STRING, LEN)                     \
57     do {                                                                \
58         const char* strptr = STRING;                                    \
59         const SkString& sks = SKSTRING;                                 \
60         if (!eq(sks, strptr, LEN)) {                                    \
61             REPORT_FAILURE(                                             \
62                     REPORTER,                                           \
63                     "",                                                 \
64                     SkStringPrintf("'%s' != '%s'", strptr, sks.c_str()));  \
65         }                                                               \
66     } while (false)
67 
68 #define ASSERT_EQ(REPORTER, SKSTRING, STRING)             \
69     do {                                                  \
70         const char* str = STRING;                         \
71         ASSERT_EQL(REPORTER, SKSTRING, str, strlen(str)); \
72     } while (false)
73 
74 #define ASSERT_EMIT_EQ(REPORTER, OBJECT, STRING)          \
75     do {                                                  \
76         SkString result = emit_to_string(OBJECT);         \
77         ASSERT_EQ(REPORTER, result, STRING);              \
78     } while (false)
79 
80 
81 
TestPDFStream(skiatest::Reporter * reporter)82 static void TestPDFStream(skiatest::Reporter* reporter) {
83     char streamBytes[] = "Test\nFoo\tBar";
84     SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream(
85         streamBytes, strlen(streamBytes), true));
86     SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
87     ASSERT_EMIT_EQ(reporter,
88                    *stream,
89                    "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
90     stream->insertInt("Attribute", 42);
91     ASSERT_EMIT_EQ(reporter,
92                    *stream,
93                    "<</Length 12\n/Attribute 42>> stream\n"
94                    "Test\nFoo\tBar\nendstream");
95 
96     {
97         char streamBytes2[] = "This is a longer string, so that compression "
98                               "can do something with it. With shorter strings, "
99                               "the short circuit logic cuts in and we end up "
100                               "with an uncompressed string.";
101         SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
102                                                         strlen(streamBytes2)));
103         SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
104 
105         SkDynamicMemoryWStream compressedByteStream;
106         SkDeflateWStream deflateWStream(&compressedByteStream);
107         deflateWStream.write(streamBytes2, strlen(streamBytes2));
108         deflateWStream.finalize();
109 
110         SkDynamicMemoryWStream expected;
111         expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
112         compressedByteStream.writeToStream(&expected);
113         compressedByteStream.reset();
114         expected.writeText("\nendstream");
115         SkAutoDataUnref expectedResultData2(expected.copyToData());
116         SkString result = emit_to_string(*stream);
117         ASSERT_EQL(reporter,
118                    result,
119                    (const char*)expectedResultData2->data(),
120                    expectedResultData2->size());
121     }
122 }
123 
TestObjectNumberMap(skiatest::Reporter * reporter)124 static void TestObjectNumberMap(skiatest::Reporter* reporter) {
125     SkPDFObjNumMap objNumMap;
126     SkAutoTUnref<SkPDFArray> a1(new SkPDFArray);
127     SkAutoTUnref<SkPDFArray> a2(new SkPDFArray);
128     SkAutoTUnref<SkPDFArray> a3(new SkPDFArray);
129 
130     objNumMap.addObject(a1.get());
131     objNumMap.addObject(a2.get());
132     objNumMap.addObject(a3.get());
133 
134     // The objects should be numbered in the order they are added,
135     // starting with 1.
136     REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
137     REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a2.get()) == 2);
138     REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a3.get()) == 3);
139     // Assert that repeated calls to get the object number return
140     // consistent result.
141     REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
142 }
143 
TestObjectRef(skiatest::Reporter * reporter)144 static void TestObjectRef(skiatest::Reporter* reporter) {
145     SkAutoTUnref<SkPDFArray> a1(new SkPDFArray);
146     SkAutoTUnref<SkPDFArray> a2(new SkPDFArray);
147     a2->appendObjRef(SkRef(a1.get()));
148 
149     Catalog catalog;
150     catalog.numbers.addObject(a1.get());
151     REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1);
152 
153     SkString result = emit_to_string(*a2, &catalog);
154     // If appendObjRef misbehaves, then the result would
155     // be [[]], not [1 0 R].
156     ASSERT_EQ(reporter, result, "[1 0 R]");
157 }
158 
TestSubstitute(skiatest::Reporter * reporter)159 static void TestSubstitute(skiatest::Reporter* reporter) {
160     SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict());
161     SkAutoTUnref<SkPDFDict> stub(new SkPDFDict());
162 
163     proxy->insertInt("Value", 33);
164     stub->insertInt("Value", 44);
165 
166     SkPDFSubstituteMap substituteMap;
167     substituteMap.setSubstitute(proxy.get(), stub.get());
168     SkPDFObjNumMap catalog;
169     catalog.addObject(proxy.get());
170 
171     REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy));
172     REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub));
173 }
174 
175 // This test used to assert without the fix submitted for
176 // http://code.google.com/p/skia/issues/detail?id=1083.
177 // SKP files might have invalid glyph ids. This test ensures they are ignored,
178 // and there is no assert on input data in Debug mode.
test_issue1083()179 static void test_issue1083() {
180     SkDynamicMemoryWStream outStream;
181     SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&outStream));
182     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
183     SkPaint paint;
184     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
185 
186     uint16_t glyphID = 65000;
187     canvas->drawText(&glyphID, 2, 0, 0, paint);
188 
189     doc->close();
190 }
191 
TestPDFUnion(skiatest::Reporter * reporter)192 static void TestPDFUnion(skiatest::Reporter* reporter) {
193     SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
194     ASSERT_EMIT_EQ(reporter, boolTrue, "true");
195 
196     SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
197     ASSERT_EMIT_EQ(reporter, boolFalse, "false");
198 
199     SkPDFUnion int42 = SkPDFUnion::Int(42);
200     ASSERT_EMIT_EQ(reporter, int42, "42");
201 
202     SkPDFUnion realHalf = SkPDFUnion::Scalar(SK_ScalarHalf);
203     ASSERT_EMIT_EQ(reporter, realHalf, ".5");
204 
205     SkPDFUnion bigScalar = SkPDFUnion::Scalar(110999.75f);
206     ASSERT_EMIT_EQ(reporter, bigScalar, "110999.75");
207 
208     SkPDFUnion biggerScalar = SkPDFUnion::Scalar(50000000.1f);
209     ASSERT_EMIT_EQ(reporter, biggerScalar, "50000000");
210 
211     SkPDFUnion smallestScalar = SkPDFUnion::Scalar(1.0f / 65536);
212     ASSERT_EMIT_EQ(reporter, smallestScalar, ".0000152587890");
213 
214     SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo");
215     ASSERT_EMIT_EQ(reporter, stringSimple, "(test \\) string \\( foo)");
216 
217     SkString stringComplexInput("\ttest ) string ( foo");
218     SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput);
219     ASSERT_EMIT_EQ(reporter,
220                    stringComplex,
221                    "<0974657374202920737472696E67202820666F6F>");
222 
223     SkString nameInput("Test name\twith#tab");
224     SkPDFUnion name = SkPDFUnion::Name(nameInput);
225     ASSERT_EMIT_EQ(reporter, name, "/Test#20name#09with#23tab");
226 
227     SkString nameInput2("A#/%()<>[]{}B");
228     SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
229     ASSERT_EMIT_EQ(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
230 
231     SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
232     ASSERT_EMIT_EQ(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
233 
234     // Test that we correctly handle characters with the high-bit set.
235     SkString highBitString("\xDE\xAD" "be\xEF");
236     SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
237     ASSERT_EMIT_EQ(reporter, highBitName, "/#DE#ADbe#EF");
238 }
239 
TestPDFArray(skiatest::Reporter * reporter)240 static void TestPDFArray(skiatest::Reporter* reporter) {
241     SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
242     ASSERT_EMIT_EQ(reporter, *array, "[]");
243 
244     array->appendInt(42);
245     ASSERT_EMIT_EQ(reporter, *array, "[42]");
246 
247     array->appendScalar(SK_ScalarHalf);
248     ASSERT_EMIT_EQ(reporter, *array, "[42 .5]");
249 
250     array->appendInt(0);
251     ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0]");
252 
253     array->appendBool(true);
254     ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true]");
255 
256     array->appendName("ThisName");
257     ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName]");
258 
259     array->appendName(SkString("AnotherName"));
260     ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
261 
262     array->appendString("This String");
263     ASSERT_EMIT_EQ(reporter, *array,
264                    "[42 .5 0 true /ThisName /AnotherName (This String)]");
265 
266     array->appendString(SkString("Another String"));
267     ASSERT_EMIT_EQ(reporter, *array,
268                    "[42 .5 0 true /ThisName /AnotherName (This String) "
269                    "(Another String)]");
270 
271     SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray);
272     innerArray->appendInt(-1);
273     array->appendObject(innerArray.detach());
274     ASSERT_EMIT_EQ(reporter, *array,
275                    "[42 .5 0 true /ThisName /AnotherName (This String) "
276                    "(Another String) [-1]]");
277 
278     SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray);
279     Catalog catalog;
280     catalog.numbers.addObject(referencedArray.get());
281     REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
282                             referencedArray.get()) == 1);
283     array->appendObjRef(referencedArray.detach());
284 
285     SkString result = emit_to_string(*array, &catalog);
286     ASSERT_EQ(reporter, result,
287               "[42 .5 0 true /ThisName /AnotherName (This String) "
288               "(Another String) [-1] 1 0 R]");
289 }
290 
TestPDFDict(skiatest::Reporter * reporter)291 static void TestPDFDict(skiatest::Reporter* reporter) {
292     SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
293     ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
294 
295     dict->insertInt("n1", SkToSizeT(42));
296     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
297 
298     dict.reset(new SkPDFDict);
299     ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
300 
301     dict->insertInt("n1", 42);
302     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
303 
304     dict->insertScalar("n2", SK_ScalarHalf);
305 
306     SkString n3("n3");
307     SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray);
308     innerArray->appendInt(-100);
309     dict->insertObject(n3, innerArray.detach());
310     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
311 
312     dict.reset(new SkPDFDict);
313     ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
314 
315     dict->insertInt("n1", 24);
316     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24>>");
317 
318     dict->insertInt("n2", SkToSizeT(99));
319     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99>>");
320 
321     dict->insertScalar("n3", SK_ScalarHalf);
322     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
323 
324     dict->insertName("n4", "AName");
325     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
326 
327     dict->insertName("n5", SkString("AnotherName"));
328     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
329                    "/n5 /AnotherName>>");
330 
331     dict->insertString("n6", "A String");
332     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
333                    "/n5 /AnotherName\n/n6 (A String)>>");
334 
335     dict->insertString("n7", SkString("Another String"));
336     ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
337                    "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
338 
339     dict.reset(new SkPDFDict("DType"));
340     ASSERT_EMIT_EQ(reporter, *dict, "<</Type /DType>>");
341 
342     SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray);
343     Catalog catalog;
344     catalog.numbers.addObject(referencedArray.get());
345     REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
346                             referencedArray.get()) == 1);
347     dict->insertObjRef("n1", referencedArray.detach());
348     SkString result = emit_to_string(*dict, &catalog);
349     ASSERT_EQ(reporter, result, "<</Type /DType\n/n1 1 0 R>>");
350 }
351 
DEF_TEST(PDFPrimitives,reporter)352 DEF_TEST(PDFPrimitives, reporter) {
353     TestPDFUnion(reporter);
354     TestPDFArray(reporter);
355     TestPDFDict(reporter);
356     TestPDFStream(reporter);
357     TestObjectNumberMap(reporter);
358     TestObjectRef(reporter);
359     TestSubstitute(reporter);
360     test_issue1083();
361 }
362 
363 namespace {
364 
365 class DummyImageFilter : public SkImageFilter {
366 public:
DummyImageFilter(bool visited=false)367     DummyImageFilter(bool visited = false) : SkImageFilter(0, nullptr), fVisited(visited) {}
~DummyImageFilter()368     ~DummyImageFilter() override {}
onFilterImageDeprecated(Proxy *,const SkBitmap & src,const Context &,SkBitmap * result,SkIPoint * offset) const369     bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&,
370                                  SkBitmap* result, SkIPoint* offset) const override {
371         fVisited = true;
372         offset->fX = offset->fY = 0;
373         *result = src;
374         return true;
375     }
376     SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)377     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)
378     bool visited() const { return fVisited; }
379 
380 private:
381     mutable bool fVisited;
382 };
383 
CreateProc(SkReadBuffer & buffer)384 SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
385     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
386     bool visited = buffer.readBool();
387     return new DummyImageFilter(visited);
388 }
389 
390 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const391 void DummyImageFilter::toString(SkString* str) const {
392     str->appendf("DummyImageFilter: (");
393     str->append(")");
394 }
395 #endif
396 
397 };
398 
399 // Check that PDF rendering of image filters successfully falls back to
400 // CPU rasterization.
DEF_TEST(PDFImageFilter,reporter)401 DEF_TEST(PDFImageFilter, reporter) {
402     SkDynamicMemoryWStream stream;
403     SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
404     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
405 
406     SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
407 
408     // Filter just created; should be unvisited.
409     REPORTER_ASSERT(reporter, !filter->visited());
410     SkPaint paint;
411     paint.setImageFilter(filter.get());
412     canvas->drawRect(SkRect::MakeWH(100, 100), paint);
413     doc->close();
414 
415     // Filter was used in rendering; should be visited.
416     REPORTER_ASSERT(reporter, filter->visited());
417 }
418 
419 // Check that PDF rendering of image filters successfully falls back to
420 // CPU rasterization.
DEF_TEST(PDFFontCanEmbedTypeface,reporter)421 DEF_TEST(PDFFontCanEmbedTypeface, reporter) {
422     SkPDFCanon canon;
423 
424     const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
425     SkAutoTUnref<SkTypeface> noEmbedTypeface(GetResourceAsTypeface(resource));
426     if (noEmbedTypeface) {
427         REPORTER_ASSERT(reporter,
428                         !SkPDFFont::CanEmbedTypeface(noEmbedTypeface, &canon));
429     }
430     SkAutoTUnref<SkTypeface> portableTypeface(
431             sk_tool_utils::create_portable_typeface(NULL, SkTypeface::kNormal));
432     REPORTER_ASSERT(reporter,
433                     SkPDFFont::CanEmbedTypeface(portableTypeface, &canon));
434 }
435 
436 
437 // test to see that all finite scalars round trip via scanf().
check_pdf_scalar_serialization(skiatest::Reporter * reporter,float inputFloat)438 static void check_pdf_scalar_serialization(
439         skiatest::Reporter* reporter, float inputFloat) {
440     char floatString[SkPDFUtils::kMaximumFloatDecimalLength];
441     size_t len = SkPDFUtils::FloatToDecimal(inputFloat, floatString);
442     if (len >= sizeof(floatString)) {
443         ERRORF(reporter, "string too long: %u", (unsigned)len);
444         return;
445     }
446     if (floatString[len] != '\0' || strlen(floatString) != len) {
447         ERRORF(reporter, "terminator misplaced.");
448         return;  // The terminator is needed for sscanf().
449     }
450     if (reporter->verbose()) {
451         SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString);
452     }
453     float roundTripFloat;
454     if (1 != sscanf(floatString, "%f", &roundTripFloat)) {
455         ERRORF(reporter, "unscannable result: %s", floatString);
456         return;
457     }
458     if (isfinite(inputFloat) && roundTripFloat != inputFloat) {
459         ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)",
460                roundTripFloat, inputFloat);
461     }
462 }
463 
464 // Test SkPDFUtils::AppendScalar for accuracy.
DEF_TEST(PDFPrimitives_Scalar,reporter)465 DEF_TEST(PDFPrimitives_Scalar, reporter) {
466     SkRandom random(0x5EED);
467     int iterationCount = 512;
468     while (iterationCount-- > 0) {
469         union { uint32_t u; float f; };
470         u = random.nextU();
471         static_assert(sizeof(float) == sizeof(uint32_t), "");
472         check_pdf_scalar_serialization(reporter, f);
473     }
474     float alwaysCheck[] = {
475         0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX,
476         -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f,
477         SK_FloatNaN, SK_FloatInfinity, SK_FloatNegativeInfinity,
478         -FLT_MIN / 8388608.0
479     };
480     for (float inputFloat: alwaysCheck) {
481         check_pdf_scalar_serialization(reporter, inputFloat);
482     }
483 }
484