1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2013 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "Test.h"
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "Resources.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCanvas.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDocument.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkOSFile.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkOSPath.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkStream.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "sk_tool_utils.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void test_empty(skiatest::Reporter* reporter) {
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream stream;
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->close();
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void test_abort(skiatest::Reporter* reporter) {
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream stream;
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkCanvas* canvas = doc->beginPage(100, 100);
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->drawColor(SK_ColorRED);
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->endPage();
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->abort();
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Test that only the header is written, not the full document.
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void test_abortWithFile(skiatest::Reporter* reporter) {
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString tmpDir = skiatest::GetTmpDir();
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (tmpDir.isEmpty()) {
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ERRORF(reporter, "missing tmpDir.");
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!SkFILEWStream(path.c_str()).isValid()) {
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ERRORF(reporter, "unable to write to: %s", path.c_str());
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Make sure doc's destructor is called to flush.
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkFILEWStream stream(path.c_str());
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkDocument> doc = SkDocument::MakePDF(&stream);
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas* canvas = doc->beginPage(100, 100);
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawColor(SK_ColorRED);
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        doc->endPage();
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        doc->abort();
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    FILE* file = fopen(path.c_str(), "r");
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Test that only the header is written, not the full document.
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    char buffer[256];
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fclose(file);
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void test_file(skiatest::Reporter* reporter) {
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString tmpDir = skiatest::GetTmpDir();
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (tmpDir.isEmpty()) {
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ERRORF(reporter, "missing tmpDir.");
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!SkFILEWStream(path.c_str()).isValid()) {
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ERRORF(reporter, "unable to write to: %s", path.c_str());
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkFILEWStream stream(path.c_str());
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkDocument> doc = SkDocument::MakePDF(&stream);
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas* canvas = doc->beginPage(100, 100);
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawColor(SK_ColorRED);
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        doc->endPage();
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        doc->close();
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    FILE* file = fopen(path.c_str(), "r");
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, file != nullptr);
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    char header[100];
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fclose(file);
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void test_close(skiatest::Reporter* reporter) {
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream stream;
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkCanvas* canvas = doc->beginPage(100, 100);
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->drawColor(SK_ColorRED);
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->endPage();
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->close();
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDEF_TEST(SkPDF_document_tests, reporter) {
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REQUIRE_PDF_DOCUMENT(document_tests, reporter);
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    test_empty(reporter);
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    test_abort(reporter);
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    test_abortWithFile(reporter);
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    test_file(reporter);
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    test_close(reporter);
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDEF_TEST(SkPDF_document_skbug_4734, r) {
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream stream;
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkCanvas* canvas = doc->beginPage(64, 64);
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->scale(10000.0f, 10000.0f);
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->translate(20.0f, 10.0f);
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->rotate(30.0f);
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char text[] = "HELLO";
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->drawString(text, 0, 0, SkPaint());
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool contains(const uint8_t* result, size_t size, const char expectation[]) {
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t len = strlen(expectation);
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t N = 1 + size - len;
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (size_t i = 0; i < N; ++i) {
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (0 == memcmp(result + i, expectation, len)) {
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return true;
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return false;
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// verify that the PDFA flag does something.
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDEF_TEST(SkPDF_pdfa_document, r) {
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDocument::PDFMetadata pdfMetadata;
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fTitle = "test document";
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fCreation.fEnabled = true;
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fPDFA = true;
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream buffer;
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto doc = SkDocument::MakePDF(&buffer, pdfMetadata);
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->close();
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkData> data(buffer.detachAsData());
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const char* expectations[] = {
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "sRGB IEC61966-2.1",
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "/Subtype /XML",
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "/CreationDate (D:19991231235959+00'00')>>",
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const char* expectation : expectations) {
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!contains(data->bytes(), data->size(), expectation)) {
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fProducer = "phoney library";
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pdfMetadata.fPDFA = true;
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc = SkDocument::MakePDF(&buffer, pdfMetadata);
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    doc->close();
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    data = buffer.detachAsData();
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const char* moreExpectations[] = {
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "/Producer (phoney library)",
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "/ProductionLibrary (Skia/PDF m",
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "<!-- <skia:ProductionLibrary>Skia/PDF m",
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "<pdf:Producer>phoney library</pdf:Producer>",
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const char* expectation : moreExpectations) {
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!contains(data->bytes(), data->size(), expectation)) {
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
198