C++11 override should now be supported by all of {bots,Chrome,Android,Mozilla}
[platform/upstream/libSkiaSharp.git] / 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 "SkDocument.h"
12 #include "SkFlate.h"
13 #include "SkImageEncoder.h"
14 #include "SkMatrix.h"
15 #include "SkPDFCanon.h"
16 #include "SkPDFCatalog.h"
17 #include "SkPDFDevice.h"
18 #include "SkPDFStream.h"
19 #include "SkPDFTypes.h"
20 #include "SkReadBuffer.h"
21 #include "SkScalar.h"
22 #include "SkStream.h"
23 #include "SkTypes.h"
24 #include "Test.h"
25
26 #define DUMMY_TEXT "DCT compessed stream."
27
28 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
29                           const void* buffer, size_t len) {
30     SkAutoDataUnref data(stream.copyToData());
31     if (offset + len > data->size()) {
32         return false;
33     }
34     return memcmp(data->bytes() + offset, buffer, len) == 0;
35 }
36
37 static void emit_object(SkPDFObject* object,
38                         SkWStream* stream,
39                         SkPDFCatalog* catalog,
40                         bool indirect) {
41     SkPDFObject* realObject = catalog->getSubstituteObject(object);
42     if (indirect) {
43         stream->writeDecAsText(catalog->getObjectNumber(object));
44         stream->writeText(" 0 obj\n");  // Generation number is always 0.
45         realObject->emitObject(stream, catalog);
46         stream->writeText("\nendobj\n");
47     } else {
48         realObject->emitObject(stream, catalog);
49     }
50 }
51
52 static size_t get_output_size(SkPDFObject* object,
53                               SkPDFCatalog* catalog,
54                               bool indirect) {
55     SkDynamicMemoryWStream buffer;
56     emit_object(object, &buffer, catalog, indirect);
57     return buffer.getOffset();
58 }
59
60 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
61                               const char* expectedData, size_t expectedSize,
62                               bool indirect) {
63     SkPDFCatalog catalog;
64     size_t directSize = get_output_size(obj, &catalog, false);
65     REPORTER_ASSERT(reporter, directSize == expectedSize);
66
67     SkDynamicMemoryWStream buffer;
68     emit_object(obj, &buffer, &catalog, false);
69     REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
70     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
71                                             directSize));
72
73     if (indirect) {
74         // Indirect output.
75         static char header[] = "1 0 obj\n";
76         static size_t headerLen = strlen(header);
77         static char footer[] = "\nendobj\n";
78         static size_t footerLen = strlen(footer);
79
80         catalog.addObject(obj);
81
82         size_t indirectSize = get_output_size(obj, &catalog, true);
83         REPORTER_ASSERT(reporter,
84                         indirectSize == directSize + headerLen + footerLen);
85
86         buffer.reset();
87         emit_object(obj, &buffer, &catalog, true);
88         REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
89         REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
90         REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
91                                                 directSize));
92         REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
93                                                 footer, footerLen));
94     }
95 }
96
97 static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
98                                     SkPDFObject* obj,
99                                     const char* expectedResult) {
100     CheckObjectOutput(reporter, obj, expectedResult,
101                       strlen(expectedResult), true);
102 }
103
104 static void TestPDFStream(skiatest::Reporter* reporter) {
105     char streamBytes[] = "Test\nFoo\tBar";
106     SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream(
107         streamBytes, strlen(streamBytes), true));
108     SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
109     SimpleCheckObjectOutput(
110         reporter, stream.get(),
111         "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
112     stream->insert("Attribute", new SkPDFInt(42))->unref();
113     SimpleCheckObjectOutput(reporter, stream.get(),
114                             "<</Length 12\n/Attribute 42\n>> stream\n"
115                                 "Test\nFoo\tBar\nendstream");
116
117 #ifndef SK_NO_FLATE
118     {
119         char streamBytes2[] = "This is a longer string, so that compression "
120                               "can do something with it. With shorter strings, "
121                               "the short circuit logic cuts in and we end up "
122                               "with an uncompressed string.";
123         SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
124                                                         strlen(streamBytes2)));
125         SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
126
127         SkDynamicMemoryWStream compressedByteStream;
128         SkFlate::Deflate(streamData2.get(), &compressedByteStream);
129         SkAutoDataUnref compressedData(compressedByteStream.copyToData());
130
131         SkDynamicMemoryWStream expected;
132         expected.writeText("<</Filter /FlateDecode\n/Length 116\n"
133                                  ">> stream\n");
134         expected.write(compressedData->data(), compressedData->size());
135         expected.writeText("\nendstream");
136         SkAutoDataUnref expectedResultData2(expected.copyToData());
137         CheckObjectOutput(reporter, stream.get(),
138                           (const char*) expectedResultData2->data(),
139                           expectedResultData2->size(), true);
140     }
141 #endif  // SK_NO_FLATE
142 }
143
144 static void TestCatalog(skiatest::Reporter* reporter) {
145     SkPDFCatalog catalog;
146     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
147     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
148     SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
149     int1.get()->ref();
150     SkAutoTUnref<SkPDFInt> int1Again(int1.get());
151
152     catalog.addObject(int1.get());
153     catalog.addObject(int2.get());
154     catalog.addObject(int3.get());
155
156     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
157     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
158     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int3.get()) == 3);
159     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1Again.get()) == 1);
160 }
161
162 static void TestObjectRef(skiatest::Reporter* reporter) {
163     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
164     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
165     SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
166
167     SkPDFCatalog catalog;
168     catalog.addObject(int1.get());
169     catalog.addObject(int2.get());
170     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
171     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
172
173     char expectedResult[] = "2 0 R";
174     SkDynamicMemoryWStream buffer;
175     int2ref->emitObject(&buffer, &catalog);
176     REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
177     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
178                                             buffer.getOffset()));
179 }
180
181 static void TestSubstitute(skiatest::Reporter* reporter) {
182     SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict());
183     SkAutoTUnref<SkPDFDict> stub(new SkPDFDict());
184
185     proxy->insert("Value", new SkPDFInt(33))->unref();
186     stub->insert("Value", new SkPDFInt(44))->unref();
187
188     SkPDFCatalog catalog;
189     catalog.addObject(proxy.get());
190     catalog.setSubstitute(proxy.get(), stub.get());
191
192     REPORTER_ASSERT(reporter, stub.get() == catalog.getSubstituteObject(proxy));
193     REPORTER_ASSERT(reporter, proxy.get() != catalog.getSubstituteObject(stub));
194 }
195
196 // This test used to assert without the fix submitted for
197 // http://code.google.com/p/skia/issues/detail?id=1083.
198 // SKP files might have invalid glyph ids. This test ensures they are ignored,
199 // and there is no assert on input data in Debug mode.
200 static void test_issue1083() {
201     SkDynamicMemoryWStream outStream;
202     SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&outStream));
203     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
204     SkPaint paint;
205     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
206
207     uint16_t glyphID = 65000;
208     canvas->drawText(&glyphID, 2, 0, 0, paint);
209
210     doc->close();
211 }
212
213 DEF_TEST(PDFPrimitives, reporter) {
214     SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
215     SimpleCheckObjectOutput(reporter, int42.get(), "42");
216
217     SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
218     SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
219
220     SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
221 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
222     SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
223 #else
224     SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
225
226     SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
227     SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
228
229     SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
230     SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
231 #endif
232
233     SkAutoTUnref<SkPDFString> stringSimple(
234         new SkPDFString("test ) string ( foo"));
235     SimpleCheckObjectOutput(reporter, stringSimple.get(),
236                             "(test \\) string \\( foo)");
237     SkAutoTUnref<SkPDFString> stringComplex(
238         new SkPDFString("\ttest ) string ( foo"));
239     SimpleCheckObjectOutput(reporter, stringComplex.get(),
240                             "<0974657374202920737472696E67202820666F6F>");
241
242     SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
243     const char expectedResult[] = "/Test#20name#09with#23tab";
244     CheckObjectOutput(reporter, name.get(), expectedResult,
245                       strlen(expectedResult), false);
246
247     SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
248     const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
249     CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
250                       strlen(escapedNameExpected), false);
251
252     // Test that we correctly handle characters with the high-bit set.
253     const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
254     SkAutoTUnref<SkPDFName> highBitName(
255         new SkPDFName((const char*)highBitCString));
256     const char highBitExpectedResult[] = "/#DE#ADbe#EF";
257     CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
258                       strlen(highBitExpectedResult), false);
259
260     SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
261     SimpleCheckObjectOutput(reporter, array.get(), "[]");
262     array->append(int42.get());
263     SimpleCheckObjectOutput(reporter, array.get(), "[42]");
264     array->append(realHalf.get());
265     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
266     SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
267     array->append(int0.get());
268     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
269     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
270     array->setAt(0, int1.get());
271     SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
272
273     SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
274     SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
275     SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
276     dict->insert(n1.get(), int42.get());
277     SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
278     SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
279     SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
280     dict->insert(n2.get(), realHalf.get());
281     dict->insert(n3.get(), array.get());
282     SimpleCheckObjectOutput(reporter, dict.get(),
283                             "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
284
285     TestPDFStream(reporter);
286
287     TestCatalog(reporter);
288
289     TestObjectRef(reporter);
290
291     TestSubstitute(reporter);
292
293     test_issue1083();
294 }
295
296 namespace {
297
298 class DummyImageFilter : public SkImageFilter {
299 public:
300     DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {}
301     ~DummyImageFilter() override {}
302     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
303                                SkBitmap* result, SkIPoint* offset) const override {
304         fVisited = true;
305         offset->fX = offset->fY = 0;
306         *result = src;
307         return true;
308     }
309     SK_TO_STRING_OVERRIDE()
310     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)
311     bool visited() const { return fVisited; }
312
313 private:
314     mutable bool fVisited;
315 };
316
317 SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
318     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
319     bool visited = buffer.readBool();
320     return SkNEW_ARGS(DummyImageFilter, (visited));
321 }
322
323 #ifndef SK_IGNORE_TO_STRING
324 void DummyImageFilter::toString(SkString* str) const {
325     str->appendf("DummyImageFilter: (");
326     str->append(")");
327 }
328 #endif
329
330 };
331
332 // Check that PDF rendering of image filters successfully falls back to
333 // CPU rasterization.
334 DEF_TEST(PDFImageFilter, reporter) {
335     SkDynamicMemoryWStream stream;
336     SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
337     SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
338
339     SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
340
341     // Filter just created; should be unvisited.
342     REPORTER_ASSERT(reporter, !filter->visited());
343     SkPaint paint;
344     paint.setImageFilter(filter.get());
345     canvas->drawRect(SkRect::MakeWH(100, 100), paint);
346     doc->close();
347
348     // Filter was used in rendering; should be visited.
349     REPORTER_ASSERT(reporter, filter->visited());
350 }