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 <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "core/fpdfapi/edit/cpdf_creator.h"
14 #include "core/fpdfapi/parser/cpdf_array.h"
15 #include "core/fpdfapi/parser/cpdf_document.h"
16 #include "core/fpdfapi/parser/cpdf_reference.h"
17 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
18 #include "core/fpdfapi/parser/cpdf_string.h"
19 #include "core/fxcrt/fx_ext.h"
20 #include "fpdfsdk/fsdk_define.h"
21 #include "public/fpdf_edit.h"
22 
23 #ifdef PDF_ENABLE_XFA
24 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
25 #include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
26 #include "public/fpdf_formfill.h"
27 #include "xfa/fxfa/cxfa_eventparam.h"
28 #include "xfa/fxfa/xfa_checksum.h"
29 #include "xfa/fxfa/xfa_ffapp.h"
30 #include "xfa/fxfa/xfa_ffdocview.h"
31 #include "xfa/fxfa/xfa_ffwidgethandler.h"
32 #endif
33 
34 #if _FX_OS_ == _FX_ANDROID_
35 #include <time.h>
36 #else
37 #include <ctime>
38 #endif
39 
40 class CFX_IFileWrite final : public IFX_WriteStream {
41  public:
42   static CFX_RetainPtr<CFX_IFileWrite> Create();
43   bool Init(FPDF_FILEWRITE* pFileWriteStruct);
44   bool WriteBlock(const void* pData, size_t size) override;
45 
46  protected:
47   CFX_IFileWrite();
~CFX_IFileWrite()48   ~CFX_IFileWrite() override {}
49 
50   FPDF_FILEWRITE* m_pFileWriteStruct;
51 };
52 
Create()53 CFX_RetainPtr<CFX_IFileWrite> CFX_IFileWrite::Create() {
54   return CFX_RetainPtr<CFX_IFileWrite>(new CFX_IFileWrite());
55 }
56 
CFX_IFileWrite()57 CFX_IFileWrite::CFX_IFileWrite() : m_pFileWriteStruct(nullptr) {}
58 
Init(FPDF_FILEWRITE * pFileWriteStruct)59 bool CFX_IFileWrite::Init(FPDF_FILEWRITE* pFileWriteStruct) {
60   if (!pFileWriteStruct)
61     return false;
62 
63   m_pFileWriteStruct = pFileWriteStruct;
64   return true;
65 }
66 
WriteBlock(const void * pData,size_t size)67 bool CFX_IFileWrite::WriteBlock(const void* pData, size_t size) {
68   if (!m_pFileWriteStruct)
69     return false;
70 
71   m_pFileWriteStruct->WriteBlock(m_pFileWriteStruct, pData, size);
72   return true;
73 }
74 
75 namespace {
76 
77 #ifdef PDF_ENABLE_XFA
SaveXFADocumentData(CPDFXFA_Context * pContext,std::vector<CFX_RetainPtr<IFX_SeekableStream>> * fileList)78 bool SaveXFADocumentData(
79     CPDFXFA_Context* pContext,
80     std::vector<CFX_RetainPtr<IFX_SeekableStream>>* fileList) {
81   if (!pContext)
82     return false;
83 
84   if (pContext->GetDocType() != DOCTYPE_DYNAMIC_XFA &&
85       pContext->GetDocType() != DOCTYPE_STATIC_XFA)
86     return true;
87 
88   CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
89   if (!pXFADocView)
90     return true;
91 
92   CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
93   if (!pPDFDocument)
94     return false;
95 
96   CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
97   if (!pRoot)
98     return false;
99 
100   CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
101   if (!pAcroForm)
102     return false;
103 
104   CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
105   if (!pXFA)
106     return true;
107 
108   CPDF_Array* pArray = pXFA->AsArray();
109   if (!pArray)
110     return false;
111 
112   int size = pArray->GetCount();
113   int iFormIndex = -1;
114   int iDataSetsIndex = -1;
115   int iTemplate = -1;
116   int iLast = size - 2;
117   for (int i = 0; i < size - 1; i++) {
118     CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
119     if (!pPDFObj->IsString())
120       continue;
121     if (pPDFObj->GetString() == "form")
122       iFormIndex = i + 1;
123     else if (pPDFObj->GetString() == "datasets")
124       iDataSetsIndex = i + 1;
125     else if (pPDFObj->GetString() == "template")
126       iTemplate = i + 1;
127   }
128   std::unique_ptr<CXFA_ChecksumContext> pChecksum(new CXFA_ChecksumContext);
129   pChecksum->StartChecksum();
130 
131   // template
132   if (iTemplate > -1) {
133     CPDF_Stream* pTemplateStream = pArray->GetStreamAt(iTemplate);
134     CPDF_StreamAcc streamAcc;
135     streamAcc.LoadAllData(pTemplateStream);
136     uint8_t* pData = (uint8_t*)streamAcc.GetData();
137     uint32_t dwSize2 = streamAcc.GetSize();
138     CFX_RetainPtr<IFX_SeekableStream> pTemplate =
139         IFX_MemoryStream::Create(pData, dwSize2);
140     pChecksum->UpdateChecksum(pTemplate);
141   }
142   CPDF_Stream* pFormStream = nullptr;
143   CPDF_Stream* pDataSetsStream = nullptr;
144   if (iFormIndex != -1) {
145     // Get form CPDF_Stream
146     CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
147     if (pFormPDFObj->IsReference()) {
148       CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
149       if (pFormDirectObj && pFormDirectObj->IsStream()) {
150         pFormStream = (CPDF_Stream*)pFormDirectObj;
151       }
152     } else if (pFormPDFObj->IsStream()) {
153       pFormStream = (CPDF_Stream*)pFormPDFObj;
154     }
155   }
156 
157   if (iDataSetsIndex != -1) {
158     // Get datasets CPDF_Stream
159     CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
160     if (pDataSetsPDFObj->IsReference()) {
161       CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj;
162       CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
163       if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
164         pDataSetsStream = (CPDF_Stream*)pDataSetsDirectObj;
165       }
166     } else if (pDataSetsPDFObj->IsStream()) {
167       pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj;
168     }
169   }
170   // L"datasets"
171   {
172     CFX_RetainPtr<IFX_SeekableStream> pDsfileWrite = IFX_MemoryStream::Create();
173     if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Datasets, pDsfileWrite,
174                                            nullptr) &&
175         pDsfileWrite->GetSize() > 0) {
176       // Datasets
177       pChecksum->UpdateChecksum(pDsfileWrite);
178       pChecksum->FinishChecksum();
179       auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
180           pPDFDocument->GetByteStringPool());
181       if (iDataSetsIndex != -1) {
182         if (pDataSetsStream) {
183           pDataSetsStream->InitStreamFromFile(pDsfileWrite,
184                                               std::move(pDataDict));
185         }
186       } else {
187         CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
188         pData->InitStreamFromFile(pDsfileWrite, std::move(pDataDict));
189         iLast = pArray->GetCount() - 2;
190         pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
191         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
192                                             pData->GetObjNum());
193       }
194       fileList->push_back(std::move(pDsfileWrite));
195     }
196   }
197   // L"form"
198   {
199     CFX_RetainPtr<IFX_SeekableStream> pfileWrite = IFX_MemoryStream::Create();
200     if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Form, pfileWrite,
201                                            pChecksum.get()) &&
202         pfileWrite->GetSize() > 0) {
203       auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
204           pPDFDocument->GetByteStringPool());
205       if (iFormIndex != -1) {
206         if (pFormStream)
207           pFormStream->InitStreamFromFile(pfileWrite, std::move(pDataDict));
208       } else {
209         CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
210         pData->InitStreamFromFile(pfileWrite, std::move(pDataDict));
211         iLast = pArray->GetCount() - 2;
212         pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
213         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
214                                             pData->GetObjNum());
215       }
216       fileList->push_back(std::move(pfileWrite));
217     }
218   }
219   return true;
220 }
221 
SendPostSaveToXFADoc(CPDFXFA_Context * pContext)222 bool SendPostSaveToXFADoc(CPDFXFA_Context* pContext) {
223   if (!pContext)
224     return false;
225 
226   if (pContext->GetDocType() != DOCTYPE_DYNAMIC_XFA &&
227       pContext->GetDocType() != DOCTYPE_STATIC_XFA)
228     return true;
229 
230   CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
231   if (!pXFADocView)
232     return false;
233 
234   CXFA_FFWidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler();
235   std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator(
236       pXFADocView->CreateWidgetAccIterator());
237   while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
238     CXFA_EventParam preParam;
239     preParam.m_eType = XFA_EVENT_PostSave;
240     pWidgetHander->ProcessEvent(pWidgetAcc, &preParam);
241   }
242   pXFADocView->UpdateDocView();
243   pContext->ClearChangeMark();
244   return true;
245 }
246 
SendPreSaveToXFADoc(CPDFXFA_Context * pContext,std::vector<CFX_RetainPtr<IFX_SeekableStream>> * fileList)247 bool SendPreSaveToXFADoc(
248     CPDFXFA_Context* pContext,
249     std::vector<CFX_RetainPtr<IFX_SeekableStream>>* fileList) {
250   if (pContext->GetDocType() != DOCTYPE_DYNAMIC_XFA &&
251       pContext->GetDocType() != DOCTYPE_STATIC_XFA)
252     return true;
253 
254   CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
255   if (!pXFADocView)
256     return true;
257 
258   CXFA_FFWidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler();
259   std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator(
260       pXFADocView->CreateWidgetAccIterator());
261   while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
262     CXFA_EventParam preParam;
263     preParam.m_eType = XFA_EVENT_PreSave;
264     pWidgetHander->ProcessEvent(pWidgetAcc, &preParam);
265   }
266   pXFADocView->UpdateDocView();
267   return SaveXFADocumentData(pContext, fileList);
268 }
269 #endif  // PDF_ENABLE_XFA
270 
FPDF_Doc_Save(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,FPDF_BOOL bSetVersion,int fileVerion)271 bool FPDF_Doc_Save(FPDF_DOCUMENT document,
272                    FPDF_FILEWRITE* pFileWrite,
273                    FPDF_DWORD flags,
274                    FPDF_BOOL bSetVersion,
275                    int fileVerion) {
276   CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
277   if (!pPDFDoc)
278     return 0;
279 
280 #ifdef PDF_ENABLE_XFA
281   CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
282   std::vector<CFX_RetainPtr<IFX_SeekableStream>> fileList;
283   SendPreSaveToXFADoc(pContext, &fileList);
284 #endif  // PDF_ENABLE_XFA
285 
286   if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
287     flags = 0;
288 
289   CPDF_Creator FileMaker(pPDFDoc);
290   if (bSetVersion)
291     FileMaker.SetFileVersion(fileVerion);
292   if (flags == FPDF_REMOVE_SECURITY) {
293     flags = 0;
294     FileMaker.RemoveSecurity();
295   }
296 
297   CFX_RetainPtr<CFX_IFileWrite> pStreamWrite = CFX_IFileWrite::Create();
298   pStreamWrite->Init(pFileWrite);
299   bool bRet = FileMaker.Create(pStreamWrite, flags);
300 #ifdef PDF_ENABLE_XFA
301   SendPostSaveToXFADoc(pContext);
302 #endif  // PDF_ENABLE_XFA
303   return bRet;
304 }
305 
306 }  // namespace
307 
FPDF_SaveAsCopy(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags)308 DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveAsCopy(FPDF_DOCUMENT document,
309                                             FPDF_FILEWRITE* pFileWrite,
310                                             FPDF_DWORD flags) {
311   return FPDF_Doc_Save(document, pFileWrite, flags, false, 0);
312 }
313 
FPDF_SaveWithVersion(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,int fileVersion)314 DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveWithVersion(FPDF_DOCUMENT document,
315                                                  FPDF_FILEWRITE* pFileWrite,
316                                                  FPDF_DWORD flags,
317                                                  int fileVersion) {
318   return FPDF_Doc_Save(document, pFileWrite, flags, true, fileVersion);
319 }
320