blob: b3f510528a30f967bee9ce11302f64bf703adbf2 [file] [log] [blame]
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +00001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000017#include <string>
18
19#include "Test.h"
reed@google.com8a85d0c2011-06-24 19:12:12 +000020#include "SkData.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000021#include "SkFlate.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000022#include "SkPDFCatalog.h"
23#include "SkPDFStream.h"
24#include "SkPDFTypes.h"
25#include "SkScalar.h"
26#include "SkStream.h"
vandebo@chromium.org421d6442011-07-20 17:39:01 +000027#include "SkTypes.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000028
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000029class SkPDFTestDict : public SkPDFDict {
30public:
31 void getResources(SkTDArray<SkPDFObject*>* resourceList) {
32 resourceList->setReserve(resourceList->count() + fResources.count());
33 for (int i = 0; i < fResources.count(); i++) {
34 resourceList->push(fResources[i]);
35 fResources[i]->ref();
36 }
37 }
38
39 void addResource(SkPDFObject* object) {
40 fResources.append(1, &object);
41 }
42
43private:
44 SkTDArray<SkPDFObject*> fResources;
45};
46
reed@google.com8a85d0c2011-06-24 19:12:12 +000047static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
48 const void* buffer, size_t len) {
49 SkAutoDataUnref data(stream.copyToData());
50 if (offset + len > data.size()) {
51 return false;
52 }
53 return memcmp(data.bytes() + offset, buffer, len) == 0;
54}
55
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000056static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
vandebo@chromium.org421d6442011-07-20 17:39:01 +000057 const char* expectedData, size_t expectedSize,
58 bool indirect, bool compression) {
59 SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
60 if (!compression) {
61 docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flag);
62 }
63 SkPDFCatalog catalog(docFlags);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000064 size_t directSize = obj->getOutputSize(&catalog, false);
vandebo@chromium.org421d6442011-07-20 17:39:01 +000065 REPORTER_ASSERT(reporter, directSize == expectedSize);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000066
67 SkDynamicMemoryWStream buffer;
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000068 obj->emit(&buffer, &catalog, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000069 REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
vandebo@chromium.org421d6442011-07-20 17:39:01 +000070 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000071 directSize));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000072
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
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000080 catalog.addObject(obj, false);
81
82 size_t indirectSize = obj->getOutputSize(&catalog, true);
83 REPORTER_ASSERT(reporter,
84 indirectSize == directSize + headerLen + footerLen);
85
86 buffer.reset();
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000087 obj->emit(&buffer, &catalog, true);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000088 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
reed@google.com8a85d0c2011-06-24 19:12:12 +000089 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
vandebo@chromium.org421d6442011-07-20 17:39:01 +000090 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
reed@google.com8a85d0c2011-06-24 19:12:12 +000091 directSize));
92 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
93 footer, footerLen));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000094 }
95}
96
vandebo@chromium.org421d6442011-07-20 17:39:01 +000097static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
98 SkPDFObject* obj,
99 const std::string& expectedResult) {
100 CheckObjectOutput(reporter, obj, expectedResult.c_str(),
101 expectedResult.length(), true, false);
102}
103
104static void TestPDFStream(skiatest::Reporter* reporter) {
105 char streamBytes[] = "Test\nFoo\tBar";
106 SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream(
107 streamBytes, strlen(streamBytes), true);
108 streamData->unref(); // SkRefPtr and new both took a reference.
109 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
110 stream->unref(); // SkRefPtr and new both took a reference.
111 SimpleCheckObjectOutput(
112 reporter, stream.get(),
113 "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
114 stream->insert("Attribute", new SkPDFInt(42))->unref();
115 SimpleCheckObjectOutput(reporter, stream.get(),
116 "<</Length 12\n/Attribute 42\n>> stream\n"
117 "Test\nFoo\tBar\nendstream");
118
119 if (SkFlate::HaveFlate()) {
120 char streamBytes2[] = "This is a longer string, so that compression "
121 "can do something with it. With shorter strings, "
122 "the short circuit logic cuts in and we end up "
123 "with an uncompressed string.";
124 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
125 strlen(streamBytes2)));
126 SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get());
127 stream->unref(); // SkRefPtr and new both took a reference.
128
129 SkDynamicMemoryWStream compressedByteStream;
130 SkFlate::Deflate(streamData2.get(), &compressedByteStream);
131 SkAutoDataUnref compressedData(compressedByteStream.copyToData());
132
133 // Check first without compression.
134 SkDynamicMemoryWStream expectedResult1;
135 expectedResult1.writeText("<</Length 167\n>> stream\n");
136 expectedResult1.writeText(streamBytes2);
137 expectedResult1.writeText("\nendstream");
138 SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
139 CheckObjectOutput(reporter, stream.get(),
140 (const char*) expectedResultData1.data(),
141 expectedResultData1.size(), true, false);
142
143 // Then again with compression.
144 SkDynamicMemoryWStream expectedResult2;
145 expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
146 ">> stream\n");
147 expectedResult2.write(compressedData.data(), compressedData.size());
148 expectedResult2.writeText("\nendstream");
149 SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
150 CheckObjectOutput(reporter, stream.get(),
151 (const char*) expectedResultData2.data(),
152 expectedResultData2.size(), true, true);
153 }
154}
155
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000156static void TestCatalog(skiatest::Reporter* reporter) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000157 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000158 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
159 int1->unref(); // SkRefPtr and new both took a reference.
160 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
161 int2->unref(); // SkRefPtr and new both took a reference.
162 SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
163 int3->unref(); // SkRefPtr and new both took a reference.
164 SkRefPtr<SkPDFInt> int1Again(int1.get());
165
166 catalog.addObject(int1.get(), false);
167 catalog.addObject(int2.get(), false);
168 catalog.addObject(int3.get(), false);
169
170 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
171 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
172 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
173
174 SkDynamicMemoryWStream buffer;
175 catalog.emitObjectNumber(&buffer, int1.get());
176 catalog.emitObjectNumber(&buffer, int2.get());
177 catalog.emitObjectNumber(&buffer, int3.get());
178 catalog.emitObjectNumber(&buffer, int1Again.get());
179 char expectedResult[] = "1 02 03 01 0";
reed@google.com8a85d0c2011-06-24 19:12:12 +0000180 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
181 strlen(expectedResult)));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000182}
183
184static void TestObjectRef(skiatest::Reporter* reporter) {
185 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
186 int1->unref(); // SkRefPtr and new both took a reference.
187 SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
188 int2->unref(); // SkRefPtr and new both took a reference.
189 SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
190 int2ref->unref(); // SkRefPtr and new both took a reference.
191
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000192 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000193 catalog.addObject(int1.get(), false);
194 catalog.addObject(int2.get(), false);
195 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
196 REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
197
198 char expectedResult[] = "2 0 R";
199 SkDynamicMemoryWStream buffer;
200 int2ref->emitObject(&buffer, &catalog, false);
201 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
reed@google.com8a85d0c2011-06-24 19:12:12 +0000202 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
203 buffer.getOffset()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000204}
205
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000206static void TestSubstitute(skiatest::Reporter* reporter) {
207 SkRefPtr<SkPDFTestDict> proxy = new SkPDFTestDict();
208 proxy->unref(); // SkRefPtr and new both took a reference.
209 SkRefPtr<SkPDFTestDict> stub = new SkPDFTestDict();
210 stub->unref(); // SkRefPtr and new both took a reference.
211 SkRefPtr<SkPDFInt> int33 = new SkPDFInt(33);
212 int33->unref(); // SkRefPtr and new both took a reference.
213 SkRefPtr<SkPDFDict> stubResource = new SkPDFDict();
214 stubResource->unref(); // SkRefPtr and new both took a reference.
215 SkRefPtr<SkPDFInt> int44 = new SkPDFInt(44);
216 int44->unref(); // SkRefPtr and new both took a reference.
217
218 stub->insert("Value", int33.get());
219 stubResource->insert("InnerValue", int44.get());
220 stub->addResource(stubResource.get());
221
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000222 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000223 catalog.addObject(proxy.get(), false);
224 catalog.setSubstitute(proxy.get(), stub.get());
225
226 SkDynamicMemoryWStream buffer;
227 proxy->emit(&buffer, &catalog, false);
228 catalog.emitSubstituteResources(&buffer, false);
229
230 char expectedResult[] =
231 "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
232 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
233 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
234 buffer.getOffset()));
235}
236
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000237static void TestPDFPrimitives(skiatest::Reporter* reporter) {
238 SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
239 int42->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000240 SimpleCheckObjectOutput(reporter, int42.get(), "42");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000241
242 SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
243 realHalf->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000244 SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000245
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000246#if defined(SK_SCALAR_IS_FLOAT)
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000247 SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
248 bigScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000249#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000250 SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000251#else
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000252 SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000253
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000254 SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
255 biggerScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000256 SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000257
258 SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
259 smallestScalar->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000260 SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000261#endif
vandebo@chromium.org6cc26da2011-05-18 17:08:05 +0000262#endif
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000263
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000264 SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
265 stringSimple->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000266 SimpleCheckObjectOutput(reporter, stringSimple.get(),
267 "(test \\) string \\( foo)");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000268 SkRefPtr<SkPDFString> stringComplex =
269 new SkPDFString("\ttest ) string ( foo");
270 stringComplex->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000271 SimpleCheckObjectOutput(reporter, stringComplex.get(),
272 "<0974657374202920737472696E67202820666F6F>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000273
274 SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
275 name->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000276 const char expectedResult[] = "/Test#20name#09with#23tab";
277 CheckObjectOutput(reporter, name.get(), expectedResult,
278 strlen(expectedResult), false, false);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000279
280 SkRefPtr<SkPDFArray> array = new SkPDFArray;
281 array->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000282 SimpleCheckObjectOutput(reporter, array.get(), "[]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000283 array->append(int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000284 SimpleCheckObjectOutput(reporter, array.get(), "[42]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000285 array->append(realHalf.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000286 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000287 SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
288 int0->unref(); // SkRefPtr and new both took a reference.
289 array->append(int0.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000290 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000291 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
292 int1->unref(); // SkRefPtr and new both took a reference.
293 array->setAt(0, int1.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000294 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000295
296 SkRefPtr<SkPDFDict> dict = new SkPDFDict;
297 dict->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000298 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000299 SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
300 n1->unref(); // SkRefPtr and new both took a reference.
301 dict->insert(n1.get(), int42.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000302 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000303 SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
304 n2->unref(); // SkRefPtr and new both took a reference.
305 SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
306 n3->unref(); // SkRefPtr and new both took a reference.
307 dict->insert(n2.get(), realHalf.get());
308 dict->insert(n3.get(), array.get());
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000309 SimpleCheckObjectOutput(reporter, dict.get(),
310 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000311
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000312 TestPDFStream(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000313
314 TestCatalog(reporter);
315
316 TestObjectRef(reporter);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000317
318 TestSubstitute(reporter);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000319}
320
321#include "TestClassDef.h"
322DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)