• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkExecutor.h"
12 #include "SkOSFile.h"
13 #include "SkOSPath.h"
14 #include "SkPDFDocument.h"
15 #include "SkStream.h"
16 
17 #include "sk_tool_utils.h"
18 
test_empty(skiatest::Reporter * reporter)19 static void test_empty(skiatest::Reporter* reporter) {
20     SkDynamicMemoryWStream stream;
21 
22     auto doc = SkPDF::MakeDocument(&stream);
23 
24     doc->close();
25 
26     REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
27 }
28 
test_abort(skiatest::Reporter * reporter)29 static void test_abort(skiatest::Reporter* reporter) {
30     SkDynamicMemoryWStream stream;
31     auto doc = SkPDF::MakeDocument(&stream);
32 
33     SkCanvas* canvas = doc->beginPage(100, 100);
34     canvas->drawColor(SK_ColorRED);
35     doc->endPage();
36 
37     doc->abort();
38 
39     // Test that only the header is written, not the full document.
40     REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
41 }
42 
test_abortWithFile(skiatest::Reporter * reporter)43 static void test_abortWithFile(skiatest::Reporter* reporter) {
44     SkString tmpDir = skiatest::GetTmpDir();
45 
46     if (tmpDir.isEmpty()) {
47         ERRORF(reporter, "missing tmpDir.");
48         return;
49     }
50 
51     SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
52     if (!SkFILEWStream(path.c_str()).isValid()) {
53         ERRORF(reporter, "unable to write to: %s", path.c_str());
54         return;
55     }
56 
57     // Make sure doc's destructor is called to flush.
58     {
59         SkFILEWStream stream(path.c_str());
60         auto doc = SkPDF::MakeDocument(&stream);
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");
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));
73     fclose(file);
74 }
75 
test_file(skiatest::Reporter * reporter)76 static void test_file(skiatest::Reporter* reporter) {
77     SkString tmpDir = skiatest::GetTmpDir();
78     if (tmpDir.isEmpty()) {
79         ERRORF(reporter, "missing tmpDir.");
80         return;
81     }
82 
83     SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
84     if (!SkFILEWStream(path.c_str()).isValid()) {
85         ERRORF(reporter, "unable to write to: %s", path.c_str());
86         return;
87     }
88 
89     {
90         SkFILEWStream stream(path.c_str());
91         auto doc = SkPDF::MakeDocument(&stream);
92         SkCanvas* canvas = doc->beginPage(100, 100);
93 
94         canvas->drawColor(SK_ColorRED);
95         doc->endPage();
96         doc->close();
97     }
98 
99     FILE* file = fopen(path.c_str(), "r");
100     REPORTER_ASSERT(reporter, file != nullptr);
101     char header[100];
102     REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
103     REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
104     fclose(file);
105 }
106 
test_close(skiatest::Reporter * reporter)107 static void test_close(skiatest::Reporter* reporter) {
108     SkDynamicMemoryWStream stream;
109     auto doc = SkPDF::MakeDocument(&stream);
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 
DEF_TEST(SkPDF_document_tests,reporter)120 DEF_TEST(SkPDF_document_tests, reporter) {
121     REQUIRE_PDF_DOCUMENT(document_tests, reporter);
122     test_empty(reporter);
123     test_abort(reporter);
124     test_abortWithFile(reporter);
125     test_file(reporter);
126     test_close(reporter);
127 }
128 
DEF_TEST(SkPDF_document_skbug_4734,r)129 DEF_TEST(SkPDF_document_skbug_4734, r) {
130     REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
131     SkDynamicMemoryWStream stream;
132     auto doc = SkPDF::MakeDocument(&stream);
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";
138     canvas->drawString(text, 0, 0, SkFont(), SkPaint());
139 }
140 
contains(const uint8_t * result,size_t size,const char expectation[])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.
DEF_TEST(SkPDF_pdfa_document,r)153 DEF_TEST(SkPDF_pdfa_document, r) {
154     REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
155 
156     SkPDF::Metadata pdfMetadata;
157     pdfMetadata.fTitle = "test document";
158     pdfMetadata.fCreation = {0, 1999, 12, 5, 31, 23, 59, 59};
159     pdfMetadata.fPDFA = true;
160 
161     SkDynamicMemoryWStream buffer;
162     auto doc = SkPDF::MakeDocument(&buffer, pdfMetadata);
163     doc->beginPage(64, 64)->drawColor(SK_ColorRED);
164     doc->close();
165     sk_sp<SkData> data(buffer.detachAsData());
166 
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";
180     pdfMetadata.fPDFA = true;
181     doc = SkPDF::MakeDocument(&buffer, pdfMetadata);
182     doc->beginPage(64, 64)->drawColor(SK_ColorRED);
183     doc->close();
184     data = buffer.detachAsData();
185 
186     static const char* moreExpectations[] = {
187         "/Producer (phoney library)",
188         "/ProductionLibrary (Skia/PDF m",
189         "<!-- <skia:ProductionLibrary>Skia/PDF m",
190         "<pdf:Producer>phoney library</pdf:Producer>",
191     };
192     for (const char* expectation : moreExpectations) {
193         if (!contains(data->bytes(), data->size(), expectation)) {
194             ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
195         }
196     }
197 }
198 
199 
DEF_TEST(SkPDF_unicode_metadata,r)200 DEF_TEST(SkPDF_unicode_metadata, r) {
201     REQUIRE_PDF_DOCUMENT(SkPDF_unicode_metadata, r);
202     SkPDF::Metadata pdfMetadata;
203     pdfMetadata.fTitle   = "���������� ����������"; // Out of basic multilingual plane
204     pdfMetadata.fAuthor  = "ABCDE FGHIJ"; // ASCII
205     pdfMetadata.fSubject = "αβγδε ζηθικ"; // inside  basic multilingual plane
206     pdfMetadata.fPDFA = true;
207     SkDynamicMemoryWStream wStream;
208     {
209         auto doc = SkPDF::MakeDocument(&wStream, pdfMetadata);
210         doc->beginPage(612, 792)->drawColor(SK_ColorCYAN);
211     }
212     sk_sp<SkData> data(wStream.detachAsData());
213     static const char* expectations[] = {
214         "<</Title <FEFFD835DCD0D835DCD1D835DCD2D835DCD3D835DCD40020"
215             "D835DCD5D835DCD6D835DCD7D835DCD8D835DCD9>",
216         "/Author (ABCDE FGHIJ)",
217         "Subject <FEFF03B103B203B303B403B5002003B603B703B803B903BA>",
218     };
219     for (const char* expectation : expectations) {
220         if (!contains(data->bytes(), data->size(), expectation)) {
221             ERRORF(r, "PDF expectation missing: '%s'.", expectation);
222         }
223     }
224 }
225 
226 // Make sure we excercise the multi-page functionality without problems.
227 // Add this to args.gn to output the PDF to a file:
228 //   extra_cflags = [ "-DSK_PDF_TEST_MULTIPAGE=\"/tmp/skpdf_test_multipage.pdf\"" ]
DEF_TEST(SkPDF_multiple_pages,r)229 DEF_TEST(SkPDF_multiple_pages, r) {
230     REQUIRE_PDF_DOCUMENT(SkPDF_multiple_pages, r);
231     int n = 100;
232 #ifdef SK_PDF_TEST_MULTIPAGE
233     SkFILEWStream wStream(SK_PDF_TEST_MULTIPAGE);
234 #else
235     SkDynamicMemoryWStream wStream;
236 #endif
237     auto doc = SkPDF::MakeDocument(&wStream);
238     for (int i = 0; i < n; ++i) {
239         doc->beginPage(612, 792)->drawColor(
240                 SkColorSetARGB(0xFF, 0x00, (uint8_t)(255.0f * i / (n - 1)), 0x00));
241     }
242 }
243 
244 // Test to make sure that jobs launched by PDF backend don't cause a segfault
245 // after calling abort().
DEF_TEST(SkPDF_abort_jobs,rep)246 DEF_TEST(SkPDF_abort_jobs, rep) {
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;
254     auto doc = SkPDF::MakeDocument(&dst, metadata);
255     doc->beginPage(612, 792)->drawBitmap(b, 0, 0);
256     doc->abort();
257 }
258 
259