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 "build/build_config.h"
14 #include "core/fpdfapi/edit/cpdf_creator.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_reference.h"
19 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
20 #include "core/fpdfapi/parser/cpdf_string.h"
21 #include "core/fxcrt/fx_extension.h"
22 #include "fpdfsdk/cpdfsdk_filewriteadapter.h"
23 #include "fpdfsdk/cpdfsdk_helpers.h"
24 #include "public/fpdf_edit.h"
25 #include "third_party/base/optional.h"
26 
27 #ifdef PDF_ENABLE_XFA
28 #include "core/fpdfapi/parser/cpdf_stream.h"
29 #include "core/fxcrt/cfx_memorystream.h"
30 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
31 #include "public/fpdf_formfill.h"
32 #endif
33 
34 #if defined(OS_ANDROID)
35 #include <time.h>
36 #else
37 #include <ctime>
38 #endif
39 
40 namespace {
41 
42 #ifdef PDF_ENABLE_XFA
SaveXFADocumentData(CPDFXFA_Context * pContext,std::vector<RetainPtr<IFX_SeekableStream>> * fileList)43 bool SaveXFADocumentData(CPDFXFA_Context* pContext,
44                          std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
45   if (!pContext)
46     return false;
47 
48   if (!pContext->ContainsExtensionForm())
49     return true;
50 
51   CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
52   if (!pPDFDocument)
53     return false;
54 
55   CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
56   if (!pRoot)
57     return false;
58 
59   CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
60   if (!pAcroForm)
61     return false;
62 
63   CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
64   if (!pXFA)
65     return true;
66 
67   CPDF_Array* pArray = pXFA->AsArray();
68   if (!pArray)
69     return false;
70 
71   int size = pArray->size();
72   int iFormIndex = -1;
73   int iDataSetsIndex = -1;
74   for (int i = 0; i < size - 1; i++) {
75     const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
76     if (!pPDFObj->IsString())
77       continue;
78     if (pPDFObj->GetString() == "form")
79       iFormIndex = i + 1;
80     else if (pPDFObj->GetString() == "datasets")
81       iDataSetsIndex = i + 1;
82   }
83 
84   CPDF_Stream* pFormStream = nullptr;
85   if (iFormIndex != -1) {
86     // Get form CPDF_Stream
87     CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
88     if (pFormPDFObj->IsReference()) {
89       CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
90       if (pFormDirectObj && pFormDirectObj->IsStream()) {
91         pFormStream = pFormDirectObj->AsStream();
92       }
93     } else if (pFormPDFObj->IsStream()) {
94       pFormStream = pFormPDFObj->AsStream();
95     }
96   }
97 
98   CPDF_Stream* pDataSetsStream = nullptr;
99   if (iDataSetsIndex != -1) {
100     // Get datasets CPDF_Stream
101     CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
102     if (pDataSetsPDFObj->IsReference()) {
103       CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsReference();
104       CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
105       if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
106         pDataSetsStream = pDataSetsDirectObj->AsStream();
107       }
108     } else if (pDataSetsPDFObj->IsStream()) {
109       pDataSetsStream = pDataSetsPDFObj->AsStream();
110     }
111   }
112   // L"datasets"
113   {
114     RetainPtr<IFX_SeekableStream> pFileWrite =
115         pdfium::MakeRetain<CFX_MemoryStream>();
116     if (pContext->SaveDatasetsPackage(pFileWrite) &&
117         pFileWrite->GetSize() > 0) {
118       auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
119       if (iDataSetsIndex != -1) {
120         if (pDataSetsStream) {
121           pDataSetsStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
122         }
123       } else {
124         CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
125         pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
126         int iLast = pArray->size() - 2;
127         pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
128         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
129                                             pData->GetObjNum());
130       }
131       fileList->push_back(std::move(pFileWrite));
132     }
133   }
134   // L"form"
135   {
136     RetainPtr<IFX_SeekableStream> pFileWrite =
137         pdfium::MakeRetain<CFX_MemoryStream>();
138     if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
139       auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
140       if (iFormIndex != -1) {
141         if (pFormStream)
142           pFormStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
143       } else {
144         CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
145         pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
146         int iLast = pArray->size() - 2;
147         pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
148         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
149                                             pData->GetObjNum());
150       }
151       fileList->push_back(std::move(pFileWrite));
152     }
153   }
154   return true;
155 }
156 #endif  // PDF_ENABLE_XFA
157 
DoDocSave(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,Optional<int> version)158 bool DoDocSave(FPDF_DOCUMENT document,
159                FPDF_FILEWRITE* pFileWrite,
160                FPDF_DWORD flags,
161                Optional<int> version) {
162   CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
163   if (!pPDFDoc)
164     return 0;
165 
166 #ifdef PDF_ENABLE_XFA
167   auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
168   if (pContext) {
169     std::vector<RetainPtr<IFX_SeekableStream>> fileList;
170     pContext->SendPreSaveToXFADoc(&fileList);
171     SaveXFADocumentData(pContext, &fileList);
172   }
173 #endif  // PDF_ENABLE_XFA
174 
175   if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
176     flags = 0;
177 
178   CPDF_Creator fileMaker(
179       pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
180   if (version.has_value())
181     fileMaker.SetFileVersion(version.value());
182   if (flags == FPDF_REMOVE_SECURITY) {
183     flags = 0;
184     fileMaker.RemoveSecurity();
185   }
186 
187   bool bRet = fileMaker.Create(flags);
188 
189 #ifdef PDF_ENABLE_XFA
190   if (pContext)
191     pContext->SendPostSaveToXFADoc();
192 #endif  // PDF_ENABLE_XFA
193 
194   return bRet;
195 }
196 
197 }  // namespace
198 
FPDF_SaveAsCopy(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags)199 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
200                                                     FPDF_FILEWRITE* pFileWrite,
201                                                     FPDF_DWORD flags) {
202   return DoDocSave(document, pFileWrite, flags, {});
203 }
204 
205 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,int fileVersion)206 FPDF_SaveWithVersion(FPDF_DOCUMENT document,
207                      FPDF_FILEWRITE* pFileWrite,
208                      FPDF_DWORD flags,
209                      int fileVersion) {
210   return DoDocSave(document, pFileWrite, flags, fileVersion);
211 }
212