• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <optional>
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 "core/fxcrt/stl_util.h"
23 #include "fpdfsdk/cpdfsdk_filewriteadapter.h"
24 #include "fpdfsdk/cpdfsdk_helpers.h"
25 #include "public/fpdf_edit.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       if (iDataSetsIndex != -1) {
115         if (pDataSetsStream) {
116           pDataSetsStream->InitStreamFromFile(pFileWrite);
117         }
118       } else {
119         auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
120             pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
121         int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
122         pArray->InsertNewAt<CPDF_String>(iLast, "datasets");
123         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
124                                             data_stream->GetObjNum());
125       }
126       fileList->push_back(std::move(pFileWrite));
127     }
128   }
129   // L"form"
130   {
131     RetainPtr<IFX_SeekableStream> pFileWrite =
132         pdfium::MakeRetain<CFX_MemoryStream>();
133     if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
134       if (iFormIndex != -1) {
135         if (pFormStream) {
136           pFormStream->InitStreamFromFile(pFileWrite);
137         }
138       } else {
139         auto data_stream = pPDFDocument->NewIndirect<CPDF_Stream>(
140             pFileWrite, pPDFDocument->New<CPDF_Dictionary>());
141         int iLast = fxcrt::CollectionSize<int>(*pArray) - 2;
142         pArray->InsertNewAt<CPDF_String>(iLast, "form");
143         pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
144                                             data_stream->GetObjNum());
145       }
146       fileList->push_back(std::move(pFileWrite));
147     }
148   }
149   return true;
150 }
151 #endif  // PDF_ENABLE_XFA
152 
DoDocSave(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,std::optional<int> version)153 bool DoDocSave(FPDF_DOCUMENT document,
154                FPDF_FILEWRITE* pFileWrite,
155                FPDF_DWORD flags,
156                std::optional<int> version) {
157   CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
158   if (!pPDFDoc)
159     return false;
160 
161 #ifdef PDF_ENABLE_XFA
162   auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
163   if (pContext) {
164     std::vector<RetainPtr<IFX_SeekableStream>> fileList;
165     pContext->SendPreSaveToXFADoc(&fileList);
166     SaveXFADocumentData(pContext, &fileList);
167   }
168 #endif  // PDF_ENABLE_XFA
169 
170   if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
171     flags = 0;
172 
173   CPDF_Creator fileMaker(
174       pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
175   if (version.has_value())
176     fileMaker.SetFileVersion(version.value());
177   if (flags == FPDF_REMOVE_SECURITY) {
178     flags = 0;
179     fileMaker.RemoveSecurity();
180   }
181 
182   bool bRet = fileMaker.Create(static_cast<uint32_t>(flags));
183 
184 #ifdef PDF_ENABLE_XFA
185   if (pContext)
186     pContext->SendPostSaveToXFADoc();
187 #endif  // PDF_ENABLE_XFA
188 
189   return bRet;
190 }
191 
192 }  // namespace
193 
FPDF_SaveAsCopy(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags)194 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
195                                                     FPDF_FILEWRITE* pFileWrite,
196                                                     FPDF_DWORD flags) {
197   return DoDocSave(document, pFileWrite, flags, {});
198 }
199 
200 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_SaveWithVersion(FPDF_DOCUMENT document,FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags,int fileVersion)201 FPDF_SaveWithVersion(FPDF_DOCUMENT document,
202                      FPDF_FILEWRITE* pFileWrite,
203                      FPDF_DWORD flags,
204                      int fileVersion) {
205   return DoDocSave(document, pFileWrite, flags, fileVersion);
206 }
207