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