Skip to main content

pdfium_render/pdf/document/page/
field.rs

1//! Defines the [PdfFormField] enum, exposing functionality related to a single interactive
2//! form field in a [PdfForm].
3
4pub mod button;
5pub mod checkbox;
6pub mod combo;
7pub mod list;
8pub mod option;
9pub mod options;
10pub(crate) mod private; // Keep private so that the PdfFormFieldPrivate trait is not exposed.
11pub mod radio;
12pub mod signature;
13pub mod text;
14pub mod unknown;
15
16use crate::bindgen::{
17    FPDF_ANNOTATION, FPDF_FORMFIELD_CHECKBOX, FPDF_FORMFIELD_COMBOBOX, FPDF_FORMFIELD_LISTBOX,
18    FPDF_FORMFIELD_PUSHBUTTON, FPDF_FORMFIELD_RADIOBUTTON, FPDF_FORMFIELD_SIGNATURE,
19    FPDF_FORMFIELD_TEXTFIELD, FPDF_FORMFIELD_UNKNOWN, FPDF_FORMHANDLE,
20};
21use crate::bindings::PdfiumLibraryBindings;
22use crate::error::PdfiumError;
23use crate::pdf::appearance_mode::PdfAppearanceMode;
24use crate::pdf::document::page::field::button::PdfFormPushButtonField;
25use crate::pdf::document::page::field::checkbox::PdfFormCheckboxField;
26use crate::pdf::document::page::field::combo::PdfFormComboBoxField;
27use crate::pdf::document::page::field::list::PdfFormListBoxField;
28use crate::pdf::document::page::field::private::internal::{
29    PdfFormFieldFlags, PdfFormFieldPrivate,
30};
31use crate::pdf::document::page::field::radio::PdfFormRadioButtonField;
32use crate::pdf::document::page::field::signature::PdfFormSignatureField;
33use crate::pdf::document::page::field::text::PdfFormTextField;
34use crate::pdf::document::page::field::unknown::PdfFormUnknownField;
35use crate::pdfium::PdfiumLibraryBindingsAccessor;
36use std::os::raw::c_int;
37
38#[cfg(doc)]
39use crate::pdf::document::form::PdfForm;
40
41/// The widget display type of a single interactive form field in a [PdfForm].
42#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
43pub enum PdfFormFieldType {
44    // The FPDF_FORMFIELD_COUNT constant simply specifies the number of form field
45    // widget types supported by Pdfium; we do not need to expose it.
46    Unknown = FPDF_FORMFIELD_UNKNOWN as isize,
47    PushButton = FPDF_FORMFIELD_PUSHBUTTON as isize,
48    Checkbox = FPDF_FORMFIELD_CHECKBOX as isize,
49    RadioButton = FPDF_FORMFIELD_RADIOBUTTON as isize,
50    ComboBox = FPDF_FORMFIELD_COMBOBOX as isize,
51    ListBox = FPDF_FORMFIELD_LISTBOX as isize,
52    Text = FPDF_FORMFIELD_TEXTFIELD as isize,
53    Signature = FPDF_FORMFIELD_SIGNATURE as isize,
54}
55
56impl PdfFormFieldType {
57    #[inline]
58    #[allow(dead_code)]
59    pub(crate) fn from_pdfium(value: c_int) -> Result<PdfFormFieldType, PdfiumError> {
60        match value as u32 {
61            FPDF_FORMFIELD_UNKNOWN => Ok(PdfFormFieldType::Unknown),
62            FPDF_FORMFIELD_PUSHBUTTON => Ok(PdfFormFieldType::PushButton),
63            FPDF_FORMFIELD_CHECKBOX => Ok(PdfFormFieldType::Checkbox),
64            FPDF_FORMFIELD_RADIOBUTTON => Ok(PdfFormFieldType::RadioButton),
65            FPDF_FORMFIELD_COMBOBOX => Ok(PdfFormFieldType::ComboBox),
66            FPDF_FORMFIELD_LISTBOX => Ok(PdfFormFieldType::ListBox),
67            FPDF_FORMFIELD_TEXTFIELD => Ok(PdfFormFieldType::Text),
68            FPDF_FORMFIELD_SIGNATURE => Ok(PdfFormFieldType::Signature),
69            _ => Err(PdfiumError::UnknownFormFieldType),
70        }
71    }
72
73    #[inline]
74    #[allow(dead_code)]
75    // The as_pdfium() function is not currently used, but we expect it to be in future
76    pub(crate) fn as_pdfium(&self) -> u32 {
77        match self {
78            PdfFormFieldType::Unknown => FPDF_FORMFIELD_UNKNOWN,
79            PdfFormFieldType::PushButton => FPDF_FORMFIELD_PUSHBUTTON,
80            PdfFormFieldType::Checkbox => FPDF_FORMFIELD_CHECKBOX,
81            PdfFormFieldType::RadioButton => FPDF_FORMFIELD_RADIOBUTTON,
82            PdfFormFieldType::ComboBox => FPDF_FORMFIELD_COMBOBOX,
83            PdfFormFieldType::ListBox => FPDF_FORMFIELD_LISTBOX,
84            PdfFormFieldType::Text => FPDF_FORMFIELD_TEXTFIELD,
85            PdfFormFieldType::Signature => FPDF_FORMFIELD_SIGNATURE,
86        }
87    }
88}
89
90/// A single interactive form field in a [PdfForm].
91pub enum PdfFormField<'a> {
92    PushButton(PdfFormPushButtonField<'a>),
93    Checkbox(PdfFormCheckboxField<'a>),
94    RadioButton(PdfFormRadioButtonField<'a>),
95    ComboBox(PdfFormComboBoxField<'a>),
96    ListBox(PdfFormListBoxField<'a>),
97    Signature(PdfFormSignatureField<'a>),
98    Text(PdfFormTextField<'a>),
99    Unknown(PdfFormUnknownField<'a>),
100}
101
102impl<'a> PdfFormField<'a> {
103    pub(crate) fn from_pdfium(
104        form_handle: FPDF_FORMHANDLE,
105        annotation_handle: FPDF_ANNOTATION,
106        bindings: &'a dyn PdfiumLibraryBindings,
107    ) -> Option<Self> {
108        let result = unsafe { bindings.FPDFAnnot_GetFormFieldType(form_handle, annotation_handle) };
109
110        if result == -1 {
111            return None;
112        }
113
114        let form_field_type =
115            PdfFormFieldType::from_pdfium(result).unwrap_or(PdfFormFieldType::Unknown);
116
117        Some(match form_field_type {
118            PdfFormFieldType::PushButton => PdfFormField::PushButton(
119                PdfFormPushButtonField::from_pdfium(form_handle, annotation_handle),
120            ),
121            PdfFormFieldType::Checkbox => PdfFormField::Checkbox(
122                PdfFormCheckboxField::from_pdfium(form_handle, annotation_handle),
123            ),
124            PdfFormFieldType::RadioButton => PdfFormField::RadioButton(
125                PdfFormRadioButtonField::from_pdfium(form_handle, annotation_handle),
126            ),
127            PdfFormFieldType::ComboBox => PdfFormField::ComboBox(
128                PdfFormComboBoxField::from_pdfium(form_handle, annotation_handle),
129            ),
130            PdfFormFieldType::ListBox => PdfFormField::ListBox(PdfFormListBoxField::from_pdfium(
131                form_handle,
132                annotation_handle,
133            )),
134            PdfFormFieldType::Text => PdfFormField::Text(PdfFormTextField::from_pdfium(
135                form_handle,
136                annotation_handle,
137            )),
138            PdfFormFieldType::Signature => PdfFormField::Signature(
139                PdfFormSignatureField::from_pdfium(form_handle, annotation_handle),
140            ),
141            _ => PdfFormField::Unknown(PdfFormUnknownField::from_pdfium(
142                form_handle,
143                annotation_handle,
144            )),
145        })
146    }
147
148    #[inline]
149    pub(crate) fn unwrap_as_trait(&self) -> &dyn PdfFormFieldPrivate<'a> {
150        match self {
151            PdfFormField::PushButton(field) => field,
152            PdfFormField::Checkbox(field) => field,
153            PdfFormField::RadioButton(field) => field,
154            PdfFormField::ComboBox(field) => field,
155            PdfFormField::ListBox(field) => field,
156            PdfFormField::Signature(field) => field,
157            PdfFormField::Text(field) => field,
158            PdfFormField::Unknown(field) => field,
159        }
160    }
161
162    /// The type of this [PdfFormField].
163    #[inline]
164    pub fn field_type(&self) -> PdfFormFieldType {
165        match self {
166            PdfFormField::PushButton(_) => PdfFormFieldType::PushButton,
167            PdfFormField::Checkbox(_) => PdfFormFieldType::Checkbox,
168            PdfFormField::RadioButton(_) => PdfFormFieldType::RadioButton,
169            PdfFormField::ComboBox(_) => PdfFormFieldType::ComboBox,
170            PdfFormField::ListBox(_) => PdfFormFieldType::ListBox,
171            PdfFormField::Signature(_) => PdfFormFieldType::Signature,
172            PdfFormField::Text(_) => PdfFormFieldType::Text,
173            PdfFormField::Unknown(_) => PdfFormFieldType::Unknown,
174        }
175    }
176
177    /// Returns a reference to the underlying [PdfFormPushButtonField] for this [PdfFormField],
178    /// if this form field has a field type of [PdfFormField::PushButton].
179    #[inline]
180    pub fn as_push_button_field(&self) -> Option<&PdfFormPushButtonField<'_>> {
181        match self {
182            PdfFormField::PushButton(field) => Some(field),
183            _ => None,
184        }
185    }
186
187    /// Returns a reference to the underlying [PdfFormCheckboxField] for this [PdfFormField],
188    /// if this form field has a field type of [PdfFormField::Checkbox].
189    #[inline]
190    pub fn as_checkbox_field(&self) -> Option<&PdfFormCheckboxField<'_>> {
191        match self {
192            PdfFormField::Checkbox(field) => Some(field),
193            _ => None,
194        }
195    }
196
197    /// Returns a mutable reference to the underlying [PdfFormCheckboxField]
198    /// for this [PdfFormField], if this form field has a field type of [PdfFormField::Checkbox].
199    #[inline]
200    pub fn as_checkbox_field_mut(&mut self) -> Option<&mut PdfFormCheckboxField<'a>> {
201        match self {
202            PdfFormField::Checkbox(field) => Some(field),
203            _ => None,
204        }
205    }
206
207    /// Returns a reference to the underlying [PdfFormRadioButtonField] for this [PdfFormField],
208    /// if this form field has a field type of [PdfFormField::RadioButton].
209    #[inline]
210    pub fn as_radio_button_field(&self) -> Option<&PdfFormRadioButtonField<'_>> {
211        match self {
212            PdfFormField::RadioButton(field) => Some(field),
213            _ => None,
214        }
215    }
216
217    /// Returns a mutable reference to the underlying [PdfFormRadioButtonField]
218    /// for this [PdfFormField], if this form field has a field type of [PdfFormField::RadioButton].
219    #[inline]
220    pub fn as_radio_button_field_mut(&mut self) -> Option<&mut PdfFormRadioButtonField<'a>> {
221        match self {
222            PdfFormField::RadioButton(field) => Some(field),
223            _ => None,
224        }
225    }
226
227    /// Returns a reference to the underlying [PdfFormComboBoxField] for this [PdfFormField],
228    /// if this form field has a field type of [PdfFormField::ComboBox].
229    #[inline]
230    pub fn as_combo_box_field(&self) -> Option<&PdfFormComboBoxField<'_>> {
231        match self {
232            PdfFormField::ComboBox(field) => Some(field),
233            _ => None,
234        }
235    }
236
237    /// Returns a reference to the underlying [PdfFormListBoxField] for this [PdfFormField],
238    /// if this form field has a field type of [PdfFormField::ListBox].
239    #[inline]
240    pub fn as_list_box_field(&self) -> Option<&PdfFormListBoxField<'_>> {
241        match self {
242            PdfFormField::ListBox(field) => Some(field),
243            _ => None,
244        }
245    }
246
247    /// Returns a reference to the underlying [PdfFormSignatureField] for this [PdfFormField],
248    /// if this form field has a field type of [PdfFormField::Signature].
249    #[inline]
250    pub fn as_signature_field(&self) -> Option<&PdfFormSignatureField<'_>> {
251        match self {
252            PdfFormField::Signature(field) => Some(field),
253            _ => None,
254        }
255    }
256
257    /// Returns a reference to the underlying [PdfFormTextField] for this [PdfFormField],
258    /// if this form field has a field type of [PdfFormField::Text].
259    #[inline]
260    pub fn as_text_field(&self) -> Option<&PdfFormTextField<'_>> {
261        match self {
262            PdfFormField::Text(field) => Some(field),
263            _ => None,
264        }
265    }
266
267    /// Returns a mutable reference to the underlying [PdfFormTextField] for this
268    /// [PdfFormField], if this form field has a field type of [PdfFormField::Text].
269    pub fn as_text_field_mut(&mut self) -> Option<&mut PdfFormTextField<'a>> {
270        match self {
271            PdfFormField::Text(field) => Some(field),
272            _ => None,
273        }
274    }
275
276    /// Returns a reference to the underlying [PdfFormUnknownField] for this [PdfFormField],
277    /// if this form field has a field type of [PdfFormField::Unknown].
278    #[inline]
279    pub fn as_unknown_field(&self) -> Option<&PdfFormUnknownField<'_>> {
280        match self {
281            PdfFormField::Unknown(field) => Some(field),
282            _ => None,
283        }
284    }
285}
286
287/// Functionality common to all [PdfFormField] objects, regardless of their [PdfFormFieldType].
288pub trait PdfFormFieldCommon {
289    /// Returns the name of this [PdfFormField], if any.
290    fn name(&self) -> Option<String>;
291
292    /// Returns the name of the currently set appearance stream for this [PdfFormField], if any.
293    fn appearance_stream(&self) -> Option<String>;
294
295    /// Returns the value currently set for the given appearance mode for this [PdfFormField],
296    /// if any.
297    fn appearance_mode_value(&self, appearance_mode: PdfAppearanceMode) -> Option<String>;
298
299    /// Returns `true` if the value of this [PdfFormField] is read only.
300    ///
301    /// Users may not change the value of read-only fields, and any associated widget
302    /// annotations will not interact with the user; that is, they will not respond
303    /// to mouse clicks or change their appearance in response to mouse motions.
304    fn is_read_only(&self) -> bool;
305
306    #[cfg(any(
307        feature = "pdfium_future",
308        feature = "pdfium_8125",
309        feature = "pdfium_7881",
310        feature = "pdfium_7763",
311        feature = "pdfium_7543",
312        feature = "pdfium_7350"
313    ))]
314    /// Controls whether or not the value of this [PdfFormField] is read only.
315    fn set_is_read_only(&mut self, is_read_only: bool) -> Result<(), PdfiumError>;
316
317    /// Returns `true` if this [PdfFormField] must have a value at the time it is exported
318    /// by any "submit form" action.
319    ///
320    /// For more information on "submit form" actions, refer to Section 8.6.4 of
321    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
322    fn is_required(&self) -> bool;
323
324    #[cfg(any(
325        feature = "pdfium_future",
326        feature = "pdfium_8125",
327        feature = "pdfium_7881",
328        feature = "pdfium_7763",
329        feature = "pdfium_7543",
330        feature = "pdfium_7350"
331    ))]
332    /// Controls whether or not this [PdfFormField] must have a value at the time it is
333    /// exported by any "submit form" action.
334    ///
335    /// For more information on "submit form" actions, refer to Section 8.6.4 of
336    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
337    fn set_is_required(&mut self, is_required: bool) -> Result<(), PdfiumError>;
338
339    /// Returns `true` if the value of this [PdfFormField] will be exported by any
340    /// "submit form" action.
341    ///
342    /// For more information on "submit form" actions, refer to Section 8.6.4 of
343    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
344    fn is_exported_on_submit(&self) -> bool;
345
346    #[cfg(any(
347        feature = "pdfium_future",
348        feature = "pdfium_8125",
349        feature = "pdfium_7881",
350        feature = "pdfium_7763",
351        feature = "pdfium_7543",
352        feature = "pdfium_7350"
353    ))]
354    /// Controls whether or not the value of this [PdfFormField] will be exported by any
355    /// "submit form" action.
356    ///
357    /// For more information on "submit form" actions, refer to Section 8.6.4 of
358    /// The PDF Reference Manual, version 1.7, starting on page 702.
359    fn set_is_exported_on_submit(&mut self, is_exported: bool) -> Result<(), PdfiumError>;
360}
361
362// Blanket implementation for all PdfFormFieldCommon types.
363
364impl<'a, T> PdfFormFieldCommon for T
365where
366    T: PdfFormFieldPrivate<'a>,
367{
368    #[inline]
369    fn name(&self) -> Option<String> {
370        self.name_impl()
371    }
372
373    #[inline]
374    fn appearance_stream(&self) -> Option<String> {
375        self.appearance_stream_impl()
376    }
377
378    #[inline]
379    fn appearance_mode_value(&self, appearance_mode: PdfAppearanceMode) -> Option<String> {
380        self.appearance_mode_value_impl(appearance_mode)
381    }
382
383    #[inline]
384    fn is_read_only(&self) -> bool {
385        self.get_flags_impl().contains(PdfFormFieldFlags::ReadOnly)
386    }
387
388    #[cfg(any(
389        feature = "pdfium_future",
390        feature = "pdfium_8125",
391        feature = "pdfium_7881",
392        feature = "pdfium_7763",
393        feature = "pdfium_7543",
394        feature = "pdfium_7350"
395    ))]
396    #[inline]
397    fn set_is_read_only(&mut self, is_read_only: bool) -> Result<(), PdfiumError> {
398        self.update_one_flag_impl(PdfFormFieldFlags::ReadOnly, is_read_only)
399    }
400
401    #[inline]
402    fn is_required(&self) -> bool {
403        self.get_flags_impl().contains(PdfFormFieldFlags::Required)
404    }
405
406    #[cfg(any(
407        feature = "pdfium_future",
408        feature = "pdfium_8125",
409        feature = "pdfium_7881",
410        feature = "pdfium_7763",
411        feature = "pdfium_7543",
412        feature = "pdfium_7350"
413    ))]
414    #[inline]
415    fn set_is_required(&mut self, is_required: bool) -> Result<(), PdfiumError> {
416        self.update_one_flag_impl(PdfFormFieldFlags::Required, is_required)
417    }
418
419    #[inline]
420    fn is_exported_on_submit(&self) -> bool {
421        !self.get_flags_impl().contains(PdfFormFieldFlags::NoExport)
422    }
423
424    #[cfg(any(
425        feature = "pdfium_future",
426        feature = "pdfium_8125",
427        feature = "pdfium_7881",
428        feature = "pdfium_7763",
429        feature = "pdfium_7543",
430        feature = "pdfium_7350"
431    ))]
432    #[inline]
433    fn set_is_exported_on_submit(&mut self, is_exported: bool) -> Result<(), PdfiumError> {
434        self.update_one_flag_impl(PdfFormFieldFlags::NoExport, !is_exported)
435    }
436}
437
438impl<'a> PdfFormFieldPrivate<'a> for PdfFormField<'a> {
439    #[inline]
440    fn form_handle(&self) -> FPDF_FORMHANDLE {
441        self.unwrap_as_trait().form_handle()
442    }
443
444    #[inline]
445    fn annotation_handle(&self) -> FPDF_ANNOTATION {
446        self.unwrap_as_trait().annotation_handle()
447    }
448}
449
450impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFormField<'a> {}
451
452#[cfg(feature = "thread_safe")]
453unsafe impl<'a> Send for PdfFormField<'a> {}
454
455#[cfg(feature = "thread_safe")]
456unsafe impl<'a> Sync for PdfFormField<'a> {}
457
458impl<'a> From<PdfFormPushButtonField<'a>> for PdfFormField<'a> {
459    #[inline]
460    fn from(field: PdfFormPushButtonField<'a>) -> Self {
461        Self::PushButton(field)
462    }
463}
464
465impl<'a> From<PdfFormCheckboxField<'a>> for PdfFormField<'a> {
466    #[inline]
467    fn from(field: PdfFormCheckboxField<'a>) -> Self {
468        Self::Checkbox(field)
469    }
470}
471
472impl<'a> From<PdfFormRadioButtonField<'a>> for PdfFormField<'a> {
473    #[inline]
474    fn from(field: PdfFormRadioButtonField<'a>) -> Self {
475        Self::RadioButton(field)
476    }
477}
478
479impl<'a> From<PdfFormComboBoxField<'a>> for PdfFormField<'a> {
480    #[inline]
481    fn from(field: PdfFormComboBoxField<'a>) -> Self {
482        Self::ComboBox(field)
483    }
484}
485
486impl<'a> From<PdfFormListBoxField<'a>> for PdfFormField<'a> {
487    #[inline]
488    fn from(field: PdfFormListBoxField<'a>) -> Self {
489        Self::ListBox(field)
490    }
491}
492
493impl<'a> From<PdfFormTextField<'a>> for PdfFormField<'a> {
494    #[inline]
495    fn from(field: PdfFormTextField<'a>) -> Self {
496        Self::Text(field)
497    }
498}
499
500impl<'a> From<PdfFormSignatureField<'a>> for PdfFormField<'a> {
501    #[inline]
502    fn from(field: PdfFormSignatureField<'a>) -> Self {
503        Self::Signature(field)
504    }
505}
506
507impl<'a> From<PdfFormUnknownField<'a>> for PdfFormField<'a> {
508    #[inline]
509    fn from(field: PdfFormUnknownField<'a>) -> Self {
510        Self::Unknown(field)
511    }
512}