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