blob: d3e03a127f415906c7056013bea5ebf2ba1f0efe [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_save.h"
#include <optional>
#include <utility>
#include <vector>
#include "build/build_config.h"
#include "core/fpdfapi/edit/cpdf_creator.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_reference.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/stl_util.h"
#include "fpdfsdk/cpdfsdk_filewriteadapter.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "public/fpdf_edit.h"
#ifdef PDF_ENABLE_XFA
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fxcrt/cfx_memorystream.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#include "public/fpdf_formfill.h"
#endif
namespace {
#ifdef PDF_ENABLE_XFA
bool SaveXFADocumentData(CPDFXFA_Context* pContext,
std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
if (!pContext)
return false;
if (!pContext->ContainsExtensionForm())
return true;
CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
if (!pPDFDocument)
return false;
RetainPtr<CPDF_Dictionary> pRoot = pPDFDocument->GetMutableRoot();
if (!pRoot)
return false;
RetainPtr<CPDF_Dictionary> pAcroForm = pRoot->GetMutableDictFor("AcroForm");
if (!pAcroForm)
return false;
RetainPtr<CPDF_Object> pXFA = pAcroForm->GetMutableObjectFor("XFA");
if (!pXFA)
return true;
CPDF_Array* pArray = pXFA->AsMutableArray();
if (!pArray)
return false;
int size = fxcrt::CollectionSize<int>(*pArray);
int iFormIndex = -1;
int iDataSetsIndex = -1;
for (int i = 0; i < size - 1; i++) {
RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
if (!pPDFObj->IsString())
continue;
if (pPDFObj->GetString() == "form")
iFormIndex = i + 1;
else if (pPDFObj->GetString() == "datasets")
iDataSetsIndex = i + 1;
}
RetainPtr<CPDF_Stream> pFormStream;
if (iFormIndex != -1) {
// Get form CPDF_Stream
RetainPtr<CPDF_Object> pFormPDFObj = pArray->GetMutableObjectAt(iFormIndex);
if (pFormPDFObj->IsReference()) {
RetainPtr<CPDF_Object> pFormDirectObj = pFormPDFObj->GetMutableDirect();
if (pFormDirectObj && pFormDirectObj->IsStream()) {
pFormStream.Reset(pFormDirectObj->AsMutableStream());
}
} else if (pFormPDFObj->IsStream()) {
pFormStream.Reset(pFormPDFObj->AsMutableStream());
}
}
RetainPtr<CPDF_Stream> pDataSetsStream;
if (iDataSetsIndex != -1) {
// Get datasets CPDF_Stream
RetainPtr<CPDF_Object> pDataSetsPDFObj =
pArray->GetMutableObjectAt(iDataSetsIndex);
if (pDataSetsPDFObj->IsReference()) {
CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsMutableReference();
RetainPtr<CPDF_Object> pDataSetsDirectObj =
pDataSetsRefObj->GetMutableDirect();
if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
pDataSetsStream.Reset(pDataSetsDirectObj->AsMutableStream());
}
} else if (pDataSetsPDFObj->IsStream()) {
pDataSetsStream.Reset(pDataSetsPDFObj->AsMutableStream());
}
}
// L"datasets"
{
RetainPtr<IFX_SeekableStream> pFileWrite =
pdfium::MakeRetain<CFX_MemoryStream>();
if (pContext->SaveDatasetsPackage(pFileWrite) &&
pFileWrite->GetSize() > 0) {
if (iDataSetsIndex != -1) {
if (pDataSetsStream) {
pDataSetsStream->InitStreamFromFile(pFileWrite);
}
} else {
auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
data_stream->GetObjNum());
}
fileList->push_back(std::move(pFileWrite));
}
}
// L"form"
{
RetainPtr<IFX_SeekableStream> pFileWrite =
pdfium::MakeRetain<CFX_MemoryStream>();
if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
if (iFormIndex != -1) {
if (pFormStream) {
pFormStream->InitStreamFromFile(pFileWrite);
}
} else {
auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
data_stream->GetObjNum());
}
fileList->push_back(std::move(pFileWrite));
}
}
return true;
}
#endif // PDF_ENABLE_XFA
bool DoDocSave(FPDF_DOCUMENT document,
FPDF_FILEWRITE* pFileWrite,
FPDF_DWORD flags,
std::optional<int> version) {
CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
if (!pPDFDoc)
return false;
#ifdef PDF_ENABLE_XFA
auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
if (pContext) {
std::vector<RetainPtr<IFX_SeekableStream>> fileList;
pContext->SendPreSaveToXFADoc(&fileList);
SaveXFADocumentData(pContext, &fileList);
}
#endif // PDF_ENABLE_XFA
if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
flags = 0;
CPDF_Creator fileMaker(
pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
if (version.has_value())
fileMaker.SetFileVersion(version.value());
if (flags == FPDF_REMOVE_SECURITY) {
flags = 0;
fileMaker.RemoveSecurity();
}
bool bRet = fileMaker.Create(static_cast<uint32_t>(flags));
#ifdef PDF_ENABLE_XFA
if (pContext)
pContext->SendPostSaveToXFADoc();
#endif // PDF_ENABLE_XFA
return bRet;
}
} // namespace
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
FPDF_FILEWRITE* pFileWrite,
FPDF_DWORD flags) {
return DoDocSave(document, pFileWrite, flags, {});
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,
FPDF_FILEWRITE* pFileWrite,
FPDF_DWORD flags,
int fileVersion) {
return DoDocSave(document, pFileWrite, flags, fileVersion);
}