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