1 /* 2 * Copyright 2010 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "Resources.h" 9 #include "SkBitmap.h" 10 #include "SkCanvas.h" 11 #include "SkData.h" 12 #include "SkDocument.h" 13 #include "SkDeflate.h" 14 #include "SkImageEncoder.h" 15 #include "SkMatrix.h" 16 #include "SkPDFCanon.h" 17 #include "SkPDFDevice.h" 18 #include "SkPDFFont.h" 19 #include "SkPDFStream.h" 20 #include "SkPDFTypes.h" 21 #include "SkPDFUtils.h" 22 #include "SkReadBuffer.h" 23 #include "SkScalar.h" 24 #include "SkStream.h" 25 #include "SkTypes.h" 26 #include "Test.h" 27 #include "sk_tool_utils.h" 28 29 #define DUMMY_TEXT "DCT compessed stream." 30 31 namespace { 32 struct Catalog { 33 SkPDFSubstituteMap substitutes; 34 SkPDFObjNumMap numbers; 35 }; 36 } // namespace 37 38 template <typename T> 39 static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) { 40 Catalog catalog; 41 SkDynamicMemoryWStream buffer; 42 if (!catPtr) { 43 catPtr = &catalog; 44 } 45 obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes); 46 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); 47 SkString tmp(asset->getLength()); 48 asset->read(tmp.writable_str(), asset->getLength()); 49 return tmp; 50 } 51 52 static bool eq(const SkString& str, const char* strPtr, size_t len) { 53 return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len); 54 } 55 56 #define ASSERT_EQL(REPORTER, SKSTRING, STRING, LEN) \ 57 do { \ 58 const char* strptr = STRING; \ 59 const SkString& sks = SKSTRING; \ 60 if (!eq(sks, strptr, LEN)) { \ 61 REPORT_FAILURE( \ 62 REPORTER, \ 63 "", \ 64 SkStringPrintf("'%s' != '%s'", strptr, sks.c_str())); \ 65 } \ 66 } while (false) 67 68 #define ASSERT_EQ(REPORTER, SKSTRING, STRING) \ 69 do { \ 70 const char* str = STRING; \ 71 ASSERT_EQL(REPORTER, SKSTRING, str, strlen(str)); \ 72 } while (false) 73 74 #define ASSERT_EMIT_EQ(REPORTER, OBJECT, STRING) \ 75 do { \ 76 SkString result = emit_to_string(OBJECT); \ 77 ASSERT_EQ(REPORTER, result, STRING); \ 78 } while (false) 79 80 81 82 static void TestPDFStream(skiatest::Reporter* reporter) { 83 char streamBytes[] = "Test\nFoo\tBar"; 84 SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream( 85 streamBytes, strlen(streamBytes), true)); 86 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get())); 87 ASSERT_EMIT_EQ(reporter, 88 *stream, 89 "<</Length 12>> stream\nTest\nFoo\tBar\nendstream"); 90 stream->insertInt("Attribute", 42); 91 ASSERT_EMIT_EQ(reporter, 92 *stream, 93 "<</Length 12\n/Attribute 42>> stream\n" 94 "Test\nFoo\tBar\nendstream"); 95 96 { 97 char streamBytes2[] = "This is a longer string, so that compression " 98 "can do something with it. With shorter strings, " 99 "the short circuit logic cuts in and we end up " 100 "with an uncompressed string."; 101 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, 102 strlen(streamBytes2))); 103 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get())); 104 105 SkDynamicMemoryWStream compressedByteStream; 106 SkDeflateWStream deflateWStream(&compressedByteStream); 107 deflateWStream.write(streamBytes2, strlen(streamBytes2)); 108 deflateWStream.finalize(); 109 110 SkDynamicMemoryWStream expected; 111 expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n"); 112 compressedByteStream.writeToStream(&expected); 113 compressedByteStream.reset(); 114 expected.writeText("\nendstream"); 115 SkAutoDataUnref expectedResultData2(expected.copyToData()); 116 SkString result = emit_to_string(*stream); 117 ASSERT_EQL(reporter, 118 result, 119 (const char*)expectedResultData2->data(), 120 expectedResultData2->size()); 121 } 122 } 123 124 static void TestObjectNumberMap(skiatest::Reporter* reporter) { 125 SkPDFObjNumMap objNumMap; 126 SkAutoTUnref<SkPDFArray> a1(new SkPDFArray); 127 SkAutoTUnref<SkPDFArray> a2(new SkPDFArray); 128 SkAutoTUnref<SkPDFArray> a3(new SkPDFArray); 129 130 objNumMap.addObject(a1.get()); 131 objNumMap.addObject(a2.get()); 132 objNumMap.addObject(a3.get()); 133 134 // The objects should be numbered in the order they are added, 135 // starting with 1. 136 REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1); 137 REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a2.get()) == 2); 138 REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a3.get()) == 3); 139 // Assert that repeated calls to get the object number return 140 // consistent result. 141 REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1); 142 } 143 144 static void TestObjectRef(skiatest::Reporter* reporter) { 145 SkAutoTUnref<SkPDFArray> a1(new SkPDFArray); 146 SkAutoTUnref<SkPDFArray> a2(new SkPDFArray); 147 a2->appendObjRef(SkRef(a1.get())); 148 149 Catalog catalog; 150 catalog.numbers.addObject(a1.get()); 151 REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1); 152 153 SkString result = emit_to_string(*a2, &catalog); 154 // If appendObjRef misbehaves, then the result would 155 // be [[]], not [1 0 R]. 156 ASSERT_EQ(reporter, result, "[1 0 R]"); 157 } 158 159 static void TestSubstitute(skiatest::Reporter* reporter) { 160 SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict()); 161 SkAutoTUnref<SkPDFDict> stub(new SkPDFDict()); 162 163 proxy->insertInt("Value", 33); 164 stub->insertInt("Value", 44); 165 166 SkPDFSubstituteMap substituteMap; 167 substituteMap.setSubstitute(proxy.get(), stub.get()); 168 SkPDFObjNumMap catalog; 169 catalog.addObject(proxy.get()); 170 171 REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy)); 172 REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub)); 173 } 174 175 // This test used to assert without the fix submitted for 176 // http://code.google.com/p/skia/issues/detail?id=1083. 177 // SKP files might have invalid glyph ids. This test ensures they are ignored, 178 // and there is no assert on input data in Debug mode. 179 static void test_issue1083() { 180 SkDynamicMemoryWStream outStream; 181 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&outStream)); 182 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); 183 SkPaint paint; 184 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 185 186 uint16_t glyphID = 65000; 187 canvas->drawText(&glyphID, 2, 0, 0, paint); 188 189 doc->close(); 190 } 191 192 static void TestPDFUnion(skiatest::Reporter* reporter) { 193 SkPDFUnion boolTrue = SkPDFUnion::Bool(true); 194 ASSERT_EMIT_EQ(reporter, boolTrue, "true"); 195 196 SkPDFUnion boolFalse = SkPDFUnion::Bool(false); 197 ASSERT_EMIT_EQ(reporter, boolFalse, "false"); 198 199 SkPDFUnion int42 = SkPDFUnion::Int(42); 200 ASSERT_EMIT_EQ(reporter, int42, "42"); 201 202 SkPDFUnion realHalf = SkPDFUnion::Scalar(SK_ScalarHalf); 203 ASSERT_EMIT_EQ(reporter, realHalf, ".5"); 204 205 SkPDFUnion bigScalar = SkPDFUnion::Scalar(110999.75f); 206 ASSERT_EMIT_EQ(reporter, bigScalar, "110999.75"); 207 208 SkPDFUnion biggerScalar = SkPDFUnion::Scalar(50000000.1f); 209 ASSERT_EMIT_EQ(reporter, biggerScalar, "50000000"); 210 211 SkPDFUnion smallestScalar = SkPDFUnion::Scalar(1.0f / 65536); 212 ASSERT_EMIT_EQ(reporter, smallestScalar, ".0000152587890"); 213 214 SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo"); 215 ASSERT_EMIT_EQ(reporter, stringSimple, "(test \\) string \\( foo)"); 216 217 SkString stringComplexInput("\ttest ) string ( foo"); 218 SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput); 219 ASSERT_EMIT_EQ(reporter, 220 stringComplex, 221 "<0974657374202920737472696E67202820666F6F>"); 222 223 SkString nameInput("Test name\twith#tab"); 224 SkPDFUnion name = SkPDFUnion::Name(nameInput); 225 ASSERT_EMIT_EQ(reporter, name, "/Test#20name#09with#23tab"); 226 227 SkString nameInput2("A#/%()<>[]{}B"); 228 SkPDFUnion name2 = SkPDFUnion::Name(nameInput2); 229 ASSERT_EMIT_EQ(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB"); 230 231 SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII"); 232 ASSERT_EMIT_EQ(reporter, name3, "/SimpleNameWithOnlyPrintableASCII"); 233 234 // Test that we correctly handle characters with the high-bit set. 235 SkString highBitString("\xDE\xAD" "be\xEF"); 236 SkPDFUnion highBitName = SkPDFUnion::Name(highBitString); 237 ASSERT_EMIT_EQ(reporter, highBitName, "/#DE#ADbe#EF"); 238 } 239 240 static void TestPDFArray(skiatest::Reporter* reporter) { 241 SkAutoTUnref<SkPDFArray> array(new SkPDFArray); 242 ASSERT_EMIT_EQ(reporter, *array, "[]"); 243 244 array->appendInt(42); 245 ASSERT_EMIT_EQ(reporter, *array, "[42]"); 246 247 array->appendScalar(SK_ScalarHalf); 248 ASSERT_EMIT_EQ(reporter, *array, "[42 .5]"); 249 250 array->appendInt(0); 251 ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0]"); 252 253 array->appendBool(true); 254 ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true]"); 255 256 array->appendName("ThisName"); 257 ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName]"); 258 259 array->appendName(SkString("AnotherName")); 260 ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]"); 261 262 array->appendString("This String"); 263 ASSERT_EMIT_EQ(reporter, *array, 264 "[42 .5 0 true /ThisName /AnotherName (This String)]"); 265 266 array->appendString(SkString("Another String")); 267 ASSERT_EMIT_EQ(reporter, *array, 268 "[42 .5 0 true /ThisName /AnotherName (This String) " 269 "(Another String)]"); 270 271 SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray); 272 innerArray->appendInt(-1); 273 array->appendObject(innerArray.detach()); 274 ASSERT_EMIT_EQ(reporter, *array, 275 "[42 .5 0 true /ThisName /AnotherName (This String) " 276 "(Another String) [-1]]"); 277 278 SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray); 279 Catalog catalog; 280 catalog.numbers.addObject(referencedArray.get()); 281 REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber( 282 referencedArray.get()) == 1); 283 array->appendObjRef(referencedArray.detach()); 284 285 SkString result = emit_to_string(*array, &catalog); 286 ASSERT_EQ(reporter, result, 287 "[42 .5 0 true /ThisName /AnotherName (This String) " 288 "(Another String) [-1] 1 0 R]"); 289 } 290 291 static void TestPDFDict(skiatest::Reporter* reporter) { 292 SkAutoTUnref<SkPDFDict> dict(new SkPDFDict); 293 ASSERT_EMIT_EQ(reporter, *dict, "<<>>"); 294 295 dict->insertInt("n1", SkToSizeT(42)); 296 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>"); 297 298 dict.reset(new SkPDFDict); 299 ASSERT_EMIT_EQ(reporter, *dict, "<<>>"); 300 301 dict->insertInt("n1", 42); 302 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>"); 303 304 dict->insertScalar("n2", SK_ScalarHalf); 305 306 SkString n3("n3"); 307 SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray); 308 innerArray->appendInt(-100); 309 dict->insertObject(n3, innerArray.detach()); 310 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>"); 311 312 dict.reset(new SkPDFDict); 313 ASSERT_EMIT_EQ(reporter, *dict, "<<>>"); 314 315 dict->insertInt("n1", 24); 316 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24>>"); 317 318 dict->insertInt("n2", SkToSizeT(99)); 319 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99>>"); 320 321 dict->insertScalar("n3", SK_ScalarHalf); 322 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>"); 323 324 dict->insertName("n4", "AName"); 325 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>"); 326 327 dict->insertName("n5", SkString("AnotherName")); 328 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" 329 "/n5 /AnotherName>>"); 330 331 dict->insertString("n6", "A String"); 332 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" 333 "/n5 /AnotherName\n/n6 (A String)>>"); 334 335 dict->insertString("n7", SkString("Another String")); 336 ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" 337 "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>"); 338 339 dict.reset(new SkPDFDict("DType")); 340 ASSERT_EMIT_EQ(reporter, *dict, "<</Type /DType>>"); 341 342 SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray); 343 Catalog catalog; 344 catalog.numbers.addObject(referencedArray.get()); 345 REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber( 346 referencedArray.get()) == 1); 347 dict->insertObjRef("n1", referencedArray.detach()); 348 SkString result = emit_to_string(*dict, &catalog); 349 ASSERT_EQ(reporter, result, "<</Type /DType\n/n1 1 0 R>>"); 350 } 351 352 DEF_TEST(PDFPrimitives, reporter) { 353 TestPDFUnion(reporter); 354 TestPDFArray(reporter); 355 TestPDFDict(reporter); 356 TestPDFStream(reporter); 357 TestObjectNumberMap(reporter); 358 TestObjectRef(reporter); 359 TestSubstitute(reporter); 360 test_issue1083(); 361 } 362 363 namespace { 364 365 class DummyImageFilter : public SkImageFilter { 366 public: 367 DummyImageFilter(bool visited = false) : SkImageFilter(0, nullptr), fVisited(visited) {} 368 ~DummyImageFilter() override {} 369 bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, 370 SkBitmap* result, SkIPoint* offset) const override { 371 fVisited = true; 372 offset->fX = offset->fY = 0; 373 *result = src; 374 return true; 375 } 376 SK_TO_STRING_OVERRIDE() 377 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter) 378 bool visited() const { return fVisited; } 379 380 private: 381 mutable bool fVisited; 382 }; 383 384 SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) { 385 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); 386 bool visited = buffer.readBool(); 387 return new DummyImageFilter(visited); 388 } 389 390 #ifndef SK_IGNORE_TO_STRING 391 void DummyImageFilter::toString(SkString* str) const { 392 str->appendf("DummyImageFilter: ("); 393 str->append(")"); 394 } 395 #endif 396 397 }; 398 399 // Check that PDF rendering of image filters successfully falls back to 400 // CPU rasterization. 401 DEF_TEST(PDFImageFilter, reporter) { 402 SkDynamicMemoryWStream stream; 403 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); 404 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); 405 406 SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter()); 407 408 // Filter just created; should be unvisited. 409 REPORTER_ASSERT(reporter, !filter->visited()); 410 SkPaint paint; 411 paint.setImageFilter(filter.get()); 412 canvas->drawRect(SkRect::MakeWH(100, 100), paint); 413 doc->close(); 414 415 // Filter was used in rendering; should be visited. 416 REPORTER_ASSERT(reporter, filter->visited()); 417 } 418 419 // Check that PDF rendering of image filters successfully falls back to 420 // CPU rasterization. 421 DEF_TEST(PDFFontCanEmbedTypeface, reporter) { 422 SkPDFCanon canon; 423 424 const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf"; 425 SkAutoTUnref<SkTypeface> noEmbedTypeface(GetResourceAsTypeface(resource)); 426 if (noEmbedTypeface) { 427 REPORTER_ASSERT(reporter, 428 !SkPDFFont::CanEmbedTypeface(noEmbedTypeface, &canon)); 429 } 430 SkAutoTUnref<SkTypeface> portableTypeface( 431 sk_tool_utils::create_portable_typeface(NULL, SkTypeface::kNormal)); 432 REPORTER_ASSERT(reporter, 433 SkPDFFont::CanEmbedTypeface(portableTypeface, &canon)); 434 } 435 436 437 // test to see that all finite scalars round trip via scanf(). 438 static void check_pdf_scalar_serialization( 439 skiatest::Reporter* reporter, float inputFloat) { 440 char floatString[SkPDFUtils::kMaximumFloatDecimalLength]; 441 size_t len = SkPDFUtils::FloatToDecimal(inputFloat, floatString); 442 if (len >= sizeof(floatString)) { 443 ERRORF(reporter, "string too long: %u", (unsigned)len); 444 return; 445 } 446 if (floatString[len] != '\0' || strlen(floatString) != len) { 447 ERRORF(reporter, "terminator misplaced."); 448 return; // The terminator is needed for sscanf(). 449 } 450 if (reporter->verbose()) { 451 SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString); 452 } 453 float roundTripFloat; 454 if (1 != sscanf(floatString, "%f", &roundTripFloat)) { 455 ERRORF(reporter, "unscannable result: %s", floatString); 456 return; 457 } 458 if (isfinite(inputFloat) && roundTripFloat != inputFloat) { 459 ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)", 460 roundTripFloat, inputFloat); 461 } 462 } 463 464 // Test SkPDFUtils::AppendScalar for accuracy. 465 DEF_TEST(PDFPrimitives_Scalar, reporter) { 466 SkRandom random(0x5EED); 467 int iterationCount = 512; 468 while (iterationCount-- > 0) { 469 union { uint32_t u; float f; }; 470 u = random.nextU(); 471 static_assert(sizeof(float) == sizeof(uint32_t), ""); 472 check_pdf_scalar_serialization(reporter, f); 473 } 474 float alwaysCheck[] = { 475 0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX, 476 -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f, 477 SK_FloatNaN, SK_FloatInfinity, SK_FloatNegativeInfinity, 478 -FLT_MIN / 8388608.0 479 }; 480 for (float inputFloat: alwaysCheck) { 481 check_pdf_scalar_serialization(reporter, inputFloat); 482 } 483 } 484