pdfium_render/pdf/document/page/field/text.rs
1//! Defines the [PdfFormTextField] struct, exposing functionality related to a single
2//! form field of type [PdfFormFieldType::Text].
3
4use std::marker::PhantomData;
5
6use crate::bindgen::{FPDF_ANNOTATION, FPDF_FORMHANDLE};
7use crate::error::PdfiumError;
8use crate::pdf::document::page::field::private::internal::{
9 PdfFormFieldFlags, PdfFormFieldPrivate,
10};
11use crate::pdfium::PdfiumLibraryBindingsAccessor;
12
13#[cfg(doc)]
14use {
15 crate::pdf::document::form::PdfForm,
16 crate::pdf::document::page::annotation::PdfPageAnnotationType,
17 crate::pdf::document::page::field::{PdfFormField, PdfFormFieldType},
18};
19
20/// A single [PdfFormField] of type [PdfFormFieldType::Text]. The form field object defines
21/// an interactive data entry widget that allows the user to enter data by typing.
22///
23/// Form fields in Pdfium are wrapped inside page annotations of type [PdfPageAnnotationType::Widget]
24/// or [PdfPageAnnotationType::XfaWidget]. User-specified values can be retrieved directly from
25/// each form field object by unwrapping the form field from the annotation, or in bulk from the
26/// [PdfForm::field_values()] function.
27pub struct PdfFormTextField<'a> {
28 form_handle: FPDF_FORMHANDLE,
29 annotation_handle: FPDF_ANNOTATION,
30 lifetime: PhantomData<&'a FPDF_ANNOTATION>,
31}
32
33impl<'a> PdfFormTextField<'a> {
34 #[inline]
35 pub(crate) fn from_pdfium(
36 form_handle: FPDF_FORMHANDLE,
37 annotation_handle: FPDF_ANNOTATION,
38 ) -> Self {
39 PdfFormTextField {
40 form_handle,
41 annotation_handle,
42 lifetime: PhantomData,
43 }
44 }
45
46 /// Returns the value assigned to this [PdfFormTextField] object, if any.
47 #[inline]
48 pub fn value(&self) -> Option<String> {
49 if self.is_rich_text() {
50 self.get_string_value("RV")
51 } else {
52 self.value_impl()
53 }
54 }
55
56 /// Sets the value of this [PdfFormTextField] object.
57 #[inline]
58 pub fn set_value(&mut self, value: &str) -> Result<(), PdfiumError> {
59 if self.is_rich_text() {
60 self.set_string_value("RV", value)
61 } else {
62 self.set_value_impl(value)
63 }
64 }
65
66 /// Returns `true` if this [PdfFormTextField] is configured as a multi-line text field.
67 #[inline]
68 pub fn is_multiline(&self) -> bool {
69 self.get_flags_impl()
70 .contains(PdfFormFieldFlags::TextMultiline)
71 }
72
73 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
74 /// Controls whether or not this [PdfFormTextField] is configured as a multi-line text field.
75 #[inline]
76 pub fn set_is_multiline(&self, is_multiline: bool) -> Result<(), PdfiumError> {
77 self.update_one_flag_impl(PdfFormFieldFlags::TextMultiline, is_multiline)
78 }
79
80 /// Returns `true` if this [PdfFormTextField] is configured as a password field.
81 #[inline]
82 pub fn is_password(&self) -> bool {
83 self.get_flags_impl()
84 .contains(PdfFormFieldFlags::TextPassword)
85 }
86
87 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
88 /// Controls whether or not this [PdfFormTextField] is configured as a password text field.
89 #[inline]
90 pub fn set_is_password(&self, is_password: bool) -> Result<(), PdfiumError> {
91 self.update_one_flag_impl(PdfFormFieldFlags::TextPassword, is_password)
92 }
93
94 /// Returns `true` if this [PdfFormTextField] represents the path of a file
95 /// whose contents are to be submitted as the value of the field.
96 ///
97 /// This flag was added in PDF version 1.4
98 pub fn is_file_select(&self) -> bool {
99 self.get_flags_impl()
100 .contains(PdfFormFieldFlags::TextFileSelect)
101 }
102
103 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
104 /// Controls whether or not this [PdfFormTextField] represents the path of a file
105 /// whose contents are to be submitted as the value of the field.
106 ///
107 /// This flag was added in PDF version 1.4.
108 pub fn set_is_file_select(&mut self, is_file_select: bool) -> Result<(), PdfiumError> {
109 self.update_one_flag_impl(PdfFormFieldFlags::TextFileSelect, is_file_select)
110 }
111
112 /// Returns `true` if text entered into this [PdfFormTextField] should be spell checked.
113 pub fn is_spell_checked(&self) -> bool {
114 !self
115 .get_flags_impl()
116 .contains(PdfFormFieldFlags::TextDoNotSpellCheck)
117 }
118
119 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
120 /// Controls whether or not text entered into this [PdfFormTextField] should be spell checked.
121 pub fn set_is_spell_checked(&mut self, is_spell_checked: bool) -> Result<(), PdfiumError> {
122 self.update_one_flag_impl(PdfFormFieldFlags::TextDoNotSpellCheck, !is_spell_checked)
123 }
124
125 /// Returns `true` if the internal area of this [PdfFormTextField] can scroll either
126 /// horizontally or vertically to accommodate text entry longer than what can fit
127 /// within the field's annotation bounds. If this value is `false`, then once the
128 /// field is full, no further text entry will be accepted.
129 ///
130 /// This flag was added in PDF version 1.4.
131 pub fn is_scrollable(&self) -> bool {
132 !self
133 .get_flags_impl()
134 .contains(PdfFormFieldFlags::TextDoNotScroll)
135 }
136
137 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
138 /// Controls whether or not the internal area of this [PdfFormTextField] can scroll
139 /// either horizontally or vertically to accommodate text entry longer than what can fit
140 /// within the field's annotation bounds. If set to `false`, no further text entry
141 /// will be accepted once the field's annotation bounds are full.
142 ///
143 /// This flag was added in PDF version 1.4.
144 pub fn set_is_scrollable(&mut self, is_scrollable: bool) -> Result<(), PdfiumError> {
145 self.update_one_flag_impl(PdfFormFieldFlags::TextDoNotScroll, !is_scrollable)
146 }
147
148 /// Returns `true` if this [PdfFormTextField] is "combed", that is, automatically divided
149 /// into equally-spaced positions ("combs"), with the text in the field laid out into
150 /// those combs.
151 ///
152 /// For more information on this setting, refer to Table 8.77 of The PDF Reference
153 /// (Sixth Edition, PDF Format 1.7), on page 691.
154 ///
155 /// This flag was added in PDF version 1.5.
156 pub fn is_combed(&self) -> bool {
157 // This flag only takes effect if the multi-line, password, and file select flags
158 // are all unset.
159
160 !self.is_multiline()
161 && !self.is_password()
162 && !self.is_file_select()
163 && self.get_flags_impl().contains(PdfFormFieldFlags::TextComb)
164 }
165
166 // TODO: AJRC - 20/06/25 - there is little point providing the matching `set_is_combed()`
167 // function, because it makes little sense without being also able to set the `MaxValue`
168 // dictionary parameter that controls the number of combs. However, `MaxValue` must be
169 // an integer, and Pdfium does not currently provide a `FPDFAnnot_SetNumberValue()`
170 // function that could correctly set it.
171
172 /// Returns `true` if the text in this [PdfFormTextField] is a rich text string.
173 ///
174 /// This flag was added in PDF version 1.5.
175 pub fn is_rich_text(&self) -> bool {
176 self.get_flags_impl()
177 .contains(PdfFormFieldFlags::TextRichText)
178 }
179
180 #[cfg(any(feature = "pdfium_future", feature = "pdfium_7350"))]
181 /// Controls whether or not the text in this [PdfFormTextField] is a rich text string.
182 ///
183 /// This flag was added in PDF version 1.5.
184 pub fn set_is_rich_text(&mut self, is_rich_text: bool) -> Result<(), PdfiumError> {
185 self.update_one_flag_impl(PdfFormFieldFlags::TextRichText, is_rich_text)
186 }
187}
188
189impl<'a> PdfFormFieldPrivate<'a> for PdfFormTextField<'a> {
190 #[inline]
191 fn form_handle(&self) -> FPDF_FORMHANDLE {
192 self.form_handle
193 }
194
195 #[inline]
196 fn annotation_handle(&self) -> FPDF_ANNOTATION {
197 self.annotation_handle
198 }
199}
200
201impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFormTextField<'a> {}
202
203#[cfg(feature = "thread_safe")]
204unsafe impl<'a> Send for PdfFormTextField<'a> {}
205
206#[cfg(feature = "thread_safe")]
207unsafe impl<'a> Sync for PdfFormTextField<'a> {}