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