1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "document.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <fcntl.h>
22 #include <gtest/gtest.h>
23 #include <stddef.h>
24
25 #include <algorithm>
26 #include <cstring>
27 #include <memory>
28 #include <string>
29 #include <utility>
30
31 #include "file.h"
32 #include "fpdfview.h"
33 #include "linux_fileops.h"
34 #include "logging.h"
35 #include "page.h"
36
37 using pdfClient::Document;
38 using pdfClient::FileReader;
39 using pdfClient::LinuxFileOps;
40 using pdfClient::Page;
41 using std::string_view;
42
43 namespace {
44
45 #define LOG_TAG "pdf/apk/jni/pdfClient/document_test.cc"
46
47 const std::string kTestdata = "testdata";
48 const std::string kSekretNoPassword = "sekret_no_password.pdf";
49 const std::string kSecretWithPassword = "sekret_password_banana.pdf";
50 const std::string kPassword = "banana";
51
GetTestDataDir()52 std::string GetTestDataDir() {
53 return android::base::GetExecutableDirectory();
54 }
55
GetTestFile(std::string filename)56 std::string GetTestFile(std::string filename) {
57 return GetTestDataDir() + "/" + kTestdata + "/" + filename;
58 }
59
GetTempFile(std::string filename)60 std::string GetTempFile(std::string filename) {
61 return GetTestDataDir() + "/" + filename;
62 }
63
LoadDocument(string_view path,const char * password=nullptr)64 std::unique_ptr<Document> LoadDocument(string_view path, const char* password = nullptr) {
65 LinuxFileOps::FDCloser fd(open(path.data(), O_RDONLY));
66 CHECK_GT(fd.get(), 0);
67 std::unique_ptr<Document> document;
68 CHECK_EQ(pdfClient::LOADED, Document::Load(std::make_unique<FileReader>(std::move(fd)), password,
69 /* closeFdOnFailure= */ true, &document))
70 << "could not load " << path << " with password " << (password ? password : "nullptr");
71 return document;
72 }
73
compareDocuments(const std::shared_ptr<Page> page_orig,const std::shared_ptr<Page> page_copied)74 void compareDocuments(const std::shared_ptr<Page> page_orig,
75 const std::shared_ptr<Page> page_copied) {
76 static constexpr int kMaxDimension = 1024;
77 CHECK_GT(page_orig->Width(), 0) << "0 page width";
78 CHECK_GT(page_orig->Height(), 0) << "0 page height";
79 const float scale_orig =
80 static_cast<float>(kMaxDimension) / std::max(page_orig->Width(), page_orig->Height());
81 size_t width_orig = static_cast<size_t>(page_orig->Width() * scale_orig);
82 size_t height_orig = static_cast<size_t>(page_orig->Height() * scale_orig);
83
84 CHECK_GT(page_copied->Width(), 0) << "0 page width";
85 CHECK_GT(page_copied->Height(), 0) << "0 page height";
86 const float scale_copied = static_cast<float>(kMaxDimension) /
87 std::max(page_copied->Width(), page_copied->Height());
88 size_t width_copied = static_cast<size_t>(page_copied->Width() * scale_copied);
89 size_t height_copied = static_cast<size_t>(page_copied->Height() * scale_copied);
90
91 ASSERT_EQ(width_orig, width_copied);
92 ASSERT_EQ(height_orig, height_copied);
93 ASSERT_EQ(scale_orig, scale_copied);
94 }
95
loadDocumentWithoutPassword(std::string fpath)96 void loadDocumentWithoutPassword(std::string fpath) {
97 // Expect to fail for lack of password.
98 LinuxFileOps::FDCloser in(open(fpath.c_str(), O_RDONLY));
99 ASSERT_GT(in.get(), 0);
100 std::unique_ptr<Document> should_fail;
101 auto fr = std::make_unique<FileReader>(std::move(in));
102 CHECK_EQ(pdfClient::REQUIRES_PASSWORD,
103 Document::Load(std::move(fr), nullptr, /* closeFdOnFailure= */ true, &should_fail))
104 << "should not have been able to load copy of " << kSecretWithPassword
105 << " without password";
106 }
107
TEST(Test,CloneWithoutEncryption)108 TEST(Test, CloneWithoutEncryption) {
109 std::unique_ptr<Document> doc =
110 LoadDocument(GetTestFile(kSecretWithPassword), kPassword.c_str());
111 std::string cloned_path = GetTempFile("cloned.pdf");
112 LinuxFileOps::FDCloser out(open(cloned_path.c_str(), O_RDWR | O_CREAT | O_APPEND, 0600));
113 ASSERT_GT(out.get(), 0);
114 ASSERT_TRUE(doc->CloneDocumentWithoutSecurity(std::move(out)));
115 std::unique_ptr<Document> cloned = LoadDocument(cloned_path);
116 compareDocuments(doc->GetPage(0), cloned->GetPage(0));
117 }
118
TEST(Test,SaveAs)119 TEST(Test, SaveAs) {
120 std::unique_ptr<Document> doc_orig =
121 LoadDocument(GetTestFile(kSecretWithPassword), kPassword.c_str());
122 std::string copied_path = GetTempFile("copied.pdf");
123 LinuxFileOps::FDCloser out(open(copied_path.c_str(), O_RDWR | O_CREAT | O_APPEND, 0600));
124 ASSERT_GT(out.get(), 0);
125 ASSERT_TRUE(doc_orig->SaveAs(std::move(out)));
126 loadDocumentWithoutPassword(copied_path);
127 // Should load with same password.
128 std::unique_ptr<Document> copied = LoadDocument(copied_path, kPassword.c_str());
129 compareDocuments(doc_orig->GetPage(0), copied->GetPage(0));
130 }
131
132 /*
133 * Tests the retention of std::shared_ptr<Page> as requested.
134 */
TEST(Test,GetPageTest)135 TEST(Test, GetPageTest) {
136 std::unique_ptr<Document> doc = LoadDocument(GetTestFile(kSekretNoPassword), nullptr);
137 // retain == false so should be a new copy each time
138 std::shared_ptr<Page> page_zero_copy_one = doc->GetPage(0);
139 std::shared_ptr<Page> page_zero_copy_two = doc->GetPage(0);
140 EXPECT_NE(page_zero_copy_one, page_zero_copy_two);
141
142 // retain == true so should get the same ptr
143 std::shared_ptr<Page> page_zero_copy_three = doc->GetPage(0, true);
144 std::shared_ptr<Page> page_zero_copy_four = doc->GetPage(0, true);
145 EXPECT_EQ(page_zero_copy_three, page_zero_copy_four);
146
147 // since it's already retained, shouldn't matter if we request with
148 // retain == false, should still get same one
149 std::shared_ptr<Page> page_zero_copy_five = doc->GetPage(0);
150 EXPECT_EQ(page_zero_copy_four, page_zero_copy_five);
151 }
152
153 } // namespace
154
main(int argc,char ** argv)155 int main(int argc, char** argv) {
156 ::testing::InitGoogleTest(&argc, argv);
157 FPDF_InitLibrary();
158 int status = RUN_ALL_TESTS();
159 // Destroy the library to keep the memory leak checker happy.
160 FPDF_DestroyLibrary();
161 return status;
162 }
163