blob: 15c3e4a2a617dc0ad6b8f26329901dfbd51d4cb2 [file] [edit]
// Copyright 2016 The PDFium Authors
// 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 "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
#include <utility>
#include "constants/catalog.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fxcrt/check.h"
#include "core/fxcrt/retain_ptr.h"
#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "fpdfsdk/cpdfsdk_interactiveform.h"
#include "fpdfsdk/cpdfsdk_pageview.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_ffwidgethandler.h"
#include "xfa/fxfa/cxfa_readynodeiterator.h"
#include "xfa/fxfa/parser/cxfa_node.h"
#include "xfa/fxfa/parser/cxfa_submit.h"
#define IDS_XFA_Validate_Input \
"At least one required field was empty. Please fill in the required " \
"fields\r\n(highlighted) before continuing."
// Although there isn't direct casting between these types at present,
// keep the internal and exernal types in sync.
static_assert(FXFA_PAGEVIEWEVENT_POSTADDED ==
static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostAdded),
"kPostAdded mismatch");
static_assert(FXFA_PAGEVIEWEVENT_POSTREMOVED ==
static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostRemoved),
"kPostRemoved mismatch");
CPDFXFA_DocEnvironment::CPDFXFA_DocEnvironment(CPDFXFA_Context* context)
: context_(context) {
DCHECK(context_);
}
CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() = default;
void CPDFXFA_DocEnvironment::SetChangeMark(CXFA_FFDoc* hDoc) {
if (hDoc == context_->GetXFADoc() && context_->GetFormFillEnv()) {
context_->GetFormFillEnv()->SetChangeMark();
}
}
void CPDFXFA_DocEnvironment::InvalidateRect(CXFA_FFPageView* pPageView,
const CFX_RectF& rt) {
if (!context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return;
}
if (context_->GetFormType() != FormType::kXFAFull) {
return;
}
RetainPtr<CPDFXFA_Page> pPage = context_->GetXFAPage(pPageView);
if (!pPage) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
pFormFillEnv->Invalidate(pPage.Get(), rt.ToFloatRect().ToFxRect());
}
void CPDFXFA_DocEnvironment::DisplayCaret(CXFA_FFWidget* hWidget,
bool bVisible,
const CFX_RectF* pRtAnchor) {
if (!hWidget || !pRtAnchor || !context_->GetXFADoc() ||
!context_->GetFormFillEnv() || !context_->GetXFADocView()) {
return;
}
if (context_->GetFormType() != FormType::kXFAFull) {
return;
}
CXFA_FFWidgetHandler* pWidgetHandler =
context_->GetXFADocView()->GetWidgetHandler();
if (!pWidgetHandler) {
return;
}
CXFA_FFPageView* pPageView = hWidget->GetPageView();
if (!pPageView) {
return;
}
RetainPtr<CPDFXFA_Page> pPage = context_->GetXFAPage(pPageView);
if (!pPage) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
CFX_FloatRect rcCaret = pRtAnchor->ToFloatRect();
pFormFillEnv->DisplayCaret(pPage.Get(), bVisible, rcCaret.left, rcCaret.top,
rcCaret.right, rcCaret.bottom);
}
bool CPDFXFA_DocEnvironment::GetPopupPos(CXFA_FFWidget* hWidget,
float fMinPopup,
float fMaxPopup,
const CFX_RectF& rtAnchor,
CFX_RectF* pPopupRect) {
if (!hWidget) {
return false;
}
CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
if (!pXFAPageView) {
return false;
}
RetainPtr<CPDFXFA_Page> pPage = context_->GetXFAPage(pXFAPageView);
if (!pPage) {
return false;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return false;
}
FS_RECTF page_view_rect = pFormFillEnv->GetPageViewRect(pPage.Get());
int nRotate = hWidget->GetNode()->GetRotate();
int space_available_below_anchor;
int space_available_above_anchor;
switch (nRotate) {
case 0:
default: {
space_available_below_anchor =
static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
space_available_above_anchor =
static_cast<int>(rtAnchor.top - page_view_rect.top);
if (rtAnchor.left < page_view_rect.left) {
pPopupRect->left += page_view_rect.left - rtAnchor.left;
}
if (rtAnchor.right() > page_view_rect.right) {
pPopupRect->left -= rtAnchor.right() - page_view_rect.right;
}
break;
}
case 90: {
space_available_below_anchor =
static_cast<int>(page_view_rect.right - rtAnchor.right());
space_available_above_anchor =
static_cast<int>(rtAnchor.left - page_view_rect.left);
if (rtAnchor.bottom() > page_view_rect.bottom) {
pPopupRect->left += rtAnchor.bottom() - page_view_rect.bottom;
}
if (rtAnchor.top < page_view_rect.top) {
pPopupRect->left -= page_view_rect.top - rtAnchor.top;
}
break;
}
case 180: {
space_available_below_anchor =
static_cast<int>(rtAnchor.top - page_view_rect.top);
space_available_above_anchor =
static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
if (rtAnchor.right() > page_view_rect.right) {
pPopupRect->left += rtAnchor.right() - page_view_rect.right;
}
if (rtAnchor.left < page_view_rect.left) {
pPopupRect->left -= page_view_rect.left - rtAnchor.left;
}
break;
}
case 270: {
space_available_below_anchor =
static_cast<int>(rtAnchor.left - page_view_rect.left);
space_available_above_anchor =
static_cast<int>(page_view_rect.right - rtAnchor.right());
if (rtAnchor.top < page_view_rect.top) {
pPopupRect->left += page_view_rect.top - rtAnchor.top;
}
if (rtAnchor.bottom() > page_view_rect.bottom) {
pPopupRect->left -= rtAnchor.bottom() - page_view_rect.bottom;
}
break;
}
}
// If there is no space on either side, the popup can't be rendered.
if (space_available_below_anchor <= 0 && space_available_above_anchor <= 0) {
return false;
}
// Determine whether to draw above or below the anchor.
bool draw_below_anchor;
if (space_available_below_anchor <= 0) {
draw_below_anchor = false;
} else if (space_available_above_anchor <= 0) {
draw_below_anchor = true;
} else if (space_available_below_anchor > space_available_above_anchor) {
draw_below_anchor = true;
} else {
draw_below_anchor = false;
}
int space_available = (draw_below_anchor ? space_available_below_anchor
: space_available_above_anchor);
// Set the popup height and y position according to what was decided above.
float popup_height;
if (space_available < fMinPopup) {
popup_height = fMinPopup;
} else if (space_available > fMaxPopup) {
popup_height = fMaxPopup;
} else {
popup_height = static_cast<float>(space_available);
}
switch (nRotate) {
case 0:
case 180: {
if (draw_below_anchor) {
pPopupRect->top = rtAnchor.height;
} else {
pPopupRect->top = -popup_height;
}
break;
}
case 90:
case 270: {
if (draw_below_anchor) {
pPopupRect->top = rtAnchor.width;
} else {
pPopupRect->top = -popup_height;
}
break;
}
default:
break;
}
pPopupRect->height = popup_height;
return true;
}
bool CPDFXFA_DocEnvironment::PopupMenu(CXFA_FFWidget* hWidget,
const CFX_PointF& ptPopup) {
if (!hWidget) {
return false;
}
CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
if (!pXFAPageView) {
return false;
}
RetainPtr<CPDFXFA_Page> pPage = context_->GetXFAPage(pXFAPageView);
if (!pPage) {
return false;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return false;
}
int menuFlag = 0;
if (hWidget->CanUndo()) {
menuFlag |= FXFA_MENU_UNDO;
}
if (hWidget->CanRedo()) {
menuFlag |= FXFA_MENU_REDO;
}
if (hWidget->CanPaste()) {
menuFlag |= FXFA_MENU_PASTE;
}
if (hWidget->CanCopy()) {
menuFlag |= FXFA_MENU_COPY;
}
if (hWidget->CanCut()) {
menuFlag |= FXFA_MENU_CUT;
}
if (hWidget->CanSelectAll()) {
menuFlag |= FXFA_MENU_SELECTALL;
}
return pFormFillEnv->PopupMenu(pPage.Get(), menuFlag, ptPopup);
}
void CPDFXFA_DocEnvironment::OnPageViewEvent(CXFA_FFPageView* pPageView,
CXFA_FFDoc::PageViewEvent eEvent) {
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
if (context_->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kLoading ||
context_->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kClosing ||
eEvent != CXFA_FFDoc::PageViewEvent::kStopLayout) {
return;
}
int nNewCount = context_->GetPageCount();
if (nNewCount == context_->GetOriginalPageCount()) {
return;
}
CXFA_FFDocView* pXFADocView = context_->GetXFADocView();
if (!pXFADocView) {
return;
}
for (int i = 0; i < context_->GetOriginalPageCount(); ++i) {
RetainPtr<CPDFXFA_Page> pPage = context_->GetXFAPage(i);
if (!pPage) {
continue;
}
context_->GetFormFillEnv()->RemovePageView(pPage.Get());
pPage->SetXFAPageViewIndex(i);
}
int flag = (nNewCount < context_->GetOriginalPageCount())
? FXFA_PAGEVIEWEVENT_POSTREMOVED
: FXFA_PAGEVIEWEVENT_POSTADDED;
int count = abs(nNewCount - context_->GetOriginalPageCount());
context_->SetOriginalPageCount(nNewCount);
pFormFillEnv->PageEvent(count, flag);
}
void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) {
if (context_->GetFormType() != FormType::kXFAFull) {
return;
}
CXFA_FFPageView* pPageView = hWidget->GetPageView();
if (!pPageView) {
return;
}
RetainPtr<CPDFXFA_Page> pXFAPage = context_->GetXFAPage(pPageView);
if (!pXFAPage) {
return;
}
auto* formfill = context_->GetFormFillEnv();
formfill->GetOrCreatePageView(pXFAPage.Get())->AddAnnotForFFWidget(hWidget);
}
void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) {
if (context_->GetFormType() != FormType::kXFAFull) {
return;
}
CXFA_FFPageView* pPageView = hWidget->GetPageView();
if (!pPageView) {
return;
}
RetainPtr<CPDFXFA_Page> pXFAPage = context_->GetXFAPage(pPageView);
if (!pXFAPage) {
return;
}
CPDFSDK_PageView* pSdkPageView =
context_->GetFormFillEnv()->GetOrCreatePageView(pXFAPage.Get());
pSdkPageView->DeleteAnnotForFFWidget(hWidget);
}
int32_t CPDFXFA_DocEnvironment::CountPages(const CXFA_FFDoc* hDoc) const {
if (hDoc == context_->GetXFADoc() && context_->GetFormFillEnv()) {
return context_->GetPageCount();
}
return 0;
}
int32_t CPDFXFA_DocEnvironment::GetCurrentPage(const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return -1;
}
if (context_->GetFormType() != FormType::kXFAFull) {
return -1;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
return pFormFillEnv ? pFormFillEnv->GetCurrentPageIndex() : -1;
}
void CPDFXFA_DocEnvironment::SetCurrentPage(CXFA_FFDoc* hDoc,
int32_t iCurPage) {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv() ||
!context_->ContainsExtensionForm() || iCurPage < 0 ||
iCurPage >= context_->GetFormFillEnv()->GetPageCount()) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
pFormFillEnv->SetCurrentPage(iCurPage);
}
bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(
const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return false;
}
auto* pForm = context_->GetFormFillEnv()->GetInteractiveForm();
return pForm->IsXfaCalculateEnabled();
}
void CPDFXFA_DocEnvironment::SetCalculationsEnabled(CXFA_FFDoc* hDoc,
bool bEnabled) {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return;
}
context_->GetFormFillEnv()->GetInteractiveForm()->XfaEnableCalculate(
bEnabled);
}
WideString CPDFXFA_DocEnvironment::GetTitle(const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc()) {
return WideString();
}
CPDF_Document* pPDFDoc = context_->GetPDFDoc();
if (!pPDFDoc) {
return WideString();
}
RetainPtr<const CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
if (!pInfoDict) {
return WideString();
}
ByteString csTitle = pInfoDict->GetByteStringFor("Title");
return WideString::FromDefANSI(csTitle.AsStringView());
}
void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc,
const WideString& wsTitle) {
if (hDoc != context_->GetXFADoc()) {
return;
}
CPDF_Document* pPDFDoc = context_->GetPDFDoc();
if (!pPDFDoc) {
return;
}
RetainPtr<CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
if (pInfoDict) {
pInfoDict->SetNewFor<CPDF_String>("Title", wsTitle.AsStringView());
}
}
void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc,
const WideString& wsFilePath,
bool bXDP) {
if (hDoc != context_->GetXFADoc()) {
return;
}
if (!context_->ContainsExtensionForm()) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
int fileType = bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML;
ByteString bs = wsFilePath.ToUTF16LE();
if (wsFilePath.IsEmpty()) {
if (!pFormFillEnv->IsJSPlatformPresent()) {
return;
}
WideString filepath = pFormFillEnv->JS_fieldBrowse();
bs = filepath.ToUTF16LE();
}
FPDF_FILEHANDLER* pFileHandler = pFormFillEnv->OpenFile(
bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML, AsFPDFWideString(&bs), "wb");
if (!pFileHandler) {
return;
}
RetainPtr<IFX_SeekableStream> fileWrite = MakeSeekableStream(pFileHandler);
if (fileType == FXFA_SAVEAS_XML) {
fileWrite->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
CXFA_FFDoc* ffdoc = context_->GetXFADocView()->GetDoc();
ffdoc->SavePackage(
ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileWrite);
} else if (fileType == FXFA_SAVEAS_XDP) {
if (!context_->GetPDFDoc()) {
return;
}
const CPDF_Dictionary* pRoot = context_->GetPDFDoc()->GetRoot();
if (!pRoot) {
return;
}
RetainPtr<const CPDF_Dictionary> pAcroForm =
pRoot->GetDictFor(pdfium::catalog::kAcroForm);
if (!pAcroForm) {
return;
}
RetainPtr<const CPDF_Array> pArray =
ToArray(pAcroForm->GetObjectFor("XFA"));
if (!pArray) {
return;
}
for (size_t i = 1; i < pArray->size(); i += 2) {
RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
if (!pPrePDFObj->IsString()) {
continue;
}
if (!pPDFObj->IsReference()) {
continue;
}
RetainPtr<const CPDF_Stream> pStream = ToStream(pPDFObj->GetDirect());
if (!pStream) {
continue;
}
if (pPrePDFObj->GetString() == "form") {
CXFA_FFDoc* ffdoc = context_->GetXFADocView()->GetDoc();
ffdoc->SavePackage(
ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
fileWrite);
continue;
}
if (pPrePDFObj->GetString() == "datasets") {
CXFA_FFDoc* ffdoc = context_->GetXFADocView()->GetDoc();
ffdoc->SavePackage(
ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
fileWrite);
continue;
}
if (i == pArray->size() - 1) {
WideString wPath = WideString::FromUTF16LE(bs.unsigned_span());
ByteString bPath = wPath.ToUTF8();
static const char kFormat[] =
"\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
ByteString content = ByteString::Format(kFormat, bPath.c_str());
fileWrite->WriteString(content.AsStringView());
}
auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
pAcc->LoadAllDataFiltered();
fileWrite->WriteBlock(pAcc->GetSpan());
}
}
fileWrite->Flush();
}
void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
const WideString& wsURL) {
if (hDoc != context_->GetXFADoc()) {
return;
}
if (context_->GetFormType() != FormType::kXFAFull) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
pFormFillEnv->GotoURL(wsURL);
}
bool CPDFXFA_DocEnvironment::IsValidationsEnabled(
const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return false;
}
auto* pForm = context_->GetFormFillEnv()->GetInteractiveForm();
return pForm->IsXfaValidationsEnabled();
}
void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
bool bEnabled) {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return;
}
context_->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
bEnabled);
}
void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
CXFA_FFWidget* hWidget) {
if (hDoc != context_->GetXFADoc()) {
return;
}
if (!hWidget) {
ObservedPtr<CPDFSDK_Annot> pNull;
context_->GetFormFillEnv()->SetFocusAnnot(pNull);
return;
}
int pageViewCount = context_->GetFormFillEnv()->GetPageViewCount();
for (int i = 0; i < pageViewCount; i++) {
CPDFSDK_PageView* pPageView =
context_->GetFormFillEnv()->GetPageViewAtIndex(i);
if (!pPageView) {
continue;
}
ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotForFFWidget(hWidget));
if (pAnnot) {
context_->GetFormFillEnv()->SetFocusAnnot(pAnnot);
break;
}
}
}
void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
int32_t nStartPage,
int32_t nEndPage,
Mask<XFA_PrintOpt> dwOptions) {
if (hDoc != context_->GetXFADoc()) {
return;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return;
}
IPDF_JSPLATFORM* js_platform = pFormFillEnv->GetJSPlatform();
if (!js_platform || !js_platform->Doc_print) {
return;
}
js_platform->Doc_print(js_platform, !!(dwOptions & XFA_PrintOpt::kShowDialog),
nStartPage, nEndPage,
!!(dwOptions & XFA_PrintOpt::kCanCancel),
!!(dwOptions & XFA_PrintOpt::kShrinkPage),
!!(dwOptions & XFA_PrintOpt::kAsImage),
!!(dwOptions & XFA_PrintOpt::kReverseOrder),
!!(dwOptions & XFA_PrintOpt::kPrintAnnot));
}
FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(
const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc() || !context_->GetFormFillEnv()) {
return 0;
}
CPDFSDK_InteractiveForm* pForm =
context_->GetFormFillEnv()->GetInteractiveForm();
return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
pForm->GetHighlightColor(FormFieldType::kXFA));
}
IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(
const CXFA_FFDoc* hDoc) const {
if (hDoc != context_->GetXFADoc()) {
return nullptr;
}
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
}
CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const {
return context_->GetXMLDoc();
}
RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
CXFA_FFDoc* hDoc,
const WideString& wsLink) {
CPDFSDK_FormFillEnvironment* pFormFillEnv = context_->GetFormFillEnv();
if (!pFormFillEnv) {
return nullptr;
}
ByteString bs = wsLink.ToUTF16LE();
FPDF_FILEHANDLER* pFileHandler =
pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
if (!pFileHandler) {
return nullptr;
}
return MakeSeekableStream(pFileHandler);
}