blob: 8e794a558468583d940db75417415900c83bc624 [file] [log] [blame] [edit]
// 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);
}