blob: 330572ff9c0898784395db954d66e42cadf6d66d [file] [log] [blame]
// 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 "xfa/fxfa/cxfa_ffdoc.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
#include "core/fpdfdoc/cpdf_nametree.h"
#include "core/fxcrt/cfx_memorystream.h"
#include "core/fxcrt/cfx_seekablemultistream.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_memory.h"
#include "core/fxcrt/xml/cfx_xmldocument.h"
#include "core/fxcrt/xml/cfx_xmlelement.h"
#include "core/fxcrt/xml/cfx_xmlnode.h"
#include "fxjs/xfa/cjx_object.h"
#include "third_party/base/ptr_util.h"
#include "xfa/fwl/cfwl_notedriver.h"
#include "xfa/fxfa/cxfa_ffapp.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_ffnotify.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_fontmgr.h"
#include "xfa/fxfa/parser/cxfa_acrobat.h"
#include "xfa/fxfa/parser/cxfa_acrobat7.h"
#include "xfa/fxfa/parser/cxfa_dataexporter.h"
#include "xfa/fxfa/parser/cxfa_document.h"
#include "xfa/fxfa/parser/cxfa_document_parser.h"
#include "xfa/fxfa/parser/cxfa_dynamicrender.h"
#include "xfa/fxfa/parser/cxfa_node.h"
CXFA_FFDoc::CXFA_FFDoc(CXFA_FFApp* pApp, IXFA_DocEnvironment* pDocEnvironment)
: m_pDocEnvironment(pDocEnvironment), m_pApp(pApp) {}
CXFA_FFDoc::~CXFA_FFDoc() {
CloseDoc();
}
bool CXFA_FFDoc::ParseDoc(const CPDF_Object* pElementXFA) {
std::vector<const CPDF_Stream*> xfaStreams;
if (pElementXFA->IsArray()) {
const CPDF_Array* pXFAArray = pElementXFA->AsArray();
for (size_t i = 0; i < pXFAArray->GetCount() / 2; i++) {
if (const CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1))
xfaStreams.push_back(pStream);
}
} else if (pElementXFA->IsStream()) {
xfaStreams.push_back(pElementXFA->AsStream());
}
if (xfaStreams.empty())
return false;
auto stream = pdfium::MakeRetain<CFX_SeekableMultiStream>(xfaStreams);
CXFA_DocumentParser parser(m_pDocument.get());
bool parsed = parser.Parse(stream, XFA_PacketType::Xdp);
// We have to set the XML document before we return so that we can clean
// up in the OpenDoc method. If we don't, the XMLDocument will get free'd
// when this method returns and UnownedPtrs get unhappy.
m_pXMLDoc = parser.GetXMLDoc();
if (!parsed)
return false;
m_pDocument->SetRoot(parser.GetRootNode());
return true;
}
CXFA_FFDocView* CXFA_FFDoc::CreateDocView() {
if (!m_DocView)
m_DocView = pdfium::MakeUnique<CXFA_FFDocView>(this);
return m_DocView.get();
}
CXFA_FFDocView* CXFA_FFDoc::GetDocView(CXFA_LayoutProcessor* pLayout) {
return m_DocView && m_DocView->GetXFALayout() == pLayout ? m_DocView.get()
: nullptr;
}
CXFA_FFDocView* CXFA_FFDoc::GetDocView() {
return m_DocView.get();
}
bool CXFA_FFDoc::OpenDoc(CPDF_Document* pPDFDoc) {
if (!pPDFDoc)
return false;
const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot();
if (!pRoot)
return false;
const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
if (!pAcroForm)
return false;
const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA");
if (!pElementXFA)
return false;
m_pPDFDoc = pPDFDoc;
m_pNotify = pdfium::MakeUnique<CXFA_FFNotify>(this);
m_pDocument = pdfium::MakeUnique<CXFA_Document>(m_pNotify.get());
if (!ParseDoc(pElementXFA)) {
CloseDoc();
return false;
}
CFGAS_FontMgr* mgr = GetApp()->GetFDEFontMgr();
if (!mgr)
return false;
// At this point we've got an XFA document and we want to always return
// true to signify the load succeeded.
m_pPDFFontMgr = pdfium::MakeUnique<CFGAS_PDFFontMgr>(GetPDFDoc(), mgr);
m_FormType = FormType::kXFAForeground;
CXFA_Node* pConfig = ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Config));
if (!pConfig)
return true;
CXFA_Acrobat* pAcrobat =
pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
if (!pAcrobat)
return true;
CXFA_Acrobat7* pAcrobat7 =
pAcrobat->GetFirstChildByClass<CXFA_Acrobat7>(XFA_Element::Acrobat7);
if (!pAcrobat7)
return true;
CXFA_DynamicRender* pDynamicRender =
pAcrobat7->GetFirstChildByClass<CXFA_DynamicRender>(
XFA_Element::DynamicRender);
if (!pDynamicRender)
return true;
WideString wsType = pDynamicRender->JSObject()->GetContent(false);
if (wsType == L"required")
m_FormType = FormType::kXFAFull;
return true;
}
void CXFA_FFDoc::CloseDoc() {
if (m_DocView) {
m_DocView->RunDocClose();
m_DocView.reset();
}
if (m_pDocument)
m_pDocument->ClearLayoutData();
m_pDocument.reset();
m_pXMLDoc.reset();
m_pNotify.reset();
m_pPDFFontMgr.reset();
m_HashToDibDpiMap.clear();
m_pApp->ClearEventTargets();
}
RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage(
const WideStringView& wsName,
int32_t& iImageXDpi,
int32_t& iImageYDpi) {
if (!m_pPDFDoc)
return nullptr;
uint32_t dwHash = FX_HashCode_GetW(wsName, false);
auto it = m_HashToDibDpiMap.find(dwHash);
if (it != m_HashToDibDpiMap.end()) {
iImageXDpi = it->second.iImageXDpi;
iImageYDpi = it->second.iImageYDpi;
return it->second.pDibSource.As<CFX_DIBitmap>();
}
CPDF_Dictionary* pRoot = m_pPDFDoc->GetRoot();
if (!pRoot)
return nullptr;
CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
if (!pNames)
return nullptr;
CPDF_Dictionary* pXFAImages = pNames->GetDictFor("XFAImages");
if (!pXFAImages)
return nullptr;
CPDF_NameTree nametree(pXFAImages);
CPDF_Object* pObject = nametree.LookupValue(WideString(wsName));
if (!pObject) {
for (size_t i = 0; i < nametree.GetCount(); i++) {
WideString wsTemp;
CPDF_Object* pTempObject = nametree.LookupValueAndName(i, &wsTemp);
if (wsTemp == wsName) {
pObject = pTempObject;
break;
}
}
}
CPDF_Stream* pStream = ToStream(pObject);
if (!pStream)
return nullptr;
auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
pAcc->LoadAllDataFiltered();
RetainPtr<IFX_SeekableStream> pImageFileRead =
pdfium::MakeRetain<CFX_MemoryStream>(
const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false);
RetainPtr<CFX_DIBitmap> pDibSource = XFA_LoadImageFromBuffer(
pImageFileRead, FXCODEC_IMAGE_UNKNOWN, iImageXDpi, iImageYDpi);
m_HashToDibDpiMap[dwHash] = {pDibSource, iImageXDpi, iImageYDpi};
return pDibSource;
}
bool CXFA_FFDoc::SavePackage(CXFA_Node* pNode,
const RetainPtr<IFX_SeekableStream>& pFile) {
ASSERT(pNode || GetXFADoc()->GetRoot());
CXFA_DataExporter exporter;
return exporter.Export(pFile, pNode ? pNode : GetXFADoc()->GetRoot());
}