halcanary | 8ee06f2 | 2015-08-11 10:30:12 -0700 | [diff] [blame] | 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 | */ |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 7 | #include "tests/Test.h" |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 8 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 9 | #include "include/core/SkCanvas.h" |
| 10 | #include "include/core/SkExecutor.h" |
| 11 | #include "include/core/SkStream.h" |
| 12 | #include "include/docs/SkPDFDocument.h" |
| 13 | #include "src/core/SkOSFile.h" |
| 14 | #include "src/utils/SkOSPath.h" |
| 15 | #include "tools/Resources.h" |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 16 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 17 | #include "tools/ToolUtils.h" |
Hal Canary | db68301 | 2016-11-23 08:55:18 -0700 | [diff] [blame] | 18 | |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 19 | static void test_empty(skiatest::Reporter* reporter) { |
| 20 | SkDynamicMemoryWStream stream; |
| 21 | |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 22 | auto doc = SkPDF::MakeDocument(&stream); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 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; |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 31 | auto doc = SkPDF::MakeDocument(&stream); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 32 | |
| 33 | SkCanvas* canvas = doc->beginPage(100, 100); |
| 34 | canvas->drawColor(SK_ColorRED); |
| 35 | doc->endPage(); |
| 36 | |
| 37 | doc->abort(); |
| 38 | |
halcanary | 50e82e6 | 2016-03-21 13:45:05 -0700 | [diff] [blame] | 39 | // Test that only the header is written, not the full document. |
| 40 | REPORTER_ASSERT(reporter, stream.bytesWritten() < 256); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 41 | } |
| 42 | |
| 43 | static void test_abortWithFile(skiatest::Reporter* reporter) { |
halcanary | 87f3ba4 | 2015-01-20 09:30:20 -0800 | [diff] [blame] | 44 | SkString tmpDir = skiatest::GetTmpDir(); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 45 | |
| 46 | if (tmpDir.isEmpty()) { |
Hal Canary | 925e31e | 2017-12-11 14:42:58 -0500 | [diff] [blame] | 47 | ERRORF(reporter, "missing tmpDir."); |
| 48 | return; |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 49 | } |
| 50 | |
tfarina | a8e2e15 | 2014-07-28 19:26:58 -0700 | [diff] [blame] | 51 | SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf"); |
Hal Canary | 925e31e | 2017-12-11 14:42:58 -0500 | [diff] [blame] | 52 | if (!SkFILEWStream(path.c_str()).isValid()) { |
| 53 | ERRORF(reporter, "unable to write to: %s", path.c_str()); |
| 54 | return; |
| 55 | } |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 56 | |
| 57 | // Make sure doc's destructor is called to flush. |
| 58 | { |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 59 | SkFILEWStream stream(path.c_str()); |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 60 | auto doc = SkPDF::MakeDocument(&stream); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 61 | |
| 62 | SkCanvas* canvas = doc->beginPage(100, 100); |
| 63 | canvas->drawColor(SK_ColorRED); |
| 64 | doc->endPage(); |
| 65 | |
| 66 | doc->abort(); |
| 67 | } |
| 68 | |
| 69 | FILE* file = fopen(path.c_str(), "r"); |
Hal Canary | 399bbd9 | 2016-11-10 13:03:21 -0500 | [diff] [blame] | 70 | // Test that only the header is written, not the full document. |
| 71 | char buffer[256]; |
| 72 | REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer)); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 73 | fclose(file); |
| 74 | } |
| 75 | |
| 76 | static void test_file(skiatest::Reporter* reporter) { |
halcanary | 87f3ba4 | 2015-01-20 09:30:20 -0800 | [diff] [blame] | 77 | SkString tmpDir = skiatest::GetTmpDir(); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 78 | if (tmpDir.isEmpty()) { |
Hal Canary | 925e31e | 2017-12-11 14:42:58 -0500 | [diff] [blame] | 79 | ERRORF(reporter, "missing tmpDir."); |
| 80 | return; |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 81 | } |
| 82 | |
tfarina | a8e2e15 | 2014-07-28 19:26:58 -0700 | [diff] [blame] | 83 | SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf"); |
Hal Canary | 925e31e | 2017-12-11 14:42:58 -0500 | [diff] [blame] | 84 | if (!SkFILEWStream(path.c_str()).isValid()) { |
| 85 | ERRORF(reporter, "unable to write to: %s", path.c_str()); |
| 86 | return; |
| 87 | } |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 88 | |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 89 | { |
| 90 | SkFILEWStream stream(path.c_str()); |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 91 | auto doc = SkPDF::MakeDocument(&stream); |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 92 | SkCanvas* canvas = doc->beginPage(100, 100); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 93 | |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 94 | canvas->drawColor(SK_ColorRED); |
| 95 | doc->endPage(); |
| 96 | doc->close(); |
| 97 | } |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 98 | |
| 99 | FILE* file = fopen(path.c_str(), "r"); |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 100 | REPORTER_ASSERT(reporter, file != nullptr); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 101 | char header[100]; |
edisonn@google.com | 5237b7f | 2013-10-22 18:33:21 +0000 | [diff] [blame] | 102 | REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 103 | REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0); |
| 104 | fclose(file); |
| 105 | } |
| 106 | |
| 107 | static void test_close(skiatest::Reporter* reporter) { |
| 108 | SkDynamicMemoryWStream stream; |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 109 | auto doc = SkPDF::MakeDocument(&stream); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 110 | |
| 111 | SkCanvas* canvas = doc->beginPage(100, 100); |
| 112 | canvas->drawColor(SK_ColorRED); |
| 113 | doc->endPage(); |
| 114 | |
| 115 | doc->close(); |
| 116 | |
| 117 | REPORTER_ASSERT(reporter, stream.bytesWritten() != 0); |
| 118 | } |
| 119 | |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 120 | DEF_TEST(SkPDF_document_tests, reporter) { |
halcanary | 2ccdb63 | 2015-08-11 13:35:12 -0700 | [diff] [blame] | 121 | REQUIRE_PDF_DOCUMENT(document_tests, reporter); |
commit-bot@chromium.org | 8c90827 | 2013-10-22 14:49:03 +0000 | [diff] [blame] | 122 | test_empty(reporter); |
| 123 | test_abort(reporter); |
| 124 | test_abortWithFile(reporter); |
| 125 | test_file(reporter); |
| 126 | test_close(reporter); |
| 127 | } |
halcanary | 712fdf7 | 2015-12-10 08:59:43 -0800 | [diff] [blame] | 128 | |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 129 | DEF_TEST(SkPDF_document_skbug_4734, r) { |
| 130 | REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r); |
halcanary | 53b1c09 | 2016-01-06 09:02:25 -0800 | [diff] [blame] | 131 | SkDynamicMemoryWStream stream; |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 132 | auto doc = SkPDF::MakeDocument(&stream); |
halcanary | 53b1c09 | 2016-01-06 09:02:25 -0800 | [diff] [blame] | 133 | SkCanvas* canvas = doc->beginPage(64, 64); |
| 134 | canvas->scale(10000.0f, 10000.0f); |
| 135 | canvas->translate(20.0f, 10.0f); |
| 136 | canvas->rotate(30.0f); |
| 137 | const char text[] = "HELLO"; |
Mike Reed | c4745d6 | 2019-01-07 09:31:58 -0500 | [diff] [blame] | 138 | canvas->drawString(text, 0, 0, SkFont(), SkPaint()); |
halcanary | 53b1c09 | 2016-01-06 09:02:25 -0800 | [diff] [blame] | 139 | } |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 140 | |
| 141 | static bool contains(const uint8_t* result, size_t size, const char expectation[]) { |
| 142 | size_t len = strlen(expectation); |
| 143 | size_t N = 1 + size - len; |
| 144 | for (size_t i = 0; i < N; ++i) { |
| 145 | if (0 == memcmp(result + i, expectation, len)) { |
| 146 | return true; |
| 147 | } |
| 148 | } |
| 149 | return false; |
| 150 | } |
| 151 | |
| 152 | // verify that the PDFA flag does something. |
| 153 | DEF_TEST(SkPDF_pdfa_document, r) { |
| 154 | REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r); |
| 155 | |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 156 | SkPDF::Metadata pdfMetadata; |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 157 | pdfMetadata.fTitle = "test document"; |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 158 | pdfMetadata.fCreation = {0, 1999, 12, 5, 31, 23, 59, 59}; |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 159 | pdfMetadata.fPDFA = true; |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 160 | |
| 161 | SkDynamicMemoryWStream buffer; |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 162 | auto doc = SkPDF::MakeDocument(&buffer, pdfMetadata); |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 163 | doc->beginPage(64, 64)->drawColor(SK_ColorRED); |
| 164 | doc->close(); |
reed | 42943c8 | 2016-09-12 12:01:44 -0700 | [diff] [blame] | 165 | sk_sp<SkData> data(buffer.detachAsData()); |
| 166 | |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 167 | static const char* expectations[] = { |
| 168 | "sRGB IEC61966-2.1", |
| 169 | "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document", |
| 170 | "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>", |
| 171 | "/Subtype /XML", |
| 172 | "/CreationDate (D:19991231235959+00'00')>>", |
| 173 | }; |
| 174 | for (const char* expectation : expectations) { |
| 175 | if (!contains(data->bytes(), data->size(), expectation)) { |
| 176 | ERRORF(r, "PDFA expectation missing: '%s'.", expectation); |
| 177 | } |
| 178 | } |
| 179 | pdfMetadata.fProducer = "phoney library"; |
Mike Reed | a4daf19 | 2017-12-14 13:25:04 -0500 | [diff] [blame] | 180 | pdfMetadata.fPDFA = true; |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 181 | doc = SkPDF::MakeDocument(&buffer, pdfMetadata); |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 182 | doc->beginPage(64, 64)->drawColor(SK_ColorRED); |
| 183 | doc->close(); |
reed | 42943c8 | 2016-09-12 12:01:44 -0700 | [diff] [blame] | 184 | data = buffer.detachAsData(); |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 185 | |
| 186 | static const char* moreExpectations[] = { |
| 187 | "/Producer (phoney library)", |
halcanary | 57f744e | 2016-09-09 11:41:59 -0700 | [diff] [blame] | 188 | "<pdf:Producer>phoney library</pdf:Producer>", |
| 189 | }; |
| 190 | for (const char* expectation : moreExpectations) { |
| 191 | if (!contains(data->bytes(), data->size(), expectation)) { |
| 192 | ERRORF(r, "PDFA expectation missing: '%s'.", expectation); |
| 193 | } |
| 194 | } |
| 195 | } |
Hal Canary | 691fd1b | 2018-02-28 14:10:42 -0500 | [diff] [blame] | 196 | |
| 197 | |
| 198 | DEF_TEST(SkPDF_unicode_metadata, r) { |
| 199 | REQUIRE_PDF_DOCUMENT(SkPDF_unicode_metadata, r); |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 200 | SkPDF::Metadata pdfMetadata; |
Hal Canary | 691fd1b | 2018-02-28 14:10:42 -0500 | [diff] [blame] | 201 | pdfMetadata.fTitle = "πππππ πππππ"; // Out of basic multilingual plane |
| 202 | pdfMetadata.fAuthor = "ABCDE FGHIJ"; // ASCII |
| 203 | pdfMetadata.fSubject = "αβγδΡ ΢ηθικ"; // inside basic multilingual plane |
| 204 | pdfMetadata.fPDFA = true; |
| 205 | SkDynamicMemoryWStream wStream; |
| 206 | { |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 207 | auto doc = SkPDF::MakeDocument(&wStream, pdfMetadata); |
Hal Canary | 691fd1b | 2018-02-28 14:10:42 -0500 | [diff] [blame] | 208 | doc->beginPage(612, 792)->drawColor(SK_ColorCYAN); |
| 209 | } |
| 210 | sk_sp<SkData> data(wStream.detachAsData()); |
| 211 | static const char* expectations[] = { |
| 212 | "<</Title <FEFFD835DCD0D835DCD1D835DCD2D835DCD3D835DCD40020" |
| 213 | "D835DCD5D835DCD6D835DCD7D835DCD8D835DCD9>", |
| 214 | "/Author (ABCDE FGHIJ)", |
| 215 | "Subject <FEFF03B103B203B303B403B5002003B603B703B803B903BA>", |
| 216 | }; |
| 217 | for (const char* expectation : expectations) { |
| 218 | if (!contains(data->bytes(), data->size(), expectation)) { |
| 219 | ERRORF(r, "PDF expectation missing: '%s'.", expectation); |
| 220 | } |
| 221 | } |
| 222 | } |
Hal Canary | b5ccf6f | 2018-10-08 11:36:12 -0400 | [diff] [blame] | 223 | |
| 224 | // Make sure we excercise the multi-page functionality without problems. |
| 225 | // Add this to args.gn to output the PDF to a file: |
| 226 | // extra_cflags = [ "-DSK_PDF_TEST_MULTIPAGE=\"/tmp/skpdf_test_multipage.pdf\"" ] |
| 227 | DEF_TEST(SkPDF_multiple_pages, r) { |
| 228 | REQUIRE_PDF_DOCUMENT(SkPDF_multiple_pages, r); |
| 229 | int n = 100; |
| 230 | #ifdef SK_PDF_TEST_MULTIPAGE |
| 231 | SkFILEWStream wStream(SK_PDF_TEST_MULTIPAGE); |
| 232 | #else |
| 233 | SkDynamicMemoryWStream wStream; |
| 234 | #endif |
| 235 | auto doc = SkPDF::MakeDocument(&wStream); |
| 236 | for (int i = 0; i < n; ++i) { |
| 237 | doc->beginPage(612, 792)->drawColor( |
| 238 | SkColorSetARGB(0xFF, 0x00, (uint8_t)(255.0f * i / (n - 1)), 0x00)); |
| 239 | } |
| 240 | } |
Hal Canary | dd9003a | 2018-12-21 13:44:31 -0500 | [diff] [blame] | 241 | |
| 242 | // Test to make sure that jobs launched by PDF backend don't cause a segfault |
| 243 | // after calling abort(). |
| 244 | DEF_TEST(SkPDF_abort_jobs, rep) { |
Robert Phillips | e5f7328 | 2019-06-18 17:15:04 -0400 | [diff] [blame] | 245 | REQUIRE_PDF_DOCUMENT(SkPDF_abort_jobs, rep); |
Hal Canary | dd9003a | 2018-12-21 13:44:31 -0500 | [diff] [blame] | 246 | SkBitmap b; |
| 247 | b.allocN32Pixels(612, 792); |
| 248 | b.eraseColor(0x4F9643A0); |
| 249 | SkPDF::Metadata metadata; |
| 250 | std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool(); |
| 251 | metadata.fExecutor = executor.get(); |
| 252 | SkNullWStream dst; |
Hal Canary | 3026d4b | 2019-01-07 10:00:48 -0500 | [diff] [blame] | 253 | auto doc = SkPDF::MakeDocument(&dst, metadata); |
Hal Canary | dd9003a | 2018-12-21 13:44:31 -0500 | [diff] [blame] | 254 | doc->beginPage(612, 792)->drawBitmap(b, 0, 0); |
| 255 | doc->abort(); |
| 256 | } |
| 257 | |