1
2 /*
3 * Copyright 2010 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10 #include <string>
11
12 #include "Test.h"
13 #include "SkData.h"
14 #include "SkFlate.h"
15 #include "SkPDFCatalog.h"
16 #include "SkPDFStream.h"
17 #include "SkPDFTypes.h"
18 #include "SkScalar.h"
19 #include "SkStream.h"
20 #include "SkTypes.h"
21
22 class SkPDFTestDict : public SkPDFDict {
23 public:
getResources(SkTDArray<SkPDFObject * > * resourceList)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
addResource(SkPDFObject * object)32 void addResource(SkPDFObject* object) {
33 fResources.append(1, &object);
34 }
35
36 private:
37 SkTDArray<SkPDFObject*> fResources;
38 };
39
stream_equals(const SkDynamicMemoryWStream & stream,size_t offset,const void * buffer,size_t len)40 static 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
CheckObjectOutput(skiatest::Reporter * reporter,SkPDFObject * obj,const char * expectedData,size_t expectedSize,bool indirect,bool compression)49 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
50 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);
57 size_t directSize = obj->getOutputSize(&catalog, false);
58 REPORTER_ASSERT(reporter, directSize == expectedSize);
59
60 SkDynamicMemoryWStream buffer;
61 obj->emit(&buffer, &catalog, false);
62 REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
63 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
64 directSize));
65
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
73 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();
80 obj->emit(&buffer, &catalog, true);
81 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
82 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
83 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
84 directSize));
85 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
86 footer, footerLen));
87 }
88 }
89
SimpleCheckObjectOutput(skiatest::Reporter * reporter,SkPDFObject * obj,const std::string & expectedResult)90 static 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
TestPDFStream(skiatest::Reporter * reporter)97 static 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
TestCatalog(skiatest::Reporter * reporter)149 static void TestCatalog(skiatest::Reporter* reporter) {
150 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
151 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";
173 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
174 strlen(expectedResult)));
175 }
176
TestObjectRef(skiatest::Reporter * reporter)177 static 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
185 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
186 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));
195 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
196 buffer.getOffset()));
197 }
198
TestSubstitute(skiatest::Reporter * reporter)199 static 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
215 SkPDFCatalog catalog((SkPDFDocument::Flags)0);
216 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
223 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
228 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
TestPDFPrimitives(skiatest::Reporter * reporter)235 static void TestPDFPrimitives(skiatest::Reporter* reporter) {
236 SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
237 int42->unref(); // SkRefPtr and new both took a reference.
238 SimpleCheckObjectOutput(reporter, int42.get(), "42");
239
240 SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
241 realHalf->unref(); // SkRefPtr and new both took a reference.
242 SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
243
244 #if defined(SK_SCALAR_IS_FLOAT)
245 SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
246 bigScalar->unref(); // SkRefPtr and new both took a reference.
247 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
248 SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
249 #else
250 SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
251
252 SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
253 biggerScalar->unref(); // SkRefPtr and new both took a reference.
254 SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
255
256 SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
257 smallestScalar->unref(); // SkRefPtr and new both took a reference.
258 SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
259 #endif
260 #endif
261
262 SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
263 stringSimple->unref(); // SkRefPtr and new both took a reference.
264 SimpleCheckObjectOutput(reporter, stringSimple.get(),
265 "(test \\) string \\( foo)");
266 SkRefPtr<SkPDFString> stringComplex =
267 new SkPDFString("\ttest ) string ( foo");
268 stringComplex->unref(); // SkRefPtr and new both took a reference.
269 SimpleCheckObjectOutput(reporter, stringComplex.get(),
270 "<0974657374202920737472696E67202820666F6F>");
271
272 SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
273 name->unref(); // SkRefPtr and new both took a reference.
274 const char expectedResult[] = "/Test#20name#09with#23tab";
275 CheckObjectOutput(reporter, name.get(), expectedResult,
276 strlen(expectedResult), false, false);
277
278 SkRefPtr<SkPDFArray> array = new SkPDFArray;
279 array->unref(); // SkRefPtr and new both took a reference.
280 SimpleCheckObjectOutput(reporter, array.get(), "[]");
281 array->append(int42.get());
282 SimpleCheckObjectOutput(reporter, array.get(), "[42]");
283 array->append(realHalf.get());
284 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
285 SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
286 int0->unref(); // SkRefPtr and new both took a reference.
287 array->append(int0.get());
288 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
289 SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
290 int1->unref(); // SkRefPtr and new both took a reference.
291 array->setAt(0, int1.get());
292 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
293
294 SkRefPtr<SkPDFDict> dict = new SkPDFDict;
295 dict->unref(); // SkRefPtr and new both took a reference.
296 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
297 SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
298 n1->unref(); // SkRefPtr and new both took a reference.
299 dict->insert(n1.get(), int42.get());
300 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
301 SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
302 n2->unref(); // SkRefPtr and new both took a reference.
303 SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
304 n3->unref(); // SkRefPtr and new both took a reference.
305 dict->insert(n2.get(), realHalf.get());
306 dict->insert(n3.get(), array.get());
307 SimpleCheckObjectOutput(reporter, dict.get(),
308 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
309
310 TestPDFStream(reporter);
311
312 TestCatalog(reporter);
313
314 TestObjectRef(reporter);
315
316 TestSubstitute(reporter);
317 }
318
319 #include "TestClassDef.h"
320 DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)
321