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