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