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 <stdio.h>
20 #include <unistd.h>
21
22 #include <memory>
23 #include <utility>
24
25 #include "cpp/fpdf_scopers.h"
26 #include "file.h"
27 #include "fpdf_dataavail.h"
28 #include "fpdf_save.h"
29 #include "fpdfview.h"
30 #include "linux_fileops.h"
31 #include "logging.h"
32 #include "page.h"
33 #include "rect.h"
34
35 #define LOG_TAG "document"
36
37 extern int GetLastError();
38
39 namespace pdfClient {
40
InitLibrary()41 void InitLibrary() {
42 FPDF_InitLibrary();
43 }
44
Load(std::unique_ptr<FileReader> fileReader,const char * password,bool closeFdOnFailure,std::unique_ptr<Document> * result,int * requestedHeaderSize,int * requestedFooterSize)45 Status Document::Load(std::unique_ptr<FileReader> fileReader, const char* password,
46 bool closeFdOnFailure, std::unique_ptr<Document>* result,
47 int* requestedHeaderSize, int* requestedFooterSize) {
48 *result = nullptr;
49
50 ScopedFPDFDocument fpdf_doc;
51
52 bool is_linearized = false;
53 if ((is_linearized = FPDFAvail_IsLinearized(fileReader->fpdf_avail_.get())) == PDF_LINEARIZED) {
54 fpdf_doc.reset(FPDFAvail_GetDocument(fileReader->fpdf_avail_.get(), password));
55 } else {
56 fpdf_doc.reset(FPDF_LoadCustomDocument(fileReader.get(), password));
57 }
58 FPDF_BOOL should_scale_for_print = FPDF_VIEWERREF_GetPrintScaling(fpdf_doc.get());
59 if (fpdf_doc) {
60 // Use WrapUnique instead of MakeUnique since this Document constructor is
61 // private.
62 *result = std::unique_ptr<Document>(
63 new Document(std::move(fpdf_doc), (password && password[0] != '\0'),
64 std::move(fileReader), is_linearized, should_scale_for_print));
65 return LOADED;
66 }
67
68 if (!closeFdOnFailure) {
69 fileReader->ReleaseFd();
70 }
71
72 if (requestedHeaderSize) {
73 *requestedHeaderSize = fileReader->RequestedHeaderSize();
74 }
75 if (requestedFooterSize) {
76 *requestedFooterSize = fileReader->RequestedFooterSize();
77 }
78
79 // Error - failed to load document.
80 int error = FPDF_GetLastError();
81 if (error == FPDF_ERR_PASSWORD) {
82 return REQUIRES_PASSWORD;
83 } else {
84 LOGE("Parse Document failed (err=%d).\n", error);
85 return PDF_ERROR;
86 }
87 }
88
~Document()89 Document::~Document() {
90 // Allow pages to do any internal cleanup before deletion.
91 for (const auto& entry : pages_) {
92 entry.second->TerminateFormFilling();
93 }
94 }
95
SaveAs(LinuxFileOps::FDCloser fd)96 bool Document::SaveAs(LinuxFileOps::FDCloser fd) {
97 FileWriter fw(std::move(fd));
98 constexpr int flags = 0;
99 if (!FPDF_SaveAsCopy(document_.get(), &fw, flags)) {
100 LOGW("Failed to save-as to fd %d.", fw.Fd());
101 return false;
102 }
103 size_t destSize = lseek(fw.Fd(), 0, SEEK_END);
104 LOGV("Save-as to fd %d [%zd bytes], flags=%d.", fw.Fd(), destSize, flags);
105 return true;
106 }
107
GetPage(int pageNum,bool retain)108 std::shared_ptr<Page> Document::GetPage(int pageNum, bool retain) {
109 if (pages_.find(pageNum) != pages_.end()) {
110 return pages_.at(pageNum);
111 }
112
113 IsPageAvailable(pageNum);
114 auto page = std::make_shared<Page>(document_.get(), pageNum, &form_filler_);
115
116 if (retain) {
117 page->InitializeFormFilling();
118 pages_.try_emplace(pageNum, page);
119 fpdf_page_index_lookup_.try_emplace(page->page(), pageNum);
120 }
121
122 return page;
123 }
124
NotifyInvalidRect(FPDF_PAGE page,Rectangle_i rect)125 void Document::NotifyInvalidRect(FPDF_PAGE page, Rectangle_i rect) {
126 // invalid rects are only relevant to pages that are being retained
127 // since pages save them until a caller asks for them
128 if (fpdf_page_index_lookup_.find(page) != fpdf_page_index_lookup_.end()) {
129 int retained_page_index = fpdf_page_index_lookup_.at(page);
130 pages_.at(retained_page_index)->NotifyInvalidRect(rect);
131 }
132 }
133
ReleaseRetainedPage(int pageNum)134 void Document::ReleaseRetainedPage(int pageNum) {
135 if (pages_.find(pageNum) != pages_.end()) {
136 std::shared_ptr<pdfClient::Page> page = pages_.at(pageNum);
137 page->TerminateFormFilling();
138 pages_.erase(pageNum);
139 fpdf_page_index_lookup_.erase(page->page());
140 }
141 }
142
IsPageAvailable(int pageNum) const143 bool Document::IsPageAvailable(int pageNum) const {
144 // This call should be made before attempting to render or otherwise access
145 // the given page, even if the results are ignored
146 if (file_reader_) {
147 return FPDFAvail_IsPageAvail(file_reader_->fpdf_avail_.get(), pageNum,
148 file_reader_.get()) != 0;
149 }
150 return true;
151 }
152
CloneDocumentWithoutSecurity(LinuxFileOps::FDCloser fd)153 bool Document::CloneDocumentWithoutSecurity(LinuxFileOps::FDCloser fd) {
154 if (file_reader_ && !IsPasswordProtected()) {
155 // Document has no security - just clone the raw file.
156 return CloneRawFile(file_reader_->Fd(), fd.Release());
157 } else {
158 // Document has security or we couldn't just copy the file. Use SaveAsCopy:
159 return SaveAsCopyWithoutSecurity(std::move(fd));
160 }
161 }
162
CloneRawFile(int source,int dest)163 bool Document::CloneRawFile(int source, int dest) {
164 lseek(source, 0, SEEK_SET);
165 char buf[4096];
166 size_t bytesRead;
167 while ((bytesRead = read(source, buf, 4096)) > 0) {
168 write(dest, buf, bytesRead);
169 }
170
171 size_t sourceSize = lseek(source, 0, SEEK_END);
172 size_t destSize = lseek(dest, 0, SEEK_END);
173 bool success = (destSize == sourceSize);
174 if (success) {
175 LOGV("Copied raw file to fd %d [%zd bytes].", dest, destSize);
176 } else {
177 LOGV("Failed to copy raw file to fd %d (wrote %zd out of %zd).",
178 dest, destSize, sourceSize);
179 }
180 // We own the FD and have to make sure to close it.
181 LinuxFileOps::CloseFD(dest);
182 return success;
183 }
184
SaveAsCopyWithoutSecurity(LinuxFileOps::FDCloser dest)185 bool Document::SaveAsCopyWithoutSecurity(LinuxFileOps::FDCloser dest) {
186 FileWriter fw(std::move(dest));
187 int flags = IsPasswordProtected() ? FPDF_REMOVE_SECURITY : 0;
188 bool success = FPDF_SaveAsCopy(document_.get(), &fw, flags);
189
190 size_t destSize = lseek(fw.Fd(), 0, SEEK_END);
191 if (success) {
192 LOGV("Save-as to fd %d [%zd bytes], flags=%d.", fw.Fd(), destSize, flags);
193 } else {
194 LOGV("Failed to save-as to fd %d, flags=%d.", fw.Fd(), flags);
195 }
196 // No need to close the FD as lower level code already does that.
197 return success;
198 }
199
200 } // namespace pdfClient