1 // Copyright 2014 The PDFium Authors
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 "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
8
9 #include <memory>
10 #include <utility>
11
12 #include "core/fpdfapi/page/cpdf_page.h"
13 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
14 #include "core/fpdfapi/parser/cpdf_document.h"
15 #include "fpdfsdk/cpdfsdk_pageview.h"
16 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
17 #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
18 #include "third_party/base/check.h"
19 #include "xfa/fgas/graphics/cfgas_gegraphics.h"
20 #include "xfa/fxfa/cxfa_ffdocview.h"
21 #include "xfa/fxfa/cxfa_ffpageview.h"
22 #include "xfa/fxfa/cxfa_ffwidget.h"
23 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
24
25 namespace {
26
27 constexpr Mask<XFA_WidgetStatus> kIteratorFilter = {
28 XFA_WidgetStatus::kVisible,
29 XFA_WidgetStatus::kViewable,
30 XFA_WidgetStatus::kFocused,
31 };
32
GCedWidgetIteratorForPage(CXFA_FFPageView * pFFPageView,CPDFSDK_PageView * pPageView)33 CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForPage(
34 CXFA_FFPageView* pFFPageView,
35 CPDFSDK_PageView* pPageView) {
36 if (!pFFPageView)
37 return nullptr;
38
39 ObservedPtr<CPDFSDK_PageView> pWatchedPageView(pPageView);
40 CXFA_FFWidget::IteratorIface* pIterator =
41 pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
42
43 // Check |pPageView| again because JS may have destroyed it.
44 return pWatchedPageView ? pIterator : nullptr;
45 }
46
GCedWidgetIteratorForAnnot(CXFA_FFPageView * pFFPageView,CPDFSDK_Annot * pSDKAnnot)47 CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForAnnot(
48 CXFA_FFPageView* pFFPageView,
49 CPDFSDK_Annot* pSDKAnnot) {
50 if (!pFFPageView)
51 return nullptr;
52
53 CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot);
54 if (!pXFAWidget)
55 return nullptr;
56
57 ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pSDKAnnot);
58 CXFA_FFWidget::IteratorIface* pWidgetIterator =
59 pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter);
60
61 // Check |pSDKAnnot| again because JS may have destroyed it.
62 if (!pObservedAnnot)
63 return nullptr;
64
65 if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget())
66 pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget());
67
68 return pWidgetIterator;
69 }
70
71 } // namespace
72
CPDFXFA_Page(CPDF_Document * pDocument,int page_index)73 CPDFXFA_Page::CPDFXFA_Page(CPDF_Document* pDocument, int page_index)
74 : m_pDocument(pDocument), m_iPageIndex(page_index) {
75 DCHECK(m_pDocument->GetExtension());
76 DCHECK(m_iPageIndex >= 0);
77 }
78
79 CPDFXFA_Page::~CPDFXFA_Page() = default;
80
AsPDFPage()81 CPDF_Page* CPDFXFA_Page::AsPDFPage() {
82 return m_pPDFPage.Get();
83 }
84
AsXFAPage()85 CPDFXFA_Page* CPDFXFA_Page::AsXFAPage() {
86 return this;
87 }
88
GetDocument() const89 CPDF_Document* CPDFXFA_Page::GetDocument() const {
90 return m_pDocument;
91 }
92
LoadPDFPage()93 bool CPDFXFA_Page::LoadPDFPage() {
94 RetainPtr<CPDF_Dictionary> pDict =
95 GetDocument()->GetMutablePageDictionary(m_iPageIndex);
96 if (!pDict)
97 return false;
98
99 if (!m_pPDFPage || m_pPDFPage->GetDict() != pDict)
100 LoadPDFPageFromDict(std::move(pDict));
101
102 return true;
103 }
104
GetXFAPageView() const105 CXFA_FFPageView* CPDFXFA_Page::GetXFAPageView() const {
106 auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
107 CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
108 return pXFADocView ? pXFADocView->GetPageView(m_iPageIndex) : nullptr;
109 }
110
LoadPage()111 bool CPDFXFA_Page::LoadPage() {
112 auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
113 switch (pContext->GetFormType()) {
114 case FormType::kNone:
115 case FormType::kAcroForm:
116 case FormType::kXFAForeground:
117 return LoadPDFPage();
118 case FormType::kXFAFull:
119 return !!GetXFAPageView();
120 }
121 }
122
LoadPDFPageFromDict(RetainPtr<CPDF_Dictionary> pPageDict)123 void CPDFXFA_Page::LoadPDFPageFromDict(RetainPtr<CPDF_Dictionary> pPageDict) {
124 DCHECK(pPageDict);
125 m_pPDFPage =
126 pdfium::MakeRetain<CPDF_Page>(GetDocument(), std::move(pPageDict));
127 m_pPDFPage->AddPageImageCache();
128 m_pPDFPage->ParseContent();
129 }
130
GetPageWidth() const131 float CPDFXFA_Page::GetPageWidth() const {
132 CXFA_FFPageView* pPageView = GetXFAPageView();
133 if (!m_pPDFPage && !pPageView)
134 return 0.0f;
135
136 auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
137 switch (pContext->GetFormType()) {
138 case FormType::kNone:
139 case FormType::kAcroForm:
140 case FormType::kXFAForeground:
141 if (m_pPDFPage)
142 return m_pPDFPage->GetPageWidth();
143 [[fallthrough]];
144 case FormType::kXFAFull:
145 if (pPageView)
146 return pPageView->GetPageViewRect().width;
147 break;
148 }
149
150 return 0.0f;
151 }
152
GetPageHeight() const153 float CPDFXFA_Page::GetPageHeight() const {
154 CXFA_FFPageView* pPageView = GetXFAPageView();
155 if (!m_pPDFPage && !pPageView)
156 return 0.0f;
157
158 auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
159 switch (pContext->GetFormType()) {
160 case FormType::kNone:
161 case FormType::kAcroForm:
162 case FormType::kXFAForeground:
163 if (m_pPDFPage)
164 return m_pPDFPage->GetPageHeight();
165 [[fallthrough]];
166 case FormType::kXFAFull:
167 if (pPageView)
168 return pPageView->GetPageViewRect().height;
169 break;
170 }
171
172 return 0.0f;
173 }
174
DeviceToPage(const FX_RECT & rect,int rotate,const CFX_PointF & device_point) const175 absl::optional<CFX_PointF> CPDFXFA_Page::DeviceToPage(
176 const FX_RECT& rect,
177 int rotate,
178 const CFX_PointF& device_point) const {
179 CXFA_FFPageView* pPageView = GetXFAPageView();
180 if (!m_pPDFPage && !pPageView)
181 return absl::nullopt;
182
183 CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
184 return page2device.GetInverse().Transform(device_point);
185 }
186
PageToDevice(const FX_RECT & rect,int rotate,const CFX_PointF & page_point) const187 absl::optional<CFX_PointF> CPDFXFA_Page::PageToDevice(
188 const FX_RECT& rect,
189 int rotate,
190 const CFX_PointF& page_point) const {
191 CXFA_FFPageView* pPageView = GetXFAPageView();
192 if (!m_pPDFPage && !pPageView)
193 return absl::nullopt;
194
195 CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
196 return page2device.Transform(page_point);
197 }
198
GetDisplayMatrix(const FX_RECT & rect,int iRotate) const199 CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(const FX_RECT& rect,
200 int iRotate) const {
201 CXFA_FFPageView* pPageView = GetXFAPageView();
202 if (!m_pPDFPage && !pPageView)
203 return CFX_Matrix();
204
205 auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
206 switch (pContext->GetFormType()) {
207 case FormType::kNone:
208 case FormType::kAcroForm:
209 case FormType::kXFAForeground:
210 if (m_pPDFPage)
211 return m_pPDFPage->GetDisplayMatrix(rect, iRotate);
212 [[fallthrough]];
213 case FormType::kXFAFull:
214 if (pPageView)
215 return pPageView->GetDisplayMatrix(rect, iRotate);
216 break;
217 }
218
219 return CFX_Matrix();
220 }
221
GetNextXFAAnnot(CPDFSDK_Annot * pSDKAnnot) const222 CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
223 CXFA_FFWidget::IteratorIface* pWidgetIterator =
224 GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
225 if (!pWidgetIterator)
226 return nullptr;
227
228 return pSDKAnnot->GetPageView()->GetAnnotForFFWidget(
229 pWidgetIterator->MoveToNext());
230 }
231
GetPrevXFAAnnot(CPDFSDK_Annot * pSDKAnnot) const232 CPDFSDK_Annot* CPDFXFA_Page::GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const {
233 CXFA_FFWidget::IteratorIface* pWidgetIterator =
234 GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot);
235 if (!pWidgetIterator)
236 return nullptr;
237
238 return pSDKAnnot->GetPageView()->GetAnnotForFFWidget(
239 pWidgetIterator->MoveToPrevious());
240 }
241
GetFirstXFAAnnot(CPDFSDK_PageView * page_view) const242 CPDFSDK_Annot* CPDFXFA_Page::GetFirstXFAAnnot(
243 CPDFSDK_PageView* page_view) const {
244 CXFA_FFWidget::IteratorIface* pWidgetIterator =
245 GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
246 if (!pWidgetIterator)
247 return nullptr;
248
249 return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToFirst());
250 }
251
GetLastXFAAnnot(CPDFSDK_PageView * page_view) const252 CPDFSDK_Annot* CPDFXFA_Page::GetLastXFAAnnot(
253 CPDFSDK_PageView* page_view) const {
254 CXFA_FFWidget::IteratorIface* pWidgetIterator =
255 GCedWidgetIteratorForPage(GetXFAPageView(), page_view);
256 if (!pWidgetIterator)
257 return nullptr;
258
259 return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToLast());
260 }
261
HasFormFieldAtPoint(const CFX_PointF & point) const262 int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const {
263 CXFA_FFPageView* pPageView = GetXFAPageView();
264 if (!pPageView)
265 return -1;
266
267 CXFA_FFDocView* pDocView = pPageView->GetDocView();
268 if (!pDocView)
269 return -1;
270
271 CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
272 if (!pWidgetHandler)
273 return -1;
274 CXFA_FFPageWidgetIterator pWidgetIterator(pPageView,
275 XFA_WidgetStatus::kViewable);
276
277 CXFA_FFWidget* pXFAAnnot;
278 while ((pXFAAnnot = pWidgetIterator.MoveToNext()) != nullptr) {
279 if (pXFAAnnot->GetFormFieldType() == FormFieldType::kXFA)
280 continue;
281
282 CFX_FloatRect rcWidget = pXFAAnnot->GetWidgetRect().ToFloatRect();
283 rcWidget.Inflate(1.0f, 1.0f);
284 if (rcWidget.Contains(point))
285 return static_cast<int>(pXFAAnnot->GetFormFieldType());
286 }
287
288 return -1;
289 }
290
DrawFocusAnnot(CFX_RenderDevice * pDevice,CPDFSDK_Annot * pAnnot,const CFX_Matrix & mtUser2Device,const FX_RECT & rtClip)291 void CPDFXFA_Page::DrawFocusAnnot(CFX_RenderDevice* pDevice,
292 CPDFSDK_Annot* pAnnot,
293 const CFX_Matrix& mtUser2Device,
294 const FX_RECT& rtClip) {
295 CFX_RectF rectClip(rtClip);
296 CFGAS_GEGraphics gs(pDevice);
297 gs.SetClipRect(rectClip);
298
299 CXFA_FFPageView* xfaView = GetXFAPageView();
300 CXFA_FFPageWidgetIterator pWidgetIterator(
301 xfaView, Mask<XFA_WidgetStatus>{XFA_WidgetStatus::kVisible,
302 XFA_WidgetStatus::kViewable});
303
304 while (true) {
305 CXFA_FFWidget* pWidget = pWidgetIterator.MoveToNext();
306 if (!pWidget)
307 break;
308
309 CFX_RectF rtWidgetBox = pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus);
310 ++rtWidgetBox.width;
311 ++rtWidgetBox.height;
312 if (rtWidgetBox.IntersectWith(rectClip))
313 pWidget->RenderWidget(&gs, mtUser2Device, CXFA_FFWidget::kHighlight);
314 }
315
316 CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
317 if (!pXFAWidget)
318 return;
319
320 CXFA_FFDocView* docView = xfaView->GetDocView();
321 if (!docView)
322 return;
323
324 docView->GetWidgetHandler()->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs,
325 mtUser2Device, false);
326 }
327