|  | // 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_dictionary.h" | 
|  | #include "core/fpdfapi/parser/cpdf_document.h" | 
|  | #include "core/fpdfapi/parser/cpdf_seekablemultistream.h" | 
|  | #include "core/fpdfapi/parser/cpdf_stream.h" | 
|  | #include "core/fpdfapi/parser/cpdf_stream_acc.h" | 
|  | #include "core/fpdfdoc/cpdf_nametree.h" | 
|  | #include "core/fxcrt/cfx_readonlymemorystream.h" | 
|  | #include "core/fxcrt/fx_extension.h" | 
|  | #include "core/fxcrt/xml/cfx_xmldocument.h" | 
|  | #include "core/fxcrt/xml/cfx_xmlelement.h" | 
|  | #include "core/fxcrt/xml/cfx_xmlnode.h" | 
|  | #include "core/fxge/dib/cfx_dibitmap.h" | 
|  | #include "fxjs/xfa/cjx_object.h" | 
|  | #include "third_party/base/ptr_util.h" | 
|  | #include "xfa/fgas/font/cfgas_pdffontmgr.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/layout/cxfa_layoutprocessor.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" | 
|  |  | 
|  | FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI() = default; | 
|  | FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(const FX_IMAGEDIB_AND_DPI& that) = | 
|  | default; | 
|  |  | 
|  | FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(const RetainPtr<CFX_DIBBase>& pDib, | 
|  | int32_t xDpi, | 
|  | int32_t yDpi) | 
|  | : pDibSource(pDib), iImageXDpi(xDpi), iImageYDpi(yDpi) {} | 
|  |  | 
|  | FX_IMAGEDIB_AND_DPI::~FX_IMAGEDIB_AND_DPI() = default; | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<CXFA_FFDoc> CXFA_FFDoc::CreateAndOpen( | 
|  | CXFA_FFApp* pApp, | 
|  | IXFA_DocEnvironment* pDocEnvironment, | 
|  | CPDF_Document* pPDFDoc) { | 
|  | ASSERT(pApp); | 
|  | ASSERT(pDocEnvironment); | 
|  | ASSERT(pPDFDoc); | 
|  |  | 
|  | const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot(); | 
|  | if (!pRoot) | 
|  | return nullptr; | 
|  |  | 
|  | const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); | 
|  | if (!pAcroForm) | 
|  | return nullptr; | 
|  |  | 
|  | const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA"); | 
|  | if (!pElementXFA) | 
|  | return nullptr; | 
|  |  | 
|  | // Use WrapUnique() to keep constructor private. | 
|  | auto result = | 
|  | pdfium::WrapUnique(new CXFA_FFDoc(pApp, pDocEnvironment, pPDFDoc)); | 
|  | if (!result->OpenDoc(pElementXFA)) | 
|  | return nullptr; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | CXFA_FFDoc::CXFA_FFDoc(CXFA_FFApp* pApp, | 
|  | IXFA_DocEnvironment* pDocEnvironment, | 
|  | CPDF_Document* pPDFDoc) | 
|  | : m_pDocEnvironment(pDocEnvironment), | 
|  | m_pApp(pApp), | 
|  | m_pPDFDoc(pPDFDoc), | 
|  | m_pNotify(pdfium::MakeUnique<CXFA_FFNotify>(this)), | 
|  | m_pDocument(pdfium::MakeUnique<CXFA_Document>( | 
|  | m_pNotify.get(), | 
|  | pdfium::MakeUnique<CXFA_LayoutProcessor>())) {} | 
|  |  | 
|  | CXFA_FFDoc::~CXFA_FFDoc() { | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | 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->size() / 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<CPDF_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(const CPDF_Object* pElementXFA) { | 
|  | if (!ParseDoc(pElementXFA)) | 
|  | 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.EqualsASCII("required")) | 
|  | m_FormType = FormType::kXFAFull; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage(WideStringView wsName, | 
|  | int32_t& iImageXDpi, | 
|  | int32_t& iImageYDpi) { | 
|  | 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(); | 
|  |  | 
|  | auto pImageFileRead = | 
|  | pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pAcc->GetSpan()); | 
|  |  | 
|  | 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()); | 
|  | } |