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