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