You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.5 KiB
212 lines
6.5 KiB
// Copyright 2014 PDFium Authors. All rights reserved.
|
|
// 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 <memory>
|
|
#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 "fpdfsdk/cpdfsdk_filewriteadapter.h"
|
|
#include "fpdfsdk/cpdfsdk_helpers.h"
|
|
#include "public/fpdf_edit.h"
|
|
#include "third_party/base/optional.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
|
|
|
|
#if defined(OS_ANDROID)
|
|
#include <time.h>
|
|
#else
|
|
#include <ctime>
|
|
#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;
|
|
|
|
CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
|
|
if (!pRoot)
|
|
return false;
|
|
|
|
CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
|
|
if (!pAcroForm)
|
|
return false;
|
|
|
|
CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
|
|
if (!pXFA)
|
|
return true;
|
|
|
|
CPDF_Array* pArray = pXFA->AsArray();
|
|
if (!pArray)
|
|
return false;
|
|
|
|
int size = pArray->size();
|
|
int iFormIndex = -1;
|
|
int iDataSetsIndex = -1;
|
|
for (int i = 0; i < size - 1; i++) {
|
|
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;
|
|
}
|
|
|
|
CPDF_Stream* pFormStream = nullptr;
|
|
if (iFormIndex != -1) {
|
|
// Get form CPDF_Stream
|
|
CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
|
|
if (pFormPDFObj->IsReference()) {
|
|
CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
|
|
if (pFormDirectObj && pFormDirectObj->IsStream()) {
|
|
pFormStream = pFormDirectObj->AsStream();
|
|
}
|
|
} else if (pFormPDFObj->IsStream()) {
|
|
pFormStream = pFormPDFObj->AsStream();
|
|
}
|
|
}
|
|
|
|
CPDF_Stream* pDataSetsStream = nullptr;
|
|
if (iDataSetsIndex != -1) {
|
|
// Get datasets CPDF_Stream
|
|
CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
|
|
if (pDataSetsPDFObj->IsReference()) {
|
|
CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsReference();
|
|
CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
|
|
if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
|
|
pDataSetsStream = pDataSetsDirectObj->AsStream();
|
|
}
|
|
} else if (pDataSetsPDFObj->IsStream()) {
|
|
pDataSetsStream = pDataSetsPDFObj->AsStream();
|
|
}
|
|
}
|
|
// L"datasets"
|
|
{
|
|
RetainPtr<IFX_SeekableStream> pFileWrite =
|
|
pdfium::MakeRetain<CFX_MemoryStream>();
|
|
if (pContext->SaveDatasetsPackage(pFileWrite) &&
|
|
pFileWrite->GetSize() > 0) {
|
|
auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
|
|
if (iDataSetsIndex != -1) {
|
|
if (pDataSetsStream) {
|
|
pDataSetsStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
|
|
}
|
|
} else {
|
|
CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
|
|
pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
|
|
int iLast = pArray->size() - 2;
|
|
pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
|
|
pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
|
|
pData->GetObjNum());
|
|
}
|
|
fileList->push_back(std::move(pFileWrite));
|
|
}
|
|
}
|
|
// L"form"
|
|
{
|
|
RetainPtr<IFX_SeekableStream> pFileWrite =
|
|
pdfium::MakeRetain<CFX_MemoryStream>();
|
|
if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
|
|
auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
|
|
if (iFormIndex != -1) {
|
|
if (pFormStream)
|
|
pFormStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
|
|
} else {
|
|
CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
|
|
pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
|
|
int iLast = pArray->size() - 2;
|
|
pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
|
|
pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
|
|
pData->GetObjNum());
|
|
}
|
|
fileList->push_back(std::move(pFileWrite));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif // PDF_ENABLE_XFA
|
|
|
|
bool DoDocSave(FPDF_DOCUMENT document,
|
|
FPDF_FILEWRITE* pFileWrite,
|
|
FPDF_DWORD flags,
|
|
Optional<int> version) {
|
|
CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
|
|
if (!pPDFDoc)
|
|
return 0;
|
|
|
|
#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(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);
|
|
}
|