// 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_edit.h" #include "../../public/fpdf_formfill.h" #include "../../public/fpdf_save.h" #include "../include/fsdk_define.h" #include "../include/fpdfxfa/fpdfxfa_doc.h" #include "../include/fpdfxfa/fpdfxfa_app.h" #include "../include/fpdfxfa/fpdfxfa_util.h" #if _FX_OS_ == _FX_ANDROID_ #include "time.h" #else #include <ctime> #endif class CFX_IFileWrite final : public IFX_StreamWrite { public: CFX_IFileWrite(); FX_BOOL Init( FPDF_FILEWRITE * pFileWriteStruct ); virtual FX_BOOL WriteBlock(const void* pData, size_t size) override; virtual void Release() override {} protected: FPDF_FILEWRITE* m_pFileWriteStruct; }; CFX_IFileWrite::CFX_IFileWrite() { m_pFileWriteStruct = NULL; } FX_BOOL CFX_IFileWrite::Init( FPDF_FILEWRITE * pFileWriteStruct ) { if (!pFileWriteStruct) return FALSE; else { m_pFileWriteStruct = pFileWriteStruct; } return TRUE; } FX_BOOL CFX_IFileWrite::WriteBlock(const void* pData, size_t size) { if (m_pFileWriteStruct) { m_pFileWriteStruct->WriteBlock( m_pFileWriteStruct, pData, size ); return TRUE; } else return FALSE; } #define XFA_DATASETS 0 #define XFA_FORMS 1 FX_BOOL _SaveXFADocumentData(CPDFXFA_Document* pDocument, CFX_PtrArray& fileList) { if (!pDocument) return FALSE; if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA) return TRUE; if (!CPDFXFA_App::GetInstance()->GetXFAApp()) return TRUE; IXFA_DocView* pXFADocView = pDocument->GetXFADocView(); if (NULL == pXFADocView) return TRUE; IXFA_DocHandler *pXFADocHandler = CPDFXFA_App::GetInstance()->GetXFAApp()->GetDocHandler(); CPDF_Document * pPDFDocument = pDocument->GetPDFDoc(); if (pDocument == NULL) return FALSE; CPDF_Dictionary* pRoot = pPDFDocument->GetRoot(); if (pRoot == NULL) return FALSE; CPDF_Dictionary* pAcroForm = pRoot->GetDict("AcroForm"); if (NULL == pAcroForm) return FALSE; CPDF_Object* pXFA = pAcroForm->GetElement("XFA"); if (pXFA == NULL) return TRUE; if(pXFA->GetType() != PDFOBJ_ARRAY) return FALSE; CPDF_Array* pArray = pXFA->GetArray(); if (NULL == 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->GetElement(i); if (pPDFObj->GetType() != PDFOBJ_STRING) continue; if (pPDFObj->GetString() == "form") iFormIndex = i+1; else if (pPDFObj->GetString() == "datasets") iDataSetsIndex = i+1; else if (pPDFObj->GetString() == FX_BSTRC("template")) iTemplate = i + 1; } IXFA_ChecksumContext* pContext = NULL; #define XFA_USECKSUM #ifdef XFA_USECKSUM //Checksum pContext = XFA_Checksum_Create(); FXSYS_assert(pContext); pContext->StartChecksum(); //template if (iTemplate > -1) { CPDF_Stream *pTemplateStream = pArray->GetStream(iTemplate); CPDF_StreamAcc streamAcc; streamAcc.LoadAllData(pTemplateStream); uint8_t* pData = (uint8_t*)streamAcc.GetData(); FX_DWORD dwSize2 = streamAcc.GetSize(); IFX_FileStream *pTemplate = FX_CreateMemoryStream(pData, dwSize2); pContext->UpdateChecksum((IFX_FileRead*)pTemplate); pTemplate->Release(); } #endif CPDF_Stream* pFormStream = NULL; CPDF_Stream* pDataSetsStream = NULL; if (iFormIndex != -1) { //Get form CPDF_Stream CPDF_Object* pFormPDFObj = pArray->GetElement(iFormIndex); if (pFormPDFObj->GetType() == PDFOBJ_REFERENCE) { CPDF_Reference* pFormRefObj = (CPDF_Reference*)pFormPDFObj; CPDF_Object* pFormDircetObj = pFormPDFObj->GetDirect(); if (NULL != pFormDircetObj && pFormDircetObj->GetType() == PDFOBJ_STREAM) { pFormStream = (CPDF_Stream*)pFormDircetObj; } } else if (pFormPDFObj->GetType() == PDFOBJ_STREAM) { pFormStream = (CPDF_Stream*)pFormPDFObj; } } if (iDataSetsIndex != -1) { //Get datasets CPDF_Stream CPDF_Object* pDataSetsPDFObj = pArray->GetElement(iDataSetsIndex); if (pDataSetsPDFObj->GetType() == PDFOBJ_REFERENCE) { CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj; CPDF_Object* pDataSetsDircetObj = pDataSetsRefObj->GetDirect(); if (NULL != pDataSetsDircetObj && pDataSetsDircetObj->GetType() == PDFOBJ_STREAM) { pDataSetsStream = (CPDF_Stream*)pDataSetsDircetObj; } } else if (pDataSetsPDFObj->GetType() == PDFOBJ_STREAM) { pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj; } } //end //L"datasets" { IFX_FileStream* pDsfileWrite = FX_CreateMemoryStream(); if ( NULL == pDsfileWrite ) { pContext->Release(); pDsfileWrite->Release(); return FALSE; } if (pXFADocHandler->SavePackage(pXFADocView->GetDoc(), CFX_WideStringC(L"datasets"), pDsfileWrite) && pDsfileWrite->GetSize()>0) { #ifdef XFA_USECKSUM //Datasets pContext->UpdateChecksum((IFX_FileRead*)pDsfileWrite); pContext->FinishChecksum(); #endif CPDF_Dictionary* pDataDict = FX_NEW CPDF_Dictionary; if (iDataSetsIndex != -1) { if (pDataSetsStream) pDataSetsStream->InitStream(pDsfileWrite, pDataDict); } else { CPDF_Stream* pData = FX_NEW CPDF_Stream(NULL, 0, NULL); pData->InitStream(pDsfileWrite, pDataDict); FX_DWORD AppStreamobjnum = pPDFDocument->AddIndirectObject(pData); CPDF_Reference* pRef = (CPDF_Reference*)pPDFDocument->GetIndirectObject(AppStreamobjnum); { iLast = pArray->GetCount() -2; pArray->InsertAt(iLast,CPDF_String::Create("datasets")); pArray->InsertAt(iLast+1, pData, pPDFDocument); } } fileList.Add(pDsfileWrite); } } //L"form" { IFX_FileStream* pfileWrite = FX_CreateMemoryStream(); if (NULL == pfileWrite) { pContext->Release(); return FALSE; } if(pXFADocHandler->SavePackage(pXFADocView->GetDoc(), CFX_WideStringC(L"form"), pfileWrite, pContext) && pfileWrite > 0) { CPDF_Dictionary* pDataDict = FX_NEW CPDF_Dictionary; if (iFormIndex != -1) { if (pFormStream) pFormStream->InitStream(pfileWrite, pDataDict); } else { CPDF_Stream* pData = FX_NEW CPDF_Stream(NULL, 0, NULL); pData->InitStream(pfileWrite, pDataDict); FX_DWORD AppStreamobjnum = pPDFDocument->AddIndirectObject(pData); CPDF_Reference* pRef = (CPDF_Reference*)pPDFDocument->GetIndirectObject(AppStreamobjnum); { iLast = pArray->GetCount() -2; pArray->InsertAt(iLast, CPDF_String::Create("form")); pArray->InsertAt(iLast+1, pData, pPDFDocument); } } fileList.Add(pfileWrite); } } pContext->Release(); return TRUE; } FX_BOOL _SendPostSaveToXFADoc(CPDFXFA_Document* pDocument) { if (!pDocument) return FALSE; if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA) return TRUE; IXFA_DocView* pXFADocView = pDocument->GetXFADocView(); if (NULL == pXFADocView) return FALSE; IXFA_WidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler(); CXFA_WidgetAcc* pWidgetAcc = NULL; IXFA_WidgetAccIterator* pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator(); pWidgetAcc = pWidgetAccIterator->MoveToNext(); while(pWidgetAcc) { CXFA_EventParam preParam; preParam.m_eType = XFA_EVENT_PostSave; pWidgetHander->ProcessEvent(pWidgetAcc,&preParam); pWidgetAcc = pWidgetAccIterator->MoveToNext(); } pWidgetAccIterator->Release(); pXFADocView->UpdateDocView(); pDocument->_ClearChangeMark(); return TRUE; } FX_BOOL _SendPreSaveToXFADoc(CPDFXFA_Document* pDocument, CFX_PtrArray& fileList) { if (pDocument->GetDocType() != DOCTYPE_DYNIMIC_XFA && pDocument->GetDocType() != DOCTYPE_STATIC_XFA) return TRUE; IXFA_DocView* pXFADocView = pDocument->GetXFADocView(); if (NULL == pXFADocView) return TRUE; IXFA_WidgetHandler* pWidgetHander = pXFADocView->GetWidgetHandler(); CXFA_WidgetAcc* pWidgetAcc = NULL; IXFA_WidgetAccIterator* pWidgetAccIterator = pXFADocView->CreateWidgetAccIterator(); pWidgetAcc = pWidgetAccIterator->MoveToNext(); while(pWidgetAcc) { CXFA_EventParam preParam; preParam.m_eType = XFA_EVENT_PreSave; pWidgetHander->ProcessEvent(pWidgetAcc, &preParam); pWidgetAcc = pWidgetAccIterator->MoveToNext(); } pWidgetAccIterator->Release(); pXFADocView->UpdateDocView(); return _SaveXFADocumentData(pDocument, fileList); } FPDF_BOOL _FPDF_Doc_Save(FPDF_DOCUMENT document, FPDF_FILEWRITE * pFileWrite,FPDF_DWORD flags, FPDF_BOOL bSetVersion, int fileVerion) { CPDFXFA_Document* pDoc = (CPDFXFA_Document*)document; CFX_PtrArray fileList; _SendPreSaveToXFADoc(pDoc, fileList); CPDF_Document* pPDFDoc = pDoc->GetPDFDoc(); if (!pPDFDoc) return 0; 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_IFileWrite* pStreamWrite = NULL; FX_BOOL bRet; pStreamWrite = new CFX_IFileWrite; pStreamWrite->Init( pFileWrite ); bRet = FileMaker.Create(pStreamWrite, flags); _SendPostSaveToXFADoc(pDoc); //pDoc->_ClearChangeMark(); for (int i = 0; i < fileList.GetSize(); i++) { IFX_FileStream* pFile = (IFX_FileStream*)fileList.GetAt(i); pFile->Release(); } fileList.RemoveAll(); delete pStreamWrite; return bRet; } 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); }