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