blob: 33366be5602259ce8c416b1d675e5558a67737a8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2010 The Android Open Source Project
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000010#include "Test.h"
reed@google.com8a85d0c2011-06-24 19:12:12 +000011#include "SkData.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000012#include "SkFlate.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000013#include "SkPDFCatalog.h"
14#include "SkPDFStream.h"
15#include "SkPDFTypes.h"
16#include "SkScalar.h"
17#include "SkStream.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000018#include "SkTypes.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000019
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000020class SkPDFTestDict : public SkPDFDict {
21public:
22 void getResources(SkTDArray<SkPDFObject*>* resourceList) {
23 resourceList->setReserve(resourceList->count() + fResources.count());
24 for (int i = 0; i < fResources.count(); i++) {
25 resourceList->push(fResources[i]);
26 fResources[i]->ref();
27 }
28 }
29
30 void addResource(SkPDFObject* object) {
31 fResources.append(1, &object);
32 }
33
34private:
35 SkTDArray<SkPDFObject*> fResources;
36};
37
reed@google.com8a85d0c2011-06-24 19:12:12 +000038static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
39 const void* buffer, size_t len) {
40 SkAutoDataUnref data(stream.copyToData());
robertphillips@google.com59f46b82012-07-10 17:30:58 +000041 if (offset + len > data->size()) {
reed@google.com8a85d0c2011-06-24 19:12:12 +000042 return false;
43 }
robertphillips@google.com59f46b82012-07-10 17:30:58 +000044 return memcmp(data->bytes() + offset, buffer, len) == 0;
reed@google.com8a85d0c2011-06-24 19:12:12 +000045}
46
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000047static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
vandebo@chromium.org421d6442011-07-20 17:39:01 +000048 const char* expectedData, size_t expectedSize,
49 bool indirect, bool compression) {
50 SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
51 if (!compression) {
vandebo@chromium.org238be8c2012-07-13 20:06:02 +000052 docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flags);
vandebo@chromium.org421d6442011-07-20 17:39:01 +000053 }
54 SkPDFCatalog catalog(docFlags);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000055 size_t directSize = obj->getOutputSize(&catalog, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +000056 REPORTER_ASSERT(reporter, directSize == expectedSize);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000057
58 SkDynamicMemoryWStream buffer;
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000059 obj->emit(&buffer, &catalog, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000060 REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
vandebo@chromium.org421d6442011-07-20 17:39:01 +000061 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000062 directSize));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000063
64 if (indirect) {
65 // Indirect output.
66 static char header[] = "1 0 obj\n";
67 static size_t headerLen = strlen(header);
68 static char footer[] = "\nendobj\n";
69 static size_t footerLen = strlen(footer);
70
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000071 catalog.addObject(obj, false);
72
73 size_t indirectSize = obj->getOutputSize(&catalog, true);
74 REPORTER_ASSERT(reporter,
75 indirectSize == directSize + headerLen + footerLen);
76
77 buffer.reset();
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000078 obj->emit(&buffer, &catalog, true);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000079 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
reed@google.com8a85d0c2011-06-24 19:12:12 +000080 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
vandebo@chromium.org421d6442011-07-20 17:39:01 +000081 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000082 directSize));
83 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
84 footer, footerLen));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000085 }
86}
87
vandebo@chromium.org421d6442011-07-20 17:39:01 +000088static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
89 SkPDFObject* obj,
robertphillips@google.com4debcac2012-05-14 16:33:36 +000090 const char* expectedResult) {
91 CheckObjectOutput(reporter, obj, expectedResult,
92 strlen(expectedResult), true, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +000093}
94
95static void TestPDFStream(skiatest::Reporter* reporter) {
96 char streamBytes[] = "Test\nFoo\tBar";
97 SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream(
98 streamBytes, strlen(streamBytes), true);
99 streamData->unref(); // SkRefPtr and new both took a reference.
100 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
101 stream->unref(); // SkRefPtr and new both took a reference.
102 SimpleCheckObjectOutput(
103 reporter, stream.get(),
104 "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
105 stream->insert("Attribute", new SkPDFInt(42))->unref();
106 SimpleCheckObjectOutput(reporter, stream.get(),
107 "<</Length 12\n/Attribute 42\n>> stream\n"
108 "Test\nFoo\tBar\nendstream");
109
110 if (SkFlate::HaveFlate()) {
111 char streamBytes2[] = "This is a longer string, so that compression "
112 "can do something with it. With shorter strings, "
113 "the short circuit logic cuts in and we end up "
114 "with an uncompressed string.";
115 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
116 strlen(streamBytes2)));
117 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get());
118 stream->unref(); // SkRefPtr and new both took a reference.
119
120 SkDynamicMemoryWStream compressedByteStream;
121 SkFlate::Deflate(streamData2.get(), &compressedByteStream);
122 SkAutoDataUnref compressedData(compressedByteStream.copyToData());
123
124 // Check first without compression.
125 SkDynamicMemoryWStream expectedResult1;
126 expectedResult1.writeText("<</Length 167\n>> stream\n");
127 expectedResult1.writeText(streamBytes2);
128 expectedResult1.writeText("\nendstream");
129 SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
130 CheckObjectOutput(reporter, stream.get(),
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000131 (const char*) expectedResultData1->data(),
132 expectedResultData1->size(), true, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000133
134 // Then again with compression.
135 SkDynamicMemoryWStream expectedResult2;
136 expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
137 ">> stream\n");
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000138 expectedResult2.write(compressedData->data(), compressedData->size());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000139 expectedResult2.writeText("\nendstream");
140 SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
141 CheckObjectOutput(reporter, stream.get(),
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000142 (const char*) expectedResultData2->data(),
143 expectedResultData2->size(), true, true);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000144 }
145}
146
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000147static void TestCatalog(skiatest::Reporter* reporter) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000148 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000149 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
150 int1->unref(); // SkRefPtr and new both took a reference.
151 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
152 int2->unref(); // SkRefPtr and new both took a reference.
153 SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
154 int3->unref(); // SkRefPtr and new both took a reference.
155 SkRefPtr<SkPDFInt> int1Again(int1.get());
156
157 catalog.addObject(int1.get(), false);
158 catalog.addObject(int2.get(), false);
159 catalog.addObject(int3.get(), false);
160
161 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
162 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
163 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
164
165 SkDynamicMemoryWStream buffer;
166 catalog.emitObjectNumber(&buffer, int1.get());
167 catalog.emitObjectNumber(&buffer, int2.get());
168 catalog.emitObjectNumber(&buffer, int3.get());
169 catalog.emitObjectNumber(&buffer, int1Again.get());
170 char expectedResult[] = "1 02 03 01 0";
reed@google.com8a85d0c2011-06-24 19:12:12 +0000171 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
172 strlen(expectedResult)));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000173}
174
175static void TestObjectRef(skiatest::Reporter* reporter) {
176 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
177 int1->unref(); // SkRefPtr and new both took a reference.
178 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
179 int2->unref(); // SkRefPtr and new both took a reference.
180 SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
181 int2ref->unref(); // SkRefPtr and new both took a reference.
182
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000183 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000184 catalog.addObject(int1.get(), false);
185 catalog.addObject(int2.get(), false);
186 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
187 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
188
189 char expectedResult[] = "2 0 R";
190 SkDynamicMemoryWStream buffer;
191 int2ref->emitObject(&buffer, &catalog, false);
192 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
reed@google.com8a85d0c2011-06-24 19:12:12 +0000193 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
194 buffer.getOffset()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000195}
196
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000197static void TestSubstitute(skiatest::Reporter* reporter) {
198 SkRefPtr<SkPDFTestDict> proxy = new SkPDFTestDict();
199 proxy->unref(); // SkRefPtr and new both took a reference.
200 SkRefPtr<SkPDFTestDict> stub = new SkPDFTestDict();
201 stub->unref(); // SkRefPtr and new both took a reference.
202 SkRefPtr<SkPDFInt> int33 = new SkPDFInt(33);
203 int33->unref(); // SkRefPtr and new both took a reference.
204 SkRefPtr<SkPDFDict> stubResource = new SkPDFDict();
205 stubResource->unref(); // SkRefPtr and new both took a reference.
206 SkRefPtr<SkPDFInt> int44 = new SkPDFInt(44);
207 int44->unref(); // SkRefPtr and new both took a reference.
208
209 stub->insert("Value", int33.get());
210 stubResource->insert("InnerValue", int44.get());
211 stub->addResource(stubResource.get());
212
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000213 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000214 catalog.addObject(proxy.get(), false);
215 catalog.setSubstitute(proxy.get(), stub.get());
216
217 SkDynamicMemoryWStream buffer;
218 proxy->emit(&buffer, &catalog, false);
219 catalog.emitSubstituteResources(&buffer, false);
220
vandebo@chromium.orgd3a094c2011-07-25 22:22:25 +0000221 char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
222 REPORTER_ASSERT(
223 reporter,
224 catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
225
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000226 char expectedResult[] =
227 "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
228 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
229 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
230 buffer.getOffset()));
231}
232
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000233static void TestPDFPrimitives(skiatest::Reporter* reporter) {
234 SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
235 int42->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000236 SimpleCheckObjectOutput(reporter, int42.get(), "42");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000237
238 SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
239 realHalf->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000240 SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000241
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000242#if defined(SK_SCALAR_IS_FLOAT)
caryclark@google.com42639cd2012-06-06 12:03:39 +0000243 SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75f);
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000244 bigScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000245#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000246 SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000247#else
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000248 SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000249
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000250 SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
251 biggerScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000252 SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000253
254 SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
255 smallestScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000256 SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000257#endif
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000258#endif
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000259
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000260 SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
261 stringSimple->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000262 SimpleCheckObjectOutput(reporter, stringSimple.get(),
263 "(test \\) string \\( foo)");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000264 SkRefPtr<SkPDFString> stringComplex =
265 new SkPDFString("\ttest ) string ( foo");
266 stringComplex->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000267 SimpleCheckObjectOutput(reporter, stringComplex.get(),
268 "<0974657374202920737472696E67202820666F6F>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000269
270 SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
271 name->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000272 const char expectedResult[] = "/Test#20name#09with#23tab";
273 CheckObjectOutput(reporter, name.get(), expectedResult,
274 strlen(expectedResult), false, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000275
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000276 // Test that we correctly handle characters with the high-bit set.
bungeman@google.comf8aa18c2012-03-19 21:04:52 +0000277 const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
278 SkRefPtr<SkPDFName> highBitName = new SkPDFName((const char*)highBitCString);
vandebo@chromium.orgcc46a0a2012-03-05 19:14:37 +0000279 highBitName->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000280 const char highBitExpectedResult[] = "/#DE#ADbe#EF";
281 CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
282 strlen(highBitExpectedResult), false, false);
283
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000284 SkRefPtr<SkPDFArray> array = new SkPDFArray;
285 array->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000286 SimpleCheckObjectOutput(reporter, array.get(), "[]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000287 array->append(int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000288 SimpleCheckObjectOutput(reporter, array.get(), "[42]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000289 array->append(realHalf.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000290 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000291 SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
292 int0->unref(); // SkRefPtr and new both took a reference.
293 array->append(int0.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000294 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000295 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
296 int1->unref(); // SkRefPtr and new both took a reference.
297 array->setAt(0, int1.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000298 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000299
300 SkRefPtr<SkPDFDict> dict = new SkPDFDict;
301 dict->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000302 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000303 SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
304 n1->unref(); // SkRefPtr and new both took a reference.
305 dict->insert(n1.get(), int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000306 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000307 SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
308 n2->unref(); // SkRefPtr and new both took a reference.
309 SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
310 n3->unref(); // SkRefPtr and new both took a reference.
311 dict->insert(n2.get(), realHalf.get());
312 dict->insert(n3.get(), array.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000313 SimpleCheckObjectOutput(reporter, dict.get(),
314 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000315
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000316 TestPDFStream(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000317
318 TestCatalog(reporter);
319
320 TestObjectRef(reporter);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000321
322 TestSubstitute(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000323}
324
325#include "TestClassDef.h"
326DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)