blob: 79258383e66a1bd40cde854c7f525fd12429c59d [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 "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
#include <memory>
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/render/cpdf_pagerendercache.h"
#include "fpdfsdk/cpdfsdk_pageview.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
#include "third_party/base/check.h"
#include "third_party/base/notreached.h"
#include "xfa/fgas/graphics/cfgas_gegraphics.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_ffpageview.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_ffwidgethandler.h"
namespace {
constexpr XFA_WidgetStatusMask kIteratorFilter = XFA_WidgetStatus_Visible |
XFA_WidgetStatus_Viewable |
XFA_WidgetStatus_Focused;
CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForPage(
CXFA_FFPageView* pFFPageView,
CPDFSDK_PageView* pPageView) {
if (!pFFPageView)
return nullptr;
ObservedPtr<CPDFSDK_PageView> pWatchedPageView(pPageView);
CXFA_FFWidget::IteratorIface* pIterator =
pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
// Check |pPageView| again because JS may have destroyed it.
return pWatchedPageView ? pIterator : nullptr;
}
CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForAnnot(
CXFA_FFPageView* pFFPageView,
CPDFSDK_Annot* pSDKAnnot) {
if (!pFFPageView)
return nullptr;
CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot);
if (!pXFAWidget)
return nullptr;
ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pSDKAnnot);
CXFA_FFWidget::IteratorIface* pWidgetIterator =
pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
// Check |pSDKAnnot| again because JS may have destroyed it.
if (!pObservedAnnot)
return nullptr;
if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget())
pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget());
return pWidgetIterator;
}
} // namespace
CPDFXFA_Page::CPDFXFA_Page(CPDF_Document* pDocument, int page_index)
: m_pDocument(pDocument), m_iPageIndex(page_index) {
DCHECK(m_pDocument->GetExtension());
DCHECK(m_iPageIndex >= 0);
}
CPDFXFA_Page::~CPDFXFA_Page() = default;
CPDF_Page* CPDFXFA_Page::AsPDFPage() {
return m_pPDFPage.Get();
}
CPDFXFA_Page* CPDFXFA_Page::AsXFAPage() {
return this;
}
CPDF_Document* CPDFXFA_Page::GetDocument() const {
return m_pDocument.Get();
}
bool CPDFXFA_Page::LoadPDFPage() {
CPDF_Document* pPDFDoc = GetDocument();
CPDF_Dictionary* pDict = pPDFDoc->GetPageDictionary(m_iPageIndex);
if (!pDict)
return false;
if (!m_pPDFPage || m_pPDFPage->GetDict() != pDict)
LoadPDFPageFromDict(pDict);
return true;
}
CXFA_FFPageView* CPDFXFA_Page::GetXFAPageView() const {
auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
return pXFADocView ? pXFADocView->GetPageView(m_iPageIndex) : nullptr;
}
bool CPDFXFA_Page::LoadPage() {
auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
switch (pContext->GetFormType()) {
case FormType::kNone:
case FormType::kAcroForm:
case FormType::kXFAForeground:
return LoadPDFPage();
case FormType::kXFAFull:
return !!GetXFAPageView();
}
NOTREACHED();
return false;
}
void CPDFXFA_Page::LoadPDFPageFromDict(CPDF_Dictionary* pPageDict) {
DCHECK(pPageDict);
m_pPDFPage = pdfium::MakeRetain<CPDF_Page>(GetDocument(), pPageDict);
m_pPDFPage->SetRenderCache(
std::make_unique<CPDF_PageRenderCache>(m_pPDFPage.Get()));
m_pPDFPage->ParseContent();
}
float CPDFXFA_Page::GetPageWidth() const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!m_pPDFPage && !pPageView)
return 0.0f;
auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
switch (pContext->GetFormType()) {
case FormType::kNone:
case FormType::kAcroForm:
case FormType::kXFAForeground:
if (m_pPDFPage)
return m_pPDFPage->GetPageWidth();
FALLTHROUGH;
case FormType::kXFAFull:
if (pPageView)
return pPageView->GetPageViewRect().width;
break;
}
return 0.0f;
}
float CPDFXFA_Page::GetPageHeight() const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!m_pPDFPage && !pPageView)
return 0.0f;
auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
switch (pContext->GetFormType()) {
case FormType::kNone:
case FormType::kAcroForm:
case FormType::kXFAForeground:
if (m_pPDFPage)
return m_pPDFPage->GetPageHeight();
FALLTHROUGH;
case FormType::kXFAFull:
if (pPageView)
return pPageView->GetPageViewRect().height;
break;
}
return 0.0f;
}
Optional<CFX_PointF> CPDFXFA_Page::DeviceToPage(
const FX_RECT& rect,
int rotate,
const CFX_PointF& device_point) const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!m_pPDFPage && !pPageView)
return pdfium::nullopt;
CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
return page2device.GetInverse().Transform(device_point);
}
Optional<CFX_PointF> CPDFXFA_Page::PageToDevice(
const FX_RECT& rect,
int rotate,
const CFX_PointF& page_point) const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!m_pPDFPage && !pPageView)
return pdfium::nullopt;
CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
return page2device.Transform(page_point);
}
CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(const FX_RECT& rect,
int iRotate) const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!m_pPDFPage && !pPageView)
return CFX_Matrix();
auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
switch (pContext->GetFormType()) {
case FormType::kNone:
case FormType::kAcroForm:
case FormType::kXFAForeground:
if (m_pPDFPage)
return m_pPDFPage->GetDisplayMatrix(rect, iRotate);
FALLTHROUGH;
case FormType::kXFAFull:
if (pPageView)
return pPageView->GetDisplayMatrix(rect, iRotate);
break;
}
return CFX_Matrix();
}
CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
CXFA_FFWidget::IteratorIface* pWidgetIterator =
GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
if (!pWidgetIterator)
return nullptr;
return pSDKAnnot->GetPageView()->GetAnnotByXFAWidget(
pWidgetIterator->MoveToNext());
}
CPDFSDK_Annot* CPDFXFA_Page::GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
CXFA_FFWidget::IteratorIface* pWidgetIterator =
GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
if (!pWidgetIterator)
return nullptr;
return pSDKAnnot->GetPageView()->GetAnnotByXFAWidget(
pWidgetIterator->MoveToPrevious());
}
CPDFSDK_Annot* CPDFXFA_Page::GetFirstXFAAnnot(
CPDFSDK_PageView* page_view) const {
CXFA_FFWidget::IteratorIface* pWidgetIterator =
GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
if (!pWidgetIterator)
return nullptr;
return page_view->GetAnnotByXFAWidget(pWidgetIterator->MoveToFirst());
}
CPDFSDK_Annot* CPDFXFA_Page::GetLastXFAAnnot(
CPDFSDK_PageView* page_view) const {
CXFA_FFWidget::IteratorIface* pWidgetIterator =
GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
if (!pWidgetIterator)
return nullptr;
return page_view->GetAnnotByXFAWidget(pWidgetIterator->MoveToLast());
}
int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const {
CXFA_FFPageView* pPageView = GetXFAPageView();
if (!pPageView)
return -1;
CXFA_FFDocView* pDocView = pPageView->GetDocView();
if (!pDocView)
return -1;
CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
if (!pWidgetHandler)
return -1;
CXFA_FFWidget::IteratorIface* pWidgetIterator =
pPageView->CreateGCedFormWidgetIterator(XFA_WidgetStatus_Viewable);
CXFA_FFWidget* pXFAAnnot;
while ((pXFAAnnot = pWidgetIterator->MoveToNext()) != nullptr) {
if (pXFAAnnot->GetFormFieldType() == FormFieldType::kXFA)
continue;
CFX_FloatRect rcWidget = pXFAAnnot->GetWidgetRect().ToFloatRect();
rcWidget.Inflate(1.0f, 1.0f);
if (rcWidget.Contains(point))
return static_cast<int>(pXFAAnnot->GetFormFieldType());
}
return -1;
}
void CPDFXFA_Page::DrawFocusAnnot(CFX_RenderDevice* pDevice,
CPDFSDK_Annot* pAnnot,
const CFX_Matrix& mtUser2Device,
const FX_RECT& rtClip) {
CFX_RectF rectClip(rtClip);
CFGAS_GEGraphics gs(pDevice);
gs.SetClipRect(rectClip);
CXFA_FFPageView* xfaView = GetXFAPageView();
CXFA_FFWidget::IteratorIface* pWidgetIterator =
xfaView->CreateGCedFormWidgetIterator(XFA_WidgetStatus_Visible |
XFA_WidgetStatus_Viewable);
while (1) {
CXFA_FFWidget* pWidget = pWidgetIterator->MoveToNext();
if (!pWidget)
break;
CFX_RectF rtWidgetBox = pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus);
++rtWidgetBox.width;
++rtWidgetBox.height;
if (rtWidgetBox.IntersectWith(rectClip))
pWidget->RenderWidget(&gs, mtUser2Device, CXFA_FFWidget::kHighlight);
}
CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
if (!pXFAWidget)
return;
CXFA_FFDocView* docView = xfaView->GetDocView();
if (!docView)
return;
docView->GetWidgetHandler()->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs,
mtUser2Device, false);
}