Skip to main content

pdfium_render/pdf/document/page/field/
options.rs

1//! Defines the [PdfFormFieldOptions] struct, a collection of all the selectable options
2//! displayed in a combo box or list box form field.
3
4use crate::bindgen::{FPDF_ANNOTATION, FPDF_FORMHANDLE, FPDF_WCHAR};
5use crate::error::PdfiumError;
6use crate::pdf::document::page::field::option::PdfFormFieldOption;
7use crate::pdfium::PdfiumLibraryBindingsAccessor;
8use crate::utils::mem::create_byte_buffer;
9use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
10use std::marker::PhantomData;
11use std::ops::{Range, RangeInclusive};
12use std::os::raw::c_int;
13
14/// The zero-based index of a single [PdfFormFieldOption] inside its [PdfFormFieldOptions] collection.
15pub type PdfFormFieldOptionIndex = usize;
16
17/// A collection of all selectable options in a list box or check box form field widget.
18pub struct PdfFormFieldOptions<'a> {
19    form_handle: FPDF_FORMHANDLE,
20    annotation_handle: FPDF_ANNOTATION,
21    lifetime: PhantomData<&'a FPDF_ANNOTATION>,
22}
23
24impl<'a> PdfFormFieldOptions<'a> {
25    #[inline]
26    pub(crate) fn from_pdfium(
27        form_handle: FPDF_FORMHANDLE,
28        annotation_handle: FPDF_ANNOTATION,
29    ) -> Self {
30        PdfFormFieldOptions {
31            form_handle,
32            annotation_handle,
33            lifetime: PhantomData,
34        }
35    }
36
37    /// Returns the number of options in this [PdfFormFieldOptions] collection.
38    pub fn len(&self) -> PdfFormFieldOptionIndex {
39        let result = unsafe {
40            self.bindings()
41                .FPDFAnnot_GetOptionCount(self.form_handle, self.annotation_handle)
42        };
43
44        if result == -1 {
45            0
46        } else {
47            result as PdfFormFieldOptionIndex
48        }
49    }
50
51    /// Returns `true` if this [PdfFormFieldOptions] collection is empty.
52    #[inline]
53    pub fn is_empty(&self) -> bool {
54        self.len() == 0
55    }
56
57    /// Returns a Range from `0..(number of options)` for this [PdfFormFieldOptions] collection.
58    #[inline]
59    pub fn as_range(&self) -> Range<PdfFormFieldOptionIndex> {
60        0..self.len()
61    }
62
63    /// Returns an inclusive Range from `0..=(number of options - 1)` for this
64    /// [PdfFormFieldOptions] collection.
65    #[inline]
66    pub fn as_range_inclusive(&self) -> RangeInclusive<PdfFormFieldOptionIndex> {
67        if self.is_empty() {
68            0..=0
69        } else {
70            0..=(self.len() - 1)
71        }
72    }
73
74    /// Returns a single [PdfFormFieldOption] from this [PdfFormFieldOptions] collection.
75    pub fn get(&self, index: PdfFormFieldOptionIndex) -> Result<PdfFormFieldOption, PdfiumError> {
76        if index >= self.len() {
77            return Err(PdfiumError::FormFieldOptionIndexOutOfBounds);
78        }
79
80        // Retrieving the option label from Pdfium is a two-step operation. First, we call
81        // FPDFAnnot_GetOptionLabel() with a null buffer; this will retrieve the length of
82        // the option label text in bytes. If the length is zero, then the option has no label.
83
84        // If the length is non-zero, then we reserve a byte buffer of the given
85        // length and call FPDFAnnot_GetOptionLabel() again with a pointer to the buffer;
86        // this will write the option label to the buffer in UTF16LE format.
87
88        let buffer_length = unsafe {
89            self.bindings().FPDFAnnot_GetOptionLabel(
90                self.form_handle,
91                self.annotation_handle,
92                index as c_int,
93                std::ptr::null_mut(),
94                0,
95            )
96        };
97
98        let option_label = if buffer_length == 0 {
99            // The field value is not present.
100
101            None
102        } else {
103            let mut buffer = create_byte_buffer(buffer_length as usize);
104
105            let result = unsafe {
106                self.bindings().FPDFAnnot_GetOptionLabel(
107                    self.form_handle,
108                    self.annotation_handle,
109                    index as c_int,
110                    buffer.as_mut_ptr() as *mut FPDF_WCHAR,
111                    buffer_length,
112                )
113            };
114
115            debug_assert_eq!(result, buffer_length);
116
117            get_string_from_pdfium_utf16le_bytes(buffer)
118        };
119
120        let option_is_set = self.bindings().is_true(unsafe {
121            self.bindings().FPDFAnnot_IsOptionSelected(
122                self.form_handle,
123                self.annotation_handle,
124                index as c_int,
125            )
126        });
127
128        Ok(PdfFormFieldOption::new(index, option_is_set, option_label))
129    }
130
131    /// Returns an iterator over all the options in this [PdfFormFieldOptions] collection.
132    #[inline]
133    pub fn iter(&self) -> PdfFormFieldOptionsIterator<'_> {
134        PdfFormFieldOptionsIterator::new(self)
135    }
136}
137
138impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFormFieldOptions<'a> {}
139
140#[cfg(feature = "thread_safe")]
141unsafe impl<'a> Send for PdfFormFieldOptions<'a> {}
142
143#[cfg(feature = "thread_safe")]
144unsafe impl<'a> Sync for PdfFormFieldOptions<'a> {}
145
146/// An iterator over all the [PdfFormFieldOption] objects in a [PdfFormFieldOptions] collection.
147pub struct PdfFormFieldOptionsIterator<'a> {
148    options: &'a PdfFormFieldOptions<'a>,
149    next_index: PdfFormFieldOptionIndex,
150}
151
152impl<'a> PdfFormFieldOptionsIterator<'a> {
153    #[inline]
154    pub(crate) fn new(options: &'a PdfFormFieldOptions<'a>) -> Self {
155        PdfFormFieldOptionsIterator {
156            options,
157            next_index: 0,
158        }
159    }
160}
161
162impl<'a> Iterator for PdfFormFieldOptionsIterator<'a> {
163    type Item = PdfFormFieldOption;
164
165    fn next(&mut self) -> Option<Self::Item> {
166        let next = self.options.get(self.next_index);
167
168        self.next_index += 1;
169
170        next.ok()
171    }
172}