Home | History | Annotate | Download | only in tests
      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