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