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/check.h"
17 #include "core/fxcrt/retain_ptr.h"
18 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
19 #include "fpdfsdk/cpdfsdk_helpers.h"
20 #include "fpdfsdk/cpdfsdk_interactiveform.h"
21 #include "fpdfsdk/cpdfsdk_pageview.h"
22 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
23 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.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(bs.unsigned_span());
507 ByteString bPath = wPath.ToUTF8();
508 static const char kFormat[] =
509 "\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
510 ByteString content = ByteString::Format(kFormat, bPath.c_str());
511 fileWrite->WriteString(content.AsStringView());
512 }
513 auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
514 pAcc->LoadAllDataFiltered();
515 fileWrite->WriteBlock(pAcc->GetSpan());
516 }
517 }
518 fileWrite->Flush();
519 }
520
GotoURL(CXFA_FFDoc * hDoc,const WideString & wsURL)521 void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
522 const WideString& wsURL) {
523 if (hDoc != m_pContext->GetXFADoc())
524 return;
525
526 if (m_pContext->GetFormType() != FormType::kXFAFull)
527 return;
528
529 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
530 if (!pFormFillEnv)
531 return;
532
533 pFormFillEnv->GotoURL(wsURL);
534 }
535
IsValidationsEnabled(const CXFA_FFDoc * hDoc) const536 bool CPDFXFA_DocEnvironment::IsValidationsEnabled(
537 const CXFA_FFDoc* hDoc) const {
538 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
539 return false;
540
541 auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
542 return pForm->IsXfaValidationsEnabled();
543 }
544
SetValidationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)545 void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
546 bool bEnabled) {
547 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
548 return;
549
550 m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
551 bEnabled);
552 }
553
SetFocusWidget(CXFA_FFDoc * hDoc,CXFA_FFWidget * hWidget)554 void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
555 CXFA_FFWidget* hWidget) {
556 if (hDoc != m_pContext->GetXFADoc())
557 return;
558
559 if (!hWidget) {
560 ObservedPtr<CPDFSDK_Annot> pNull;
561 m_pContext->GetFormFillEnv()->SetFocusAnnot(pNull);
562 return;
563 }
564
565 int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount();
566 for (int i = 0; i < pageViewCount; i++) {
567 CPDFSDK_PageView* pPageView =
568 m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i);
569 if (!pPageView)
570 continue;
571
572 ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotForFFWidget(hWidget));
573 if (pAnnot) {
574 m_pContext->GetFormFillEnv()->SetFocusAnnot(pAnnot);
575 break;
576 }
577 }
578 }
579
Print(CXFA_FFDoc * hDoc,int32_t nStartPage,int32_t nEndPage,Mask<XFA_PrintOpt> dwOptions)580 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
581 int32_t nStartPage,
582 int32_t nEndPage,
583 Mask<XFA_PrintOpt> dwOptions) {
584 if (hDoc != m_pContext->GetXFADoc())
585 return;
586
587 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
588 if (!pFormFillEnv || !pFormFillEnv->GetFormFillInfo() ||
589 !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform ||
590 !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print) {
591 return;
592 }
593
594 pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
595 pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
596 !!(dwOptions & XFA_PrintOpt::kShowDialog), nStartPage, nEndPage,
597 !!(dwOptions & XFA_PrintOpt::kCanCancel),
598 !!(dwOptions & XFA_PrintOpt::kShrinkPage),
599 !!(dwOptions & XFA_PrintOpt::kAsImage),
600 !!(dwOptions & XFA_PrintOpt::kReverseOrder),
601 !!(dwOptions & XFA_PrintOpt::kPrintAnnot));
602 }
603
GetHighlightColor(const CXFA_FFDoc * hDoc) const604 FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(
605 const CXFA_FFDoc* hDoc) const {
606 if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
607 return 0;
608
609 CPDFSDK_InteractiveForm* pForm =
610 m_pContext->GetFormFillEnv()->GetInteractiveForm();
611 return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
612 pForm->GetHighlightColor(FormFieldType::kXFA));
613 }
614
GetIJSRuntime(const CXFA_FFDoc * hDoc) const615 IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(
616 const CXFA_FFDoc* hDoc) const {
617 if (hDoc != m_pContext->GetXFADoc())
618 return nullptr;
619
620 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
621 return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
622 }
623
GetXMLDoc() const624 CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const {
625 return m_pContext->GetXMLDoc();
626 }
627
OpenLinkedFile(CXFA_FFDoc * hDoc,const WideString & wsLink)628 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
629 CXFA_FFDoc* hDoc,
630 const WideString& wsLink) {
631 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
632 if (!pFormFillEnv)
633 return nullptr;
634
635 ByteString bs = wsLink.ToUTF16LE();
636 FPDF_FILEHANDLER* pFileHandler =
637 pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
638 if (!pFileHandler)
639 return nullptr;
640
641 return MakeSeekableStream(pFileHandler);
642 }
643
644 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
Submit(CXFA_FFDoc * hDoc,CXFA_Submit * submit)645 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
646 if (!OnBeforeNotifySubmit() || !m_pContext->GetXFADocView())
647 return false;
648
649 m_pContext->GetXFADocView()->UpdateDocView();
650 bool ret = SubmitInternal(hDoc, submit);
651 OnAfterNotifySubmit();
652 return ret;
653 }
654
MailToInfo(WideString & csURL,WideString & csToAddress,WideString & csCCAddress,WideString & csBCCAddress,WideString & csSubject,WideString & csMsg)655 bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
656 WideString& csToAddress,
657 WideString& csCCAddress,
658 WideString& csBCCAddress,
659 WideString& csSubject,
660 WideString& csMsg) {
661 WideString srcURL = csURL;
662 srcURL.TrimWhitespaceFront();
663 if (!srcURL.Left(7).EqualsASCIINoCase("mailto:")) {
664 return false;
665 }
666 auto pos = srcURL.Find(L'?');
667 {
668 WideString tmp;
669 if (!pos.has_value()) {
670 pos = srcURL.Find(L'@');
671 if (!pos.has_value())
672 return false;
673
674 tmp = srcURL.Right(csURL.GetLength() - 7);
675 } else {
676 tmp = srcURL.Left(pos.value());
677 tmp = tmp.Right(tmp.GetLength() - 7);
678 }
679 tmp.TrimWhitespace();
680 csToAddress = std::move(tmp);
681 }
682
683 srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
684 while (!srcURL.IsEmpty()) {
685 srcURL.TrimWhitespace();
686 pos = srcURL.Find(L'&');
687 WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
688 tmp.TrimWhitespace();
689 if (tmp.GetLength() >= 3 && tmp.Left(3).EqualsASCIINoCase("cc=")) {
690 tmp = tmp.Right(tmp.GetLength() - 3);
691 if (!csCCAddress.IsEmpty())
692 csCCAddress += L';';
693 csCCAddress += tmp;
694 } else if (tmp.GetLength() >= 4 && tmp.Left(4).EqualsASCIINoCase("bcc=")) {
695 tmp = tmp.Right(tmp.GetLength() - 4);
696 if (!csBCCAddress.IsEmpty())
697 csBCCAddress += L';';
698 csBCCAddress += tmp;
699 } else if (tmp.GetLength() >= 8 &&
700 tmp.Left(8).EqualsASCIINoCase("subject=")) {
701 tmp = tmp.Right(tmp.GetLength() - 8);
702 csSubject += tmp;
703 } else if (tmp.GetLength() >= 5 && tmp.Left(5).EqualsASCIINoCase("body=")) {
704 tmp = tmp.Right(tmp.GetLength() - 5);
705 csMsg += tmp;
706 }
707 srcURL = pos.has_value()
708 ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
709 : WideString();
710 }
711 csToAddress.Replace(L",", L";");
712 csCCAddress.Replace(L",", L";");
713 csBCCAddress.Replace(L",", L";");
714 return true;
715 }
716
ExportSubmitFile(FPDF_FILEHANDLER * pFileHandler,int fileType,FPDF_DWORD encodeType,FPDF_DWORD flag)717 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
718 int fileType,
719 FPDF_DWORD encodeType,
720 FPDF_DWORD flag) {
721 if (!m_pContext->GetXFADocView())
722 return false;
723
724 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
725 if (!pFormFillEnv)
726 return false;
727
728 CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
729 RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
730 if (fileType == FXFA_SAVEAS_XML) {
731 fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
732 ffdoc->SavePackage(
733 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
734 fileStream);
735 return true;
736 }
737
738 if (fileType != FXFA_SAVEAS_XDP)
739 return true;
740
741 if (!flag) {
742 flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
743 FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
744 }
745 if (!m_pContext->GetPDFDoc()) {
746 fileStream->Flush();
747 return false;
748 }
749
750 const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
751 if (!pRoot) {
752 fileStream->Flush();
753 return false;
754 }
755
756 RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
757 if (!pAcroForm) {
758 fileStream->Flush();
759 return false;
760 }
761
762 RetainPtr<const CPDF_Array> pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
763 if (!pArray) {
764 fileStream->Flush();
765 return false;
766 }
767
768 for (size_t i = 1; i < pArray->size(); i += 2) {
769 RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
770 RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
771 if (!pPrePDFObj->IsString())
772 continue;
773 if (!pPDFObj->IsReference())
774 continue;
775
776 RetainPtr<const CPDF_Object> pDirectObj = pPDFObj->GetDirect();
777 if (!pDirectObj->IsStream())
778 continue;
779 ByteString bsType = pPrePDFObj->GetString();
780 if (bsType == "config" && !(flag & FXFA_CONFIG))
781 continue;
782 if (bsType == "template" && !(flag & FXFA_TEMPLATE))
783 continue;
784 if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
785 continue;
786 if (bsType == "datasets" && !(flag & FXFA_DATASETS))
787 continue;
788 if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
789 continue;
790 if (bsType == "xfdf" && !(flag & FXFA_XFDF))
791 continue;
792 if (bsType == "form" && !(flag & FXFA_FORM))
793 continue;
794
795 if (bsType == "form") {
796 ffdoc->SavePackage(
797 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
798 fileStream);
799 } else if (pPrePDFObj->GetString() == "datasets") {
800 ffdoc->SavePackage(
801 ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
802 fileStream);
803 }
804 }
805 return true;
806 }
807
ToXFAContentFlags(WideString csSrcContent,FPDF_DWORD & flag)808 void CPDFXFA_DocEnvironment::ToXFAContentFlags(WideString csSrcContent,
809 FPDF_DWORD& flag) {
810 if (csSrcContent.Contains(L" config "))
811 flag |= FXFA_CONFIG;
812 if (csSrcContent.Contains(L" template "))
813 flag |= FXFA_TEMPLATE;
814 if (csSrcContent.Contains(L" localeSet "))
815 flag |= FXFA_LOCALESET;
816 if (csSrcContent.Contains(L" datasets "))
817 flag |= FXFA_DATASETS;
818 if (csSrcContent.Contains(L" xmpmeta "))
819 flag |= FXFA_XMPMETA;
820 if (csSrcContent.Contains(L" xfdf "))
821 flag |= FXFA_XFDF;
822 if (csSrcContent.Contains(L" form "))
823 flag |= FXFA_FORM;
824 if (flag == 0) {
825 flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
826 FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
827 }
828 }
829
OnBeforeNotifySubmit()830 bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
831 if (!m_pContext->ContainsXFAForm())
832 return true;
833
834 CXFA_FFDocView* docView = m_pContext->GetXFADocView();
835 if (!docView)
836 return true;
837
838 CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
839 if (!pWidgetHandler)
840 return true;
841
842 auto it = docView->CreateReadyNodeIterator();
843 if (it) {
844 CXFA_EventParam Param;
845 Param.m_eType = XFA_EVENT_PreSubmit;
846 while (CXFA_Node* pNode = it->MoveToNext())
847 pWidgetHandler->ProcessEvent(pNode, &Param);
848 }
849
850 it = docView->CreateReadyNodeIterator();
851 if (!it)
852 return true;
853
854 (void)it->MoveToNext();
855 CXFA_Node* pNode = it->MoveToNext();
856
857 while (pNode) {
858 if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
859 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
860 if (!pFormFillEnv)
861 return false;
862
863 pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
864 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
865 JSPLATFORM_ALERT_ICON_WARNING);
866 return false;
867 }
868 pNode = it->MoveToNext();
869 }
870
871 docView->UpdateDocView();
872 return true;
873 }
874
OnAfterNotifySubmit()875 void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
876 if (!m_pContext->ContainsXFAForm())
877 return;
878
879 if (!m_pContext->GetXFADocView())
880 return;
881
882 CXFA_FFWidgetHandler* pWidgetHandler =
883 m_pContext->GetXFADocView()->GetWidgetHandler();
884 if (!pWidgetHandler)
885 return;
886
887 auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
888 if (!it)
889 return;
890
891 CXFA_EventParam Param;
892 Param.m_eType = XFA_EVENT_PostSubmit;
893 CXFA_Node* pNode = it->MoveToNext();
894 while (pNode) {
895 pWidgetHandler->ProcessEvent(pNode, &Param);
896 pNode = it->MoveToNext();
897 }
898 m_pContext->GetXFADocView()->UpdateDocView();
899 }
900
SubmitInternal(CXFA_FFDoc * hDoc,CXFA_Submit * submit)901 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
902 CXFA_Submit* submit) {
903 CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
904 if (!pFormFillEnv)
905 return false;
906
907 WideString csURL = submit->GetSubmitTarget();
908 if (csURL.IsEmpty()) {
909 pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
910 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
911 JSPLATFORM_ALERT_ICON_ASTERISK);
912 return false;
913 }
914
915 FPDF_FILEHANDLER* pFileHandler = nullptr;
916 int fileFlag = -1;
917 switch (submit->GetSubmitFormat()) {
918 case XFA_AttributeValue::Xdp: {
919 WideString csContent = submit->GetSubmitXDPContent();
920 csContent.TrimWhitespace();
921
922 WideString space = WideString::FromDefANSI(" ");
923 csContent = space + csContent + space;
924 FPDF_DWORD flag = 0;
925 if (submit->IsSubmitEmbedPDF())
926 flag |= FXFA_PDF;
927
928 ToXFAContentFlags(csContent, flag);
929 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XDP, nullptr, "wb");
930 fileFlag = FXFA_SAVEAS_XDP;
931 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
932 break;
933 }
934 case XFA_AttributeValue::Xml:
935 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
936 fileFlag = FXFA_SAVEAS_XML;
937 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
938 break;
939 case XFA_AttributeValue::Pdf:
940 break;
941 case XFA_AttributeValue::Urlencoded:
942 pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
943 fileFlag = FXFA_SAVEAS_XML;
944 ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
945 break;
946 default:
947 return false;
948 }
949 if (!pFileHandler)
950 return false;
951
952 if (csURL.Left(7).EqualsASCIINoCase("mailto:")) {
953 WideString csToAddress;
954 WideString csCCAddress;
955 WideString csBCCAddress;
956 WideString csSubject;
957 WideString csMsg;
958 if (!MailToInfo(csURL, csToAddress, csCCAddress, csBCCAddress, csSubject,
959 csMsg)) {
960 return false;
961 }
962 ByteString bsTo = WideString(csToAddress).ToUTF16LE();
963 ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
964 ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
965 ByteString bsSubject = WideString(csSubject).ToUTF16LE();
966 ByteString bsMsg = WideString(csMsg).ToUTF16LE();
967 pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
968 AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
969 AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
970 return true;
971 }
972
973 // HTTP or FTP
974 ByteString bs = csURL.ToUTF16LE();
975 pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
976 return true;
977 }
978 #endif // PDF_XFA_ELEMENT_SUBMIT_ENABLED
979