Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / tests / PDFPrimitivesTest.cpp
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 "SkBitmap.h"
9 #include "SkCanvas.h"
10 #include "SkData.h"
11 #include "SkFlate.h"
12 #include "SkImageEncoder.h"
13 #include "SkMatrix.h"
14 #include "SkPDFCatalog.h"
15 #include "SkPDFDevice.h"
16 #include "SkPDFStream.h"
17 #include "SkPDFTypes.h"
18 #include "SkReadBuffer.h"
19 #include "SkScalar.h"
20 #include "SkStream.h"
21 #include "SkTypes.h"
22 #include "Test.h"
23
24 class SkPDFTestDict : public SkPDFDict {
25 public:
26   virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
27                             SkTSet<SkPDFObject*>* newResourceObjects) {
28         for (int i = 0; i < fResources.count(); i++) {
29             newResourceObjects->add(fResources[i]);
30             fResources[i]->ref();
31         }
32     }
33
34     void addResource(SkPDFObject* object) {
35         fResources.append(1, &object);
36     }
37
38 private:
39     SkTDArray<SkPDFObject*> fResources;
40 };
41
42 #define DUMMY_TEXT "DCT compessed stream."
43
44 static SkData* encode_to_dct_data(size_t* pixelRefOffset, const SkBitmap& bitmap) {
45     *pixelRefOffset = 0;
46     return SkData::NewWithProc(DUMMY_TEXT, sizeof(DUMMY_TEXT) - 1, NULL, NULL);
47 }
48
49 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
50                           const void* buffer, size_t len) {
51     SkAutoDataUnref data(stream.copyToData());
52     if (offset + len > data->size()) {
53         return false;
54     }
55     return memcmp(data->bytes() + offset, buffer, len) == 0;
56 }
57
58 static bool stream_contains(const SkDynamicMemoryWStream& stream,
59                             const char* buffer) {
60     SkAutoDataUnref data(stream.copyToData());
61     int len = strlen(buffer);  // our buffer does not have EOSs.
62
63     for (int offset = 0 ; offset < (int)data->size() - len; offset++) {
64         if (memcmp(data->bytes() + offset, buffer, len) == 0) {
65             return true;
66         }
67     }
68
69     return false;
70 }
71
72 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
73                               const char* expectedData, size_t expectedSize,
74                               bool indirect, bool compression) {
75     SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
76     if (!compression) {
77         docFlags = SkTBitOr(docFlags, SkPDFDocument::kFavorSpeedOverSize_Flags);
78     }
79     SkPDFCatalog catalog(docFlags);
80     size_t directSize = obj->getOutputSize(&catalog, false);
81     REPORTER_ASSERT(reporter, directSize == expectedSize);
82
83     SkDynamicMemoryWStream buffer;
84     obj->emit(&buffer, &catalog, false);
85     REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
86     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
87                                             directSize));
88
89     if (indirect) {
90         // Indirect output.
91         static char header[] = "1 0 obj\n";
92         static size_t headerLen = strlen(header);
93         static char footer[] = "\nendobj\n";
94         static size_t footerLen = strlen(footer);
95
96         catalog.addObject(obj, false);
97
98         size_t indirectSize = obj->getOutputSize(&catalog, true);
99         REPORTER_ASSERT(reporter,
100                         indirectSize == directSize + headerLen + footerLen);
101
102         buffer.reset();
103         obj->emit(&buffer, &catalog, true);
104         REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
105         REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
106         REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
107                                                 directSize));
108         REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
109                                                 footer, footerLen));
110     }
111 }
112
113 static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
114                                     SkPDFObject* obj,
115                                     const char* expectedResult) {
116     CheckObjectOutput(reporter, obj, expectedResult,
117                       strlen(expectedResult), true, false);
118 }
119
120 static void TestPDFStream(skiatest::Reporter* reporter) {
121     char streamBytes[] = "Test\nFoo\tBar";
122     SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream(
123         streamBytes, strlen(streamBytes), true));
124     SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
125     SimpleCheckObjectOutput(
126         reporter, stream.get(),
127         "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
128     stream->insert("Attribute", new SkPDFInt(42))->unref();
129     SimpleCheckObjectOutput(reporter, stream.get(),
130                             "<</Length 12\n/Attribute 42\n>> stream\n"
131                                 "Test\nFoo\tBar\nendstream");
132
133     if (SkFlate::HaveFlate()) {
134         char streamBytes2[] = "This is a longer string, so that compression "
135                               "can do something with it. With shorter strings, "
136                               "the short circuit logic cuts in and we end up "
137                               "with an uncompressed string.";
138         SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
139                                                         strlen(streamBytes2)));
140         SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
141
142         SkDynamicMemoryWStream compressedByteStream;
143         SkFlate::Deflate(streamData2.get(), &compressedByteStream);
144         SkAutoDataUnref compressedData(compressedByteStream.copyToData());
145
146         // Check first without compression.
147         SkDynamicMemoryWStream expectedResult1;
148         expectedResult1.writeText("<</Length 167\n>> stream\n");
149         expectedResult1.writeText(streamBytes2);
150         expectedResult1.writeText("\nendstream");
151         SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
152         CheckObjectOutput(reporter, stream.get(),
153                           (const char*) expectedResultData1->data(),
154                           expectedResultData1->size(), true, false);
155
156         // Then again with compression.
157         SkDynamicMemoryWStream expectedResult2;
158         expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
159                                  ">> stream\n");
160         expectedResult2.write(compressedData->data(), compressedData->size());
161         expectedResult2.writeText("\nendstream");
162         SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
163         CheckObjectOutput(reporter, stream.get(),
164                           (const char*) expectedResultData2->data(),
165                           expectedResultData2->size(), true, true);
166     }
167 }
168
169 static void TestCatalog(skiatest::Reporter* reporter) {
170     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
171     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
172     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
173     SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
174     int1.get()->ref();
175     SkAutoTUnref<SkPDFInt> int1Again(int1.get());
176
177     catalog.addObject(int1.get(), false);
178     catalog.addObject(int2.get(), false);
179     catalog.addObject(int3.get(), false);
180
181     REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
182     REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
183     REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
184
185     SkDynamicMemoryWStream buffer;
186     catalog.emitObjectNumber(&buffer, int1.get());
187     catalog.emitObjectNumber(&buffer, int2.get());
188     catalog.emitObjectNumber(&buffer, int3.get());
189     catalog.emitObjectNumber(&buffer, int1Again.get());
190     char expectedResult[] = "1 02 03 01 0";
191     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
192                                             strlen(expectedResult)));
193 }
194
195 static void TestObjectRef(skiatest::Reporter* reporter) {
196     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
197     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
198     SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
199
200     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
201     catalog.addObject(int1.get(), false);
202     catalog.addObject(int2.get(), false);
203     REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
204     REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
205
206     char expectedResult[] = "2 0 R";
207     SkDynamicMemoryWStream buffer;
208     int2ref->emitObject(&buffer, &catalog, false);
209     REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
210     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
211                                             buffer.getOffset()));
212 }
213
214 static void TestSubstitute(skiatest::Reporter* reporter) {
215     SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
216     SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
217     SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
218     SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
219     SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
220
221     stub->insert("Value", int33.get());
222     stubResource->insert("InnerValue", int44.get());
223     stub->addResource(stubResource.get());
224
225     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
226     catalog.addObject(proxy.get(), false);
227     catalog.setSubstitute(proxy.get(), stub.get());
228
229     SkDynamicMemoryWStream buffer;
230     proxy->emit(&buffer, &catalog, false);
231     catalog.emitSubstituteResources(&buffer, false);
232
233     char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
234     REPORTER_ASSERT(
235         reporter,
236         catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
237
238     char expectedResult[] =
239         "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
240     REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
241     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
242                                             buffer.getOffset()));
243 }
244
245 // Create a bitmap that would be very eficiently compressed in a ZIP.
246 static void setup_bitmap(SkBitmap* bitmap, int width, int height) {
247     bitmap->allocN32Pixels(width, height);
248     bitmap->eraseColor(SK_ColorWHITE);
249 }
250
251 static void TestImage(skiatest::Reporter* reporter, const SkBitmap& bitmap,
252                       const char* expected, bool useDCTEncoder) {
253     SkISize pageSize = SkISize::Make(bitmap.width(), bitmap.height());
254     SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
255
256     if (useDCTEncoder) {
257         dev->setDCTEncoder(encode_to_dct_data);
258     }
259
260     SkCanvas c(dev);
261     c.drawBitmap(bitmap, 0, 0, NULL);
262
263     SkPDFDocument doc;
264     doc.appendPage(dev);
265
266     SkDynamicMemoryWStream stream;
267     doc.emitPDF(&stream);
268
269     REPORTER_ASSERT(reporter, stream_contains(stream, expected));
270 }
271
272 static void TestUncompressed(skiatest::Reporter* reporter) {
273     SkBitmap bitmap;
274     setup_bitmap(&bitmap, 1, 1);
275     TestImage(reporter, bitmap,
276               "/Subtype /Image\n"
277               "/Width 1\n"
278               "/Height 1\n"
279               "/ColorSpace /DeviceRGB\n"
280               "/BitsPerComponent 8\n"
281               "/Length 3\n"
282               ">> stream",
283               true);
284 }
285
286 static void TestFlateDecode(skiatest::Reporter* reporter) {
287     if (!SkFlate::HaveFlate()) {
288         return;
289     }
290     SkBitmap bitmap;
291     setup_bitmap(&bitmap, 10, 10);
292     TestImage(reporter, bitmap,
293               "/Subtype /Image\n"
294               "/Width 10\n"
295               "/Height 10\n"
296               "/ColorSpace /DeviceRGB\n"
297               "/BitsPerComponent 8\n"
298               "/Filter /FlateDecode\n"
299               "/Length 13\n"
300               ">> stream",
301               false);
302 }
303
304 static void TestDCTDecode(skiatest::Reporter* reporter) {
305     SkBitmap bitmap;
306     setup_bitmap(&bitmap, 32, 32);
307     TestImage(reporter, bitmap,
308               "/Subtype /Image\n"
309               "/Width 32\n"
310               "/Height 32\n"
311               "/ColorSpace /DeviceRGB\n"
312               "/BitsPerComponent 8\n"
313               "/Filter /DCTDecode\n"
314               "/ColorTransform 0\n"
315               "/Length 21\n"
316               ">> stream",
317               true);
318 }
319
320 static void TestImages(skiatest::Reporter* reporter) {
321     TestUncompressed(reporter);
322     TestFlateDecode(reporter);
323     TestDCTDecode(reporter);
324 }
325
326 // This test used to assert without the fix submitted for
327 // http://code.google.com/p/skia/issues/detail?id=1083.
328 // SKP files might have invalid glyph ids. This test ensures they are ignored,
329 // and there is no assert on input data in Debug mode.
330 static void test_issue1083() {
331     SkISize pageSize = SkISize::Make(100, 100);
332     SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
333
334     SkCanvas c(dev);
335     SkPaint paint;
336     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
337
338     uint16_t glyphID = 65000;
339     c.drawText(&glyphID, 2, 0, 0, paint);
340
341     SkPDFDocument doc;
342     doc.appendPage(dev);
343
344     SkDynamicMemoryWStream stream;
345     doc.emitPDF(&stream);
346 }
347
348 DEF_TEST(PDFPrimitives, reporter) {
349     SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
350     SimpleCheckObjectOutput(reporter, int42.get(), "42");
351
352     SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
353     SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
354
355     SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
356 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
357     SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
358 #else
359     SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
360
361     SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
362     SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
363
364     SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
365     SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
366 #endif
367
368     SkAutoTUnref<SkPDFString> stringSimple(
369         new SkPDFString("test ) string ( foo"));
370     SimpleCheckObjectOutput(reporter, stringSimple.get(),
371                             "(test \\) string \\( foo)");
372     SkAutoTUnref<SkPDFString> stringComplex(
373         new SkPDFString("\ttest ) string ( foo"));
374     SimpleCheckObjectOutput(reporter, stringComplex.get(),
375                             "<0974657374202920737472696E67202820666F6F>");
376
377     SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
378     const char expectedResult[] = "/Test#20name#09with#23tab";
379     CheckObjectOutput(reporter, name.get(), expectedResult,
380                       strlen(expectedResult), false, false);
381
382     SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
383     const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
384     CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
385                       strlen(escapedNameExpected), false, false);
386
387     // Test that we correctly handle characters with the high-bit set.
388     const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
389     SkAutoTUnref<SkPDFName> highBitName(
390         new SkPDFName((const char*)highBitCString));
391     const char highBitExpectedResult[] = "/#DE#ADbe#EF";
392     CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
393                       strlen(highBitExpectedResult), false, false);
394
395     SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
396     SimpleCheckObjectOutput(reporter, array.get(), "[]");
397     array->append(int42.get());
398     SimpleCheckObjectOutput(reporter, array.get(), "[42]");
399     array->append(realHalf.get());
400     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
401     SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
402     array->append(int0.get());
403     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
404     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
405     array->setAt(0, int1.get());
406     SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
407
408     SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
409     SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
410     SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
411     dict->insert(n1.get(), int42.get());
412     SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
413     SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
414     SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
415     dict->insert(n2.get(), realHalf.get());
416     dict->insert(n3.get(), array.get());
417     SimpleCheckObjectOutput(reporter, dict.get(),
418                             "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
419
420     TestPDFStream(reporter);
421
422     TestCatalog(reporter);
423
424     TestObjectRef(reporter);
425
426     TestSubstitute(reporter);
427
428     test_issue1083();
429
430     TestImages(reporter);
431 }
432
433 namespace {
434
435 class DummyImageFilter : public SkImageFilter {
436 public:
437     DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {}
438     virtual ~DummyImageFilter() SK_OVERRIDE {}
439     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
440                                SkBitmap* result, SkIPoint* offset) const {
441         fVisited = true;
442         offset->fX = offset->fY = 0;
443         *result = src;
444         return true;
445     }
446     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)
447 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
448     explicit DummyImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) {
449         fVisited = buffer.readBool();
450     }
451 #endif
452     bool visited() const { return fVisited; }
453
454 private:
455     mutable bool fVisited;
456 };
457
458 SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
459     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
460     bool visited = buffer.readBool();
461     return SkNEW_ARGS(DummyImageFilter, (visited));
462 }
463
464 };
465
466 // Check that PDF rendering of image filters successfully falls back to
467 // CPU rasterization.
468 DEF_TEST(PDFImageFilter, reporter) {
469     SkISize pageSize = SkISize::Make(100, 100);
470     SkAutoTUnref<SkPDFDevice> device(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
471     SkCanvas canvas(device.get());
472     SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
473
474     // Filter just created; should be unvisited.
475     REPORTER_ASSERT(reporter, !filter->visited());
476     SkPaint paint;
477     paint.setImageFilter(filter.get());
478     canvas.drawRect(SkRect::MakeWH(100, 100), paint);
479
480     // Filter was used in rendering; should be visited.
481     REPORTER_ASSERT(reporter, filter->visited());
482 }