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