blob: 42fc133b3c36ecff11ead502d52ee8a1f54ef40f [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";
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +000097 SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream(
98 streamBytes, strlen(streamBytes), true));
99 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000100 SimpleCheckObjectOutput(
101 reporter, stream.get(),
102 "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
103 stream->insert("Attribute", new SkPDFInt(42))->unref();
104 SimpleCheckObjectOutput(reporter, stream.get(),
105 "<</Length 12\n/Attribute 42\n>> stream\n"
106 "Test\nFoo\tBar\nendstream");
107
108 if (SkFlate::HaveFlate()) {
109 char streamBytes2[] = "This is a longer string, so that compression "
110 "can do something with it. With shorter strings, "
111 "the short circuit logic cuts in and we end up "
112 "with an uncompressed string.";
113 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
114 strlen(streamBytes2)));
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000115 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000116
117 SkDynamicMemoryWStream compressedByteStream;
118 SkFlate::Deflate(streamData2.get(), &compressedByteStream);
119 SkAutoDataUnref compressedData(compressedByteStream.copyToData());
120
121 // Check first without compression.
122 SkDynamicMemoryWStream expectedResult1;
123 expectedResult1.writeText("<</Length 167\n>> stream\n");
124 expectedResult1.writeText(streamBytes2);
125 expectedResult1.writeText("\nendstream");
126 SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
127 CheckObjectOutput(reporter, stream.get(),
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000128 (const char*) expectedResultData1->data(),
129 expectedResultData1->size(), true, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000130
131 // Then again with compression.
132 SkDynamicMemoryWStream expectedResult2;
133 expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
134 ">> stream\n");
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000135 expectedResult2.write(compressedData->data(), compressedData->size());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000136 expectedResult2.writeText("\nendstream");
137 SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
138 CheckObjectOutput(reporter, stream.get(),
robertphillips@google.com59f46b82012-07-10 17:30:58 +0000139 (const char*) expectedResultData2->data(),
140 expectedResultData2->size(), true, true);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000141 }
142}
143
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000144static void TestCatalog(skiatest::Reporter* reporter) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000145 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000146 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());
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000151
152 catalog.addObject(int1.get(), false);
153 catalog.addObject(int2.get(), false);
154 catalog.addObject(int3.get(), false);
155
156 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
157 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
158 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
159
160 SkDynamicMemoryWStream buffer;
161 catalog.emitObjectNumber(&buffer, int1.get());
162 catalog.emitObjectNumber(&buffer, int2.get());
163 catalog.emitObjectNumber(&buffer, int3.get());
164 catalog.emitObjectNumber(&buffer, int1Again.get());
165 char expectedResult[] = "1 02 03 01 0";
reed@google.com8a85d0c2011-06-24 19:12:12 +0000166 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
167 strlen(expectedResult)));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000168}
169
170static void TestObjectRef(skiatest::Reporter* reporter) {
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000171 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
172 SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
173 SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000174
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000175 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000176 catalog.addObject(int1.get(), false);
177 catalog.addObject(int2.get(), false);
178 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
179 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
180
181 char expectedResult[] = "2 0 R";
182 SkDynamicMemoryWStream buffer;
183 int2ref->emitObject(&buffer, &catalog, false);
184 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
reed@google.com8a85d0c2011-06-24 19:12:12 +0000185 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
186 buffer.getOffset()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000187}
188
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000189static void TestSubstitute(skiatest::Reporter* reporter) {
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000190 SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
191 SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
192 SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
193 SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
194 SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000195
196 stub->insert("Value", int33.get());
197 stubResource->insert("InnerValue", int44.get());
198 stub->addResource(stubResource.get());
199
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000200 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000201 catalog.addObject(proxy.get(), false);
202 catalog.setSubstitute(proxy.get(), stub.get());
203
204 SkDynamicMemoryWStream buffer;
205 proxy->emit(&buffer, &catalog, false);
206 catalog.emitSubstituteResources(&buffer, false);
207
vandebo@chromium.orgd3a094c2011-07-25 22:22:25 +0000208 char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
209 REPORTER_ASSERT(
210 reporter,
211 catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
212
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000213 char expectedResult[] =
214 "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
215 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
216 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
217 buffer.getOffset()));
218}
219
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000220static void TestPDFPrimitives(skiatest::Reporter* reporter) {
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000221 SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000222 SimpleCheckObjectOutput(reporter, int42.get(), "42");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000223
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000224 SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000225 SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000226
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000227#if defined(SK_SCALAR_IS_FLOAT)
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000228 SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000229#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000230 SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000231#else
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000232 SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000233
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000234 SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000235 SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000236
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000237 SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000238 SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000239#endif
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000240#endif
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000241
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000242 SkAutoTUnref<SkPDFString> stringSimple(
243 new SkPDFString("test ) string ( foo"));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000244 SimpleCheckObjectOutput(reporter, stringSimple.get(),
245 "(test \\) string \\( foo)");
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000246 SkAutoTUnref<SkPDFString> stringComplex(
247 new SkPDFString("\ttest ) string ( foo"));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000248 SimpleCheckObjectOutput(reporter, stringComplex.get(),
249 "<0974657374202920737472696E67202820666F6F>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000250
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000251 SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000252 const char expectedResult[] = "/Test#20name#09with#23tab";
253 CheckObjectOutput(reporter, name.get(), expectedResult,
254 strlen(expectedResult), false, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000255
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000256 SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
vandebo@chromium.org251a7662012-09-21 17:50:50 +0000257 const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
258 CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
259 strlen(escapedNameExpected), false, false);
260
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000261 // Test that we correctly handle characters with the high-bit set.
bungeman@google.comf8aa18c2012-03-19 21:04:52 +0000262 const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000263 SkAutoTUnref<SkPDFName> highBitName(
264 new SkPDFName((const char*)highBitCString));
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000265 const char highBitExpectedResult[] = "/#DE#ADbe#EF";
266 CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
267 strlen(highBitExpectedResult), false, false);
268
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000269 SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000270 SimpleCheckObjectOutput(reporter, array.get(), "[]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000271 array->append(int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000272 SimpleCheckObjectOutput(reporter, array.get(), "[42]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000273 array->append(realHalf.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000274 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000275 SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000276 array->append(int0.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000277 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000278 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000279 array->setAt(0, int1.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000280 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000281
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000282 SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000283 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000284 SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000285 dict->insert(n1.get(), int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000286 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000287 SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
288 SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000289 dict->insert(n2.get(), realHalf.get());
290 dict->insert(n3.get(), array.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000291 SimpleCheckObjectOutput(reporter, dict.get(),
292 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000293
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000294 TestPDFStream(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000295
296 TestCatalog(reporter);
297
298 TestObjectRef(reporter);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000299
300 TestSubstitute(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000301}
302
303#include "TestClassDef.h"
304DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)