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/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