use crate::bindgen::{
FORMTYPE_ACRO_FORM, FORMTYPE_NONE, FORMTYPE_XFA_FOREGROUND, FORMTYPE_XFA_FULL, FPDF_DOCUMENT,
FPDF_FORMFILLINFO, FPDF_FORMHANDLE,
};
use crate::error::PdfiumError;
use crate::pdf::document::page::field::PdfFormFieldCommon;
use crate::pdf::document::page::field::PdfFormFieldType;
use crate::pdf::document::pages::PdfPages;
use crate::pdfium::PdfiumLibraryBindingsAccessor;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::ops::DerefMut;
use std::pin::Pin;
#[cfg(doc)]
use crate::pdf::document::PdfDocument;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PdfFormType {
None = FORMTYPE_NONE as isize,
Acrobat = FORMTYPE_ACRO_FORM as isize,
XfaFull = FORMTYPE_XFA_FULL as isize,
XfaForeground = FORMTYPE_XFA_FOREGROUND as isize,
}
impl PdfFormType {
#[inline]
pub(crate) fn from_pdfium(form_type: u32) -> Result<PdfFormType, PdfiumError> {
match form_type {
FORMTYPE_NONE => Ok(PdfFormType::None),
FORMTYPE_ACRO_FORM => Ok(PdfFormType::Acrobat),
FORMTYPE_XFA_FULL => Ok(PdfFormType::XfaFull),
FORMTYPE_XFA_FOREGROUND => Ok(PdfFormType::XfaForeground),
_ => Err(PdfiumError::UnknownFormType),
}
}
#[inline]
#[allow(dead_code)]
pub(crate) fn as_pdfium(&self) -> u32 {
match self {
PdfFormType::None => FORMTYPE_NONE,
PdfFormType::Acrobat => FORMTYPE_ACRO_FORM,
PdfFormType::XfaFull => FORMTYPE_XFA_FULL,
PdfFormType::XfaForeground => FORMTYPE_XFA_FOREGROUND,
}
}
}
pub struct PdfForm<'a> {
form_handle: FPDF_FORMHANDLE,
document_handle: FPDF_DOCUMENT,
#[allow(dead_code)]
form_fill_info: Pin<Box<FPDF_FORMFILLINFO>>,
lifetime: PhantomData<&'a FPDF_FORMHANDLE>,
}
impl<'a> PdfForm<'a> {
#[inline]
pub(crate) fn from_pdfium(document_handle: FPDF_DOCUMENT) -> Option<Self> {
let form_fill_info = Box::pin(FPDF_FORMFILLINFO {
version: 2,
Release: None,
FFI_Invalidate: None,
FFI_OutputSelectedRect: None,
FFI_SetCursor: None,
FFI_SetTimer: None,
FFI_KillTimer: None,
FFI_GetLocalTime: None,
FFI_OnChange: None,
FFI_GetPage: None,
FFI_GetCurrentPage: None,
FFI_GetRotation: None,
FFI_ExecuteNamedAction: None,
FFI_SetTextFieldFocus: None,
FFI_DoURIAction: None,
FFI_DoGoToAction: None,
m_pJsPlatform: std::ptr::null_mut(),
xfa_disabled: 0,
FFI_DisplayCaret: None,
FFI_GetCurrentPageIndex: None,
FFI_SetCurrentPage: None,
FFI_GotoURL: None,
FFI_GetPageViewRect: None,
FFI_PageEvent: None,
FFI_PopupMenu: None,
FFI_OpenFile: None,
FFI_EmailTo: None,
FFI_UploadTo: None,
FFI_GetPlatform: None,
FFI_GetLanguage: None,
FFI_DownloadFromURL: None,
FFI_PostRequestURL: None,
FFI_PutRequestURL: None,
FFI_OnFocusChange: None,
FFI_DoURIActionWithKeyboardModifier: None,
});
let mut form = PdfForm {
form_handle: std::ptr::null_mut(),
document_handle,
form_fill_info,
lifetime: PhantomData,
};
let form_handle = unsafe {
form.bindings()
.FPDFDOC_InitFormFillEnvironment(document_handle, form.form_fill_info.deref_mut())
};
if !form_handle.is_null() {
form.form_handle = form_handle;
if form.form_type() != PdfFormType::None {
Some(form)
} else {
None
}
} else {
None
}
}
#[inline]
pub(crate) fn handle(&self) -> FPDF_FORMHANDLE {
self.form_handle
}
#[inline]
pub fn form_type(&self) -> PdfFormType {
PdfFormType::from_pdfium(
unsafe { self.bindings().FPDF_GetFormType(self.document_handle) } as u32,
)
.unwrap()
}
pub fn field_values(&self, pages: &'a PdfPages<'a>) -> HashMap<String, Option<String>> {
let mut result = HashMap::new();
let field_value_true = Some("true".to_string());
let field_value_false = Some("false".to_string());
for page in pages.iter() {
for annotation in page.annotations().iter() {
if let Some(field) = annotation.as_form_field() {
let field_type = field.field_type();
let field_value = match field_type {
PdfFormFieldType::Checkbox => {
if field
.as_checkbox_field()
.unwrap()
.is_checked()
.unwrap_or(false)
{
field_value_true.clone()
} else {
field_value_false.clone()
}
}
PdfFormFieldType::ComboBox => field.as_combo_box_field().unwrap().value(),
PdfFormFieldType::ListBox => field.as_list_box_field().unwrap().value(),
PdfFormFieldType::RadioButton => {
let field = field.as_radio_button_field().unwrap();
if field.is_checked().unwrap_or(false) {
field.group_value()
} else {
field_value_false.clone()
}
}
PdfFormFieldType::Text => field.as_text_field().unwrap().value(),
PdfFormFieldType::PushButton
| PdfFormFieldType::Signature
| PdfFormFieldType::Unknown => None,
};
let field_name = field.name().unwrap_or_default();
if (field_type == PdfFormFieldType::Checkbox
|| field_type == PdfFormFieldType::RadioButton)
&& result.contains_key(&field_name)
{
if field_value != field_value_false {
result.insert(field_name, field_value);
}
} else {
result.insert(field_name, field_value);
}
}
}
}
result
}
}
impl<'a> Drop for PdfForm<'a> {
#[inline]
fn drop(&mut self) {
unsafe {
self.bindings()
.FPDFDOC_ExitFormFillEnvironment(self.form_handle);
}
}
}
impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfForm<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfForm<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfForm<'a> {}