blob: fca8496f2679f360024a49c0c612b1c6f81e9adc [file] [log] [blame]
halcanary8ee06f22015-08-11 10:30:12 -07001/*
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 */
commit-bot@chromium.org8c908272013-10-22 14:49:03 +00007#include "Test.h"
commit-bot@chromium.org8c908272013-10-22 14:49:03 +00008
halcanary712fdf72015-12-10 08:59:43 -08009#include "Resources.h"
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000010#include "SkCanvas.h"
11#include "SkDocument.h"
12#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050013#include "SkOSPath.h"
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000014#include "SkStream.h"
halcanary712fdf72015-12-10 08:59:43 -080015#include "SkPixelSerializer.h"
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000016
17static void test_empty(skiatest::Reporter* reporter) {
18 SkDynamicMemoryWStream stream;
19
halcanary4b656662016-04-27 07:45:18 -070020 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000021
22 doc->close();
23
24 REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
25}
26
27static void test_abort(skiatest::Reporter* reporter) {
28 SkDynamicMemoryWStream stream;
halcanary4b656662016-04-27 07:45:18 -070029 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000030
31 SkCanvas* canvas = doc->beginPage(100, 100);
32 canvas->drawColor(SK_ColorRED);
33 doc->endPage();
34
35 doc->abort();
36
halcanary50e82e62016-03-21 13:45:05 -070037 // Test that only the header is written, not the full document.
38 REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000039}
40
41static void test_abortWithFile(skiatest::Reporter* reporter) {
halcanary87f3ba42015-01-20 09:30:20 -080042 SkString tmpDir = skiatest::GetTmpDir();
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000043
44 if (tmpDir.isEmpty()) {
45 return; // TODO(edisonn): unfortunatelly this pattern is used in other
46 // tests, but if GetTmpDir() starts returning and empty dir
47 // allways, then all these tests will be disabled.
48 }
49
tfarinaa8e2e152014-07-28 19:26:58 -070050 SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000051
52 // Make sure doc's destructor is called to flush.
53 {
halcanary4b656662016-04-27 07:45:18 -070054 sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000055
56 SkCanvas* canvas = doc->beginPage(100, 100);
57 canvas->drawColor(SK_ColorRED);
58 doc->endPage();
59
60 doc->abort();
61 }
62
63 FILE* file = fopen(path.c_str(), "r");
Hal Canary399bbd92016-11-10 13:03:21 -050064 // Test that only the header is written, not the full document.
65 char buffer[256];
66 REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000067 fclose(file);
68}
69
70static void test_file(skiatest::Reporter* reporter) {
halcanary87f3ba42015-01-20 09:30:20 -080071 SkString tmpDir = skiatest::GetTmpDir();
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000072 if (tmpDir.isEmpty()) {
73 return; // TODO(edisonn): unfortunatelly this pattern is used in other
74 // tests, but if GetTmpDir() starts returning and empty dir
75 // allways, then all these tests will be disabled.
76 }
77
tfarinaa8e2e152014-07-28 19:26:58 -070078 SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000079
halcanary4b656662016-04-27 07:45:18 -070080 sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000081
82 SkCanvas* canvas = doc->beginPage(100, 100);
83
84 canvas->drawColor(SK_ColorRED);
85 doc->endPage();
86 doc->close();
87
88 FILE* file = fopen(path.c_str(), "r");
halcanary96fcdcc2015-08-27 07:41:13 -070089 REPORTER_ASSERT(reporter, file != nullptr);
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000090 char header[100];
edisonn@google.com5237b7f2013-10-22 18:33:21 +000091 REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000092 REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
93 fclose(file);
94}
95
96static void test_close(skiatest::Reporter* reporter) {
97 SkDynamicMemoryWStream stream;
halcanary4b656662016-04-27 07:45:18 -070098 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
commit-bot@chromium.org8c908272013-10-22 14:49:03 +000099
100 SkCanvas* canvas = doc->beginPage(100, 100);
101 canvas->drawColor(SK_ColorRED);
102 doc->endPage();
103
104 doc->close();
105
106 REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
107}
108
halcanary57f744e2016-09-09 11:41:59 -0700109DEF_TEST(SkPDF_document_tests, reporter) {
halcanary2ccdb632015-08-11 13:35:12 -0700110 REQUIRE_PDF_DOCUMENT(document_tests, reporter);
commit-bot@chromium.org8c908272013-10-22 14:49:03 +0000111 test_empty(reporter);
112 test_abort(reporter);
113 test_abortWithFile(reporter);
114 test_file(reporter);
115 test_close(reporter);
116}
halcanary712fdf72015-12-10 08:59:43 -0800117
118namespace {
119class JPEGSerializer final : public SkPixelSerializer {
120 bool onUseEncodedData(const void*, size_t) override { return true; }
121 SkData* onEncode(const SkPixmap& pixmap) override {
122 SkBitmap bm;
123 return bm.installPixels(pixmap.info(),
124 pixmap.writable_addr(),
125 pixmap.rowBytes(),
126 pixmap.ctable(),
127 nullptr, nullptr)
128 ? SkImageEncoder::EncodeData(bm, SkImageEncoder::kJPEG_Type, 85)
129 : nullptr;
130 }
131};
132} // namespace
133
134size_t count_bytes(const SkBitmap& bm, bool useDCT) {
135 SkDynamicMemoryWStream stream;
halcanary4b656662016-04-27 07:45:18 -0700136 sk_sp<SkDocument> doc;
halcanary712fdf72015-12-10 08:59:43 -0800137 if (useDCT) {
halcanary4b656662016-04-27 07:45:18 -0700138 doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI,
139 SkDocument::PDFMetadata(),
140 sk_make_sp<JPEGSerializer>(), false);
halcanary712fdf72015-12-10 08:59:43 -0800141 } else {
halcanary4b656662016-04-27 07:45:18 -0700142 doc = SkDocument::MakePDF(&stream);
halcanary712fdf72015-12-10 08:59:43 -0800143 }
144 SkCanvas* canvas = doc->beginPage(64, 64);
145 canvas->drawBitmap(bm, 0, 0);
146 doc->endPage();
147 doc->close();
148 return stream.bytesWritten();
149}
150
halcanary57f744e2016-09-09 11:41:59 -0700151DEF_TEST(SkPDF_document_dct_encoder, r) {
152 REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r);
halcanary712fdf72015-12-10 08:59:43 -0800153 SkBitmap bm;
154 if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
155 // Lossy encoding works better on photographs.
156 REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
157 }
158}
halcanary53b1c092016-01-06 09:02:25 -0800159
halcanary57f744e2016-09-09 11:41:59 -0700160DEF_TEST(SkPDF_document_skbug_4734, r) {
161 REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
halcanary53b1c092016-01-06 09:02:25 -0800162 SkDynamicMemoryWStream stream;
halcanary4b656662016-04-27 07:45:18 -0700163 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
halcanary53b1c092016-01-06 09:02:25 -0800164 SkCanvas* canvas = doc->beginPage(64, 64);
165 canvas->scale(10000.0f, 10000.0f);
166 canvas->translate(20.0f, 10.0f);
167 canvas->rotate(30.0f);
168 const char text[] = "HELLO";
169 canvas->drawText(text, strlen(text), 0, 0, SkPaint());
170}
halcanary57f744e2016-09-09 11:41:59 -0700171
172static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
173 size_t len = strlen(expectation);
174 size_t N = 1 + size - len;
175 for (size_t i = 0; i < N; ++i) {
176 if (0 == memcmp(result + i, expectation, len)) {
177 return true;
178 }
179 }
180 return false;
181}
182
183// verify that the PDFA flag does something.
184DEF_TEST(SkPDF_pdfa_document, r) {
185 REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
186
187 SkDocument::PDFMetadata pdfMetadata;
188 pdfMetadata.fTitle = "test document";
189 pdfMetadata.fCreation.fEnabled = true;
190 pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};
191
192 SkDynamicMemoryWStream buffer;
193 auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
194 pdfMetadata, nullptr, /* pdfa = */ true);
195 doc->beginPage(64, 64)->drawColor(SK_ColorRED);
196 doc->close();
reed42943c82016-09-12 12:01:44 -0700197 sk_sp<SkData> data(buffer.detachAsData());
198
halcanary57f744e2016-09-09 11:41:59 -0700199 static const char* expectations[] = {
200 "sRGB IEC61966-2.1",
201 "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
202 "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
203 "/Subtype /XML",
204 "/CreationDate (D:19991231235959+00'00')>>",
205 };
206 for (const char* expectation : expectations) {
207 if (!contains(data->bytes(), data->size(), expectation)) {
208 ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
209 }
210 }
211 pdfMetadata.fProducer = "phoney library";
212 doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
213 pdfMetadata, nullptr, /* pdfa = */ true);
214 doc->beginPage(64, 64)->drawColor(SK_ColorRED);
215 doc->close();
reed42943c82016-09-12 12:01:44 -0700216 data = buffer.detachAsData();
halcanary57f744e2016-09-09 11:41:59 -0700217
218 static const char* moreExpectations[] = {
219 "/Producer (phoney library)",
220 "/ProductionLibrary (Skia/PDF m",
221 "<!-- <skia:ProductionLibrary>Skia/PDF m",
222 "<pdf:Producer>phoney library</pdf:Producer>",
223 };
224 for (const char* expectation : moreExpectations) {
225 if (!contains(data->bytes(), data->size(), expectation)) {
226 ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
227 }
228 }
229}