1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "public/fpdf_save.h"
8
9 #include <optional>
10 #include <utility>
11 #include <vector>
12
13 #include "build/build_config.h"
14 #include "core/fpdfapi/edit/cpdf_creator.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #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"
21 #include "core/fxcrt/fx_extension.h"
22 #include "core/fxcrt/stl_util.h"
23 #include "fpdfsdk/cpdfsdk_filewriteadapter.h"
24 #include "fpdfsdk/cpdfsdk_helpers.h"
25 #include "public/fpdf_edit.h"
26
27 #ifdef PDF_ENABLE_XFA
28 #include "core/fpdfapi/parser/cpdf_stream.h"
29 #include "core/fxcrt/cfx_memorystream.h"
30 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
31 #include "public/fpdf_formfill.h"
32 #endif
33
34 namespace {
35
36 #ifdef PDF_ENABLE_XFA
SaveXFADocumentData(CPDFXFA_Context * pContext,std::vector<RetainPtr<IFX_SeekableStream>> * fileList)37 bool SaveXFADocumentData(CPDFXFA_Context* pContext,
38 std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
39 if (!pContext)
40 return false;
41
42 if (!pContext->ContainsExtensionForm())
43 return true;
44
45 CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
46 if (!pPDFDocument)
47 return false;
48
49 RetainPtr<CPDF_Dictionary> pRoot = pPDFDocument->GetMutableRoot();
50 if (!pRoot)
51 return false;
52
53 RetainPtr<CPDF_Dictionary> pAcroForm = pRoot->GetMutableDictFor("AcroForm");
54 if (!pAcroForm)
55 return false;
56
57 RetainPtr<CPDF_Object> pXFA = pAcroForm->GetMutableObjectFor("XFA");
58 if (!pXFA)
59 return true;
60
61 CPDF_Array* pArray = pXFA->AsMutableArray();
62 if (!pArray)
63 return false;
64
65 int size = fxcrt::CollectionSize<int>(*pArray);
66 int iFormIndex = -1;
67 int iDataSetsIndex = -1;
68 for (int i = 0; i < size - 1; i++) {
69 RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
70 if (!pPDFObj->IsString())
71 continue;
72 if (pPDFObj->GetString() == "form")
73 iFormIndex = i + 1;
74 else if (pPDFObj->GetString() == "datasets")
75 iDataSetsIndex = i + 1;
76 }
77
78 RetainPtr<CPDF_Stream> pFormStream;
79 if (iFormIndex != -1) {
80 // Get form CPDF_Stream
81 RetainPtr<CPDF_Object> pFormPDFObj = pArray->GetMutableObjectAt(iFormIndex);
82 if (pFormPDFObj->IsReference()) {
83 RetainPtr<CPDF_Object> pFormDirectObj = pFormPDFObj->GetMutableDirect();
84 if (pFormDirectObj && pFormDirectObj->IsStream()) {
85 pFormStream.Reset(pFormDirectObj->AsMutableStream());
86 }
87 } else if (pFormPDFObj->IsStream()) {
88 pFormStream.Reset(pFormPDFObj->AsMutableStream());
89 }
90 }
91
92 RetainPtr<CPDF_Stream> pDataSetsStream;
93 if (iDataSetsIndex != -1) {
94 // Get datasets CPDF_Stream
95 RetainPtr<CPDF_Object> pDataSetsPDFObj =
96 pArray->GetMutableObjectAt(iDataSetsIndex);
97 if (pDataSetsPDFObj->IsReference()) {
98 CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsMutableReference();
99 RetainPtr<CPDF_Object> pDataSetsDirectObj =
100 pDataSetsRefObj->GetMutableDirect();
101 if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
102 pDataSetsStream.Reset(pDataSetsDirectObj->AsMutableStream());
103 }
104 } else if (pDataSetsPDFObj->IsStream()) {
105 pDataSetsStream.Reset(pDataSetsPDFObj->AsMutableStream());
106 }
107 }
108 // L"datasets"
109 {
110 RetainPtr<IFX_SeekableStream> pFileWrite =
111 pdfium::MakeRetain<CFX_MemoryStream>();
112 if (pContext->SaveDatasetsPackage(pFileWrite) &&
113 pFileWrite->GetSize() > 0) {
114 if (iDataSetsIndex != -1) {
115 if (pDataSetsStream) {
116 pDataSetsStream->InitStreamFromFile(pFileWrite);
117 }
118 } else {
119 auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
120 pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
121 int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
122 pArray->InsertNewAt<CPDF_String>(iLast, "datasets");
123 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
124 data_stream->GetObjNum());
125 }
126 fileList->push_back(std::move(pFileWrite));
127 }
128 }
129 // L"form"
130 {
131 RetainPtr<IFX_SeekableStream> pFileWrite =
132 pdfium::MakeRetain<CFX_MemoryStream>();
133 if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
134 if (iFormIndex != -1) {
135 if (pFormStream) {
136 pFormStream->InitStreamFromFile(pFileWrite);
137 }
138 } else {
139 auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
140 pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
141 int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
142 pArray->InsertNewAt<CPDF_String>(iLast, "form");
143 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
144 data_stream->GetObjNum());
145 }
146 fileList->push_back(std::move(pFileWrite));
147 }
148 }
149 return true;
150 }
151 #endif // PDF_ENABLE_XFA
152
DoDocSave(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,std::optional<int> version)153 bool DoDocSave(FPDF_DOCUMENT document,
154 FPDF_FILEWRITE* pFileWrite,
155 FPDF_DWORD flags,
156 std::optional<int> version) {
157 CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
158 if (!pPDFDoc)
159 return false;
160
161 #ifdef PDF_ENABLE_XFA
162 auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
163 if (pContext) {
164 std::vector<RetainPtr<IFX_SeekableStream>> fileList;
165 pContext->SendPreSaveToXFADoc(&fileList);
166 SaveXFADocumentData(pContext, &fileList);
167 }
168 #endif // PDF_ENABLE_XFA
169
170 if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
171 flags = 0;
172
173 CPDF_Creator fileMaker(
174 pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
175 if (version.has_value())
176 fileMaker.SetFileVersion(version.value());
177 if (flags == FPDF_REMOVE_SECURITY) {
178 flags = 0;
179 fileMaker.RemoveSecurity();
180 }
181
182 bool bRet = fileMaker.Create(static_cast<uint32_t>(flags));
183
184 #ifdef PDF_ENABLE_XFA
185 if (pContext)
186 pContext->SendPostSaveToXFADoc();
187 #endif // PDF_ENABLE_XFA
188
189 return bRet;
190 }
191
192 } // namespace
193
FPDF_SaveAsCopy(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags)194 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
195 FPDF_FILEWRITE* pFileWrite,
196 FPDF_DWORD flags) {
197 return DoDocSave(document, pFileWrite, flags, {});
198 }
199
200 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,int fileVersion)201 FPDF_SaveWithVersion(FPDF_DOCUMENT document,
202 FPDF_FILEWRITE* pFileWrite,
203 FPDF_DWORD flags,
204 int fileVersion) {
205 return DoDocSave(document, pFileWrite, flags, fileVersion);
206 }
207