• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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