Home | History | Annotate | Download | only in fpdfsdk
      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