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 
test_empty(skiatest::Reporter * reporter)16 static 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 
test_abort(skiatest::Reporter * reporter)26 static 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 
test_abortWithFile(skiatest::Reporter * reporter)39 static 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 
test_file(skiatest::Reporter * reporter)68 static 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 
test_close(skiatest::Reporter * reporter)94 static 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 
DEF_TEST(document_tests,reporter)107 DEF_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 
116 namespace {
117 class JPEGSerializer final : public SkPixelSerializer {
onUseEncodedData(const void *,size_t)118     bool onUseEncodedData(const void*, size_t) override { return true; }
onEncode(const SkPixmap & pixmap)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 
count_bytes(const SkBitmap & bm,bool useDCT)132 size_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 
DEF_TEST(document_dct_encoder,r)149 DEF_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 
DEF_TEST(document_skbug_4734,r)158 DEF_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