Flutter Engine
The Flutter Engine
PDFDocumentTest.cpp
Go to the documentation of this file.
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 */
10#include "include/core/SkData.h"
13#include "include/core/SkFont.h"
14#include "include/core/SkImage.h" // IWYU pragma: keep
20#include "src/utils/SkOSPath.h"
21#include "tests/Test.h"
23
24#include <cstdint>
25#include <cstdio>
26#include <cstring>
27#include <memory>
28
31
32 auto doc = SkPDF::MakeDocument(&stream);
33
34 doc->close();
35
36 REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
37}
38
41 auto doc = SkPDF::MakeDocument(&stream);
42
43 SkCanvas* canvas = doc->beginPage(100, 100);
44 canvas->drawColor(SK_ColorRED);
45 doc->endPage();
46
47 doc->abort();
48
49 // Test that only the header is written, not the full document.
50 REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
51}
52
55
56 if (tmpDir.isEmpty()) {
57 ERRORF(reporter, "missing tmpDir.");
58 return;
59 }
60
61 SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
62 if (!SkFILEWStream(path.c_str()).isValid()) {
63 ERRORF(reporter, "unable to write to: %s", path.c_str());
64 return;
65 }
66
67 // Make sure doc's destructor is called to flush.
68 {
69 SkFILEWStream stream(path.c_str());
70 auto doc = SkPDF::MakeDocument(&stream);
71
72 SkCanvas* canvas = doc->beginPage(100, 100);
73 canvas->drawColor(SK_ColorRED);
74 doc->endPage();
75
76 doc->abort();
77 }
78
79 FILE* file = fopen(path.c_str(), "r");
80 // Test that only the header is written, not the full document.
81 char buffer[256];
82 REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
83 fclose(file);
84}
85
88 if (tmpDir.isEmpty()) {
89 ERRORF(reporter, "missing tmpDir.");
90 return;
91 }
92
93 SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
94 if (!SkFILEWStream(path.c_str()).isValid()) {
95 ERRORF(reporter, "unable to write to: %s", path.c_str());
96 return;
97 }
98
99 {
100 SkFILEWStream stream(path.c_str());
101 auto doc = SkPDF::MakeDocument(&stream);
102 SkCanvas* canvas = doc->beginPage(100, 100);
103
104 canvas->drawColor(SK_ColorRED);
105 doc->endPage();
106 doc->close();
107 }
108
109 FILE* file = fopen(path.c_str(), "r");
110 REPORTER_ASSERT(reporter, file != nullptr);
111 char header[100];
112 REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
113 REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
114 fclose(file);
115}
116
119 auto doc = SkPDF::MakeDocument(&stream);
120
121 SkCanvas* canvas = doc->beginPage(100, 100);
122 canvas->drawColor(SK_ColorRED);
123 doc->endPage();
124
125 doc->close();
126
127 REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
128}
129
130DEF_TEST(SkPDF_document_tests, reporter) {
131 REQUIRE_PDF_DOCUMENT(document_tests, reporter);
137}
138
139DEF_TEST(SkPDF_document_skbug_4734, r) {
140 REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
142 auto doc = SkPDF::MakeDocument(&stream);
143 SkCanvas* canvas = doc->beginPage(64, 64);
144 canvas->scale(10000.0f, 10000.0f);
145 canvas->translate(20.0f, 10.0f);
146 canvas->rotate(30.0f);
147 const char text[] = "HELLO";
148 canvas->drawString(text, 0, 0, ToolUtils::DefaultFont(), SkPaint());
149}
150
151static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
152 size_t len = strlen(expectation);
153 size_t N = 1 + size - len;
154 for (size_t i = 0; i < N; ++i) {
155 if (0 == memcmp(result + i, expectation, len)) {
156 return true;
157 }
158 }
159 return false;
160}
161
162// verify that the PDFA flag does something.
163DEF_TEST(SkPDF_pdfa_document, r) {
164 REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
165
166 SkPDF::Metadata pdfMetadata;
167 pdfMetadata.fTitle = "test document";
168 pdfMetadata.fCreation = {0, 1999, 12, 5, 31, 23, 59, 59};
169 pdfMetadata.fPDFA = true;
170
172 auto doc = SkPDF::MakeDocument(&buffer, pdfMetadata);
173 doc->beginPage(64, 64)->drawColor(SK_ColorRED);
174 doc->close();
175 sk_sp<SkData> data(buffer.detachAsData());
176
177 static const char* expectations[] = {
178 "sRGB IEC61966-2.1",
179 "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
180 "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
181 "/Subtype /XML",
182 "/CreationDate (D:19991231235959+00'00')>>",
183 };
184 for (const char* expectation : expectations) {
185 if (!contains(data->bytes(), data->size(), expectation)) {
186 ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
187 }
188 }
189 pdfMetadata.fProducer = "phoney library";
190 pdfMetadata.fPDFA = true;
191 doc = SkPDF::MakeDocument(&buffer, pdfMetadata);
192 doc->beginPage(64, 64)->drawColor(SK_ColorRED);
193 doc->close();
194 data = buffer.detachAsData();
195
196 static const char* moreExpectations[] = {
197 "/Producer (phoney library)",
198 "<pdf:Producer>phoney library</pdf:Producer>",
199 };
200 for (const char* expectation : moreExpectations) {
201 if (!contains(data->bytes(), data->size(), expectation)) {
202 ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
203 }
204 }
205}
206
207
208DEF_TEST(SkPDF_unicode_metadata, r) {
209 REQUIRE_PDF_DOCUMENT(SkPDF_unicode_metadata, r);
210 SkPDF::Metadata pdfMetadata;
211 pdfMetadata.fTitle = "𝓐𝓑𝓒𝓓𝓔 π“•π“–π“—π“˜π“™"; // Out of basic multilingual plane
212 pdfMetadata.fAuthor = "ABCDE FGHIJ"; // ASCII
213 pdfMetadata.fSubject = "αβγδΡ ΢ηθικ"; // inside basic multilingual plane
214 pdfMetadata.fPDFA = true;
216 {
217 auto doc = SkPDF::MakeDocument(&wStream, pdfMetadata);
218 doc->beginPage(612, 792)->drawColor(SK_ColorCYAN);
219 }
221 static const char* expectations[] = {
222 ("<</Title <FEFFD835DCD0D835DCD1D835DCD2D835DCD3D835DCD40020"
223 "D835DCD5D835DCD6D835DCD7D835DCD8D835DCD9>"),
224 "/Author (ABCDE FGHIJ)",
225 "Subject <FEFF03B103B203B303B403B5002003B603B703B803B903BA>",
226 "/ViewerPreferences",
227 "/DisplayDocTitle true",
228 };
229 for (const char* expectation : expectations) {
230 if (!contains(data->bytes(), data->size(), expectation)) {
231 ERRORF(r, "PDF expectation missing: '%s'.", expectation);
232 }
233 }
234}
235
236// Make sure we excercise the multi-page functionality without problems.
237// Add this to args.gn to output the PDF to a file:
238// extra_cflags = [ "-DSK_PDF_TEST_MULTIPAGE=\"/tmp/skpdf_test_multipage.pdf\"" ]
239DEF_TEST(SkPDF_multiple_pages, r) {
240 REQUIRE_PDF_DOCUMENT(SkPDF_multiple_pages, r);
241 int n = 100;
242#ifdef SK_PDF_TEST_MULTIPAGE
243 SkFILEWStream wStream(SK_PDF_TEST_MULTIPAGE);
244#else
246#endif
247 auto doc = SkPDF::MakeDocument(&wStream);
248 for (int i = 0; i < n; ++i) {
249 doc->beginPage(612, 792)->drawColor(
250 SkColorSetARGB(0xFF, 0x00, (uint8_t)(255.0f * i / (n - 1)), 0x00));
251 }
252}
253
254// Test to make sure that jobs launched by PDF backend don't cause a segfault
255// after calling abort().
256DEF_TEST(SkPDF_abort_jobs, rep) {
257 REQUIRE_PDF_DOCUMENT(SkPDF_abort_jobs, rep);
258 SkBitmap b;
259 b.allocN32Pixels(612, 792);
260 b.eraseColor(0x4F9643A0);
261 SkPDF::Metadata metadata;
262 std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
263 metadata.fExecutor = executor.get();
265 auto doc = SkPDF::MakeDocument(&dst, metadata);
266 doc->beginPage(612, 792)->drawImage(b.asImage(), 0, 0);
267 doc->abort();
268}
269
reporter
Definition: FontMgrTest.cpp:39
static bool contains(const uint8_t *result, size_t size, const char expectation[])
static void test_close(skiatest::Reporter *reporter)
DEF_TEST(SkPDF_document_tests, reporter)
static void test_abort(skiatest::Reporter *reporter)
static void test_file(skiatest::Reporter *reporter)
static void test_abortWithFile(skiatest::Reporter *reporter)
static void test_empty(skiatest::Reporter *reporter)
constexpr SkColor SK_ColorCYAN
Definition: SkColor.h:143
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER)
Definition: Test.h:485
#define ERRORF(r,...)
Definition: Test.h:293
#define N
Definition: beziers.cpp:19
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
void rotate(SkScalar degrees)
Definition: SkCanvas.cpp:1300
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.h:1803
sk_sp< SkData > detachAsData()
Definition: SkStream.cpp:707
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
Definition: SkExecutor.cpp:146
bool isValid() const
Definition: SkStream.h:442
static SkString Join(const char *rootPath, const char *relativePath)
Definition: SkOSPath.cpp:14
bool isEmpty() const
Definition: SkString.h:130
const char * c_str() const
Definition: SkString.h:133
static bool b
GAsyncResult * result
std::u16string text
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
SkFont DefaultFont()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
SkString GetTmpDir()
Definition: Test.cpp:53
static const char header[]
Definition: skpbench.cpp:88
SkExecutor * fExecutor
SkString fSubject
Definition: SkPDFDocument.h:96
SkString fAuthor
Definition: SkPDFDocument.h:92
SkString fProducer
DateTime fCreation
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63