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