1 // Copyright 2016 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_docenvironment.h"
8
9 #include <utility>
10
11 #include "core/fpdfapi/parser/cpdf_array.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_stream.h"
14 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
15 #include "core/fpdfapi/parser/cpdf_string.h"
16 #include "core/fxcrt/retain_ptr.h"
17 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
18 #include "fpdfsdk/cpdfsdk_helpers.h"
19 #include "fpdfsdk/cpdfsdk_interactiveform.h"
20 #include "fpdfsdk/cpdfsdk_pageview.h"
21 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
22 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
23 #include "third_party/base/check.h"
24 #include "xfa/fxfa/cxfa_ffdocview.h"
25 #include "xfa/fxfa/cxfa_ffwidget.h"
26 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
27 #include "xfa/fxfa/cxfa_readynodeiterator.h"
28 #include "xfa/fxfa/parser/cxfa_node.h"
29 #include "xfa/fxfa/parser/cxfa_submit.h"
30
31 #define IDS_XFA_Validate_Input \
32 "At least one required field was empty. Please fill in the required " \
33 "fields\r\n(highlighted) before continuing."
34
35 // submit
36 #define FXFA_CONFIG 0x00000001
37 #define FXFA_TEMPLATE 0x00000010
38 #define FXFA_LOCALESET 0x00000100
39 #define FXFA_DATASETS 0x00001000
40 #define FXFA_XMPMETA 0x00010000
41 #define FXFA_XFDF 0x00100000
42 #define FXFA_FORM 0x01000000
43 #define FXFA_PDF 0x10000000
44 #define FXFA_XFA_ALL 0x01111111
45
46 // Although there isn't direct casting between these types at present,
47 // keep the internal and exernal types in sync.
48 static_assert(FXFA_PAGEVIEWEVENT_POSTADDED ==
49 static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostAdded),
50 "kPostAdded mismatch");
51 static_assert(FXFA_PAGEVIEWEVENT_POSTREMOVED ==
52 static_cast<int>(CXFA_FFDoc::PageViewEvent::kPostRemoved),
53 "kPostRemoved mismatch");
54
CPDFXFA_DocEnvironment(CPDFXFA_Context * pContext)55 CPDFXFA_DocEnvironment::CPDFXFA_DocEnvironment(CPDFXFA_Context* pContext)
56 : m_pContext(pContext) {
57 DCHECK(m_pContext);
58 }
59
60 CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() = default;
61
SetChangeMark(CXFA_FFDoc * hDoc)62 void CPDFXFA_DocEnvironment::SetChangeMark(CXFA_FFDoc* hDoc) {
63 if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
64 m_pContext->GetFormFillEnv()->SetChangeMark();
65 }
66
InvalidateRect(CXFA_FFPageView * pPageView,const CFX_RectF & rt)67 void CPDFXFA_DocEnvironment::InvalidateRect(CXFA_FFPageView* pPageView,
68 const CFX_RectF& rt) {
69 if (!m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
70 return;
71
72 if (m_pContext->GetFormType() != FormType::kXFAFull)
73 return;
74
75 RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pPageView);
76 if (!pPage)
77 return;
78
79 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
80 if (!pFormFillEnv)
81 return;
82
83 pFormFillEnv->Invalidate(pPage.Get(), rt.ToFloatRect().ToFxRect());
84 }
85
DisplayCaret(CXFA_FFWidget * hWidget,bool bVisible,const CFX_RectF * pRtAnchor)86 void CPDFXFA_DocEnvironment::DisplayCaret(CXFA_FFWidget* hWidget,
87 bool bVisible,
88 const CFX_RectF* pRtAnchor) {
89 if (!hWidget || !pRtAnchor || !m_pContext->GetXFADoc() ||
90 !m_pContext->GetFormFillEnv() || !m_pContext->GetXFADocView())
91 return;
92
93 if (m_pContext->GetFormType() != FormType::kXFAFull)
94 return;
95
96 CXFA_FFWidgetHandler* pWidgetHandler =
97 m_pContext->GetXFADocView()->GetWidgetHandler();
98 if (!pWidgetHandler)
99 return;
100
101 CXFA_FFPageView* pPageView = hWidget->GetPageView();
102 if (!pPageView)
103 return;
104
105 RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pPageView);
106 if (!pPage)
107 return;
108
109 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
110 if (!pFormFillEnv)
111 return;
112
113 CFX_FloatRect rcCaret = pRtAnchor->ToFloatRect();
114 pFormFillEnv->DisplayCaret(pPage.Get(), bVisible, rcCaret.left, rcCaret.top,
115 rcCaret.right, rcCaret.bottom);
116 }
117
GetPopupPos(CXFA_FFWidget * hWidget,float fMinPopup,float fMaxPopup,const CFX_RectF & rtAnchor,CFX_RectF * pPopupRect)118 bool CPDFXFA_DocEnvironment::GetPopupPos(CXFA_FFWidget* hWidget,
119 float fMinPopup,
120 float fMaxPopup,
121 const CFX_RectF& rtAnchor,
122 CFX_RectF* pPopupRect) {
123 if (!hWidget)
124 return false;
125
126 CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
127 if (!pXFAPageView)
128 return false;
129
130 RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pXFAPageView);
131 if (!pPage)
132 return false;
133
134 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
135 if (!pFormFillEnv)
136 return false;
137
138 FS_RECTF page_view_rect = pFormFillEnv->GetPageViewRect(pPage.Get());
139 int nRotate = hWidget->GetNode()->GetRotate();
140
141 int space_available_below_anchor;
142 int space_available_above_anchor;
143 switch (nRotate) {
144 case 0:
145 default: {
146 space_available_below_anchor =
147 static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
148 space_available_above_anchor =
149 static_cast<int>(rtAnchor.top - page_view_rect.top);
150
151 if (rtAnchor.left < page_view_rect.left)
152 pPopupRect->left += page_view_rect.left - rtAnchor.left;
153 if (rtAnchor.right() > page_view_rect.right)
154 pPopupRect->left -= rtAnchor.right() - page_view_rect.right;
155 break;
156 }
157 case 90: {
158 space_available_below_anchor =
159 static_cast<int>(page_view_rect.right - rtAnchor.right());
160 space_available_above_anchor =
161 static_cast<int>(rtAnchor.left - page_view_rect.left);
162
163 if (rtAnchor.bottom() > page_view_rect.bottom)
164 pPopupRect->left += rtAnchor.bottom() - page_view_rect.bottom;
165 if (rtAnchor.top < page_view_rect.top)
166 pPopupRect->left -= page_view_rect.top - rtAnchor.top;
167 break;
168 }
169 case 180: {
170 space_available_below_anchor =
171 static_cast<int>(rtAnchor.top - page_view_rect.top);
172 space_available_above_anchor =
173 static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
174
175 if (rtAnchor.right() > page_view_rect.right)
176 pPopupRect->left += rtAnchor.right() - page_view_rect.right;
177 if (rtAnchor.left < page_view_rect.left)
178 pPopupRect->left -= page_view_rect.left - rtAnchor.left;
179 break;
180 }
181 case 270: {
182 space_available_below_anchor =
183 static_cast<int>(rtAnchor.left - page_view_rect.left);
184 space_available_above_anchor =
185 static_cast<int>(page_view_rect.right - rtAnchor.right());
186
187 if (rtAnchor.top < page_view_rect.top)
188 pPopupRect->left += page_view_rect.top - rtAnchor.top;
189 if (rtAnchor.bottom() > page_view_rect.bottom)
190 pPopupRect->left -= rtAnchor.bottom() - page_view_rect.bottom;
191 break;
192 }
193 }
194
195 // If there is no space on either side, the popup can't be rendered.
196 if (space_available_below_anchor <= 0 && space_available_above_anchor <= 0)
197 return false;
198
199 // Determine whether to draw above or below the anchor.
200 bool draw_below_anchor;
201 if (space_available_below_anchor <= 0)
202 draw_below_anchor = false;
203 else if (space_available_above_anchor <= 0)
204 draw_below_anchor = true;
205 else if (space_available_below_anchor > space_available_above_anchor)
206 draw_below_anchor = true;
207 else
208 draw_below_anchor = false;
209
210 int space_available = (draw_below_anchor ? space_available_below_anchor
211 : space_available_above_anchor);
212
213 // Set the popup height and y position according to what was decided above.
214 float popup_height;
215 if (space_available < fMinPopup)
216 popup_height = fMinPopup;
217 else if (space_available > fMaxPopup)
218 popup_height = fMaxPopup;
219 else
220 popup_height = static_cast<float>(space_available);
221
222 switch (nRotate) {
223 case 0:
224 case 180: {
225 if (draw_below_anchor)
226 pPopupRect->top = rtAnchor.height;
227 else
228 pPopupRect->top = -popup_height;
229 break;
230 }
231 case 90:
232 case 270: {
233 if (draw_below_anchor)
234 pPopupRect->top = rtAnchor.width;
235 else
236 pPopupRect->top = -popup_height;
237 break;
238 }
239 default:
240 break;
241 }
242
243 pPopupRect->height = popup_height;
244 return true;
245 }
246
PopupMenu(CXFA_FFWidget * hWidget,const CFX_PointF & ptPopup)247 bool CPDFXFA_DocEnvironment::PopupMenu(CXFA_FFWidget* hWidget,
248 const CFX_PointF& ptPopup) {
249 if (!hWidget)
250 return false;
251
252 CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
253 if (!pXFAPageView)
254 return false;
255
256 RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pXFAPageView);
257 if (!pPage)
258 return false;
259
260 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
261 if (!pFormFillEnv)
262 return false;
263
264 int menuFlag = 0;
265 if (hWidget->CanUndo())
266 menuFlag |= FXFA_MENU_UNDO;
267 if (hWidget->CanRedo())
268 menuFlag |= FXFA_MENU_REDO;
269 if (hWidget->CanPaste())
270 menuFlag |= FXFA_MENU_PASTE;
271 if (hWidget->CanCopy())
272 menuFlag |= FXFA_MENU_COPY;
273 if (hWidget->CanCut())
274 menuFlag |= FXFA_MENU_CUT;
275 if (hWidget->CanSelectAll())
276 menuFlag |= FXFA_MENU_SELECTALL;
277
278 return pFormFillEnv->PopupMenu(pPage.Get(), menuFlag, ptPopup);
279 }
280
OnPageViewEvent(CXFA_FFPageView * pPageView,CXFA_FFDoc::PageViewEvent eEvent)281 void CPDFXFA_DocEnvironment::OnPageViewEvent(CXFA_FFPageView* pPageView,
282 CXFA_FFDoc::PageViewEvent eEvent) {
283 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
284 if (!pFormFillEnv)
285 return;
286
287 if (m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kLoading ||
288 m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kClosing ||
289 eEvent != CXFA_FFDoc::PageViewEvent::kStopLayout) {
290 return;
291 }
292 int nNewCount = m_pContext->GetPageCount();
293 if (nNewCount == m_pContext->GetOriginalPageCount())
294 return;
295
296 CXFA_FFDocView* pXFADocView = m_pContext->GetXFADocView();
297 if (!pXFADocView)
298 return;
299
300 for (int i = 0; i < m_pContext->GetOriginalPageCount(); ++i) {
301 RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(i);
302 if (!pPage)
303 continue;
304
305 m_pContext->GetFormFillEnv()->RemovePageView(pPage.Get());
306 pPage->SetXFAPageViewIndex(i);
307 }
308
309 int flag = (nNewCount < m_pContext->GetOriginalPageCount())
310 ? FXFA_PAGEVIEWEVENT_POSTREMOVED
311 : FXFA_PAGEVIEWEVENT_POSTADDED;
312 int count = abs(nNewCount - m_pContext->GetOriginalPageCount());
313 m_pContext->SetOriginalPageCount(nNewCount);
314 pFormFillEnv->PageEvent(count, flag);
315 }
316
WidgetPostAdd(CXFA_FFWidget * hWidget)317 void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) {
318 if (m_pContext->GetFormType() != FormType::kXFAFull)
319 return;
320
321 CXFA_FFPageView* pPageView = hWidget->GetPageView();
322 if (!pPageView)
323 return;
324
325 RetainPtr<CPDFXFA_Page> pXFAPage = m_pContext->GetXFAPage(pPageView);
326 if (!pXFAPage)
327 return;
328
329 auto* formfill = m_pContext->GetFormFillEnv();
330 formfill->GetOrCreatePageView(pXFAPage.Get())->AddAnnotForFFWidget(hWidget);
331 }
332
WidgetPreRemove(CXFA_FFWidget * hWidget)333 void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) {
334 if (m_pContext->GetFormType() != FormType::kXFAFull)
335 return;
336
337 CXFA_FFPageView* pPageView = hWidget->GetPageView();
338 if (!pPageView)
339 return;
340
341 RetainPtr<CPDFXFA_Page> pXFAPage = m_pContext->GetXFAPage(pPageView);
342 if (!pXFAPage)
343 return;
344
345 CPDFSDK_PageView* pSdkPageView =
346 m_pContext->GetFormFillEnv()->GetOrCreatePageView(pXFAPage.Get());
347 pSdkPageView->DeleteAnnotForFFWidget(hWidget);
348 }
349
CountPages(const CXFA_FFDoc * hDoc) const350 int32_t CPDFXFA_DocEnvironment::CountPages(const CXFA_FFDoc* hDoc) const {
351 if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
352 return m_pContext->GetPageCount();
353 return 0;
354 }
355
GetCurrentPage(const CXFA_FFDoc * hDoc) const356 int32_t CPDFXFA_DocEnvironment::GetCurrentPage(const CXFA_FFDoc* hDoc) const {
357 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
358 return -1;
359
360 if (m_pContext->GetFormType() != FormType::kXFAFull)
361 return -1;
362
363 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
364 return pFormFillEnv ? pFormFillEnv->GetCurrentPageIndex() : -1;
365 }
366
SetCurrentPage(CXFA_FFDoc * hDoc,int32_t iCurPage)367 void CPDFXFA_DocEnvironment::SetCurrentPage(CXFA_FFDoc* hDoc,
368 int32_t iCurPage) {
369 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv() ||
370 !m_pContext->ContainsExtensionForm() || iCurPage < 0 ||
371 iCurPage >= m_pContext->GetFormFillEnv()->GetPageCount()) {
372 return;
373 }
374
375 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
376 if (!pFormFillEnv)
377 return;
378
379 pFormFillEnv->SetCurrentPage(iCurPage);
380 }
381
IsCalculationsEnabled(const CXFA_FFDoc * hDoc) const382 bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(
383 const CXFA_FFDoc* hDoc) const {
384 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
385 return false;
386 auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
387 return pForm->IsXfaCalculateEnabled();
388 }
389
SetCalculationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)390 void CPDFXFA_DocEnvironment::SetCalculationsEnabled(CXFA_FFDoc* hDoc,
391 bool bEnabled) {
392 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
393 return;
394 m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaEnableCalculate(
395 bEnabled);
396 }
397
GetTitle(const CXFA_FFDoc * hDoc) const398 WideString CPDFXFA_DocEnvironment::GetTitle(const CXFA_FFDoc* hDoc) const {
399 if (hDoc != m_pContext->GetXFADoc())
400 return WideString();
401
402 CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc();
403 if (!pPDFDoc)
404 return WideString();
405
406 RetainPtr<const CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
407 if (!pInfoDict)
408 return WideString();
409
410 ByteString csTitle = pInfoDict->GetByteStringFor("Title");
411 return WideString::FromDefANSI(csTitle.AsStringView());
412 }
413
SetTitle(CXFA_FFDoc * hDoc,const WideString & wsTitle)414 void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc,
415 const WideString& wsTitle) {
416 if (hDoc != m_pContext->GetXFADoc())
417 return;
418
419 CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc();
420 if (!pPDFDoc)
421 return;
422
423 RetainPtr<CPDF_Dictionary> pInfoDict = pPDFDoc->GetInfo();
424 if (pInfoDict)
425 pInfoDict->SetNewFor<CPDF_String>("Title", wsTitle.AsStringView());
426 }
427
ExportData(CXFA_FFDoc * hDoc,const WideString & wsFilePath,bool bXDP)428 void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc,
429 const WideString& wsFilePath,
430 bool bXDP) {
431 if (hDoc != m_pContext->GetXFADoc())
432 return;
433
434 if (!m_pContext->ContainsExtensionForm())
435 return;
436
437 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
438 if (!pFormFillEnv)
439 return;
440
441 int fileType = bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML;
442 ByteString bs = wsFilePath.ToUTF16LE();
443 if (wsFilePath.IsEmpty()) {
444 if (!pFormFillEnv->GetFormFillInfo() ||
445 !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform) {
446 return;
447 }
448
449 WideString filepath = pFormFillEnv->JS_fieldBrowse();
450 bs = filepath.ToUTF16LE();
451 }
452 FPDF_FILEHANDLER* pFileHandler = pFormFillEnv->OpenFile(
453 bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML, AsFPDFWideString(&bs), "wb");
454 if (!pFileHandler)
455 return;
456
457 RetainPtr<IFX_SeekableStream> fileWrite = MakeSeekableStream(pFileHandler);
458 if (fileType == FXFA_SAVEAS_XML) {
459 fileWrite->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
460 CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
461 ffdoc->SavePackage(
462 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileWrite);
463 } else if (fileType == FXFA_SAVEAS_XDP) {
464 if (!m_pContext->GetPDFDoc())
465 return;
466
467 const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
468 if (!pRoot)
469 return;
470
471 RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
472 if (!pAcroForm)
473 return;
474
475 RetainPtr<const CPDF_Array> pArray =
476 ToArray(pAcroForm->GetObjectFor("XFA"));
477 if (!pArray)
478 return;
479
480 for (size_t i = 1; i < pArray->size(); i += 2) {
481 RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
482 RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
483 if (!pPrePDFObj->IsString())
484 continue;
485 if (!pPDFObj->IsReference())
486 continue;
487
488 RetainPtr<const CPDF_Stream> pStream = ToStream(pPDFObj->GetDirect());
489 if (!pStream)
490 continue;
491 if (pPrePDFObj->GetString() == "form") {
492 CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
493 ffdoc->SavePackage(
494 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
495 fileWrite);
496 continue;
497 }
498 if (pPrePDFObj->GetString() == "datasets") {
499 CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
500 ffdoc->SavePackage(
501 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
502 fileWrite);
503 continue;
504 }
505 if (i == pArray->size() - 1) {
506 WideString wPath = WideString::FromUTF16LE(
507 reinterpret_cast<const unsigned short*>(bs.c_str()),
508 bs.GetLength() / sizeof(unsigned short));
509 ByteString bPath = wPath.ToUTF8();
510 static const char kFormat[] =
511 "\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
512 ByteString content = ByteString::Format(kFormat, bPath.c_str());
513 fileWrite->WriteString(content.AsStringView());
514 }
515 auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
516 pAcc->LoadAllDataFiltered();
517 fileWrite->WriteBlock(pAcc->GetSpan());
518 }
519 }
520 fileWrite->Flush();
521 }
522
GotoURL(CXFA_FFDoc * hDoc,const WideString & wsURL)523 void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
524 const WideString& wsURL) {
525 if (hDoc != m_pContext->GetXFADoc())
526 return;
527
528 if (m_pContext->GetFormType() != FormType::kXFAFull)
529 return;
530
531 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
532 if (!pFormFillEnv)
533 return;
534
535 pFormFillEnv->GotoURL(wsURL);
536 }
537
IsValidationsEnabled(const CXFA_FFDoc * hDoc) const538 bool CPDFXFA_DocEnvironment::IsValidationsEnabled(
539 const CXFA_FFDoc* hDoc) const {
540 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
541 return false;
542
543 auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
544 return pForm->IsXfaValidationsEnabled();
545 }
546
SetValidationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)547 void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
548 bool bEnabled) {
549 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
550 return;
551
552 m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
553 bEnabled);
554 }
555
SetFocusWidget(CXFA_FFDoc * hDoc,CXFA_FFWidget * hWidget)556 void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
557 CXFA_FFWidget* hWidget) {
558 if (hDoc != m_pContext->GetXFADoc())
559 return;
560
561 if (!hWidget) {
562 ObservedPtr<CPDFSDK_Annot> pNull;
563 m_pContext->GetFormFillEnv()->SetFocusAnnot(pNull);
564 return;
565 }
566
567 int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount();
568 for (int i = 0; i < pageViewCount; i++) {
569 CPDFSDK_PageView* pPageView =
570 m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i);
571 if (!pPageView)
572 continue;
573
574 ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotForFFWidget(hWidget));
575 if (pAnnot) {
576 m_pContext->GetFormFillEnv()->SetFocusAnnot(pAnnot);
577 break;
578 }
579 }
580 }
581
Print(CXFA_FFDoc * hDoc,int32_t nStartPage,int32_t nEndPage,Mask<XFA_PrintOpt> dwOptions)582 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
583 int32_t nStartPage,
584 int32_t nEndPage,
585 Mask<XFA_PrintOpt> dwOptions) {
586 if (hDoc != m_pContext->GetXFADoc())
587 return;
588
589 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
590 if (!pFormFillEnv || !pFormFillEnv->GetFormFillInfo() ||
591 !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform ||
592 !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print) {
593 return;
594 }
595
596 pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
597 pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
598 !!(dwOptions & XFA_PrintOpt::kShowDialog), nStartPage, nEndPage,
599 !!(dwOptions & XFA_PrintOpt::kCanCancel),
600 !!(dwOptions & XFA_PrintOpt::kShrinkPage),
601 !!(dwOptions & XFA_PrintOpt::kAsImage),
602 !!(dwOptions & XFA_PrintOpt::kReverseOrder),
603 !!(dwOptions & XFA_PrintOpt::kPrintAnnot));
604 }
605
GetHighlightColor(const CXFA_FFDoc * hDoc) const606 FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(
607 const CXFA_FFDoc* hDoc) const {
608 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
609 return 0;
610
611 CPDFSDK_InteractiveForm* pForm =
612 m_pContext->GetFormFillEnv()->GetInteractiveForm();
613 return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
614 pForm->GetHighlightColor(FormFieldType::kXFA));
615 }
616
GetIJSRuntime(const CXFA_FFDoc * hDoc) const617 IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(
618 const CXFA_FFDoc* hDoc) const {
619 if (hDoc != m_pContext->GetXFADoc())
620 return nullptr;
621
622 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
623 return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
624 }
625
GetXMLDoc() const626 CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const {
627 return m_pContext->GetXMLDoc();
628 }
629
OpenLinkedFile(CXFA_FFDoc * hDoc,const WideString & wsLink)630 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
631 CXFA_FFDoc* hDoc,
632 const WideString& wsLink) {
633 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
634 if (!pFormFillEnv)
635 return nullptr;
636
637 ByteString bs = wsLink.ToUTF16LE();
638 FPDF_FILEHANDLER* pFileHandler =
639 pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
640 if (!pFileHandler)
641 return nullptr;
642
643 return MakeSeekableStream(pFileHandler);
644 }
645
646 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
Submit(CXFA_FFDoc * hDoc,CXFA_Submit * submit)647 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
648 if (!OnBeforeNotifySubmit() || !m_pContext->GetXFADocView())
649 return false;
650
651 m_pContext->GetXFADocView()->UpdateDocView();
652 bool ret = SubmitInternal(hDoc, submit);
653 OnAfterNotifySubmit();
654 return ret;
655 }
656
MailToInfo(WideString & csURL,WideString & csToAddress,WideString & csCCAddress,WideString & csBCCAddress,WideString & csSubject,WideString & csMsg)657 bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
658 WideString& csToAddress,
659 WideString& csCCAddress,
660 WideString& csBCCAddress,
661 WideString& csSubject,
662 WideString& csMsg) {
663 WideString srcURL = csURL;
664 srcURL.TrimLeft();
665 if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
666 return false;
667
668 auto pos = srcURL.Find(L'?');
669
670 {
671 WideString tmp;
672 if (!pos.has_value()) {
673 pos = srcURL.Find(L'@');
674 if (!pos.has_value())
675 return false;
676
677 tmp = srcURL.Right(csURL.GetLength() - 7);
678 } else {
679 tmp = srcURL.Left(pos.value());
680 tmp = tmp.Right(tmp.GetLength() - 7);
681 }
682 tmp.Trim();
683 csToAddress = std::move(tmp);
684 }
685
686 srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
687 while (!srcURL.IsEmpty()) {
688 srcURL.Trim();
689 pos = srcURL.Find(L'&');
690 WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
691 tmp.Trim();
692 if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
693 tmp = tmp.Right(tmp.GetLength() - 3);
694 if (!csCCAddress.IsEmpty())
695 csCCAddress += L';';
696 csCCAddress += tmp;
697 } else if (tmp.GetLength() >= 4 &&
698 tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
699 tmp = tmp.Right(tmp.GetLength() - 4);
700 if (!csBCCAddress.IsEmpty())
701 csBCCAddress += L';';
702 csBCCAddress += tmp;
703 } else if (tmp.GetLength() >= 8 &&
704 tmp.Left(8).CompareNoCase(L"subject=") == 0) {
705 tmp = tmp.Right(tmp.GetLength() - 8);
706 csSubject += tmp;
707 } else if (tmp.GetLength() >= 5 &&
708 tmp.Left(5).CompareNoCase(L"body=") == 0) {
709 tmp = tmp.Right(tmp.GetLength() - 5);
710 csMsg += tmp;
711 }
712 srcURL = pos.has_value()
713 ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
714 : WideString();
715 }
716 csToAddress.Replace(L",", L";");
717 csCCAddress.Replace(L",", L";");
718 csBCCAddress.Replace(L",", L";");
719 return true;
720 }
721
ExportSubmitFile(FPDF_FILEHANDLER * pFileHandler,int fileType,FPDF_DWORD encodeType,FPDF_DWORD flag)722 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
723 int fileType,
724 FPDF_DWORD encodeType,
725 FPDF_DWORD flag) {
726 if (!m_pContext->GetXFADocView())
727 return false;
728
729 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
730 if (!pFormFillEnv)
731 return false;
732
733 CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
734 RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
735 if (fileType == FXFA_SAVEAS_XML) {
736 fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
737 ffdoc->SavePackage(
738 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
739 fileStream);
740 return true;
741 }
742
743 if (fileType != FXFA_SAVEAS_XDP)
744 return true;
745
746 if (!flag) {
747 flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
748 FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
749 }
750 if (!m_pContext->GetPDFDoc()) {
751 fileStream->Flush();
752 return false;
753 }
754
755 const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
756 if (!pRoot) {
757 fileStream->Flush();
758 return false;
759 }
760
761 RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
762 if (!pAcroForm) {
763 fileStream->Flush();
764 return false;
765 }
766
767 RetainPtr<const CPDF_Array> pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
768 if (!pArray) {
769 fileStream->Flush();
770 return false;
771 }
772
773 for (size_t i = 1; i < pArray->size(); i += 2) {
774 RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
775 RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
776 if (!pPrePDFObj->IsString())
777 continue;
778 if (!pPDFObj->IsReference())
779 continue;
780
781 RetainPtr<const CPDF_Object> pDirectObj = pPDFObj->GetDirect();
782 if (!pDirectObj->IsStream())
783 continue;
784 ByteString bsType = pPrePDFObj->GetString();
785 if (bsType == "config" && !(flag & FXFA_CONFIG))
786 continue;
787 if (bsType == "template" && !(flag & FXFA_TEMPLATE))
788 continue;
789 if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
790 continue;
791 if (bsType == "datasets" && !(flag & FXFA_DATASETS))
792 continue;
793 if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
794 continue;
795 if (bsType == "xfdf" && !(flag & FXFA_XFDF))
796 continue;
797 if (bsType == "form" && !(flag & FXFA_FORM))
798 continue;
799
800 if (bsType == "form") {
801 ffdoc->SavePackage(
802 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
803 fileStream);
804 } else if (pPrePDFObj->GetString() == "datasets") {
805 ffdoc->SavePackage(
806 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
807 fileStream);
808 }
809 }
810 return true;
811 }
812
ToXFAContentFlags(WideString csSrcContent,FPDF_DWORD & flag)813 void CPDFXFA_DocEnvironment::ToXFAContentFlags(WideString csSrcContent,
814 FPDF_DWORD& flag) {
815 if (csSrcContent.Contains(L" config "))
816 flag |= FXFA_CONFIG;
817 if (csSrcContent.Contains(L" template "))
818 flag |= FXFA_TEMPLATE;
819 if (csSrcContent.Contains(L" localeSet "))
820 flag |= FXFA_LOCALESET;
821 if (csSrcContent.Contains(L" datasets "))
822 flag |= FXFA_DATASETS;
823 if (csSrcContent.Contains(L" xmpmeta "))
824 flag |= FXFA_XMPMETA;
825 if (csSrcContent.Contains(L" xfdf "))
826 flag |= FXFA_XFDF;
827 if (csSrcContent.Contains(L" form "))
828 flag |= FXFA_FORM;
829 if (flag == 0) {
830 flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
831 FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
832 }
833 }
834
OnBeforeNotifySubmit()835 bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
836 if (!m_pContext->ContainsXFAForm())
837 return true;
838
839 CXFA_FFDocView* docView = m_pContext->GetXFADocView();
840 if (!docView)
841 return true;
842
843 CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
844 if (!pWidgetHandler)
845 return true;
846
847 auto it = docView->CreateReadyNodeIterator();
848 if (it) {
849 CXFA_EventParam Param;
850 Param.m_eType = XFA_EVENT_PreSubmit;
851 while (CXFA_Node* pNode = it->MoveToNext())
852 pWidgetHandler->ProcessEvent(pNode, &Param);
853 }
854
855 it = docView->CreateReadyNodeIterator();
856 if (!it)
857 return true;
858
859 (void)it->MoveToNext();
860 CXFA_Node* pNode = it->MoveToNext();
861
862 while (pNode) {
863 if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
864 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
865 if (!pFormFillEnv)
866 return false;
867
868 pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
869 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
870 JSPLATFORM_ALERT_ICON_WARNING);
871 return false;
872 }
873 pNode = it->MoveToNext();
874 }
875
876 docView->UpdateDocView();
877 return true;
878 }
879
OnAfterNotifySubmit()880 void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
881 if (!m_pContext->ContainsXFAForm())
882 return;
883
884 if (!m_pContext->GetXFADocView())
885 return;
886
887 CXFA_FFWidgetHandler* pWidgetHandler =
888 m_pContext->GetXFADocView()->GetWidgetHandler();
889 if (!pWidgetHandler)
890 return;
891
892 auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
893 if (!it)
894 return;
895
896 CXFA_EventParam Param;
897 Param.m_eType = XFA_EVENT_PostSubmit;
898 CXFA_Node* pNode = it->MoveToNext();
899 while (pNode) {
900 pWidgetHandler->ProcessEvent(pNode, &Param);
901 pNode = it->MoveToNext();
902 }
903 m_pContext->GetXFADocView()->UpdateDocView();
904 }
905
SubmitInternal(CXFA_FFDoc * hDoc,CXFA_Submit * submit)906 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
907 CXFA_Submit* submit) {
908 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
909 if (!pFormFillEnv)
910 return false;
911
912 WideString csURL = submit->GetSubmitTarget();
913 if (csURL.IsEmpty()) {
914 pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
915 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
916 JSPLATFORM_ALERT_ICON_ASTERISK);
917 return false;
918 }
919
920 FPDF_FILEHANDLER* pFileHandler = nullptr;
921 int fileFlag = -1;
922 switch (submit->GetSubmitFormat()) {
923 case XFA_AttributeValue::Xdp: {
924 WideString csContent = submit->GetSubmitXDPContent();
925 csContent.Trim();
926
927 WideString space = WideString::FromDefANSI(" ");
928 csContent = space + csContent + space;
929 FPDF_DWORD flag = 0;
930 if (submit->IsSubmitEmbedPDF())
931 flag |= FXFA_PDF;
932
933 ToXFAContentFlags(csContent, flag);
934 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XDP, nullptr, "wb");
935 fileFlag = FXFA_SAVEAS_XDP;
936 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
937 break;
938 }
939 case XFA_AttributeValue::Xml:
940 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
941 fileFlag = FXFA_SAVEAS_XML;
942 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
943 break;
944 case XFA_AttributeValue::Pdf:
945 break;
946 case XFA_AttributeValue::Urlencoded:
947 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
948 fileFlag = FXFA_SAVEAS_XML;
949 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
950 break;
951 default:
952 return false;
953 }
954 if (!pFileHandler)
955 return false;
956
957 if (csURL.Left(7).CompareNoCase(L"mailto:") == 0) {
958 WideString csToAddress;
959 WideString csCCAddress;
960 WideString csBCCAddress;
961 WideString csSubject;
962 WideString csMsg;
963 if (!MailToInfo(csURL, csToAddress, csCCAddress, csBCCAddress, csSubject,
964 csMsg)) {
965 return false;
966 }
967 ByteString bsTo = WideString(csToAddress).ToUTF16LE();
968 ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
969 ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
970 ByteString bsSubject = WideString(csSubject).ToUTF16LE();
971 ByteString bsMsg = WideString(csMsg).ToUTF16LE();
972 pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
973 AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
974 AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
975 return true;
976 }
977
978 // HTTP or FTP
979 ByteString bs = csURL.ToUTF16LE();
980 pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
981 return true;
982 }
983 #endif // PDF_XFA_ELEMENT_SUBMIT_ENABLED
984