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