// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "public/fpdf_save.h" #include <memory> #include <utility> #include <vector> #include "core/fpdfapi/edit/cpdf_creator.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_stream_acc.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "fpdfsdk/fsdk_define.h" #include "public/fpdf_edit.h" #ifdef PDF_ENABLE_XFA #include "core/fxcrt/cfx_checksumcontext.h" #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" #include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h" #include "public/fpdf_formfill.h" #include "xfa/fxfa/cxfa_eventparam.h" #include "xfa/fxfa/cxfa_ffapp.h" #include "xfa/fxfa/cxfa_ffdocview.h" #include "xfa/fxfa/cxfa_ffwidgethandler.h" #include "xfa/fxfa/cxfa_widgetacciterator.h" #endif #if _FX_OS_ == _FX_ANDROID_ #include <time.h> #else #include <ctime> #endif class CFX_IFileWrite final : public IFX_WriteStream { public: static CFX_RetainPtr<CFX_IFileWrite> Create(); bool Init(FPDF_FILEWRITE* pFileWriteStruct); bool WriteBlock(const void* pData, size_t size) override; protected: template <typename T, typename... Args> friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args); CFX_IFileWrite(); ~CFX_IFileWrite() override {} FPDF_FILEWRITE* m_pFileWriteStruct; }; CFX_RetainPtr<CFX_IFileWrite> CFX_IFileWrite::Create() { return pdfium::MakeRetain<CFX_IFileWrite>(); } CFX_IFileWrite::CFX_IFileWrite() : m_pFileWriteStruct(nullptr) {} bool CFX_IFileWrite::Init(FPDF_FILEWRITE* pFileWriteStruct) { if (!pFileWriteStruct) return false; m_pFileWriteStruct = pFileWriteStruct; return true; } bool CFX_IFileWrite::WriteBlock(const void* pData, size_t size) { if (!m_pFileWriteStruct) return false; m_pFileWriteStruct->WriteBlock(m_pFileWriteStruct, pData, size); return true; } namespace { #ifdef PDF_ENABLE_XFA bool SaveXFADocumentData( CPDFXFA_Context* pContext, std::vector<CFX_RetainPtr<IFX_SeekableStream>>* fileList) { if (!pContext) return false; if (pContext->GetDocType() != XFA_DocType::Dynamic && pContext->GetDocType() != XFA_DocType::Static) { return true; } CXFA_FFDocView* pXFADocView = pContext->GetXFADocView(); if (!pXFADocView) return true; CPDF_Document* pPDFDocument = pContext->GetPDFDoc(); if (!pPDFDocument) return false; CPDF_Dictionary* pRoot = pPDFDocument->GetRoot(); if (!pRoot) return false; CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); if (!pAcroForm) return false; CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA"); if (!pXFA) return true; CPDF_Array* pArray = pXFA->AsArray(); if (!pArray) return false; int size = pArray->GetCount(); int iFormIndex = -1; int iDataSetsIndex = -1; int iTemplate = -1; int iLast = size - 2; for (int i = 0; i < size - 1; i++) { CPDF_Object* pPDFObj = pArray->GetObjectAt(i); if (!pPDFObj->IsString()) continue; if (pPDFObj->GetString() == "form") iFormIndex = i + 1; else if (pPDFObj->GetString() == "datasets") iDataSetsIndex = i + 1; else if (pPDFObj->GetString() == "template") iTemplate = i + 1; } auto pChecksum = pdfium::MakeUnique<CFX_ChecksumContext>(); pChecksum->StartChecksum(); // template if (iTemplate > -1) { CPDF_Stream* pTemplateStream = pArray->GetStreamAt(iTemplate); auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pTemplateStream); pAcc->LoadAllData(); CFX_RetainPtr<IFX_SeekableStream> pTemplate = pdfium::MakeRetain<CFX_MemoryStream>( const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false); pChecksum->UpdateChecksum(pTemplate); } CPDF_Stream* pFormStream = nullptr; CPDF_Stream* pDataSetsStream = nullptr; if (iFormIndex != -1) { // Get form CPDF_Stream CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex); if (pFormPDFObj->IsReference()) { CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect(); if (pFormDirectObj && pFormDirectObj->IsStream()) { pFormStream = (CPDF_Stream*)pFormDirectObj; } } else if (pFormPDFObj->IsStream()) { pFormStream = (CPDF_Stream*)pFormPDFObj; } } if (iDataSetsIndex != -1) { // Get datasets CPDF_Stream CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex); if (pDataSetsPDFObj->IsReference()) { CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj; CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect(); if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) { pDataSetsStream = (CPDF_Stream*)pDataSetsDirectObj; } } else if (pDataSetsPDFObj->IsStream()) { pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj; } } // L"datasets" { CFX_RetainPtr<IFX_SeekableStream> pDsfileWrite = pdfium::MakeRetain<CFX_MemoryStream>(false); if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Datasets, pDsfileWrite, nullptr) && pDsfileWrite->GetSize() > 0) { // Datasets pChecksum->UpdateChecksum(pDsfileWrite); pChecksum->FinishChecksum(); auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>( pPDFDocument->GetByteStringPool()); if (iDataSetsIndex != -1) { if (pDataSetsStream) { pDataSetsStream->InitStreamFromFile(pDsfileWrite, std::move(pDataDict)); } } else { CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>(); pData->InitStreamFromFile(pDsfileWrite, std::move(pDataDict)); iLast = pArray->GetCount() - 2; pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false); pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument, pData->GetObjNum()); } fileList->push_back(std::move(pDsfileWrite)); } } // L"form" { CFX_RetainPtr<IFX_SeekableStream> pfileWrite = pdfium::MakeRetain<CFX_MemoryStream>(false); if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Form, pfileWrite, pChecksum.get()) && pfileWrite->GetSize() > 0) { auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>( pPDFDocument->GetByteStringPool()); if (iFormIndex != -1) { if (pFormStream) pFormStream->InitStreamFromFile(pfileWrite, std::move(pDataDict)); } else { CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>(); pData->InitStreamFromFile(pfileWrite, std::move(pDataDict)); iLast = pArray->GetCount() - 2; pArray->InsertNewAt<CPDF_String>(iLast, "form", false); pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument, pData->GetObjNum()); } fileList->push_back(std::move(pfileWrite)); } } return true; } bool SendPostSaveToXFADoc(CPDFXFA_Context* pContext) { if (!pContext) return false; if (pContext->GetDocType() != XFA_DocType::Dynamic && pContext->GetDocType() != XFA_DocType::Static) return true; CXFA_FFDocView* pXFADocView = pContext->GetXFADocView(); if (!pXFADocView) return false; CXFA_FFWidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler(); std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator(); while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) { CXFA_EventParam preParam; preParam.m_eType = XFA_EVENT_PostSave; pWidgetHander->ProcessEvent(pWidgetAcc, &preParam); } pXFADocView->UpdateDocView(); pContext->ClearChangeMark(); return true; } bool SendPreSaveToXFADoc( CPDFXFA_Context* pContext, std::vector<CFX_RetainPtr<IFX_SeekableStream>>* fileList) { if (pContext->GetDocType() != XFA_DocType::Dynamic && pContext->GetDocType() != XFA_DocType::Static) return true; CXFA_FFDocView* pXFADocView = pContext->GetXFADocView(); if (!pXFADocView) return true; CXFA_FFWidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler(); std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator(); while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) { CXFA_EventParam preParam; preParam.m_eType = XFA_EVENT_PreSave; pWidgetHander->ProcessEvent(pWidgetAcc, &preParam); } pXFADocView->UpdateDocView(); return SaveXFADocumentData(pContext, fileList); } #endif // PDF_ENABLE_XFA bool FPDF_Doc_Save(FPDF_DOCUMENT document, FPDF_FILEWRITE* pFileWrite, FPDF_DWORD flags, FPDF_BOOL bSetVersion, int fileVerion) { CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document); if (!pPDFDoc) return 0; #ifdef PDF_ENABLE_XFA CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document); std::vector<CFX_RetainPtr<IFX_SeekableStream>> fileList; SendPreSaveToXFADoc(pContext, &fileList); #endif // PDF_ENABLE_XFA if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY) flags = 0; CPDF_Creator FileMaker(pPDFDoc); if (bSetVersion) FileMaker.SetFileVersion(fileVerion); if (flags == FPDF_REMOVE_SECURITY) { flags = 0; FileMaker.RemoveSecurity(); } CFX_RetainPtr<CFX_IFileWrite> pStreamWrite = CFX_IFileWrite::Create(); pStreamWrite->Init(pFileWrite); bool bRet = FileMaker.Create(pStreamWrite, flags); #ifdef PDF_ENABLE_XFA SendPostSaveToXFADoc(pContext); #endif // PDF_ENABLE_XFA return bRet; } } // namespace DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveAsCopy(FPDF_DOCUMENT document, FPDF_FILEWRITE* pFileWrite, FPDF_DWORD flags) { return FPDF_Doc_Save(document, pFileWrite, flags, false, 0); } DLLEXPORT FPDF_BOOL STDCALL FPDF_SaveWithVersion(FPDF_DOCUMENT document, FPDF_FILEWRITE* pFileWrite, FPDF_DWORD flags, int fileVersion) { return FPDF_Doc_Save(document, pFileWrite, flags, true, fileVersion); }