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: 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 36 private: 37 SkTDArray<SkPDFObject*> fResources; 38 }; 39 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 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 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 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 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 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 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 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