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