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