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