Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2013 Google Inc.
      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 #include "Test.h"
      8 
      9 #include "Resources.h"
     10 #include "SkCanvas.h"
     11 #include "SkDocument.h"
     12 #include "SkOSFile.h"
     13 #include "SkOSPath.h"
     14 #include "SkStream.h"
     15 #include "SkPixelSerializer.h"
     16 
     17 #include "sk_tool_utils.h"
     18 
     19 static void test_empty(skiatest::Reporter* reporter) {
     20     SkDynamicMemoryWStream stream;
     21 
     22     sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
     23 
     24     doc->close();
     25 
     26     REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
     27 }
     28 
     29 static void test_abort(skiatest::Reporter* reporter) {
     30     SkDynamicMemoryWStream stream;
     31     sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
     32 
     33     SkCanvas* canvas = doc->beginPage(100, 100);
     34     canvas->drawColor(SK_ColorRED);
     35     doc->endPage();
     36 
     37     doc->abort();
     38 
     39     // Test that only the header is written, not the full document.
     40     REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
     41 }
     42 
     43 static void test_abortWithFile(skiatest::Reporter* reporter) {
     44     SkString tmpDir = skiatest::GetTmpDir();
     45 
     46     if (tmpDir.isEmpty()) {
     47         return;  // TODO(edisonn): unfortunatelly this pattern is used in other
     48                  // tests, but if GetTmpDir() starts returning and empty dir
     49                  // allways, then all these tests will be disabled.
     50     }
     51 
     52     SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
     53 
     54     // Make sure doc's destructor is called to flush.
     55     {
     56         sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
     57 
     58         SkCanvas* canvas = doc->beginPage(100, 100);
     59         canvas->drawColor(SK_ColorRED);
     60         doc->endPage();
     61 
     62         doc->abort();
     63     }
     64 
     65     FILE* file = fopen(path.c_str(), "r");
     66     // Test that only the header is written, not the full document.
     67     char buffer[256];
     68     REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
     69     fclose(file);
     70 }
     71 
     72 static void test_file(skiatest::Reporter* reporter) {
     73     SkString tmpDir = skiatest::GetTmpDir();
     74     if (tmpDir.isEmpty()) {
     75         return;  // TODO(edisonn): unfortunatelly this pattern is used in other
     76                  // tests, but if GetTmpDir() starts returning and empty dir
     77                  // allways, then all these tests will be disabled.
     78     }
     79 
     80     SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
     81 
     82     sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
     83 
     84     SkCanvas* canvas = doc->beginPage(100, 100);
     85 
     86     canvas->drawColor(SK_ColorRED);
     87     doc->endPage();
     88     doc->close();
     89 
     90     FILE* file = fopen(path.c_str(), "r");
     91     REPORTER_ASSERT(reporter, file != nullptr);
     92     char header[100];
     93     REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
     94     REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
     95     fclose(file);
     96 }
     97 
     98 static void test_close(skiatest::Reporter* reporter) {
     99     SkDynamicMemoryWStream stream;
    100     sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
    101 
    102     SkCanvas* canvas = doc->beginPage(100, 100);
    103     canvas->drawColor(SK_ColorRED);
    104     doc->endPage();
    105 
    106     doc->close();
    107 
    108     REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
    109 }
    110 
    111 DEF_TEST(SkPDF_document_tests, reporter) {
    112     REQUIRE_PDF_DOCUMENT(document_tests, reporter);
    113     test_empty(reporter);
    114     test_abort(reporter);
    115     test_abortWithFile(reporter);
    116     test_file(reporter);
    117     test_close(reporter);
    118 }
    119 
    120 namespace {
    121 class JPEGSerializer final : public SkPixelSerializer {
    122     bool onUseEncodedData(const void*, size_t) override { return true; }
    123     SkData* onEncode(const SkPixmap& pixmap) override {
    124         return sk_tool_utils::EncodeImageToData(pixmap, SkEncodedImageFormat::kJPEG, 85).release();
    125     }
    126 };
    127 }  // namespace
    128 
    129 size_t count_bytes(const SkBitmap& bm, bool useDCT) {
    130     SkDynamicMemoryWStream stream;
    131     sk_sp<SkDocument> doc;
    132     if (useDCT) {
    133         doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI,
    134                                   SkDocument::PDFMetadata(),
    135                                   sk_make_sp<JPEGSerializer>(), false);
    136     } else {
    137         doc = SkDocument::MakePDF(&stream);
    138     }
    139     SkCanvas* canvas = doc->beginPage(64, 64);
    140     canvas->drawBitmap(bm, 0, 0);
    141     doc->endPage();
    142     doc->close();
    143     return stream.bytesWritten();
    144 }
    145 
    146 DEF_TEST(SkPDF_document_dct_encoder, r) {
    147     REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r);
    148     SkBitmap bm;
    149     if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
    150         // Lossy encoding works better on photographs.
    151         REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
    152     }
    153 }
    154 
    155 DEF_TEST(SkPDF_document_skbug_4734, r) {
    156     REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
    157     SkDynamicMemoryWStream stream;
    158     sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
    159     SkCanvas* canvas = doc->beginPage(64, 64);
    160     canvas->scale(10000.0f, 10000.0f);
    161     canvas->translate(20.0f, 10.0f);
    162     canvas->rotate(30.0f);
    163     const char text[] = "HELLO";
    164     canvas->drawText(text, strlen(text), 0, 0, SkPaint());
    165 }
    166 
    167 static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
    168     size_t len = strlen(expectation);
    169     size_t N = 1 + size - len;
    170     for (size_t i = 0; i < N; ++i) {
    171         if (0 == memcmp(result + i, expectation, len)) {
    172             return true;
    173         }
    174     }
    175     return false;
    176 }
    177 
    178 // verify that the PDFA flag does something.
    179 DEF_TEST(SkPDF_pdfa_document, r) {
    180     REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
    181 
    182     SkDocument::PDFMetadata pdfMetadata;
    183     pdfMetadata.fTitle = "test document";
    184     pdfMetadata.fCreation.fEnabled = true;
    185     pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};
    186 
    187     SkDynamicMemoryWStream buffer;
    188     auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
    189                                    pdfMetadata, nullptr, /* pdfa = */ true);
    190     doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    191     doc->close();
    192     sk_sp<SkData> data(buffer.detachAsData());
    193 
    194     static const char* expectations[] = {
    195         "sRGB IEC61966-2.1",
    196         "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
    197         "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
    198         "/Subtype /XML",
    199         "/CreationDate (D:19991231235959+00'00')>>",
    200     };
    201     for (const char* expectation : expectations) {
    202         if (!contains(data->bytes(), data->size(), expectation)) {
    203             ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
    204         }
    205     }
    206     pdfMetadata.fProducer = "phoney library";
    207     doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
    208                               pdfMetadata, nullptr, /* pdfa = */ true);
    209     doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    210     doc->close();
    211     data = buffer.detachAsData();
    212 
    213     static const char* moreExpectations[] = {
    214         "/Producer (phoney library)",
    215         "/ProductionLibrary (Skia/PDF ",
    216         "<!-- <skia:ProductionLibrary>Skia/PDF ",
    217         "<pdf:Producer>phoney library</pdf:Producer>",
    218     };
    219     for (const char* expectation : moreExpectations) {
    220         if (!contains(data->bytes(), data->size(), expectation)) {
    221             ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
    222         }
    223     }
    224 }
    225