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_7763",
309        feature = "pdfium_7543",
310        feature = "pdfium_7350"
311    ))]
312    /// Controls whether or not the value of this [PdfFormField] is read only.
313    fn set_is_read_only(&mut self, is_read_only: bool) -> Result<(), PdfiumError>;
314
315    /// Returns `true` if this [PdfFormField] must have a value at the time it is exported
316    /// by any "submit form" action.
317    ///
318    /// For more information on "submit form" actions, refer to Section 8.6.4 of
319    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
320    fn is_required(&self) -> bool;
321
322    #[cfg(any(
323        feature = "pdfium_future",
324        feature = "pdfium_7763",
325        feature = "pdfium_7543",
326        feature = "pdfium_7350"
327    ))]
328    /// Controls whether or not this [PdfFormField] must have a value at the time it is
329    /// exported by any "submit form" action.
330    ///
331    /// For more information on "submit form" actions, refer to Section 8.6.4 of
332    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
333    fn set_is_required(&mut self, is_required: bool) -> Result<(), PdfiumError>;
334
335    /// Returns `true` if the value of this [PdfFormField] will be exported by any
336    /// "submit form" action.
337    ///
338    /// For more information on "submit form" actions, refer to Section 8.6.4 of
339    /// The PDF Reference (Sixth Edition, PDF Format 1.7), starting on page 702.
340    fn is_exported_on_submit(&self) -> bool;
341
342    #[cfg(any(
343        feature = "pdfium_future",
344        feature = "pdfium_7763",
345        feature = "pdfium_7543",
346        feature = "pdfium_7350"
347    ))]
348    /// Controls whether or not the value of this [PdfFormField] will be exported by any
349    /// "submit form" action.
350    ///
351    /// For more information on "submit form" actions, refer to Section 8.6.4 of
352    /// The PDF Reference Manual, version 1.7, starting on page 702.
353    fn set_is_exported_on_submit(&mut self, is_exported: bool) -> Result<(), PdfiumError>;
354}
355
356// Blanket implementation for all PdfFormFieldCommon types.
357
358impl<'a, T> PdfFormFieldCommon for T
359where
360    T: PdfFormFieldPrivate<'a>,
361{
362    #[inline]
363    fn name(&self) -> Option<String> {
364        self.name_impl()
365    }
366
367    #[inline]
368    fn appearance_stream(&self) -> Option<String> {
369        self.appearance_stream_impl()
370    }
371
372    #[inline]
373    fn appearance_mode_value(&self, appearance_mode: PdfAppearanceMode) -> Option<String> {
374        self.appearance_mode_value_impl(appearance_mode)
375    }
376
377    #[inline]
378    fn is_read_only(&self) -> bool {
379        self.get_flags_impl().contains(PdfFormFieldFlags::ReadOnly)
380    }
381
382    #[cfg(any(
383        feature = "pdfium_future",
384        feature = "pdfium_7763",
385        feature = "pdfium_7543",
386        feature = "pdfium_7350"
387    ))]
388    #[inline]
389    fn set_is_read_only(&mut self, is_read_only: bool) -> Result<(), PdfiumError> {
390        self.update_one_flag_impl(PdfFormFieldFlags::ReadOnly, is_read_only)
391    }
392
393    #[inline]
394    fn is_required(&self) -> bool {
395        self.get_flags_impl().contains(PdfFormFieldFlags::Required)
396    }
397
398    #[cfg(any(
399        feature = "pdfium_future",
400        feature = "pdfium_7763",
401        feature = "pdfium_7543",
402        feature = "pdfium_7350"
403    ))]
404    #[inline]
405    fn set_is_required(&mut self, is_required: bool) -> Result<(), PdfiumError> {
406        self.update_one_flag_impl(PdfFormFieldFlags::Required, is_required)
407    }
408
409    #[inline]
410    fn is_exported_on_submit(&self) -> bool {
411        !self.get_flags_impl().contains(PdfFormFieldFlags::NoExport)
412    }
413
414    #[cfg(any(
415        feature = "pdfium_future",
416        feature = "pdfium_7763",
417        feature = "pdfium_7543",
418        feature = "pdfium_7350"
419    ))]
420    #[inline]
421    fn set_is_exported_on_submit(&mut self, is_exported: bool) -> Result<(), PdfiumError> {
422        self.update_one_flag_impl(PdfFormFieldFlags::NoExport, !is_exported)
423    }
424}
425
426impl<'a> PdfFormFieldPrivate<'a> for PdfFormField<'a> {
427    #[inline]
428    fn form_handle(&self) -> FPDF_FORMHANDLE {
429        self.unwrap_as_trait().form_handle()
430    }
431
432    #[inline]
433    fn annotation_handle(&self) -> FPDF_ANNOTATION {
434        self.unwrap_as_trait().annotation_handle()
435    }
436}
437
438impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFormField<'a> {}
439
440#[cfg(feature = "thread_safe")]
441unsafe impl<'a> Send for PdfFormField<'a> {}
442
443#[cfg(feature = "thread_safe")]
444unsafe impl<'a> Sync for PdfFormField<'a> {}
445
446impl<'a> From<PdfFormPushButtonField<'a>> for PdfFormField<'a> {
447    #[inline]
448    fn from(field: PdfFormPushButtonField<'a>) -> Self {
449        Self::PushButton(field)
450    }
451}
452
453impl<'a> From<PdfFormCheckboxField<'a>> for PdfFormField<'a> {
454    #[inline]
455    fn from(field: PdfFormCheckboxField<'a>) -> Self {
456        Self::Checkbox(field)
457    }
458}
459
460impl<'a> From<PdfFormRadioButtonField<'a>> for PdfFormField<'a> {
461    #[inline]
462    fn from(field: PdfFormRadioButtonField<'a>) -> Self {
463        Self::RadioButton(field)
464    }
465}
466
467impl<'a> From<PdfFormComboBoxField<'a>> for PdfFormField<'a> {
468    #[inline]
469    fn from(field: PdfFormComboBoxField<'a>) -> Self {
470        Self::ComboBox(field)
471    }
472}
473
474impl<'a> From<PdfFormListBoxField<'a>> for PdfFormField<'a> {
475    #[inline]
476    fn from(field: PdfFormListBoxField<'a>) -> Self {
477        Self::ListBox(field)
478    }
479}
480
481impl<'a> From<PdfFormTextField<'a>> for PdfFormField<'a> {
482    #[inline]
483    fn from(field: PdfFormTextField<'a>) -> Self {
484        Self::Text(field)
485    }
486}
487
488impl<'a> From<PdfFormSignatureField<'a>> for PdfFormField<'a> {
489    #[inline]
490    fn from(field: PdfFormSignatureField<'a>) -> Self {
491        Self::Signature(field)
492    }
493}
494
495impl<'a> From<PdfFormUnknownField<'a>> for PdfFormField<'a> {
496    #[inline]
497    fn from(field: PdfFormUnknownField<'a>) -> Self {
498        Self::Unknown(field)
499    }
500}