1 // Copyright 2014 PDFium Authors. All rights reserved.
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 <memory>
10 #include <utility>
11 #include <vector>
12
13 #include "core/fpdfapi/edit/cpdf_creator.h"
14 #include "core/fpdfapi/parser/cpdf_array.h"
15 #include "core/fpdfapi/parser/cpdf_document.h"
16 #include "core/fpdfapi/parser/cpdf_reference.h"
17 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
18 #include "core/fpdfapi/parser/cpdf_string.h"
19 #include "core/fxcrt/cfx_memorystream.h"
20 #include "core/fxcrt/fx_extension.h"
21 #include "fpdfsdk/fsdk_define.h"
22 #include "fpdfsdk/fsdk_filewriteadapter.h"
23 #include "public/fpdf_edit.h"
24
25 #ifdef PDF_ENABLE_XFA
26 #include "core/fxcrt/cfx_checksumcontext.h"
27 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
28 #include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
29 #include "public/fpdf_formfill.h"
30 #include "xfa/fxfa/cxfa_eventparam.h"
31 #include "xfa/fxfa/cxfa_ffapp.h"
32 #include "xfa/fxfa/cxfa_ffdocview.h"
33 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
34 #include "xfa/fxfa/cxfa_widgetacciterator.h"
35 #include "xfa/fxfa/parser/cxfa_object.h"
36 #endif
37
38 #if _FX_OS_ == _FX_OS_ANDROID_
39 #include <time.h>
40 #else
41 #include <ctime>
42 #endif
43
44 namespace {
45
46 #ifdef PDF_ENABLE_XFA
SaveXFADocumentData(CPDFXFA_Context * pContext,std::vector<RetainPtr<IFX_SeekableStream>> * fileList)47 bool SaveXFADocumentData(CPDFXFA_Context* pContext,
48 std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
49 if (!pContext)
50 return false;
51
52 if (!pContext->ContainsXFAForm())
53 return true;
54
55 CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
56 if (!pXFADocView)
57 return true;
58
59 CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
60 if (!pPDFDocument)
61 return false;
62
63 const CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
64 if (!pRoot)
65 return false;
66
67 CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
68 if (!pAcroForm)
69 return false;
70
71 CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
72 if (!pXFA)
73 return true;
74
75 CPDF_Array* pArray = pXFA->AsArray();
76 if (!pArray)
77 return false;
78
79 int size = pArray->GetCount();
80 int iFormIndex = -1;
81 int iDataSetsIndex = -1;
82 int iTemplate = -1;
83 int iLast = size - 2;
84 for (int i = 0; i < size - 1; i++) {
85 CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
86 if (!pPDFObj->IsString())
87 continue;
88 if (pPDFObj->GetString() == "form")
89 iFormIndex = i + 1;
90 else if (pPDFObj->GetString() == "datasets")
91 iDataSetsIndex = i + 1;
92 else if (pPDFObj->GetString() == "template")
93 iTemplate = i + 1;
94 }
95 auto pChecksum = pdfium::MakeUnique<CFX_ChecksumContext>();
96 pChecksum->StartChecksum();
97
98 // template
99 if (iTemplate > -1) {
100 CPDF_Stream* pTemplateStream = pArray->GetStreamAt(iTemplate);
101 auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pTemplateStream);
102 pAcc->LoadAllDataFiltered();
103 RetainPtr<IFX_SeekableStream> pTemplate =
104 pdfium::MakeRetain<CFX_MemoryStream>(
105 const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false);
106 pChecksum->UpdateChecksum(pTemplate);
107 }
108 CPDF_Stream* pFormStream = nullptr;
109 CPDF_Stream* pDataSetsStream = nullptr;
110 if (iFormIndex != -1) {
111 // Get form CPDF_Stream
112 CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
113 if (pFormPDFObj->IsReference()) {
114 CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
115 if (pFormDirectObj && pFormDirectObj->IsStream()) {
116 pFormStream = (CPDF_Stream*)pFormDirectObj;
117 }
118 } else if (pFormPDFObj->IsStream()) {
119 pFormStream = (CPDF_Stream*)pFormPDFObj;
120 }
121 }
122
123 if (iDataSetsIndex != -1) {
124 // Get datasets CPDF_Stream
125 CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
126 if (pDataSetsPDFObj->IsReference()) {
127 CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj;
128 CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
129 if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
130 pDataSetsStream = (CPDF_Stream*)pDataSetsDirectObj;
131 }
132 } else if (pDataSetsPDFObj->IsStream()) {
133 pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj;
134 }
135 }
136 // L"datasets"
137 {
138 RetainPtr<IFX_SeekableStream> pDsfileWrite =
139 pdfium::MakeRetain<CFX_MemoryStream>(false);
140 CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
141 if (ffdoc->SavePackage(
142 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
143 pDsfileWrite, nullptr) &&
144 pDsfileWrite->GetSize() > 0) {
145 // Datasets
146 pChecksum->UpdateChecksum(pDsfileWrite);
147 pChecksum->FinishChecksum();
148 auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
149 pPDFDocument->GetByteStringPool());
150 if (iDataSetsIndex != -1) {
151 if (pDataSetsStream) {
152 pDataSetsStream->InitStreamFromFile(pDsfileWrite,
153 std::move(pDataDict));
154 }
155 } else {
156 CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
157 pData->InitStreamFromFile(pDsfileWrite, std::move(pDataDict));
158 iLast = pArray->GetCount() - 2;
159 pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
160 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
161 pData->GetObjNum());
162 }
163 fileList->push_back(std::move(pDsfileWrite));
164 }
165 }
166 // L"form"
167 {
168 RetainPtr<IFX_SeekableStream> pfileWrite =
169 pdfium::MakeRetain<CFX_MemoryStream>(false);
170
171 CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
172 if (ffdoc->SavePackage(
173 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
174 pfileWrite, pChecksum.get()) &&
175 pfileWrite->GetSize() > 0) {
176 auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
177 pPDFDocument->GetByteStringPool());
178 if (iFormIndex != -1) {
179 if (pFormStream)
180 pFormStream->InitStreamFromFile(pfileWrite, std::move(pDataDict));
181 } else {
182 CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
183 pData->InitStreamFromFile(pfileWrite, std::move(pDataDict));
184 iLast = pArray->GetCount() - 2;
185 pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
186 pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
187 pData->GetObjNum());
188 }
189 fileList->push_back(std::move(pfileWrite));
190 }
191 }
192 return true;
193 }
194
SendPostSaveToXFADoc(CPDFXFA_Context * pContext)195 bool SendPostSaveToXFADoc(CPDFXFA_Context* pContext) {
196 if (!pContext)
197 return false;
198
199 if (!pContext->ContainsXFAForm())
200 return true;
201
202 CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
203 if (!pXFADocView)
204 return false;
205
206 CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
207 std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
208 pXFADocView->CreateWidgetAccIterator();
209 while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
210 CXFA_EventParam preParam;
211 preParam.m_eType = XFA_EVENT_PostSave;
212 pWidgetHandler->ProcessEvent(pWidgetAcc, &preParam);
213 }
214 pXFADocView->UpdateDocView();
215 pContext->ClearChangeMark();
216 return true;
217 }
218
SendPreSaveToXFADoc(CPDFXFA_Context * pContext,std::vector<RetainPtr<IFX_SeekableStream>> * fileList)219 bool SendPreSaveToXFADoc(CPDFXFA_Context* pContext,
220 std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
221 if (!pContext->ContainsXFAForm())
222 return true;
223
224 CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
225 if (!pXFADocView)
226 return true;
227
228 CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
229 std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
230 pXFADocView->CreateWidgetAccIterator();
231 while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
232 CXFA_EventParam preParam;
233 preParam.m_eType = XFA_EVENT_PreSave;
234 pWidgetHandler->ProcessEvent(pWidgetAcc, &preParam);
235 }
236 pXFADocView->UpdateDocView();
237 return SaveXFADocumentData(pContext, fileList);
238 }
239 #endif // PDF_ENABLE_XFA
240
FPDF_Doc_Save(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,FPDF_BOOL bSetVersion,int fileVerion)241 bool FPDF_Doc_Save(FPDF_DOCUMENT document,
242 FPDF_FILEWRITE* pFileWrite,
243 FPDF_DWORD flags,
244 FPDF_BOOL bSetVersion,
245 int fileVerion) {
246 CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
247 if (!pPDFDoc)
248 return 0;
249
250 #ifdef PDF_ENABLE_XFA
251 CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
252 std::vector<RetainPtr<IFX_SeekableStream>> fileList;
253 SendPreSaveToXFADoc(pContext, &fileList);
254 #endif // PDF_ENABLE_XFA
255
256 if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
257 flags = 0;
258
259 CPDF_Creator fileMaker(pPDFDoc,
260 pdfium::MakeRetain<FSDK_FileWriteAdapter>(pFileWrite));
261 if (bSetVersion)
262 fileMaker.SetFileVersion(fileVerion);
263 if (flags == FPDF_REMOVE_SECURITY) {
264 flags = 0;
265 fileMaker.RemoveSecurity();
266 }
267
268 bool bRet = fileMaker.Create(flags);
269 #ifdef PDF_ENABLE_XFA
270 SendPostSaveToXFADoc(pContext);
271 #endif // PDF_ENABLE_XFA
272 return bRet;
273 }
274
275 } // namespace
276
FPDF_SaveAsCopy(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags)277 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
278 FPDF_FILEWRITE* pFileWrite,
279 FPDF_DWORD flags) {
280 return FPDF_Doc_Save(document, pFileWrite, flags, false, 0);
281 }
282
283 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,int fileVersion)284 FPDF_SaveWithVersion(FPDF_DOCUMENT document,
285 FPDF_FILEWRITE* pFileWrite,
286 FPDF_DWORD flags,
287 int fileVersion) {
288 return FPDF_Doc_Save(document, pFileWrite, flags, true, fileVersion);
289 }
290