blob: 334ec6eead7b645068850238df3ef02a26822a95 [file] [log] [blame]
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "public/fpdf_ppo.h"
#include <memory>
#include <numeric>
#include <utility>
#include <vector>
#include "core/fpdfapi/edit/cpdf_npagetooneexporter.h"
#include "core/fpdfapi/edit/cpdf_pageexporter.h"
#include "core/fpdfapi/page/cpdf_form.h"
#include "core/fpdfapi/page/cpdf_formobject.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/page/cpdf_pageimagecache.h"
#include "core/fpdfapi/page/cpdf_pageobject.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_object.h"
#include "core/fpdfapi/parser/fpdf_parser_utility.h"
#include "core/fxcrt/check.h"
#include "core/fxcrt/retain_ptr.h"
#include "core/fxcrt/span.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "public/cpp/fpdf_scopers.h"
namespace {
std::vector<uint32_t> GetPageIndices(const CPDF_Document& doc,
const ByteString& page_range) {
uint32_t count = doc.GetPageCount();
if (!page_range.IsEmpty()) {
return ParsePageRangeString(page_range, count);
}
std::vector<uint32_t> page_indices(count);
std::iota(page_indices.begin(), page_indices.end(), 0);
return page_indices;
}
// Make sure arrays only contain objects of basic types.
bool IsValidViewerPreferencesArray(const CPDF_Array* array) {
CPDF_ArrayLocker locker(array);
for (const auto& obj : locker) {
if (obj->IsArray() || obj->IsDictionary() || obj->IsReference() ||
obj->IsStream()) {
return false;
}
}
return true;
}
bool IsValidViewerPreferencesObject(const CPDF_Object* obj) {
// Per spec, there are no valid entries of these types.
if (obj->IsDictionary() || obj->IsNull() || obj->IsReference() ||
obj->IsStream()) {
return false;
}
const CPDF_Array* array = obj->AsArray();
if (!array) {
return true;
}
return IsValidViewerPreferencesArray(array);
}
} // namespace
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
const int* page_indices,
unsigned long length,
int index) {
CPDF_Document* cdest_doc = CPDFDocumentFromFPDFDocument(dest_doc);
if (!cdest_doc) {
return false;
}
CPDF_Document* csrc_doc = CPDFDocumentFromFPDFDocument(src_doc);
if (!csrc_doc) {
return false;
}
CPDF_PageExporter exporter(cdest_doc, csrc_doc);
if (!page_indices) {
std::vector<uint32_t> page_indices_vec(csrc_doc->GetPageCount());
std::iota(page_indices_vec.begin(), page_indices_vec.end(), 0);
return exporter.ExportPages(page_indices_vec, index);
}
if (length == 0) {
return false;
}
auto page_span = UNSAFE_TODO(pdfium::make_span(
reinterpret_cast<const uint32_t*>(page_indices), length));
return exporter.ExportPages(page_span, index);
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
FPDF_BYTESTRING pagerange,
int index) {
CPDF_Document* cdest_doc = CPDFDocumentFromFPDFDocument(dest_doc);
if (!cdest_doc) {
return false;
}
CPDF_Document* csrc_doc = CPDFDocumentFromFPDFDocument(src_doc);
if (!csrc_doc) {
return false;
}
std::vector<uint32_t> page_indices = GetPageIndices(*csrc_doc, pagerange);
if (page_indices.empty())
return false;
CPDF_PageExporter exporter(cdest_doc, csrc_doc);
return exporter.ExportPages(page_indices, index);
}
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc,
float output_width,
float output_height,
size_t pages_on_x_axis,
size_t pages_on_y_axis) {
CPDF_Document* csrc_doc = CPDFDocumentFromFPDFDocument(src_doc);
if (!csrc_doc) {
return nullptr;
}
if (output_width <= 0 || output_height <= 0 || pages_on_x_axis <= 0 ||
pages_on_y_axis <= 0) {
return nullptr;
}
ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
if (!output_doc)
return nullptr;
CPDF_Document* dest_doc = CPDFDocumentFromFPDFDocument(output_doc.get());
DCHECK(dest_doc);
std::vector<uint32_t> page_indices = GetPageIndices(*csrc_doc, ByteString());
if (page_indices.empty())
return nullptr;
if (pages_on_x_axis == 1 && pages_on_y_axis == 1) {
CPDF_PageExporter exporter(dest_doc, csrc_doc);
if (!exporter.ExportPages(page_indices, 0)) {
return nullptr;
}
return output_doc.release();
}
CPDF_NPageToOneExporter exporter(dest_doc, csrc_doc);
if (!exporter.ExportNPagesToOne(page_indices,
CFX_SizeF(output_width, output_height),
pages_on_x_axis, pages_on_y_axis)) {
return nullptr;
}
return output_doc.release();
}
FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV
FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc,
FPDF_DOCUMENT src_doc,
int src_page_index) {
CPDF_Document* dest = CPDFDocumentFromFPDFDocument(dest_doc);
if (!dest)
return nullptr;
CPDF_Document* src = CPDFDocumentFromFPDFDocument(src_doc);
if (!src)
return nullptr;
CPDF_NPageToOneExporter exporter(dest, src);
std::unique_ptr<XObjectContext> xobject =
exporter.CreateXObjectContextFromPage(src_page_index);
return FPDFXObjectFromXObjectContext(xobject.release());
}
FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject) {
std::unique_ptr<XObjectContext> xobject_deleter(
XObjectContextFromFPDFXObject(xobject));
}
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject) {
XObjectContext* xobj = XObjectContextFromFPDFXObject(xobject);
if (!xobj)
return nullptr;
auto form = std::make_unique<CPDF_Form>(xobj->dest_doc, nullptr,
xobj->xobject, nullptr);
form->ParseContent(nullptr, nullptr, nullptr);
auto form_object = std::make_unique<CPDF_FormObject>(
CPDF_PageObject::kNoContentStream, std::move(form), CFX_Matrix());
return FPDFPageObjectFromCPDFPageObject(form_object.release());
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) {
CPDF_Document* cdest_doc = CPDFDocumentFromFPDFDocument(dest_doc);
if (!cdest_doc) {
return false;
}
CPDF_Document* csrc_doc = CPDFDocumentFromFPDFDocument(src_doc);
if (!csrc_doc) {
return false;
}
RetainPtr<const CPDF_Dictionary> pref_dict =
csrc_doc->GetRoot()->GetDictFor("ViewerPreferences");
if (!pref_dict) {
return false;
}
RetainPtr<CPDF_Dictionary> dest_dict = cdest_doc->GetMutableRoot();
if (!dest_dict) {
return false;
}
auto cloned_dict = pdfium::MakeRetain<CPDF_Dictionary>();
CPDF_DictionaryLocker locker(pref_dict);
for (const auto& it : locker) {
if (IsValidViewerPreferencesObject(it.second)) {
cloned_dict->SetFor(it.first, it.second->Clone());
}
}
dest_dict->SetFor("ViewerPreferences", std::move(cloned_dict));
return true;
}