blob: d1ed217c54c689b0b9f4f4a0882d3b854279a60b [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 <string>
11
12#include "Test.h"
reed@google.com8a85d0c2011-06-24 19:12:12 +000013#include "SkData.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000014#include "SkFlate.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000015#include "SkPDFCatalog.h"
16#include "SkPDFStream.h"
17#include "SkPDFTypes.h"
18#include "SkScalar.h"
19#include "SkStream.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000020#include "SkTypes.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000021
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000022class SkPDFTestDict : public SkPDFDict {
23public:
24 void getResources(SkTDArray<SkPDFObject*>* resourceList) {
25 resourceList->setReserve(resourceList->count() + fResources.count());
26 for (int i = 0; i < fResources.count(); i++) {
27 resourceList->push(fResources[i]);
28 fResources[i]->ref();
29 }
30 }
31
32 void addResource(SkPDFObject* object) {
33 fResources.append(1, &object);
34 }
35
36private:
37 SkTDArray<SkPDFObject*> fResources;
38};
39
reed@google.com8a85d0c2011-06-24 19:12:12 +000040static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
41 const void* buffer, size_t len) {
42 SkAutoDataUnref data(stream.copyToData());
43 if (offset + len > data.size()) {
44 return false;
45 }
46 return memcmp(data.bytes() + offset, buffer, len) == 0;
47}
48
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000049static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
vandebo@chromium.org421d6442011-07-20 17:39:01 +000050 const char* expectedData, size_t expectedSize,
51 bool indirect, bool compression) {
52 SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
53 if (!compression) {
54 docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flag);
55 }
56 SkPDFCatalog catalog(docFlags);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000057 size_t directSize = obj->getOutputSize(&catalog, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +000058 REPORTER_ASSERT(reporter, directSize == expectedSize);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000059
60 SkDynamicMemoryWStream buffer;
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000061 obj->emit(&buffer, &catalog, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000062 REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
vandebo@chromium.org421d6442011-07-20 17:39:01 +000063 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000064 directSize));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000065
66 if (indirect) {
67 // Indirect output.
68 static char header[] = "1 0 obj\n";
69 static size_t headerLen = strlen(header);
70 static char footer[] = "\nendobj\n";
71 static size_t footerLen = strlen(footer);
72
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000073 catalog.addObject(obj, false);
74
75 size_t indirectSize = obj->getOutputSize(&catalog, true);
76 REPORTER_ASSERT(reporter,
77 indirectSize == directSize + headerLen + footerLen);
78
79 buffer.reset();
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000080 obj->emit(&buffer, &catalog, true);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000081 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
reed@google.com8a85d0c2011-06-24 19:12:12 +000082 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
vandebo@chromium.org421d6442011-07-20 17:39:01 +000083 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000084 directSize));
85 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
86 footer, footerLen));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000087 }
88}
89
vandebo@chromium.org421d6442011-07-20 17:39:01 +000090static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
91 SkPDFObject* obj,
92 const std::string& expectedResult) {
93 CheckObjectOutput(reporter, obj, expectedResult.c_str(),
94 expectedResult.length(), true, false);
95}
96
97static void TestPDFStream(skiatest::Reporter* reporter) {
98 char streamBytes[] = "Test\nFoo\tBar";
99 SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream(
100 streamBytes, strlen(streamBytes), true);
101 streamData->unref(); // SkRefPtr and new both took a reference.
102 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
103 stream->unref(); // SkRefPtr and new both took a reference.
104 SimpleCheckObjectOutput(
105 reporter, stream.get(),
106 "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
107 stream->insert("Attribute", new SkPDFInt(42))->unref();
108 SimpleCheckObjectOutput(reporter, stream.get(),
109 "<</Length 12\n/Attribute 42\n>> stream\n"
110 "Test\nFoo\tBar\nendstream");
111
112 if (SkFlate::HaveFlate()) {
113 char streamBytes2[] = "This is a longer string, so that compression "
114 "can do something with it. With shorter strings, "
115 "the short circuit logic cuts in and we end up "
116 "with an uncompressed string.";
117 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
118 strlen(streamBytes2)));
119 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get());
120 stream->unref(); // SkRefPtr and new both took a reference.
121
122 SkDynamicMemoryWStream compressedByteStream;
123 SkFlate::Deflate(streamData2.get(), &compressedByteStream);
124 SkAutoDataUnref compressedData(compressedByteStream.copyToData());
125
126 // Check first without compression.
127 SkDynamicMemoryWStream expectedResult1;
128 expectedResult1.writeText("<</Length 167\n>> stream\n");
129 expectedResult1.writeText(streamBytes2);
130 expectedResult1.writeText("\nendstream");
131 SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
132 CheckObjectOutput(reporter, stream.get(),
133 (const char*) expectedResultData1.data(),
134 expectedResultData1.size(), true, false);
135
136 // Then again with compression.
137 SkDynamicMemoryWStream expectedResult2;
138 expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
139 ">> stream\n");
140 expectedResult2.write(compressedData.data(), compressedData.size());
141 expectedResult2.writeText("\nendstream");
142 SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
143 CheckObjectOutput(reporter, stream.get(),
144 (const char*) expectedResultData2.data(),
145 expectedResultData2.size(), true, true);
146 }
147}
148
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000149static void TestCatalog(skiatest::Reporter* reporter) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000150 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000151 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
152 int1->unref(); // SkRefPtr and new both took a reference.
153 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
154 int2->unref(); // SkRefPtr and new both took a reference.
155 SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
156 int3->unref(); // SkRefPtr and new both took a reference.
157 SkRefPtr<SkPDFInt> int1Again(int1.get());
158
159 catalog.addObject(int1.get(), false);
160 catalog.addObject(int2.get(), false);
161 catalog.addObject(int3.get(), false);
162
163 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
164 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
165 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
166
167 SkDynamicMemoryWStream buffer;
168 catalog.emitObjectNumber(&buffer, int1.get());
169 catalog.emitObjectNumber(&buffer, int2.get());
170 catalog.emitObjectNumber(&buffer, int3.get());
171 catalog.emitObjectNumber(&buffer, int1Again.get());
172 char expectedResult[] = "1 02 03 01 0";
reed@google.com8a85d0c2011-06-24 19:12:12 +0000173 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
174 strlen(expectedResult)));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000175}
176
177static void TestObjectRef(skiatest::Reporter* reporter) {
178 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
179 int1->unref(); // SkRefPtr and new both took a reference.
180 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
181 int2->unref(); // SkRefPtr and new both took a reference.
182 SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
183 int2ref->unref(); // SkRefPtr and new both took a reference.
184
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000185 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000186 catalog.addObject(int1.get(), false);
187 catalog.addObject(int2.get(), false);
188 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
189 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
190
191 char expectedResult[] = "2 0 R";
192 SkDynamicMemoryWStream buffer;
193 int2ref->emitObject(&buffer, &catalog, false);
194 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
reed@google.com8a85d0c2011-06-24 19:12:12 +0000195 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
196 buffer.getOffset()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000197}
198
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000199static void TestSubstitute(skiatest::Reporter* reporter) {
200 SkRefPtr<SkPDFTestDict> proxy = new SkPDFTestDict();
201 proxy->unref(); // SkRefPtr and new both took a reference.
202 SkRefPtr<SkPDFTestDict> stub = new SkPDFTestDict();
203 stub->unref(); // SkRefPtr and new both took a reference.
204 SkRefPtr<SkPDFInt> int33 = new SkPDFInt(33);
205 int33->unref(); // SkRefPtr and new both took a reference.
206 SkRefPtr<SkPDFDict> stubResource = new SkPDFDict();
207 stubResource->unref(); // SkRefPtr and new both took a reference.
208 SkRefPtr<SkPDFInt> int44 = new SkPDFInt(44);
209 int44->unref(); // SkRefPtr and new both took a reference.
210
211 stub->insert("Value", int33.get());
212 stubResource->insert("InnerValue", int44.get());
213 stub->addResource(stubResource.get());
214
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000215 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000216 catalog.addObject(proxy.get(), false);
217 catalog.setSubstitute(proxy.get(), stub.get());
218
219 SkDynamicMemoryWStream buffer;
220 proxy->emit(&buffer, &catalog, false);
221 catalog.emitSubstituteResources(&buffer, false);
222
vandebo@chromium.orgd3a094c2011-07-25 22:22:25 +0000223 char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
224 REPORTER_ASSERT(
225 reporter,
226 catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
227
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000228 char expectedResult[] =
229 "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
230 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
231 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
232 buffer.getOffset()));
233}
234
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000235static void TestPDFPrimitives(skiatest::Reporter* reporter) {
236 SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
237 int42->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000238 SimpleCheckObjectOutput(reporter, int42.get(), "42");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000239
240 SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
241 realHalf->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000242 SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000243
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000244#if defined(SK_SCALAR_IS_FLOAT)
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000245 SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
246 bigScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000247#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000248 SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000249#else
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000250 SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000251
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000252 SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
253 biggerScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000254 SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000255
256 SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
257 smallestScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000258 SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000259#endif
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000260#endif
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000261
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000262 SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
263 stringSimple->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000264 SimpleCheckObjectOutput(reporter, stringSimple.get(),
265 "(test \\) string \\( foo)");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000266 SkRefPtr<SkPDFString> stringComplex =
267 new SkPDFString("\ttest ) string ( foo");
268 stringComplex->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000269 SimpleCheckObjectOutput(reporter, stringComplex.get(),
270 "<0974657374202920737472696E67202820666F6F>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000271
272 SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
273 name->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000274 const char expectedResult[] = "/Test#20name#09with#23tab";
275 CheckObjectOutput(reporter, name.get(), expectedResult,
276 strlen(expectedResult), false, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000277
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000278 // Test that we correctly handle characters with the high-bit set.
bungeman@google.comf8aa18c2012-03-19 21:04:52 +0000279 const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
280 SkRefPtr<SkPDFName> highBitName = new SkPDFName((const char*)highBitCString);
vandebo@chromium.orgcc46a0a2012-03-05 19:14:37 +0000281 highBitName->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgc0376fe2012-03-05 18:44:33 +0000282 const char highBitExpectedResult[] = "/#DE#ADbe#EF";
283 CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
284 strlen(highBitExpectedResult), false, false);
285
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000286 SkRefPtr<SkPDFArray> array = new SkPDFArray;
287 array->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000288 SimpleCheckObjectOutput(reporter, array.get(), "[]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000289 array->append(int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000290 SimpleCheckObjectOutput(reporter, array.get(), "[42]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000291 array->append(realHalf.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000292 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000293 SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
294 int0->unref(); // SkRefPtr and new both took a reference.
295 array->append(int0.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000296 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000297 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
298 int1->unref(); // SkRefPtr and new both took a reference.
299 array->setAt(0, int1.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000300 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000301
302 SkRefPtr<SkPDFDict> dict = new SkPDFDict;
303 dict->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000304 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000305 SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
306 n1->unref(); // SkRefPtr and new both took a reference.
307 dict->insert(n1.get(), int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000308 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000309 SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
310 n2->unref(); // SkRefPtr and new both took a reference.
311 SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
312 n3->unref(); // SkRefPtr and new both took a reference.
313 dict->insert(n2.get(), realHalf.get());
314 dict->insert(n3.get(), array.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000315 SimpleCheckObjectOutput(reporter, dict.get(),
316 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000317
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000318 TestPDFStream(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000319
320 TestCatalog(reporter);
321
322 TestObjectRef(reporter);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000323
324 TestSubstitute(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000325}
326
327#include "TestClassDef.h"
328DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)