blob: 8dee66897451ba7a2956df16a97176d0760d642b [file] [log] [blame]
K. Moon832a6942022-10-31 20:11:31 +00001// Copyright 2014 The PDFium Authors
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Lei Zhanga6d9f0e2015-06-13 00:48:38 -07004
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07005// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
Lei Zhangb4e7f302015-11-06 15:52:32 -08007#include "public/fpdf_save.h"
8
Lei Zhang24c6be62024-02-08 20:06:48 +00009#include <optional>
Dan Sinclair85c8e7f2016-11-21 13:50:32 -050010#include <utility>
Dan Sinclair3ebd1212016-03-09 09:59:23 -050011#include <vector>
12
Lei Zhangdd77c862019-03-29 18:57:15 +000013#include "build/build_config.h"
dsinclair24154352016-10-04 11:01:48 -070014#include "core/fpdfapi/edit/cpdf_creator.h"
dsinclair488b7ad2016-10-04 11:55:50 -070015#include "core/fpdfapi/parser/cpdf_array.h"
Lei Zhang81535612018-10-09 21:15:17 +000016#include "core/fpdfapi/parser/cpdf_dictionary.h"
dsinclair488b7ad2016-10-04 11:55:50 -070017#include "core/fpdfapi/parser/cpdf_document.h"
18#include "core/fpdfapi/parser/cpdf_reference.h"
19#include "core/fpdfapi/parser/cpdf_stream_acc.h"
20#include "core/fpdfapi/parser/cpdf_string.h"
Dan Sinclaircfb19442017-04-20 13:13:04 -040021#include "core/fxcrt/fx_extension.h"
Tom Sepez5ea5fa92022-03-08 00:15:23 +000022#include "core/fxcrt/stl_util.h"
Dan Sinclair7d125322018-03-28 18:49:34 +000023#include "fpdfsdk/cpdfsdk_filewriteadapter.h"
Dan Sinclair00d47a62018-03-28 18:39:04 +000024#include "fpdfsdk/cpdfsdk_helpers.h"
Tom Sepez40e9ff32015-11-30 12:39:54 -080025#include "public/fpdf_edit.h"
26
Tom Sepez51da0932015-11-25 16:05:49 -080027#ifdef PDF_ENABLE_XFA
Lei Zhanged975f92019-02-20 18:49:01 +000028#include "core/fpdfapi/parser/cpdf_stream.h"
Lei Zhange7998852019-01-24 20:20:26 +000029#include "core/fxcrt/cfx_memorystream.h"
dsinclair521b7502016-11-02 13:02:28 -070030#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
Lei Zhangb4e7f302015-11-06 15:52:32 -080031#include "public/fpdf_formfill.h"
Tom Sepez51da0932015-11-25 16:05:49 -080032#endif
Tom Sepez1ed8a212015-05-11 15:25:39 -070033
Tom Sepez7a73eff2016-02-08 13:39:53 -080034namespace {
35
Tom Sepez51da0932015-11-25 16:05:49 -080036#ifdef PDF_ENABLE_XFA
Dan Sinclair0b950422017-09-21 15:49:49 -040037bool SaveXFADocumentData(CPDFXFA_Context* pContext,
38 std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
dsinclair521b7502016-11-02 13:02:28 -070039 if (!pContext)
Tom Sepez7a73eff2016-02-08 13:39:53 -080040 return false;
41
Tom Sepezd48bd292019-08-14 19:48:55 +000042 if (!pContext->ContainsExtensionForm())
Tom Sepez7a73eff2016-02-08 13:39:53 -080043 return true;
44
dsinclair521b7502016-11-02 13:02:28 -070045 CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
46 if (!pPDFDocument)
Tom Sepez7a73eff2016-02-08 13:39:53 -080047 return false;
Bo Xufdc00a72014-10-28 23:03:33 -070048
Tom Sepezaa51f002022-06-25 00:46:38 +000049 RetainPtr<CPDF_Dictionary> pRoot = pPDFDocument->GetMutableRoot();
Tom Sepez7a73eff2016-02-08 13:39:53 -080050 if (!pRoot)
51 return false;
52
Tom Sepez3d64afa2022-06-24 16:40:17 +000053 RetainPtr<CPDF_Dictionary> pAcroForm = pRoot->GetMutableDictFor("AcroForm");
Tom Sepez7a73eff2016-02-08 13:39:53 -080054 if (!pAcroForm)
55 return false;
56
Tom Sepez50ba51a2022-06-27 21:56:50 +000057 RetainPtr<CPDF_Object> pXFA = pAcroForm->GetMutableObjectFor("XFA");
Tom Sepez7a73eff2016-02-08 13:39:53 -080058 if (!pXFA)
59 return true;
60
Tom Sepezd6daaed2022-09-02 23:58:32 +000061 CPDF_Array* pArray = pXFA->AsMutableArray();
Tom Sepez7a73eff2016-02-08 13:39:53 -080062 if (!pArray)
63 return false;
64
Tom Sepez5ea5fa92022-03-08 00:15:23 +000065 int size = fxcrt::CollectionSize<int>(*pArray);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070066 int iFormIndex = -1;
67 int iDataSetsIndex = -1;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070068 for (int i = 0; i < size - 1; i++) {
Tom Sepezc14b89a2022-09-06 23:41:52 +000069 RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
Tom Sepez8e5cd192016-01-26 13:20:26 -080070 if (!pPDFObj->IsString())
Nico Weber9d8ec5a2015-08-04 13:00:21 -070071 continue;
72 if (pPDFObj->GetString() == "form")
73 iFormIndex = i + 1;
74 else if (pPDFObj->GetString() == "datasets")
75 iDataSetsIndex = i + 1;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070076 }
Lei Zhanga6d9f0e2015-06-13 00:48:38 -070077
Tom Sepezcbbbed32022-06-24 20:59:08 +000078 RetainPtr<CPDF_Stream> pFormStream;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070079 if (iFormIndex != -1) {
80 // Get form CPDF_Stream
Tom Sepezcbbbed32022-06-24 20:59:08 +000081 RetainPtr<CPDF_Object> pFormPDFObj = pArray->GetMutableObjectAt(iFormIndex);
Tom Sepez8e5cd192016-01-26 13:20:26 -080082 if (pFormPDFObj->IsReference()) {
Tom Sepez96fe6af2022-06-29 17:33:41 +000083 RetainPtr<CPDF_Object> pFormDirectObj = pFormPDFObj->GetMutableDirect();
Tom Sepez8e5cd192016-01-26 13:20:26 -080084 if (pFormDirectObj && pFormDirectObj->IsStream()) {
Tom Sepez7037a482022-09-20 22:10:06 +000085 pFormStream.Reset(pFormDirectObj->AsMutableStream());
Nico Weber9d8ec5a2015-08-04 13:00:21 -070086 }
Tom Sepez8e5cd192016-01-26 13:20:26 -080087 } else if (pFormPDFObj->IsStream()) {
Tom Sepez7037a482022-09-20 22:10:06 +000088 pFormStream.Reset(pFormPDFObj->AsMutableStream());
Tom Sepez2f2ffec2015-07-23 14:42:09 -070089 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -070090 }
Lei Zhanga6d9f0e2015-06-13 00:48:38 -070091
Tom Sepezcbbbed32022-06-24 20:59:08 +000092 RetainPtr<CPDF_Stream> pDataSetsStream;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070093 if (iDataSetsIndex != -1) {
94 // Get datasets CPDF_Stream
Tom Sepezcbbbed32022-06-24 20:59:08 +000095 RetainPtr<CPDF_Object> pDataSetsPDFObj =
96 pArray->GetMutableObjectAt(iDataSetsIndex);
Tom Sepez8e5cd192016-01-26 13:20:26 -080097 if (pDataSetsPDFObj->IsReference()) {
Tom Sepezd6daaed2022-09-02 23:58:32 +000098 CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsMutableReference();
Tom Sepez96fe6af2022-06-29 17:33:41 +000099 RetainPtr<CPDF_Object> pDataSetsDirectObj =
100 pDataSetsRefObj->GetMutableDirect();
Tom Sepez8e5cd192016-01-26 13:20:26 -0800101 if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
Tom Sepez7037a482022-09-20 22:10:06 +0000102 pDataSetsStream.Reset(pDataSetsDirectObj->AsMutableStream());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700103 }
Tom Sepez8e5cd192016-01-26 13:20:26 -0800104 } else if (pDataSetsPDFObj->IsStream()) {
Tom Sepez7037a482022-09-20 22:10:06 +0000105 pDataSetsStream.Reset(pDataSetsPDFObj->AsMutableStream());
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700106 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700107 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700108 // L"datasets"
109 {
Tom Sepez88357f12019-12-20 17:16:24 +0000110 RetainPtr<IFX_SeekableStream> pFileWrite =
Lei Zhang48ae3072018-08-15 18:42:32 +0000111 pdfium::MakeRetain<CFX_MemoryStream>();
Tom Sepez88357f12019-12-20 17:16:24 +0000112 if (pContext->SaveDatasetsPackage(pFileWrite) &&
113 pFileWrite->GetSize() > 0) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700114 if (iDataSetsIndex != -1) {
tsepez9e05ee12016-11-21 13:19:10 -0800115 if (pDataSetsStream) {
Lei Zhang88d3d562024-01-23 00:06:53 +0000116 pDataSetsStream->InitStreamFromFile(pFileWrite);
tsepez9e05ee12016-11-21 13:19:10 -0800117 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700118 } else {
Lei Zhangbfc71b92024-01-22 21:53:44 +0000119 auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
120 pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
Tom Sepez5ea5fa92022-03-08 00:15:23 +0000121 int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
Lei Zhang7d870c72024-05-21 16:44:46 +0000122 pArray->InsertNewAt<CPDF_String>(iLast, "datasets");
Tom Sepez8964d102019-05-06 17:57:59 +0000123 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
Lei Zhangbfc71b92024-01-22 21:53:44 +0000124 data_stream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700125 }
Tom Sepez88357f12019-12-20 17:16:24 +0000126 fileList->push_back(std::move(pFileWrite));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700127 }
128 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700129 // L"form"
130 {
Tom Sepez88357f12019-12-20 17:16:24 +0000131 RetainPtr<IFX_SeekableStream> pFileWrite =
Lei Zhang48ae3072018-08-15 18:42:32 +0000132 pdfium::MakeRetain<CFX_MemoryStream>();
Tom Sepez88357f12019-12-20 17:16:24 +0000133 if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700134 if (iFormIndex != -1) {
Lei Zhangbfc71b92024-01-22 21:53:44 +0000135 if (pFormStream) {
Lei Zhang88d3d562024-01-23 00:06:53 +0000136 pFormStream->InitStreamFromFile(pFileWrite);
Lei Zhangbfc71b92024-01-22 21:53:44 +0000137 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700138 } else {
Lei Zhangbfc71b92024-01-22 21:53:44 +0000139 auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
140 pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
Tom Sepez5ea5fa92022-03-08 00:15:23 +0000141 int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
Lei Zhang7d870c72024-05-21 16:44:46 +0000142 pArray->InsertNewAt<CPDF_String>(iLast, "form");
Tom Sepez8964d102019-05-06 17:57:59 +0000143 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
Lei Zhangbfc71b92024-01-22 21:53:44 +0000144 data_stream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700145 }
Tom Sepez88357f12019-12-20 17:16:24 +0000146 fileList->push_back(std::move(pFileWrite));
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700147 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700148 }
Tom Sepez7a73eff2016-02-08 13:39:53 -0800149 return true;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700150}
Tom Sepez40e9ff32015-11-30 12:39:54 -0800151#endif // PDF_ENABLE_XFA
Bo Xufdc00a72014-10-28 23:03:33 -0700152
Lei Zhangb739b4a2018-09-20 16:22:12 +0000153bool DoDocSave(FPDF_DOCUMENT document,
154 FPDF_FILEWRITE* pFileWrite,
155 FPDF_DWORD flags,
Lei Zhang24c6be62024-02-08 20:06:48 +0000156 std::optional<int> version) {
Tom Sepez1b246282015-11-25 15:15:31 -0800157 CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700158 if (!pPDFDoc)
Anton Bikineev7ac13342022-01-24 21:25:15 +0000159 return false;
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700160
Tom Sepez51da0932015-11-25 16:05:49 -0800161#ifdef PDF_ENABLE_XFA
Tom Sepez3f3c39d2018-05-01 17:46:34 +0000162 auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
163 if (pContext) {
164 std::vector<RetainPtr<IFX_SeekableStream>> fileList;
Tom Sepez31fc7602019-08-12 17:39:21 +0000165 pContext->SendPreSaveToXFADoc(&fileList);
166 SaveXFADocumentData(pContext, &fileList);
Tom Sepez3f3c39d2018-05-01 17:46:34 +0000167 }
Tom Sepez40e9ff32015-11-30 12:39:54 -0800168#endif // PDF_ENABLE_XFA
Tom Sepez1b246282015-11-25 15:15:31 -0800169
Tom Sepez7a73eff2016-02-08 13:39:53 -0800170 if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700171 flags = 0;
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700172
Dan Sinclair7d125322018-03-28 18:49:34 +0000173 CPDF_Creator fileMaker(
174 pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
Lei Zhangb739b4a2018-09-20 16:22:12 +0000175 if (version.has_value())
176 fileMaker.SetFileVersion(version.value());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700177 if (flags == FPDF_REMOVE_SECURITY) {
178 flags = 0;
Dan Sinclairae4656e2017-05-09 12:36:41 -0400179 fileMaker.RemoveSecurity();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700180 }
Tom Sepez1b246282015-11-25 15:15:31 -0800181
Tom Sepez5ea5fa92022-03-08 00:15:23 +0000182 bool bRet = fileMaker.Create(static_cast<uint32_t>(flags));
Tom Sepez3f3c39d2018-05-01 17:46:34 +0000183
Tom Sepez51da0932015-11-25 16:05:49 -0800184#ifdef PDF_ENABLE_XFA
Tom Sepez31fc7602019-08-12 17:39:21 +0000185 if (pContext)
186 pContext->SendPostSaveToXFADoc();
Tom Sepez40e9ff32015-11-30 12:39:54 -0800187#endif // PDF_ENABLE_XFA
Tom Sepez3f3c39d2018-05-01 17:46:34 +0000188
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700189 return bRet;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700190}
191
Tom Sepez7a73eff2016-02-08 13:39:53 -0800192} // namespace
193
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400194FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
195 FPDF_FILEWRITE* pFileWrite,
196 FPDF_DWORD flags) {
Lei Zhangb739b4a2018-09-20 16:22:12 +0000197 return DoDocSave(document, pFileWrite, flags, {});
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700198}
199
Dan Sinclair00d2ad12017-08-10 14:13:02 -0400200FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
201FPDF_SaveWithVersion(FPDF_DOCUMENT document,
202 FPDF_FILEWRITE* pFileWrite,
203 FPDF_DWORD flags,
204 int fileVersion) {
Lei Zhangb739b4a2018-09-20 16:22:12 +0000205 return DoDocSave(document, pFileWrite, flags, fileVersion);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700206}