/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "document.h" #include #include #include #include #include "cpp/fpdf_scopers.h" #include "file.h" #include "fpdf_dataavail.h" #include "fpdf_save.h" #include "fpdfview.h" #include "linux_fileops.h" #include "logging.h" #include "page.h" #include "rect.h" #define LOG_TAG "document" extern int GetLastError(); namespace pdfClient { void InitLibrary() { FPDF_InitLibrary(); } Status Document::Load(std::unique_ptr fileReader, const char* password, bool closeFdOnFailure, std::unique_ptr* result, int* requestedHeaderSize, int* requestedFooterSize) { *result = nullptr; ScopedFPDFDocument fpdf_doc; bool is_linearized = false; if ((is_linearized = FPDFAvail_IsLinearized(fileReader->fpdf_avail_.get())) == PDF_LINEARIZED) { fpdf_doc.reset(FPDFAvail_GetDocument(fileReader->fpdf_avail_.get(), password)); } else { fpdf_doc.reset(FPDF_LoadCustomDocument(fileReader.get(), password)); } FPDF_BOOL should_scale_for_print = FPDF_VIEWERREF_GetPrintScaling(fpdf_doc.get()); if (fpdf_doc) { // Use WrapUnique instead of MakeUnique since this Document constructor is // private. *result = std::unique_ptr( new Document(std::move(fpdf_doc), (password && password[0] != '\0'), std::move(fileReader), is_linearized, should_scale_for_print)); return LOADED; } if (!closeFdOnFailure) { fileReader->ReleaseFd(); } if (requestedHeaderSize) { *requestedHeaderSize = fileReader->RequestedHeaderSize(); } if (requestedFooterSize) { *requestedFooterSize = fileReader->RequestedFooterSize(); } // Error - failed to load document. int error = FPDF_GetLastError(); if (error == FPDF_ERR_PASSWORD) { return REQUIRES_PASSWORD; } else { LOGE("Parse Document failed (err=%d).\n", error); return PDF_ERROR; } } Document::~Document() { // Allow pages to do any internal cleanup before deletion. for (const auto& entry : pages_) { entry.second->TerminateFormFilling(); } } bool Document::SaveAs(LinuxFileOps::FDCloser fd) { FileWriter fw(std::move(fd)); constexpr int flags = 0; if (!FPDF_SaveAsCopy(document_.get(), &fw, flags)) { LOGW("Failed to save-as to fd %d.", fw.Fd()); return false; } size_t destSize = lseek(fw.Fd(), 0, SEEK_END); LOGV("Save-as to fd %d [%zd bytes], flags=%d.", fw.Fd(), destSize, flags); return true; } std::shared_ptr Document::GetPage(int pageNum, bool retain) { if (pages_.find(pageNum) != pages_.end()) { return pages_.at(pageNum); } IsPageAvailable(pageNum); auto page = std::make_shared(document_.get(), pageNum, &form_filler_); if (retain) { page->InitializeFormFilling(); pages_.try_emplace(pageNum, page); fpdf_page_index_lookup_.try_emplace(page->page(), pageNum); } return page; } void Document::NotifyInvalidRect(FPDF_PAGE page, Rectangle_i rect) { // invalid rects are only relevant to pages that are being retained // since pages save them until a caller asks for them if (fpdf_page_index_lookup_.find(page) != fpdf_page_index_lookup_.end()) { int retained_page_index = fpdf_page_index_lookup_.at(page); pages_.at(retained_page_index)->NotifyInvalidRect(rect); } } void Document::ReleaseRetainedPage(int pageNum) { if (pages_.find(pageNum) != pages_.end()) { std::shared_ptr page = pages_.at(pageNum); page->TerminateFormFilling(); pages_.erase(pageNum); fpdf_page_index_lookup_.erase(page->page()); } } bool Document::IsPageAvailable(int pageNum) const { // This call should be made before attempting to render or otherwise access // the given page, even if the results are ignored if (file_reader_) { return FPDFAvail_IsPageAvail(file_reader_->fpdf_avail_.get(), pageNum, file_reader_.get()) != 0; } return true; } bool Document::CloneDocumentWithoutSecurity(LinuxFileOps::FDCloser fd) { if (file_reader_ && !IsPasswordProtected()) { // Document has no security - just clone the raw file. return CloneRawFile(file_reader_->Fd(), fd.Release()); } else { // Document has security or we couldn't just copy the file. Use SaveAsCopy: return SaveAsCopyWithoutSecurity(std::move(fd)); } } bool Document::CloneRawFile(int source, int dest) { lseek(source, 0, SEEK_SET); char buf[4096]; size_t bytesRead; while ((bytesRead = read(source, buf, 4096)) > 0) { write(dest, buf, bytesRead); } size_t sourceSize = lseek(source, 0, SEEK_END); size_t destSize = lseek(dest, 0, SEEK_END); bool success = (destSize == sourceSize); if (success) { LOGV("Copied raw file to fd %d [%zd bytes].", dest, destSize); } else { LOGV("Failed to copy raw file to fd %d (wrote %zd out of %zd).", dest, destSize, sourceSize); } // We own the FD and have to make sure to close it. LinuxFileOps::CloseFD(dest); return success; } bool Document::SaveAsCopyWithoutSecurity(LinuxFileOps::FDCloser dest) { FileWriter fw(std::move(dest)); int flags = IsPasswordProtected() ? FPDF_REMOVE_SECURITY : 0; bool success = FPDF_SaveAsCopy(document_.get(), &fw, flags); size_t destSize = lseek(fw.Fd(), 0, SEEK_END); if (success) { LOGV("Save-as to fd %d [%zd bytes], flags=%d.", fw.Fd(), destSize, flags); } else { LOGV("Failed to save-as to fd %d, flags=%d.", fw.Fd(), flags); } // No need to close the FD as lower level code already does that. return success; } } // namespace pdfClient