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 "SkStream.h"
14#include "SkPixelSerializer.h"
15
16static void test_empty(skiatest::Reporter* reporter) {
17    SkDynamicMemoryWStream stream;
18
19    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
20
21    doc->close();
22
23    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
24}
25
26static void test_abort(skiatest::Reporter* reporter) {
27    SkDynamicMemoryWStream stream;
28    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
29
30    SkCanvas* canvas = doc->beginPage(100, 100);
31    canvas->drawColor(SK_ColorRED);
32    doc->endPage();
33
34    doc->abort();
35
36    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
37}
38
39static void test_abortWithFile(skiatest::Reporter* reporter) {
40    SkString tmpDir = skiatest::GetTmpDir();
41
42    if (tmpDir.isEmpty()) {
43        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
44                 // tests, but if GetTmpDir() starts returning and empty dir
45                 // allways, then all these tests will be disabled.
46    }
47
48    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
49
50    // Make sure doc's destructor is called to flush.
51    {
52        SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str()));
53
54        SkCanvas* canvas = doc->beginPage(100, 100);
55        canvas->drawColor(SK_ColorRED);
56        doc->endPage();
57
58        doc->abort();
59    }
60
61    FILE* file = fopen(path.c_str(), "r");
62    // The created file should be empty.
63    char buffer[100];
64    REPORTER_ASSERT(reporter, fread(buffer, 1, 1, file) == 0);
65    fclose(file);
66}
67
68static void test_file(skiatest::Reporter* reporter) {
69    SkString tmpDir = skiatest::GetTmpDir();
70    if (tmpDir.isEmpty()) {
71        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
72                 // tests, but if GetTmpDir() starts returning and empty dir
73                 // allways, then all these tests will be disabled.
74    }
75
76    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
77
78    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str()));
79
80    SkCanvas* canvas = doc->beginPage(100, 100);
81
82    canvas->drawColor(SK_ColorRED);
83    doc->endPage();
84    doc->close();
85
86    FILE* file = fopen(path.c_str(), "r");
87    REPORTER_ASSERT(reporter, file != nullptr);
88    char header[100];
89    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
90    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
91    fclose(file);
92}
93
94static void test_close(skiatest::Reporter* reporter) {
95    SkDynamicMemoryWStream stream;
96    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
97
98    SkCanvas* canvas = doc->beginPage(100, 100);
99    canvas->drawColor(SK_ColorRED);
100    doc->endPage();
101
102    doc->close();
103
104    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
105}
106
107DEF_TEST(document_tests, reporter) {
108    REQUIRE_PDF_DOCUMENT(document_tests, reporter);
109    test_empty(reporter);
110    test_abort(reporter);
111    test_abortWithFile(reporter);
112    test_file(reporter);
113    test_close(reporter);
114}
115
116namespace {
117class JPEGSerializer final : public SkPixelSerializer {
118    bool onUseEncodedData(const void*, size_t) override { return true; }
119    SkData* onEncode(const SkPixmap& pixmap) override {
120        SkBitmap bm;
121        return bm.installPixels(pixmap.info(),
122                                pixmap.writable_addr(),
123                                pixmap.rowBytes(),
124                                pixmap.ctable(),
125                                nullptr, nullptr)
126            ? SkImageEncoder::EncodeData(bm, SkImageEncoder::kJPEG_Type, 85)
127            : nullptr;
128    }
129};
130}  // namespace
131
132size_t count_bytes(const SkBitmap& bm, bool useDCT) {
133    SkDynamicMemoryWStream stream;
134    SkAutoTUnref<SkDocument> doc;
135    if (useDCT) {
136        SkAutoTUnref<SkPixelSerializer> serializer(new JPEGSerializer);
137        doc.reset(SkDocument::CreatePDF(
138                          &stream, SK_ScalarDefaultRasterDPI, serializer));
139    } else {
140        doc.reset(SkDocument::CreatePDF(&stream));
141    }
142    SkCanvas* canvas = doc->beginPage(64, 64);
143    canvas->drawBitmap(bm, 0, 0);
144    doc->endPage();
145    doc->close();
146    return stream.bytesWritten();
147}
148
149DEF_TEST(document_dct_encoder, r) {
150    REQUIRE_PDF_DOCUMENT(document_dct_encoder, r);
151    SkBitmap bm;
152    if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
153        // Lossy encoding works better on photographs.
154        REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
155    }
156}
157
158DEF_TEST(document_skbug_4734, r) {
159    REQUIRE_PDF_DOCUMENT(document_skbug_4734, r);
160    SkDynamicMemoryWStream stream;
161    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
162    SkCanvas* canvas = doc->beginPage(64, 64);
163    canvas->scale(10000.0f, 10000.0f);
164    canvas->translate(20.0f, 10.0f);
165    canvas->rotate(30.0f);
166    const char text[] = "HELLO";
167    canvas->drawText(text, strlen(text), 0, 0, SkPaint());
168}
169