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 "SkOSPath.h"
14 #include "SkStream.h"
15
16 #include "sk_tool_utils.h"
17
test_empty(skiatest::Reporter * reporter)18 static void test_empty(skiatest::Reporter* reporter) {
19 SkDynamicMemoryWStream stream;
20
21 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
22
23 doc->close();
24
25 REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
26 }
27
test_abort(skiatest::Reporter * reporter)28 static void test_abort(skiatest::Reporter* reporter) {
29 SkDynamicMemoryWStream stream;
30 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
31
32 SkCanvas* canvas = doc->beginPage(100, 100);
33 canvas->drawColor(SK_ColorRED);
34 doc->endPage();
35
36 doc->abort();
37
38 // Test that only the header is written, not the full document.
39 REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
40 }
41
test_abortWithFile(skiatest::Reporter * reporter)42 static void test_abortWithFile(skiatest::Reporter* reporter) {
43 SkString tmpDir = skiatest::GetTmpDir();
44
45 if (tmpDir.isEmpty()) {
46 ERRORF(reporter, "missing tmpDir.");
47 return;
48 }
49
50 SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
51 if (!SkFILEWStream(path.c_str()).isValid()) {
52 ERRORF(reporter, "unable to write to: %s", path.c_str());
53 return;
54 }
55
56 // Make sure doc's destructor is called to flush.
57 {
58 SkFILEWStream stream(path.c_str());
59 sk_sp<SkDocument> doc = SkDocument::MakePDF(&stream);
60
61 SkCanvas* canvas = doc->beginPage(100, 100);
62 canvas->drawColor(SK_ColorRED);
63 doc->endPage();
64
65 doc->abort();
66 }
67
68 FILE* file = fopen(path.c_str(), "r");
69 // Test that only the header is written, not the full document.
70 char buffer[256];
71 REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
72 fclose(file);
73 }
74
test_file(skiatest::Reporter * reporter)75 static void test_file(skiatest::Reporter* reporter) {
76 SkString tmpDir = skiatest::GetTmpDir();
77 if (tmpDir.isEmpty()) {
78 ERRORF(reporter, "missing tmpDir.");
79 return;
80 }
81
82 SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
83 if (!SkFILEWStream(path.c_str()).isValid()) {
84 ERRORF(reporter, "unable to write to: %s", path.c_str());
85 return;
86 }
87
88 {
89 SkFILEWStream stream(path.c_str());
90 sk_sp<SkDocument> doc = SkDocument::MakePDF(&stream);
91 SkCanvas* canvas = doc->beginPage(100, 100);
92
93 canvas->drawColor(SK_ColorRED);
94 doc->endPage();
95 doc->close();
96 }
97
98 FILE* file = fopen(path.c_str(), "r");
99 REPORTER_ASSERT(reporter, file != nullptr);
100 char header[100];
101 REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
102 REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
103 fclose(file);
104 }
105
test_close(skiatest::Reporter * reporter)106 static void test_close(skiatest::Reporter* reporter) {
107 SkDynamicMemoryWStream stream;
108 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
109
110 SkCanvas* canvas = doc->beginPage(100, 100);
111 canvas->drawColor(SK_ColorRED);
112 doc->endPage();
113
114 doc->close();
115
116 REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
117 }
118
DEF_TEST(SkPDF_document_tests,reporter)119 DEF_TEST(SkPDF_document_tests, reporter) {
120 REQUIRE_PDF_DOCUMENT(document_tests, reporter);
121 test_empty(reporter);
122 test_abort(reporter);
123 test_abortWithFile(reporter);
124 test_file(reporter);
125 test_close(reporter);
126 }
127
DEF_TEST(SkPDF_document_skbug_4734,r)128 DEF_TEST(SkPDF_document_skbug_4734, r) {
129 REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
130 SkDynamicMemoryWStream stream;
131 sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
132 SkCanvas* canvas = doc->beginPage(64, 64);
133 canvas->scale(10000.0f, 10000.0f);
134 canvas->translate(20.0f, 10.0f);
135 canvas->rotate(30.0f);
136 const char text[] = "HELLO";
137 canvas->drawString(text, 0, 0, SkPaint());
138 }
139
contains(const uint8_t * result,size_t size,const char expectation[])140 static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
141 size_t len = strlen(expectation);
142 size_t N = 1 + size - len;
143 for (size_t i = 0; i < N; ++i) {
144 if (0 == memcmp(result + i, expectation, len)) {
145 return true;
146 }
147 }
148 return false;
149 }
150
151 // verify that the PDFA flag does something.
DEF_TEST(SkPDF_pdfa_document,r)152 DEF_TEST(SkPDF_pdfa_document, r) {
153 REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
154
155 SkDocument::PDFMetadata pdfMetadata;
156 pdfMetadata.fTitle = "test document";
157 pdfMetadata.fCreation.fEnabled = true;
158 pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};
159 pdfMetadata.fPDFA = true;
160
161 SkDynamicMemoryWStream buffer;
162 auto doc = SkDocument::MakePDF(&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 = SkDocument::MakePDF(&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