blob: 6c1c44f80b7557fe182e6c1c6f57a4758f565a83 [file] [log] [blame] [edit]
// Copyright 2024 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 "core/fpdfapi/edit/cpdf_pageorganizer.h"
#include <utility>
#include <vector>
#include "constants/page_object.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_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_object.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fxcrt/bytestring.h"
#include "core/fxcrt/check.h"
CPDF_PageOrganizer::CPDF_PageOrganizer(CPDF_Document* dest_doc,
CPDF_Document* src_doc)
: dest_doc_(dest_doc), src_doc_(src_doc) {}
CPDF_PageOrganizer::~CPDF_PageOrganizer() = default;
bool CPDF_PageOrganizer::Init() {
DCHECK(dest_doc_);
DCHECK(src_doc_);
return InitDestDoc();
}
bool CPDF_PageOrganizer::InitDestDoc() {
RetainPtr<CPDF_Dictionary> root = dest()->GetMutableRoot();
if (!root) {
return false;
}
RetainPtr<CPDF_Dictionary> info = dest()->GetInfo();
if (info) {
info->SetNewFor<CPDF_String>("Producer", "PDFium");
}
if (root->GetByteStringFor("Type", ByteString()).IsEmpty()) {
root->SetNewFor<CPDF_Name>("Type", "Catalog");
}
RetainPtr<CPDF_Dictionary> pages;
if (RetainPtr<CPDF_Object> current_pages = root->GetMutableObjectFor("Pages");
current_pages) {
pages = ToDictionary(current_pages->GetMutableDirect());
}
if (!pages) {
pages = dest()->NewIndirect<CPDF_Dictionary>();
root->SetNewFor<CPDF_Reference>("Pages", dest(), pages->GetObjNum());
}
if (pages->GetByteStringFor("Type", ByteString()).IsEmpty()) {
pages->SetNewFor<CPDF_Name>("Type", "Pages");
}
if (!pages->GetArrayFor("Kids")) {
auto kids_array = dest()->NewIndirect<CPDF_Array>();
pages->SetNewFor<CPDF_Number>("Count", 0);
pages->SetNewFor<CPDF_Reference>("Kids", dest(), kids_array->GetObjNum());
}
return true;
}
bool CPDF_PageOrganizer::UpdateReference(RetainPtr<CPDF_Object> obj) {
switch (obj->GetType()) {
case CPDF_Object::kReference: {
CPDF_Reference* reference = obj->AsMutableReference();
uint32_t newobjnum = GetNewObjId(reference);
if (newobjnum == 0) {
return false;
}
reference->SetRef(dest(), newobjnum);
return true;
}
case CPDF_Object::kDictionary: {
CPDF_Dictionary* dict = obj->AsMutableDictionary();
std::vector<ByteString> bad_keys;
{
CPDF_DictionaryLocker locker(dict);
for (const auto& it : locker) {
const ByteString& key = it.first;
if (key == "Parent" || key == "Prev" || key == "First") {
continue;
}
RetainPtr<CPDF_Object> next_obj = it.second;
if (!UpdateReference(next_obj)) {
bad_keys.push_back(key);
}
}
}
for (const auto& key : bad_keys) {
dict->RemoveFor(key.AsStringView());
}
return true;
}
case CPDF_Object::kArray: {
CPDF_Array* array = obj->AsMutableArray();
for (size_t i = 0; i < array->size(); ++i) {
if (!UpdateReference(array->GetMutableObjectAt(i))) {
return false;
}
}
return true;
}
case CPDF_Object::kStream: {
return UpdateReference(obj->AsMutableStream()->GetMutableDict());
}
default:
return true;
}
}
uint32_t CPDF_PageOrganizer::GetNewObjId(CPDF_Reference* ref) {
if (!ref) {
return 0;
}
uint32_t obj_num = ref->GetRefObjNum();
uint32_t new_obj_num = 0;
const auto it = object_number_map_.find(obj_num);
if (it != object_number_map_.end()) {
new_obj_num = it->second;
}
if (new_obj_num) {
return new_obj_num;
}
RetainPtr<const CPDF_Object> direct = ref->GetDirect();
if (!direct) {
return 0;
}
RetainPtr<CPDF_Object> clone = direct->Clone();
const CPDF_Dictionary* dict_clone = clone->AsDictionary();
if (dict_clone && dict_clone->KeyExist("Type")) {
ByteString type = dict_clone->GetByteStringFor("Type");
if (type.EqualNoCase("Pages")) {
return 4;
}
if (type.EqualNoCase("Page")) {
return 0;
}
}
new_obj_num = dest()->AddIndirectObject(clone);
AddObjectMapping(obj_num, new_obj_num);
if (!UpdateReference(std::move(clone))) {
return 0;
}
return new_obj_num;
}
// static
bool CPDF_PageOrganizer::CopyInheritable(
RetainPtr<CPDF_Dictionary> dest_page_dict,
RetainPtr<const CPDF_Dictionary> src_page_dict,
const ByteString& key) {
if (dest_page_dict->KeyExist(key)) {
return true;
}
RetainPtr<const CPDF_Object> inheritable =
PageDictGetInheritableTag(std::move(src_page_dict), key);
if (!inheritable) {
return false;
}
dest_page_dict->SetFor(key, inheritable->Clone());
return true;
}
// static
RetainPtr<const CPDF_Object> CPDF_PageOrganizer::PageDictGetInheritableTag(
RetainPtr<const CPDF_Dictionary> dict,
const ByteString& src_tag) {
if (!dict || src_tag.IsEmpty()) {
return nullptr;
}
if (!dict->KeyExist(pdfium::page_object::kParent) ||
!dict->KeyExist(pdfium::page_object::kType)) {
return nullptr;
}
RetainPtr<const CPDF_Name> name =
ToName(dict->GetObjectFor(pdfium::page_object::kType)->GetDirect());
if (!name || name->GetString() != "Page") {
return nullptr;
}
RetainPtr<const CPDF_Dictionary> pp = ToDictionary(
dict->GetObjectFor(pdfium::page_object::kParent)->GetDirect());
if (!pp) {
return nullptr;
}
if (dict->KeyExist(src_tag)) {
return dict->GetObjectFor(src_tag);
}
while (pp) {
if (pp->KeyExist(src_tag)) {
return pp->GetObjectFor(src_tag);
}
if (!pp->KeyExist(pdfium::page_object::kParent)) {
break;
}
pp = ToDictionary(
pp->GetObjectFor(pdfium::page_object::kParent)->GetDirect());
}
return nullptr;
}