| // Copyright 2014 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 "public/fpdf_formfill.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "constants/form_fields.h" |
| #include "core/fpdfapi/page/cpdf_annotcontext.h" |
| #include "core/fpdfapi/page/cpdf_occontext.h" |
| #include "core/fpdfapi/page/cpdf_page.h" |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" |
| #include "core/fpdfapi/parser/cpdf_document.h" |
| #include "core/fpdfapi/parser/cpdf_stream.h" |
| #include "core/fpdfapi/render/cpdf_renderoptions.h" |
| #include "core/fpdfdoc/cpdf_formcontrol.h" |
| #include "core/fpdfdoc/cpdf_formfield.h" |
| #include "core/fpdfdoc/cpdf_interactiveform.h" |
| #include "core/fxge/cfx_defaultrenderdevice.h" |
| #include "core/fxge/dib/cfx_dibitmap.h" |
| #include "fpdfsdk/cpdfsdk_annot.h" |
| #include "fpdfsdk/cpdfsdk_formfillenvironment.h" |
| #include "fpdfsdk/cpdfsdk_helpers.h" |
| #include "fpdfsdk/cpdfsdk_interactiveform.h" |
| #include "fpdfsdk/cpdfsdk_pageview.h" |
| #include "public/fpdfview.h" |
| #include "third_party/abseil-cpp/absl/types/variant.h" |
| |
| #ifdef PDF_ENABLE_XFA |
| #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" |
| #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" |
| #endif // PDF_ENABLE_XFA |
| |
| #if defined(PDF_USE_SKIA) |
| class SkCanvas; |
| #endif // defined(PDF_USE_SKIA) |
| |
| #ifdef PDF_ENABLE_XFA |
| static_assert(static_cast<int>(AlertButton::kDefault) == |
| JSPLATFORM_ALERT_BUTTON_DEFAULT, |
| "Default alert button types must match"); |
| static_assert(static_cast<int>(AlertButton::kOK) == JSPLATFORM_ALERT_BUTTON_OK, |
| "OK alert button types must match"); |
| static_assert(static_cast<int>(AlertButton::kOKCancel) == |
| JSPLATFORM_ALERT_BUTTON_OKCANCEL, |
| "OKCancel alert button types must match"); |
| static_assert(static_cast<int>(AlertButton::kYesNo) == |
| JSPLATFORM_ALERT_BUTTON_YESNO, |
| "YesNo alert button types must match"); |
| static_assert(static_cast<int>(AlertButton::kYesNoCancel) == |
| JSPLATFORM_ALERT_BUTTON_YESNOCANCEL, |
| "YesNoCancel alert button types must match"); |
| |
| static_assert(static_cast<int>(AlertIcon::kDefault) == |
| JSPLATFORM_ALERT_ICON_DEFAULT, |
| "Default alert icon types must match"); |
| static_assert(static_cast<int>(AlertIcon::kError) == |
| JSPLATFORM_ALERT_ICON_ERROR, |
| "Error alert icon types must match"); |
| static_assert(static_cast<int>(AlertIcon::kWarning) == |
| JSPLATFORM_ALERT_ICON_WARNING, |
| "Warning alert icon types must match"); |
| static_assert(static_cast<int>(AlertIcon::kQuestion) == |
| JSPLATFORM_ALERT_ICON_QUESTION, |
| "Question alert icon types must match"); |
| static_assert(static_cast<int>(AlertIcon::kStatus) == |
| JSPLATFORM_ALERT_ICON_STATUS, |
| "Status alert icon types must match"); |
| static_assert(static_cast<int>(AlertIcon::kAsterisk) == |
| JSPLATFORM_ALERT_ICON_ASTERISK, |
| "Asterisk alert icon types must match"); |
| |
| static_assert(static_cast<int>(AlertReturn::kOK) == JSPLATFORM_ALERT_RETURN_OK, |
| "OK alert return types must match"); |
| static_assert(static_cast<int>(AlertReturn::kCancel) == |
| JSPLATFORM_ALERT_RETURN_CANCEL, |
| "Cancel alert return types must match"); |
| static_assert(static_cast<int>(AlertReturn::kNo) == JSPLATFORM_ALERT_RETURN_NO, |
| "No alert return types must match"); |
| static_assert(static_cast<int>(AlertReturn::kYes) == |
| JSPLATFORM_ALERT_RETURN_YES, |
| "Yes alert return types must match"); |
| |
| static_assert(static_cast<int>(FormType::kNone) == FORMTYPE_NONE, |
| "None form types must match"); |
| static_assert(static_cast<int>(FormType::kAcroForm) == FORMTYPE_ACRO_FORM, |
| "AcroForm form types must match"); |
| static_assert(static_cast<int>(FormType::kXFAFull) == FORMTYPE_XFA_FULL, |
| "XFA full form types must match"); |
| static_assert(static_cast<int>(FormType::kXFAForeground) == |
| FORMTYPE_XFA_FOREGROUND, |
| "XFA foreground form types must match"); |
| #endif // PDF_ENABLE_XFA |
| |
| static_assert(static_cast<int>(FormFieldType::kUnknown) == |
| FPDF_FORMFIELD_UNKNOWN, |
| "Unknown form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kPushButton) == |
| FPDF_FORMFIELD_PUSHBUTTON, |
| "PushButton form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kCheckBox) == |
| FPDF_FORMFIELD_CHECKBOX, |
| "CheckBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kRadioButton) == |
| FPDF_FORMFIELD_RADIOBUTTON, |
| "RadioButton form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kComboBox) == |
| FPDF_FORMFIELD_COMBOBOX, |
| "ComboBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kListBox) == |
| FPDF_FORMFIELD_LISTBOX, |
| "ListBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kTextField) == |
| FPDF_FORMFIELD_TEXTFIELD, |
| "TextField form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kSignature) == |
| FPDF_FORMFIELD_SIGNATURE, |
| "Signature form field types must match"); |
| #ifdef PDF_ENABLE_XFA |
| static_assert(static_cast<int>(FormFieldType::kXFA) == FPDF_FORMFIELD_XFA, |
| "XFA form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_CheckBox) == |
| FPDF_FORMFIELD_XFA_CHECKBOX, |
| "XFA CheckBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_ComboBox) == |
| FPDF_FORMFIELD_XFA_COMBOBOX, |
| "XFA ComboBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_ImageField) == |
| FPDF_FORMFIELD_XFA_IMAGEFIELD, |
| "XFA ImageField form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_ListBox) == |
| FPDF_FORMFIELD_XFA_LISTBOX, |
| "XFA ListBox form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_PushButton) == |
| FPDF_FORMFIELD_XFA_PUSHBUTTON, |
| "XFA PushButton form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_Signature) == |
| FPDF_FORMFIELD_XFA_SIGNATURE, |
| "XFA Signature form field types must match"); |
| static_assert(static_cast<int>(FormFieldType::kXFA_TextField) == |
| FPDF_FORMFIELD_XFA_TEXTFIELD, |
| "XFA TextField form field types must match"); |
| #endif // PDF_ENABLE_XFA |
| static_assert(kFormFieldTypeCount == FPDF_FORMFIELD_COUNT, |
| "Number of form field types must match"); |
| |
| static_assert(static_cast<int>(CPDF_AAction::kCloseDocument) == |
| FPDFDOC_AACTION_WC, |
| "CloseDocument action must match"); |
| static_assert(static_cast<int>(CPDF_AAction::kSaveDocument) == |
| FPDFDOC_AACTION_WS, |
| "SaveDocument action must match"); |
| static_assert(static_cast<int>(CPDF_AAction::kDocumentSaved) == |
| FPDFDOC_AACTION_DS, |
| "DocumentSaved action must match"); |
| static_assert(static_cast<int>(CPDF_AAction::kPrintDocument) == |
| FPDFDOC_AACTION_WP, |
| "PrintDocument action must match"); |
| static_assert(static_cast<int>(CPDF_AAction::kDocumentPrinted) == |
| FPDFDOC_AACTION_DP, |
| "DocumentPrinted action must match"); |
| |
| namespace { |
| |
| CPDFSDK_PageView* FormHandleToPageView(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE fpdf_page) { |
| IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page); |
| if (!pPage) |
| return nullptr; |
| |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| return pFormFillEnv ? pFormFillEnv->GetOrCreatePageView(pPage) : nullptr; |
| } |
| |
| #if defined(PDF_USE_SKIA) |
| using BitmapOrCanvas = absl::variant<CFX_DIBitmap*, SkCanvas*>; |
| #else |
| using BitmapOrCanvas = absl::variant<CFX_DIBitmap*>; |
| #endif |
| |
| // `dest` must be non-null. |
| void FFLCommon(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE fpdf_page, |
| BitmapOrCanvas dest, |
| int start_x, |
| int start_y, |
| int size_x, |
| int size_y, |
| int rotate, |
| int flags) { |
| if (!hHandle) { |
| return; |
| } |
| |
| IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page); |
| if (!pPage) { |
| return; |
| } |
| |
| RetainPtr<CFX_DIBitmap> holder; |
| #if defined(PDF_USE_SKIA) |
| SkCanvas* canvas = nullptr; |
| #endif |
| |
| const bool dest_is_bitmap = absl::holds_alternative<CFX_DIBitmap*>(dest); |
| if (dest_is_bitmap) { |
| holder.Reset(absl::get<CFX_DIBitmap*>(dest)); |
| CHECK(holder); |
| } else { |
| #if defined(PDF_USE_SKIA) |
| if (!CFX_DefaultRenderDevice::UseSkiaRenderer()) { |
| return; |
| } |
| |
| canvas = absl::get<SkCanvas*>(dest); |
| CHECK(canvas); |
| #endif |
| } |
| |
| CPDF_Document* pPDFDoc = pPage->GetDocument(); |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, fpdf_page); |
| |
| const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); |
| CFX_Matrix matrix = pPage->GetDisplayMatrix(rect, rotate); |
| |
| auto pDevice = std::make_unique<CFX_DefaultRenderDevice>(); |
| if (dest_is_bitmap) { |
| if (!pDevice->AttachWithRgbByteOrder(holder, |
| !!(flags & FPDF_REVERSE_BYTE_ORDER))) { |
| return; |
| } |
| } else { |
| #if defined(PDF_USE_SKIA) |
| if (!pDevice->AttachCanvas(*canvas)) { |
| return; |
| } |
| #endif |
| } |
| |
| { |
| CFX_RenderDevice::StateRestorer restorer(pDevice.get()); |
| pDevice->SetClip_Rect(rect); |
| |
| CPDF_RenderOptions options; |
| options.GetOptions().bClearType = !!(flags & FPDF_LCD_TEXT); |
| |
| // Grayscale output |
| if (flags & FPDF_GRAYSCALE) |
| options.SetColorMode(CPDF_RenderOptions::kGray); |
| |
| options.SetDrawAnnots(flags & FPDF_ANNOT); |
| options.SetOCContext( |
| pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::kView)); |
| |
| if (pPageView) |
| pPageView->PageView_OnDraw(pDevice.get(), matrix, &options, rect); |
| } |
| } |
| |
| // Returns true if formfill version is correctly set. See |version| in |
| // FPDF_FORMFILLINFO for details regarding correct version. |
| bool CheckFormfillVersion(FPDF_FORMFILLINFO* formInfo) { |
| if (!formInfo || formInfo->version < 1 || formInfo->version > 2) |
| return false; |
| |
| #ifdef PDF_ENABLE_XFA |
| if (formInfo->version != 2) |
| return false; |
| #endif // PDF_ENABLE_XFA |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| FPDF_EXPORT int FPDF_CALLCONV |
| FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| double page_x, |
| double page_y) { |
| const CPDF_Page* pPage = CPDFPageFromFPDFPage(page); |
| if (pPage) { |
| CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); |
| if (!pForm) |
| return -1; |
| |
| const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); |
| const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint( |
| pPage, |
| CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)), |
| nullptr); |
| if (!pFormCtrl) |
| return -1; |
| const CPDF_FormField* pFormField = pFormCtrl->GetField(); |
| return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1; |
| } |
| |
| #ifdef PDF_ENABLE_XFA |
| const CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page)); |
| if (pXFAPage) { |
| return pXFAPage->HasFormFieldAtPoint( |
| CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y))); |
| } |
| #endif // PDF_ENABLE_XFA |
| |
| return -1; |
| } |
| |
| FPDF_EXPORT int FPDF_CALLCONV |
| FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| double page_x, |
| double page_y) { |
| CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); |
| if (!pForm) |
| return -1; |
| |
| CPDF_Page* pPage = CPDFPageFromFPDFPage(page); |
| if (!pPage) |
| return -1; |
| |
| CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); |
| int z_order = -1; |
| pPDFForm->GetControlAtPoint( |
| pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)), |
| &z_order); |
| return z_order; |
| } |
| |
| FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV |
| FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document, |
| FPDF_FORMFILLINFO* formInfo) { |
| if (!CheckFormfillVersion(formInfo)) |
| return nullptr; |
| |
| auto* pDocument = CPDFDocumentFromFPDFDocument(document); |
| if (!pDocument) |
| return nullptr; |
| |
| #ifdef PDF_ENABLE_XFA |
| CPDFXFA_Context* pContext = nullptr; |
| if (!formInfo->xfa_disabled) { |
| if (!pDocument->GetExtension()) { |
| pDocument->SetExtension(std::make_unique<CPDFXFA_Context>(pDocument)); |
| } |
| |
| // If the CPDFXFA_Context has a FormFillEnvironment already then we've done |
| // this and can just return the old Env. Otherwise, we'll end up setting a |
| // new environment into the XFADocument and, that could get weird. |
| pContext = static_cast<CPDFXFA_Context*>(pDocument->GetExtension()); |
| if (pContext->GetFormFillEnv()) { |
| return FPDFFormHandleFromCPDFSDKFormFillEnvironment( |
| pContext->GetFormFillEnv()); |
| } |
| } |
| #endif // PDF_ENABLE_XFA |
| |
| auto pFormFillEnv = |
| std::make_unique<CPDFSDK_FormFillEnvironment>(pDocument, formInfo); |
| |
| #ifdef PDF_ENABLE_XFA |
| if (pContext) |
| pContext->SetFormFillEnv(pFormFillEnv.get()); |
| #endif // PDF_ENABLE_XFA |
| |
| ReportUnsupportedXFA(pDocument); |
| |
| return FPDFFormHandleFromCPDFSDKFormFillEnvironment( |
| pFormFillEnv.release()); // Caller takes ownership. |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle) { |
| if (!hHandle) |
| return; |
| |
| // Take back ownership of the form fill environment. This is the inverse of |
| // FPDFDOC_InitFormFillEnvironment() above. |
| std::unique_ptr<CPDFSDK_FormFillEnvironment> pFormFillEnv( |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle)); |
| |
| #ifdef PDF_ENABLE_XFA |
| // Reset the focused annotations and remove the SDK document from the |
| // XFA document. |
| pFormFillEnv->ClearAllFocusedAnnots(); |
| // If the document was closed first, it's possible the XFA document |
| // is now a nullptr. |
| auto* pContext = |
| static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension()); |
| if (pContext) |
| pContext->SetFormFillEnv(nullptr); |
| #endif // PDF_ENABLE_XFA |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnMouseMove( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_OnMouseWheel(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| const FS_POINTF* page_coord, |
| int delta_x, |
| int delta_y) { |
| if (!page_coord) |
| return false; |
| |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnMouseWheel( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFXPointFFromFSPointF(*page_coord), CFX_Vector(delta_x, delta_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnFocus( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| #ifdef PDF_ENABLE_CLICK_LOGGING |
| fprintf(stderr, "mousedown,left,%d,%d\n", static_cast<int>(round(page_x)), |
| static_cast<int>(round(page_y))); |
| #endif // PDF_ENABLE_CLICK_LOGGING |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnLButtonDown( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| #ifdef PDF_ENABLE_CLICK_LOGGING |
| fprintf(stderr, "mouseup,left,%d,%d\n", static_cast<int>(round(page_x)), |
| static_cast<int>(round(page_y))); |
| #endif // PDF_ENABLE_CLICK_LOGGING |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnLButtonUp( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| #ifdef PDF_ENABLE_CLICK_LOGGING |
| fprintf(stderr, "mousedown,doubleleft,%d,%d\n", |
| static_cast<int>(round(page_x)), static_cast<int>(round(page_y))); |
| #endif // PDF_ENABLE_CLICK_LOGGING |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnLButtonDblClk( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| #ifdef PDF_ENABLE_CLICK_LOGGING |
| fprintf(stderr, "mousedown,right,%d,%d\n", static_cast<int>(round(page_x)), |
| static_cast<int>(round(page_y))); |
| #endif // PDF_ENABLE_CLICK_LOGGING |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnRButtonDown( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int modifier, |
| double page_x, |
| double page_y) { |
| #ifdef PDF_ENABLE_CLICK_LOGGING |
| fprintf(stderr, "mouseup,right,%d,%d\n", static_cast<int>(round(page_x)), |
| static_cast<int>(round(page_y))); |
| #endif // PDF_ENABLE_CLICK_LOGGING |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnRButtonUp( |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier), |
| CFX_PointF(page_x, page_y)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int nKeyCode, |
| int modifier) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnKeyDown( |
| static_cast<FWL_VKEYCODE>(nKeyCode), |
| Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier)); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int nKeyCode, |
| int modifier) { |
| return false; |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int nChar, |
| int modifier) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && |
| pPageView->OnChar( |
| nChar, Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier)); |
| } |
| |
| FPDF_EXPORT unsigned long FPDF_CALLCONV |
| FORM_GetFocusedText(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| void* buffer, |
| unsigned long buflen) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) { |
| return 0; |
| } |
| // SAFETY: required from caller. |
| return Utf16EncodeMaybeCopyAndReturnLength( |
| pPageView->GetFocusedFormText(), |
| UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); |
| } |
| |
| FPDF_EXPORT unsigned long FPDF_CALLCONV |
| FORM_GetSelectedText(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| void* buffer, |
| unsigned long buflen) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) { |
| return 0; |
| } |
| // SAFETY: required from caller. |
| return Utf16EncodeMaybeCopyAndReturnLength( |
| pPageView->GetSelectedText(), |
| UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen))); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| FPDF_WIDESTRING wsText) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) { |
| return; |
| } |
| // SAFETY: required from caller. |
| pPageView->ReplaceAndKeepSelection( |
| UNSAFE_BUFFERS(WideStringFromFPDFWideString(wsText))); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| FPDF_WIDESTRING wsText) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) { |
| return; |
| } |
| // SAFETY: required from caller. |
| pPageView->ReplaceSelection( |
| UNSAFE_BUFFERS(WideStringFromFPDFWideString(wsText))); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_SelectAllText(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && pPageView->SelectAllText(); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) |
| return false; |
| return pPageView->CanUndo(); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanRedo(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) |
| return false; |
| return pPageView->CanRedo(); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Undo(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) |
| return false; |
| return pPageView->Undo(); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Redo(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| if (!pPageView) |
| return false; |
| return pPageView->Redo(); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (!pFormFillEnv) |
| return false; |
| return pFormFillEnv->KillFocusAnnot({}); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle, |
| int* page_index, |
| FPDF_ANNOTATION* annot) { |
| if (!page_index || !annot) |
| return false; |
| |
| CPDFSDK_FormFillEnvironment* form_fill_env = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle); |
| if (!form_fill_env) |
| return false; |
| |
| // Set |page_index| and |annot| to default values. This is returned when there |
| // is no focused annotation. |
| *page_index = -1; |
| *annot = nullptr; |
| |
| CPDFSDK_Annot* cpdfsdk_annot = form_fill_env->GetFocusAnnot(); |
| if (!cpdfsdk_annot) |
| return true; |
| |
| // TODO(crbug.com/pdfium/1482): Handle XFA case. |
| if (cpdfsdk_annot->AsXFAWidget()) |
| return true; |
| |
| CPDFSDK_PageView* page_view = cpdfsdk_annot->GetPageView(); |
| if (!page_view->IsValid()) |
| return true; |
| |
| IPDF_Page* page = cpdfsdk_annot->GetPage(); |
| if (!page) |
| return true; |
| |
| RetainPtr<CPDF_Dictionary> annot_dict = |
| cpdfsdk_annot->GetPDFAnnot()->GetMutableAnnotDict(); |
| auto annot_context = |
| std::make_unique<CPDF_AnnotContext>(std::move(annot_dict), page); |
| |
| *page_index = page_view->GetPageIndex(); |
| // Caller takes ownership. |
| *annot = FPDFAnnotationFromCPDFAnnotContext(annot_context.release()); |
| return true; |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) { |
| CPDFSDK_FormFillEnvironment* form_fill_env = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle); |
| if (!form_fill_env) |
| return false; |
| |
| CPDF_AnnotContext* annot_context = CPDFAnnotContextFromFPDFAnnotation(annot); |
| if (!annot_context) |
| return false; |
| |
| CPDFSDK_PageView* page_view = |
| form_fill_env->GetOrCreatePageView(annot_context->GetPage()); |
| if (!page_view->IsValid()) |
| return false; |
| |
| RetainPtr<CPDF_Dictionary> annot_dict = annot_context->GetMutableAnnotDict(); |
| ObservedPtr<CPDFSDK_Annot> cpdfsdk_annot( |
| page_view->GetAnnotByDict(annot_dict.Get())); |
| if (!cpdfsdk_annot) |
| return false; |
| |
| return form_fill_env->SetFocusAnnot(cpdfsdk_annot); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, |
| FPDF_BITMAP bitmap, |
| FPDF_PAGE page, |
| int start_x, |
| int start_y, |
| int size_x, |
| int size_y, |
| int rotate, |
| int flags) { |
| CFX_DIBitmap* cbitmap = CFXDIBitmapFromFPDFBitmap(bitmap); |
| if (!cbitmap) { |
| return; |
| } |
| |
| #if defined(PDF_USE_SKIA) |
| CFX_DIBitmap::ScopedPremultiplier scoped_premultiplier( |
| pdfium::WrapRetain(cbitmap), CFX_DefaultRenderDevice::UseSkiaRenderer()); |
| #endif |
| FFLCommon(hHandle, page, cbitmap, start_x, start_y, size_x, size_y, rotate, |
| flags); |
| } |
| |
| #if defined(PDF_USE_SKIA) |
| FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDrawSkia(FPDF_FORMHANDLE hHandle, |
| FPDF_SKIA_CANVAS canvas, |
| FPDF_PAGE page, |
| int start_x, |
| int start_y, |
| int size_x, |
| int size_y, |
| int rotate, |
| int flags) { |
| SkCanvas* sk_canvas = SkCanvasFromFPDFSkiaCanvas(canvas); |
| if (!sk_canvas) { |
| return; |
| } |
| |
| FFLCommon(hHandle, page, sk_canvas, start_x, start_y, size_x, size_y, rotate, |
| flags); |
| } |
| #endif // defined(PDF_USE_SKIA) |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle, |
| int fieldType, |
| unsigned long color) { |
| CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); |
| if (!pForm) |
| return; |
| |
| std::optional<FormFieldType> cast_input = |
| CPDF_FormField::IntToFormFieldType(fieldType); |
| if (!cast_input.has_value()) |
| return; |
| |
| if (cast_input.value() == FormFieldType::kUnknown) { |
| pForm->SetAllHighlightColors(static_cast<FX_COLORREF>(color)); |
| } else { |
| pForm->SetHighlightColor(static_cast<FX_COLORREF>(color), |
| cast_input.value()); |
| } |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FPDF_SetFormFieldHighlightAlpha(FPDF_FORMHANDLE hHandle, unsigned char alpha) { |
| if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle)) |
| pForm->SetHighlightAlpha(alpha); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FPDF_RemoveFormFieldHighlight(FPDF_FORMHANDLE hHandle) { |
| if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle)) |
| pForm->RemoveAllHighLights(); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FORM_OnAfterLoadPage(FPDF_PAGE page, |
| FPDF_FORMHANDLE hHandle) { |
| if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page)) |
| pPageView->SetValid(true); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page, |
| FPDF_FORMHANDLE hHandle) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (!pFormFillEnv) |
| return; |
| |
| IPDF_Page* pPage = IPDFPageFromFPDFPage(page); |
| if (!pPage) |
| return; |
| |
| CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage); |
| if (pPageView) { |
| pPageView->SetValid(false); |
| // RemovePageView() takes care of the delete for us. |
| pFormFillEnv->RemovePageView(pPage); |
| } |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent()) |
| pFormFillEnv->ProcJavascriptAction(); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV |
| FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (pFormFillEnv) |
| pFormFillEnv->ProcOpenAction(); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle, |
| int aaType) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (!pFormFillEnv) |
| return; |
| |
| CPDF_Document* pDoc = pFormFillEnv->GetPDFDocument(); |
| const CPDF_Dictionary* pDict = pDoc->GetRoot(); |
| if (!pDict) |
| return; |
| |
| CPDF_AAction aa(pDict->GetDictFor(pdfium::form_fields::kAA)); |
| auto type = static_cast<CPDF_AAction::AActionType>(aaType); |
| if (aa.ActionExist(type)) |
| pFormFillEnv->DoActionDocument(aa.GetAction(type), type); |
| } |
| |
| FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page, |
| FPDF_FORMHANDLE hHandle, |
| int aaType) { |
| CPDFSDK_FormFillEnvironment* pFormFillEnv = |
| CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); |
| if (!pFormFillEnv) |
| return; |
| |
| IPDF_Page* pPage = IPDFPageFromFPDFPage(page); |
| CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page); |
| if (!pPDFPage) |
| return; |
| |
| if (!pFormFillEnv->GetPageView(pPage)) |
| return; |
| |
| CPDF_AAction aa(pPDFPage->GetDict()->GetDictFor(pdfium::form_fields::kAA)); |
| CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN |
| ? CPDF_AAction::kOpenPage |
| : CPDF_AAction::kClosePage; |
| if (aa.ActionExist(type)) |
| pFormFillEnv->DoActionPage(aa.GetAction(type), type); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_SetIndexSelected(FPDF_FORMHANDLE hHandle, |
| FPDF_PAGE page, |
| int index, |
| FPDF_BOOL selected) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && pPageView->SetIndexSelected(index, selected); |
| } |
| |
| FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV |
| FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index) { |
| CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); |
| return pPageView && pPageView->IsIndexSelected(index); |
| } |