Merge to XFA: Remove more cruft from fx_system.h
[pdfium.git] / fpdfsdk / src / fpdfsave.cpp
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_edit.h"
8 #include "../../public/fpdf_formfill.h"
9 #include "../../public/fpdf_save.h"
10 #include "../include/fsdk_define.h"
11 #include "../include/fpdfxfa/fpdfxfa_doc.h"
12 #include "../include/fpdfxfa/fpdfxfa_app.h"
13 #include "../include/fpdfxfa/fpdfxfa_util.h"
14
15 #if _FX_OS_ == _FX_ANDROID_
16 #include "time.h"
17 #else
18 #include <ctime>
19 #endif
20
21 class CFX_IFileWrite final : public IFX_StreamWrite
22 {
23         
24 public:
25         CFX_IFileWrite();
26         FX_BOOL                         Init( FPDF_FILEWRITE * pFileWriteStruct );
27         virtual FX_BOOL         WriteBlock(const void* pData, size_t size) override;
28         virtual void            Release() override {}
29         
30 protected:
31         FPDF_FILEWRITE*         m_pFileWriteStruct;
32 };
33
34 CFX_IFileWrite::CFX_IFileWrite()
35 {
36         m_pFileWriteStruct = NULL;
37 }
38
39 FX_BOOL CFX_IFileWrite::Init( FPDF_FILEWRITE * pFileWriteStruct )
40 {
41         if (!pFileWriteStruct)
42                 return FALSE;
43         else
44         {
45                 m_pFileWriteStruct = pFileWriteStruct;
46         }
47         return TRUE;
48 }
49
50 FX_BOOL CFX_IFileWrite::WriteBlock(const void* pData, size_t size)
51 {
52         if (m_pFileWriteStruct)
53         {
54                 m_pFileWriteStruct->WriteBlock( m_pFileWriteStruct, pData, size );
55                 return TRUE;
56         }
57         else 
58                 return FALSE;
59 }
60
61 #define  XFA_DATASETS 0
62 #define  XFA_FORMS    1
63
64 FX_BOOL _SaveXFADocumentData(CPDFXFA_Document* pDocument, CFX_PtrArray& fileList)
65 {
66         if (!pDocument)
67                 return FALSE;
68         if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA)
69                 return TRUE;
70         if (!CPDFXFA_App::GetInstance()->GetXFAApp())
71                 return TRUE;
72
73         IXFA_DocView* pXFADocView = pDocument->GetXFADocView();
74         if (NULL == pXFADocView)
75                 return TRUE;
76
77         IXFA_DocHandler *pXFADocHandler = CPDFXFA_App::GetInstance()->GetXFAApp()->GetDocHandler();
78         CPDF_Document * pPDFDocument = pDocument->GetPDFDoc();
79         if (pDocument == NULL)
80                 return FALSE;
81
82         CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
83         if (pRoot == NULL)
84                 return FALSE;
85         CPDF_Dictionary* pAcroForm = pRoot->GetDict("AcroForm");
86         if (NULL == pAcroForm)
87                 return FALSE;
88         CPDF_Object* pXFA = pAcroForm->GetElement("XFA");
89         if (pXFA == NULL) 
90                 return TRUE;
91         if(pXFA->GetType() != PDFOBJ_ARRAY)
92                 return FALSE;
93         CPDF_Array* pArray = pXFA->GetArray();
94         if (NULL == pArray)
95                 return FALSE;
96         int size = pArray->GetCount();
97         int iFormIndex = -1;
98         int iDataSetsIndex = -1;
99         int iTemplate = -1;
100         int iLast = size - 2;
101         for (int i = 0; i < size - 1; i++)
102         {
103                 CPDF_Object* pPDFObj = pArray->GetElement(i);
104                 if (pPDFObj->GetType() != PDFOBJ_STRING)
105                         continue;
106                 if (pPDFObj->GetString() == "form")
107                         iFormIndex = i+1;
108                 else if (pPDFObj->GetString() == "datasets")
109                         iDataSetsIndex = i+1;
110                 else if (pPDFObj->GetString() == FX_BSTRC("template"))
111                         iTemplate = i + 1;
112         }
113         IXFA_ChecksumContext* pContext = NULL;
114 #define XFA_USECKSUM
115 #ifdef XFA_USECKSUM
116         //Checksum
117         pContext = XFA_Checksum_Create();
118         FXSYS_assert(pContext);
119         pContext->StartChecksum();
120                 
121         //template
122         if (iTemplate > -1)
123         {
124                 CPDF_Stream *pTemplateStream = pArray->GetStream(iTemplate);
125                 CPDF_StreamAcc streamAcc;
126                 streamAcc.LoadAllData(pTemplateStream);
127                 FX_LPBYTE pData = (FX_LPBYTE)streamAcc.GetData();
128                 FX_DWORD dwSize2 = streamAcc.GetSize();
129                 IFX_FileStream *pTemplate = FX_CreateMemoryStream(pData, dwSize2);
130                 pContext->UpdateChecksum((IFX_FileRead*)pTemplate);
131                 pTemplate->Release();
132         }
133 #endif
134         CPDF_Stream* pFormStream = NULL;
135         CPDF_Stream* pDataSetsStream = NULL;
136         if (iFormIndex != -1)
137         {       
138                 //Get form CPDF_Stream
139                 CPDF_Object* pFormPDFObj = pArray->GetElement(iFormIndex);
140                 if (pFormPDFObj->GetType() == PDFOBJ_REFERENCE)
141                 {                        
142                         CPDF_Reference* pFormRefObj = (CPDF_Reference*)pFormPDFObj;
143                         CPDF_Object* pFormDircetObj = pFormPDFObj->GetDirect();
144                         if (NULL != pFormDircetObj && pFormDircetObj->GetType() == PDFOBJ_STREAM)
145                         {
146                                 pFormStream = (CPDF_Stream*)pFormDircetObj;
147                         }
148                 }
149                 else if (pFormPDFObj->GetType() == PDFOBJ_STREAM)
150                 {
151                         pFormStream = (CPDF_Stream*)pFormPDFObj;
152                 }
153         }
154         
155         if (iDataSetsIndex != -1)
156         {
157                 //Get datasets CPDF_Stream
158                 CPDF_Object* pDataSetsPDFObj = pArray->GetElement(iDataSetsIndex);
159                 if (pDataSetsPDFObj->GetType() == PDFOBJ_REFERENCE)
160                 {                        
161                         CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj;
162                         CPDF_Object* pDataSetsDircetObj = pDataSetsRefObj->GetDirect();
163                         if (NULL != pDataSetsDircetObj && pDataSetsDircetObj->GetType() == PDFOBJ_STREAM)
164                         {
165                                 pDataSetsStream = (CPDF_Stream*)pDataSetsDircetObj;
166                         }
167                 }
168                 else if (pDataSetsPDFObj->GetType() == PDFOBJ_STREAM)
169                 {
170                         pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj;
171                 }
172         } 
173         //end
174         //L"datasets"
175         {
176                 IFX_FileStream* pDsfileWrite = FX_CreateMemoryStream();
177                 if ( NULL == pDsfileWrite )
178                 {
179                         pContext->Release();
180                         pDsfileWrite->Release();
181                         return FALSE;
182                 }
183                 if (pXFADocHandler->SavePackage(pXFADocView->GetDoc(), CFX_WideStringC(L"datasets"), pDsfileWrite) && pDsfileWrite->GetSize()>0)
184                 {
185 #ifdef XFA_USECKSUM
186                 //Datasets
187                 pContext->UpdateChecksum((IFX_FileRead*)pDsfileWrite);
188                 pContext->FinishChecksum();
189 #endif
190                         CPDF_Dictionary* pDataDict = FX_NEW CPDF_Dictionary;
191                         if (iDataSetsIndex != -1)
192                         {
193                                 if (pDataSetsStream)
194                                         pDataSetsStream->InitStream(pDsfileWrite, pDataDict);
195                         }
196                         else
197                         {
198                                 CPDF_Stream* pData = FX_NEW CPDF_Stream(NULL, 0, NULL);
199                                 pData->InitStream(pDsfileWrite, pDataDict);
200                                 FX_DWORD AppStreamobjnum = pPDFDocument->AddIndirectObject(pData);
201                                 CPDF_Reference* pRef = (CPDF_Reference*)pPDFDocument->GetIndirectObject(AppStreamobjnum);
202                                 {
203                                         iLast = pArray->GetCount() -2;
204                                         pArray->InsertAt(iLast,CPDF_String::Create("datasets"));
205                                         pArray->InsertAt(iLast+1, pData, pPDFDocument);
206                                 }
207                         }
208                         fileList.Add(pDsfileWrite);
209                 }
210         }
211  
212
213         //L"form"
214         {
215         
216                 IFX_FileStream* pfileWrite = FX_CreateMemoryStream();
217                 if (NULL == pfileWrite)
218                 {
219                         pContext->Release();
220                         return FALSE;
221                 }
222                 if(pXFADocHandler->SavePackage(pXFADocView->GetDoc(), CFX_WideStringC(L"form"), pfileWrite, pContext) && pfileWrite > 0)
223                 {
224                         CPDF_Dictionary* pDataDict = FX_NEW CPDF_Dictionary;
225                         if (iFormIndex != -1)
226                         {
227                                 if (pFormStream)
228                                         pFormStream->InitStream(pfileWrite, pDataDict);
229                         }
230                         else
231                         {
232                                 CPDF_Stream* pData = FX_NEW CPDF_Stream(NULL, 0, NULL);
233                                 pData->InitStream(pfileWrite, pDataDict);
234                                 FX_DWORD AppStreamobjnum = pPDFDocument->AddIndirectObject(pData);
235                                 CPDF_Reference* pRef = (CPDF_Reference*)pPDFDocument->GetIndirectObject(AppStreamobjnum);
236                                 {
237                                         iLast = pArray->GetCount() -2;
238                                         pArray->InsertAt(iLast, CPDF_String::Create("form"));
239                                         pArray->InsertAt(iLast+1, pData, pPDFDocument);
240                                 }
241                         }
242                         fileList.Add(pfileWrite);
243                 }
244         }
245         pContext->Release();
246         return TRUE;
247 }
248
249
250 FX_BOOL _SendPostSaveToXFADoc(CPDFXFA_Document* pDocument)
251 {
252         if (!pDocument)
253                 return FALSE;
254
255         if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA)
256                 return TRUE;
257         
258         IXFA_DocView* pXFADocView = pDocument->GetXFADocView();
259         if (NULL == pXFADocView)
260                 return FALSE;
261         IXFA_WidgetHandler* pWidgetHander =  pXFADocView->GetWidgetHandler();
262
263         CXFA_WidgetAcc* pWidgetAcc = NULL;
264         IXFA_WidgetAccIterator* pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator();
265         pWidgetAcc = pWidgetAccIterator->MoveToNext();
266         while(pWidgetAcc)
267         {
268                 CXFA_EventParam preParam;
269                 preParam.m_eType =  XFA_EVENT_PostSave; 
270                 pWidgetHander->ProcessEvent(pWidgetAcc,&preParam);
271                 pWidgetAcc = pWidgetAccIterator->MoveToNext();
272         }
273         pWidgetAccIterator->Release();
274         pXFADocView->UpdateDocView();
275         pDocument->_ClearChangeMark();
276         return TRUE;
277 }
278
279
280 FX_BOOL _SendPreSaveToXFADoc(CPDFXFA_Document* pDocument, CFX_PtrArray& fileList)
281 {
282         if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA)
283                 return TRUE;
284         IXFA_DocView* pXFADocView = pDocument->GetXFADocView();
285         if (NULL == pXFADocView)
286                 return TRUE;
287         IXFA_WidgetHandler* pWidgetHander =  pXFADocView->GetWidgetHandler();
288         CXFA_WidgetAcc* pWidgetAcc = NULL;
289         IXFA_WidgetAccIterator* pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator();
290         pWidgetAcc = pWidgetAccIterator->MoveToNext();
291         while(pWidgetAcc)
292         {
293                 CXFA_EventParam preParam;
294                 preParam.m_eType =  XFA_EVENT_PreSave;  
295                 pWidgetHander->ProcessEvent(pWidgetAcc, &preParam);
296                 pWidgetAcc = pWidgetAccIterator->MoveToNext();
297         }
298         pWidgetAccIterator->Release();  
299         pXFADocView->UpdateDocView();
300         return _SaveXFADocumentData(pDocument, fileList);
301 }
302
303 FPDF_BOOL _FPDF_Doc_Save(FPDF_DOCUMENT document, FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags, FPDF_BOOL bSetVersion,
304                                                  int fileVerion)
305 {
306         CPDFXFA_Document* pDoc = (CPDFXFA_Document*)document;
307
308         CFX_PtrArray fileList;
309
310         _SendPreSaveToXFADoc(pDoc, fileList);
311
312         CPDF_Document* pPDFDoc = pDoc->GetPDFDoc();
313         if (!pPDFDoc) 
314                 return 0;
315         
316         if ( flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY )
317         {
318                 flags = 0;
319         }
320         
321         CPDF_Creator FileMaker(pPDFDoc);
322         if (bSetVersion)
323                 FileMaker.SetFileVersion(fileVerion);
324         if (flags == FPDF_REMOVE_SECURITY)
325         {
326                 flags =  0;
327                 FileMaker.RemoveSecurity();
328         }
329         CFX_IFileWrite* pStreamWrite = NULL;
330         FX_BOOL bRet;
331         pStreamWrite = new CFX_IFileWrite;
332         pStreamWrite->Init( pFileWrite );
333         bRet = FileMaker.Create(pStreamWrite, flags);
334
335         _SendPostSaveToXFADoc(pDoc);
336         //pDoc->_ClearChangeMark();
337
338         for (int i = 0; i < fileList.GetSize(); i++)
339         {
340                 IFX_FileStream* pFile = (IFX_FileStream*)fileList.GetAt(i);
341                 pFile->Release();
342         }
343         fileList.RemoveAll();
344
345         delete pStreamWrite;
346         return bRet;
347 }
348
349 DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveAsCopy(    FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,
350                                                                                                 FPDF_DWORD flags )
351 {
352         return _FPDF_Doc_Save(document, pFileWrite, flags, FALSE , 0);
353 }
354
355 DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveWithVersion(       FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,
356         FPDF_DWORD flags, int fileVersion)
357 {
358         return _FPDF_Doc_Save(document, pFileWrite, flags, TRUE , fileVersion);
359 }
360