pdfium_render/pdf/document/page/annotation/
private.rs

1pub(crate) mod internal {
2    // We want to make the PdfPageAnnotationPrivate trait private while providing a blanket
3    // implementation of PdfPageAnnotationCommon for any type T where T: PdfPageAnnotationPrivate.
4    // Rust complains, however, that by doing so we are leaking the private trait outside
5    // the crate.
6
7    // Instead of making the PdfPageAnnotationPrivate trait private, we leave it public but place it
8    // inside this pub(crate) module in order to prevent it from being visible outside the crate.
9
10    use crate::bindgen::{
11        FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_Color,
12        FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_InteriorColor, FPDF_ANNOTATION,
13        FPDF_ANNOT_FLAG_HIDDEN, FPDF_ANNOT_FLAG_INVISIBLE, FPDF_ANNOT_FLAG_LOCKED,
14        FPDF_ANNOT_FLAG_NONE, FPDF_ANNOT_FLAG_NOROTATE, FPDF_ANNOT_FLAG_NOVIEW,
15        FPDF_ANNOT_FLAG_NOZOOM, FPDF_ANNOT_FLAG_PRINT, FPDF_ANNOT_FLAG_READONLY,
16        FPDF_ANNOT_FLAG_TOGGLENOVIEW, FPDF_OBJECT_STRING, FPDF_PAGEOBJECT, FPDF_WCHAR, FS_RECTF,
17    };
18    use crate::bindings::PdfiumLibraryBindings;
19    use crate::error::{PdfiumError, PdfiumInternalError};
20    use crate::pdf::color::PdfColor;
21    use crate::pdf::document::page::annotation::attachment_points::PdfPageAnnotationAttachmentPoints;
22    use crate::pdf::document::page::annotation::objects::PdfPageAnnotationObjects;
23    use crate::pdf::document::page::annotation::{PdfPageAnnotationCommon, PdfPageAnnotationType};
24    use crate::pdf::document::page::object::ownership::PdfPageObjectOwnership;
25    use crate::pdf::points::PdfPoints;
26    use crate::pdf::rect::PdfRect;
27    use crate::utils::dates::date_time_to_pdf_string;
28    use crate::utils::mem::create_byte_buffer;
29    use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
30    use bitflags::bitflags;
31    use chrono::{DateTime, Utc};
32    use std::os::raw::{c_int, c_uint};
33
34    bitflags! {
35        /// Flags specifying various characteristics of one annotation. For more details,
36        /// refer to Section 8.4.2 of The PDF Reference (Sixth Edition, PDF Format 1.7),
37        /// starting on page 608.
38        pub struct PdfAnnotationFlags: u32 {
39            /// No flags are set for this annotation.
40            const None = FPDF_ANNOT_FLAG_NONE;
41
42            /// If set, do not display the annotation if it does not belong to one of the
43            /// standard annotation types and no annotation handler is available. If clear,
44            /// display such an unknown annotation using an appearance stream specified by
45            /// its appearance dictionary, if any.
46            const Invisible = FPDF_ANNOT_FLAG_INVISIBLE;
47
48            /// If set, do not display or print the annotation or allow it to interact
49            /// with the user, regardless of its annotation type or whether an annotation
50            /// handler is available.
51            ///
52            /// In cases where screen space is limited, the ability to hide and show annotations
53            /// selectively can be used in combination with appearance streams to display
54            /// auxiliary pop-up information, similar in function to online help systems.
55            ///
56            /// This flag was added in PDF version 1.2.
57            const Hidden = FPDF_ANNOT_FLAG_HIDDEN;
58
59            /// If set, print the annotation when the page is printed. If clear, never
60            /// print the annotation, regardless of whether it is displayed on the screen.
61            ///
62            /// This can be useful, for example, for annotations representing interactive
63            /// push buttons, which would serve no meaningful purpose on the printed page.
64            ///
65            /// This flag was added in PDF version 1.2.
66            const Print = FPDF_ANNOT_FLAG_PRINT;
67
68            /// If set, do not scale the annotation's appearance to match the magnification
69            /// of the page. The location of the annotation on the page (defined by
70            /// the upper-left corner of its annotation rectangle) remains fixed,
71            /// regardless of the page magnification.
72            ///
73            /// This flag was added in PDF version 1.3.
74            const NoZoom = FPDF_ANNOT_FLAG_NOZOOM;
75
76            /// If set, do not rotate the annotation's appearance to match the rotation
77            /// of the page. The upper-left corner of the annotation rectangle remains in
78            /// a fixed location on the page, regardless of the page rotation.
79            ///
80            /// This flag was added in PDF version 1.3.
81            const NoRotate = FPDF_ANNOT_FLAG_NOROTATE;
82
83            /// If set, do not display the annotation on the screen or allow it to
84            /// interact with the user. The annotation may be printed (depending on the
85            /// setting of the Print flag) but should be considered hidden for purposes
86            /// of on-screen display and user interaction.
87            ///
88            /// This flag was added in PDF version 1.3.
89            const NoView = FPDF_ANNOT_FLAG_NOVIEW;
90
91            /// If set, do not allow the annotation to interact with the user. The
92            /// annotation may be displayed or printed (depending on the settings of the
93            /// `NoView` and `Print` flags) but should not respond to mouse clicks or
94            /// change its appearance in response to mouse motions.
95            ///
96            /// This flag is ignored for widget annotations; its function is subsumed by
97            /// the `ReadOnly` flag of the associated form field.
98            ///
99            /// THis flag was added in PDF version 1.3.
100            const ReadOnly = FPDF_ANNOT_FLAG_READONLY;
101
102            /// If set, do not allow the annotation to be deleted or its properties
103            /// (including position and size) to be modified by the user. However, this flag
104            /// does not restrict changes to the annotation's contents, such as the value
105            /// of a form field.
106            ///
107            /// THis flag was added in PDF version 1.4.
108            const Locked = FPDF_ANNOT_FLAG_LOCKED;
109
110            /// If set, invert the interpretation of the `NoView` flag for certain
111            /// events. A typical use is to have an annotation that appears only when a
112            /// mouse cursor is held over it.
113            ///
114            /// This flag was added in PDF version 1.5.
115            const ToggleNoView = FPDF_ANNOT_FLAG_TOGGLENOVIEW;
116
117            /// If set, do not allow the contents of the annotation to be modified by
118            /// the user. This flag does not restrict deletion of the annotation or
119            /// changes to other annotation properties, such as position and size.
120            ///
121            /// This flag was added in PDF version 1.7.
122            const LockedContents = 1 << 10; // Not directly exposed by Pdfium, but we can support it inline.
123        }
124    }
125
126    /// Internal crate-specific functionality common to all [PdfPageAnnotation] objects.
127    pub(crate) trait PdfPageAnnotationPrivate<'a>: PdfPageAnnotationCommon {
128        /// Returns the internal `FPDF_ANNOTATION` handle for this [PdfPageAnnotation].
129        fn handle(&self) -> FPDF_ANNOTATION;
130
131        /// Returns the [PdfiumLibraryBindings] used by this [PdfPageAnnotation].
132        fn bindings(&self) -> &dyn PdfiumLibraryBindings;
133
134        /// Returns the ownership hierarchy for this [PdfPageAnnotation].
135        fn ownership(&self) -> &PdfPageObjectOwnership;
136
137        /// Returns the [PdfPageAnnotationType] of this [PdfPageAnnotation].
138        fn get_annotation_type(&self) -> PdfPageAnnotationType {
139            PdfPageAnnotationType::from_pdfium(self.bindings().FPDFAnnot_GetSubtype(self.handle()))
140                .unwrap_or(PdfPageAnnotationType::Unknown)
141        }
142
143        /// Returns the string value associated with the given key in the annotation dictionary
144        /// of this [PdfPageAnnotation], if any.
145        fn get_string_value(&self, key: &str) -> Option<String> {
146            if !self
147                .bindings()
148                .is_true(self.bindings().FPDFAnnot_HasKey(self.handle(), key))
149            {
150                // The key does not exist.
151
152                return None;
153            }
154
155            if self.bindings().FPDFAnnot_GetValueType(self.handle(), key) as u32
156                != FPDF_OBJECT_STRING
157            {
158                // The key exists, but the value associated with the key is not a string.
159
160                return None;
161            }
162
163            // Retrieving the string value from Pdfium is a two-step operation. First, we call
164            // FPDFAnot_GetStringValue() with a null buffer; this will retrieve the length of
165            // the value in bytes, assuming the key exists. If the length is zero, then there
166            // is no such key, or the key's value is not a string.
167
168            // If the length is non-zero, then we reserve a byte buffer of the given
169            // length and call FPDFAnot_GetStringValue() again with a pointer to the buffer;
170            // this will write the string value into the buffer.
171
172            let buffer_length = self.bindings().FPDFAnnot_GetStringValue(
173                self.handle(),
174                key,
175                std::ptr::null_mut(),
176                0,
177            );
178
179            if buffer_length <= 2 {
180                // A buffer length of 2 indicates that the string value for the given key is
181                // an empty UTF16-LE string, so there is no point in retrieving it.
182
183                return None;
184            }
185
186            let mut buffer = create_byte_buffer(buffer_length as usize);
187
188            let result = self.bindings().FPDFAnnot_GetStringValue(
189                self.handle(),
190                key,
191                buffer.as_mut_ptr() as *mut FPDF_WCHAR,
192                buffer_length,
193            );
194
195            assert_eq!(result, buffer_length);
196
197            Some(get_string_from_pdfium_utf16le_bytes(buffer).unwrap_or_default())
198        }
199
200        /// Sets the string value associated with the given key in the annotation dictionary
201        /// of this [PdfPageAnnotation].
202        fn set_string_value(&mut self, key: &str, value: &str) -> Result<(), PdfiumError> {
203            // Attempt to update the modification date first, before we apply the given value update.
204            // That way, if updating the date fails, we can fail early.
205
206            #[allow(clippy::collapsible_if)] // Prefer to keep the intent clear
207            if key != "M"
208            // Don't update the modification date if the key we have been given to update
209            // is itself the modification date!
210            {
211                self.set_string_value("M", &date_time_to_pdf_string(Utc::now()))?;
212            }
213
214            // With the modification date updated, we can now update the key and value
215            // we were given.
216
217            if self
218                .bindings()
219                .is_true(
220                    self.bindings()
221                        .FPDFAnnot_SetStringValue_str(self.handle(), key, value),
222                )
223            {
224                Ok(())
225            } else {
226                Err(PdfiumError::PdfiumLibraryInternalError(
227                    PdfiumInternalError::Unknown,
228                ))
229            }
230        }
231
232        /// Internal implementation of [PdfPageAnnotationCommon::name()].
233        #[inline]
234        fn name_impl(&self) -> Option<String> {
235            self.get_string_value("NM")
236        }
237
238        /// Internal implementation of [PdfPageAnnotationCommon::bounds()].
239        #[inline]
240        fn bounds_impl(&self) -> Result<PdfRect, PdfiumError> {
241            let mut rect = FS_RECTF {
242                left: 0_f32,
243                bottom: 0_f32,
244                right: 0_f32,
245                top: 0_f32,
246            };
247
248            let result = self.bindings().FPDFAnnot_GetRect(self.handle(), &mut rect);
249
250            PdfRect::from_pdfium_as_result(result, rect, self.bindings())
251        }
252
253        /// Internal implementation of [PdfPageAnnotationCommon::set_bounds()].
254        #[inline]
255        fn set_bounds_impl(&mut self, bounds: PdfRect) -> Result<(), PdfiumError> {
256            if self.bindings().is_true(
257                self.bindings()
258                    .FPDFAnnot_SetRect(self.handle(), &bounds.as_pdfium()),
259            ) {
260                self.set_string_value("M", &date_time_to_pdf_string(Utc::now()))
261            } else {
262                Err(PdfiumError::PdfiumLibraryInternalError(
263                    PdfiumInternalError::Unknown,
264                ))
265            }
266        }
267
268        /// Internal implementation of [PdfPageAnnotationCommon::set_position()].
269        fn set_position_impl(&mut self, x: PdfPoints, y: PdfPoints) -> Result<(), PdfiumError> {
270            let bounds = self
271                .bounds()
272                .unwrap_or(PdfRect::new_from_values(0.0, 0.0, 1.0, 1.0));
273
274            let width = bounds.width();
275
276            let height = bounds.height();
277
278            self.set_bounds(PdfRect::new(y, x, y + height, x + width))
279        }
280
281        /// Internal implementation of [PdfPageAnnotationCommon::set_width()].
282        fn set_width_impl(&mut self, width: PdfPoints) -> Result<(), PdfiumError> {
283            let bounds = self
284                .bounds()
285                .unwrap_or(PdfRect::new_from_values(0.0, 0.0, 1.0, 1.0));
286
287            let height = bounds.height();
288
289            self.set_bounds(PdfRect::new(
290                bounds.bottom(),
291                bounds.left(),
292                bounds.bottom() + height,
293                bounds.left() + width,
294            ))
295        }
296
297        /// Internal implementation of [PdfPageAnnotationCommon::set_height()].
298        fn set_height_impl(&mut self, height: PdfPoints) -> Result<(), PdfiumError> {
299            let bounds = self
300                .bounds()
301                .unwrap_or(PdfRect::new_from_values(0.0, 0.0, 1.0, 1.0));
302
303            let width = bounds.width();
304
305            self.set_bounds(PdfRect::new(
306                bounds.bottom(),
307                bounds.left(),
308                bounds.bottom() + height,
309                bounds.left() + width,
310            ))
311        }
312
313        /// Internal implementation of [PdfPageAnnotationCommon::contents()].
314        #[inline]
315        fn contents_impl(&self) -> Option<String> {
316            self.get_string_value("Contents")
317        }
318
319        /// Internal implementation of [PdfPageAnnotationCommon::set_contents()].
320        #[inline]
321        fn set_contents_impl(&mut self, contents: &str) -> Result<(), PdfiumError> {
322            self.set_string_value("Contents", contents)
323        }
324
325        /// Internal implementation of [PdfPageAnnotationCommon::creator()].
326        #[inline]
327        fn creator_impl(&self) -> Option<String> {
328            self.get_string_value("T")
329        }
330
331        /// Internal implementation of [PdfPageAnnotationCommon::set_creator()].
332        #[inline]
333        fn set_creator_impl(&mut self, creator: &str) -> Result<(), PdfiumError> {
334            self.set_string_value("T", creator)
335        }
336
337        /// Internal implementation of [PdfPageAnnotationCommon::creation_date()].
338        #[inline]
339        fn creation_date_impl(&self) -> Option<String> {
340            self.get_string_value("CreationDate")
341        }
342
343        /// Internal implementation of [PdfPageAnnotationCommon::set_creation_date()].
344        #[inline]
345        fn set_creation_date_impl(&mut self, date: DateTime<Utc>) -> Result<(), PdfiumError> {
346            self.set_string_value("CreationDate", &date_time_to_pdf_string(date))
347        }
348
349        /// Internal implementation of [PdfPageAnnotationCommon::modification_date()].
350        #[inline]
351        fn modification_date_impl(&self) -> Option<String> {
352            self.get_string_value("M")
353        }
354
355        /// Internal implementation of [PdfPageAnnotationCommon::set_modification_date()].
356        #[inline]
357        fn set_modification_date_impl(&mut self, date: DateTime<Utc>) -> Result<(), PdfiumError> {
358            self.set_string_value("M", &date_time_to_pdf_string(date))
359        }
360
361        /// Internal implementation of [PdfPageAnnotationCommon::is_markup_annotation()].
362        #[inline]
363        fn is_markup_annotation_impl(&self) -> bool {
364            // We take advantage of the fact that all markup annotations support attachment points,
365            // and the only type of annotation (other than markup annotations) that supports
366            // attachment points is the Link annotation.
367
368            self.has_attachment_points_impl()
369                && self.get_annotation_type() != PdfPageAnnotationType::Link
370        }
371
372        /// Internal implementation of [PdfPageAnnotationCommon::has_attachment_points()].
373        #[inline]
374        fn has_attachment_points_impl(&self) -> bool {
375            self.bindings()
376                .is_true(self.bindings().FPDFAnnot_HasAttachmentPoints(self.handle()))
377        }
378
379        /// Internal implementation of [PdfPageAnnotationCommon::fill_color()].
380        #[inline]
381        fn fill_color_impl(&self) -> Result<PdfColor, PdfiumError> {
382            let mut r: c_uint = 0;
383
384            let mut g: c_uint = 0;
385
386            let mut b: c_uint = 0;
387
388            let mut a: c_uint = 0;
389
390            if self.bindings().is_true(self.bindings().FPDFAnnot_GetColor(
391                self.handle(),
392                FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_InteriorColor,
393                &mut r,
394                &mut g,
395                &mut b,
396                &mut a,
397            )) {
398                Ok(PdfColor::new(r as u8, g as u8, b as u8, a as u8))
399            } else {
400                // The FPDFAnnot_GetColor() function returns false if the annotation
401                // is using appearance streams. In this case, the Pdfium documentation
402                // states that we must use FPDFPath_GetFillColor() instead; that function
403                // is deprecated, and says to use FPDFPageObj_GetFillColor().
404
405                if self
406                    .bindings()
407                    .is_true(self.bindings().FPDFPageObj_GetFillColor(
408                        self.handle() as FPDF_PAGEOBJECT,
409                        &mut r,
410                        &mut g,
411                        &mut b,
412                        &mut a,
413                    ))
414                {
415                    Ok(PdfColor::new(r as u8, g as u8, b as u8, a as u8))
416                } else {
417                    Err(PdfiumError::PdfiumLibraryInternalError(
418                        PdfiumInternalError::Unknown,
419                    ))
420                }
421            }
422        }
423
424        /// Internal implementation of [PdfPageAnnotationCommon::set_fill_color()].
425        #[inline]
426        fn set_fill_color_impl(&mut self, fill_color: PdfColor) -> Result<(), PdfiumError> {
427            if self.bindings().is_true(self.bindings().FPDFAnnot_SetColor(
428                self.handle(),
429                FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_InteriorColor,
430                fill_color.red() as c_uint,
431                fill_color.green() as c_uint,
432                fill_color.blue() as c_uint,
433                fill_color.alpha() as c_uint,
434            )) {
435                Ok(())
436            } else {
437                // The FPDFAnnot_SetColor() function returns false if the annotation
438                // is using appearance streams. In this case, the Pdfium documentation
439                // states that we must use FPDFPath_SetFillColor() instead; that function
440                // is deprecated, and says to use FPDFPageObj_SetFillColor().
441
442                if self
443                    .bindings()
444                    .is_true(self.bindings().FPDFPageObj_SetFillColor(
445                        self.handle() as FPDF_PAGEOBJECT,
446                        fill_color.red() as c_uint,
447                        fill_color.green() as c_uint,
448                        fill_color.blue() as c_uint,
449                        fill_color.alpha() as c_uint,
450                    ))
451                {
452                    Ok(())
453                } else {
454                    Err(PdfiumError::PdfiumLibraryInternalError(
455                        PdfiumInternalError::Unknown,
456                    ))
457                }
458            }
459        }
460
461        /// Internal implementation of [PdfPageAnnotationCommon::stroke_color()].
462        #[inline]
463        fn stroke_color_impl(&self) -> Result<PdfColor, PdfiumError> {
464            let mut r: c_uint = 0;
465
466            let mut g: c_uint = 0;
467
468            let mut b: c_uint = 0;
469
470            let mut a: c_uint = 0;
471
472            if self.bindings().is_true(self.bindings().FPDFAnnot_GetColor(
473                self.handle(),
474                FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_Color,
475                &mut r,
476                &mut g,
477                &mut b,
478                &mut a,
479            )) {
480                Ok(PdfColor::new(r as u8, g as u8, b as u8, a as u8))
481            } else {
482                // The FPDFAnnot_GetColor() function returns false if the annotation
483                // is using appearance streams. In this case, the Pdfium documentation
484                // states that we must use FPDFPath_GetStrokeColor() instead; that function
485                // is deprecated, and says to use FPDFPageObj_GetStrokeColor().
486
487                if self
488                    .bindings()
489                    .is_true(self.bindings().FPDFPageObj_GetStrokeColor(
490                        self.handle() as FPDF_PAGEOBJECT,
491                        &mut r,
492                        &mut g,
493                        &mut b,
494                        &mut a,
495                    ))
496                {
497                    Ok(PdfColor::new(r as u8, g as u8, b as u8, a as u8))
498                } else {
499                    Err(PdfiumError::PdfiumLibraryInternalError(
500                        PdfiumInternalError::Unknown,
501                    ))
502                }
503            }
504        }
505
506        /// Internal implementation of [PdfPageAnnotationCommon::set_stroke_color()].
507        #[inline]
508        fn set_stroke_color_impl(&mut self, stroke_color: PdfColor) -> Result<(), PdfiumError> {
509            if self.bindings().is_true(self.bindings().FPDFAnnot_SetColor(
510                self.handle(),
511                FPDFANNOT_COLORTYPE_FPDFANNOT_COLORTYPE_Color,
512                stroke_color.red() as c_uint,
513                stroke_color.green() as c_uint,
514                stroke_color.blue() as c_uint,
515                stroke_color.alpha() as c_uint,
516            )) {
517                Ok(())
518            } else {
519                // The FPDFAnnot_SetColor() function returns false if the annotation
520                // is using appearance streams. In this case, the Pdfium documentation
521                // states that we must use FPDFPath_SetStrokeColor() instead; that function
522                // is deprecated, and says to use FPDFPageObj_SetStrokeColor().
523
524                if self
525                    .bindings()
526                    .is_true(self.bindings().FPDFPageObj_SetStrokeColor(
527                        self.handle() as FPDF_PAGEOBJECT,
528                        stroke_color.red() as c_uint,
529                        stroke_color.green() as c_uint,
530                        stroke_color.blue() as c_uint,
531                        stroke_color.alpha() as c_uint,
532                    ))
533                {
534                    Ok(())
535                } else {
536                    Err(PdfiumError::PdfiumLibraryInternalError(
537                        PdfiumInternalError::Unknown,
538                    ))
539                }
540            }
541        }
542
543        /// Returns all the flags currently set on this annotation.
544        #[inline]
545        fn get_flags_impl(&self) -> PdfAnnotationFlags {
546            PdfAnnotationFlags::from_bits_truncate(
547                self.bindings().FPDFAnnot_GetFlags(self.handle()) as u32,
548            )
549        }
550
551        /// Sets all the flags on this annotation.
552        #[inline]
553        fn set_flags_impl(&mut self, flags: PdfAnnotationFlags) -> bool {
554            self.bindings().is_true(
555                self.bindings()
556                    .FPDFAnnot_SetFlags(self.handle(), flags.bits() as c_int),
557            )
558        }
559
560        /// Sets or clears a single flag on this annotation.
561        fn update_one_flag_impl(
562            &mut self,
563            flag: PdfAnnotationFlags,
564            value: bool,
565        ) -> Result<(), PdfiumError> {
566            let mut flags = self.get_flags_impl();
567
568            flags.set(flag, value);
569
570            if self.set_flags_impl(flags) {
571                Ok(())
572            } else {
573                Err(PdfiumError::PdfiumLibraryInternalError(
574                    crate::error::PdfiumInternalError::Unknown,
575                ))
576            }
577        }
578
579        /// Internal implementation of [PdfPageAnnotationCommon::objects()].
580        fn objects_impl(&self) -> &PdfPageAnnotationObjects;
581
582        /// Internal implementation of [PdfPageAnnotationCommon::attachment_points()].
583        fn attachment_points_impl(&self) -> &PdfPageAnnotationAttachmentPoints;
584    }
585}
586
587#[cfg(test)]
588mod tests {
589    use crate::pdf::document::page::annotation::private::internal::{
590        PdfAnnotationFlags, PdfPageAnnotationPrivate,
591    };
592    use crate::prelude::*;
593    use crate::utils::test::test_bind_to_pdfium;
594
595    #[test]
596    fn test_get_annotation_flags() -> Result<(), PdfiumError> {
597        let pdfium = test_bind_to_pdfium();
598        let document = pdfium.load_pdf_from_file("test/form-test.pdf", None)?;
599        let page = document.pages().first()?;
600        let annotation = page
601            .annotations()
602            .iter()
603            .find(|annotation| annotation.as_form_field().is_some())
604            .unwrap();
605        let widget = annotation.as_widget_annotation().unwrap();
606
607        let flags = widget.get_flags_impl();
608
609        assert!(!flags.contains(PdfAnnotationFlags::LockedContents));
610        assert_eq!(widget.is_editable(), true);
611
612        assert!(!flags.contains(PdfAnnotationFlags::Locked));
613        assert_eq!(widget.is_locked(), false);
614
615        assert!(!flags.contains(PdfAnnotationFlags::ReadOnly));
616        assert_eq!(widget.is_read_only(), false);
617
618        assert!(flags.contains(PdfAnnotationFlags::Print));
619        assert_eq!(widget.is_printed(), true);
620
621        assert!(!flags.contains(PdfAnnotationFlags::Hidden));
622        assert_eq!(widget.is_hidden(), false);
623
624        assert!(!flags.contains(PdfAnnotationFlags::Invisible));
625        assert_eq!(widget.is_invisible_if_unsupported(), false);
626
627        Ok(())
628    }
629
630    #[test]
631    fn test_set_annotation_flags() -> Result<(), PdfiumError> {
632        let pdfium = test_bind_to_pdfium();
633        let mut document = pdfium.load_pdf_from_file("test/form-test.pdf", None)?;
634        let mut page = document.pages_mut().first()?;
635        let mut annotation = page
636            .annotations_mut()
637            .iter()
638            .find(|annotation| annotation.as_form_field().is_some())
639            .unwrap();
640        let widget = annotation.as_widget_annotation_mut().unwrap();
641
642        assert_eq!(widget.is_editable(), true);
643        assert_eq!(widget.is_locked(), false);
644        assert_eq!(widget.is_read_only(), false);
645        assert_eq!(widget.is_printed(), true);
646        assert_eq!(widget.is_hidden(), false);
647        assert_eq!(widget.is_invisible_if_unsupported(), false);
648
649        let mut flags = widget.get_flags_impl();
650
651        flags.set(PdfAnnotationFlags::ReadOnly, true);
652        flags.set(PdfAnnotationFlags::Locked, true);
653        flags.set(PdfAnnotationFlags::LockedContents, true);
654        flags.set(PdfAnnotationFlags::Print, false);
655        flags.set(PdfAnnotationFlags::Hidden, true);
656        flags.set(PdfAnnotationFlags::Invisible, true);
657
658        assert!(widget.set_flags_impl(flags));
659
660        assert_eq!(widget.is_editable(), false);
661        assert_eq!(widget.is_locked(), true);
662        assert_eq!(widget.is_read_only(), true);
663        assert_eq!(widget.is_printed(), false);
664        assert_eq!(widget.is_hidden(), true);
665        assert_eq!(widget.is_invisible_if_unsupported(), true);
666
667        Ok(())
668    }
669
670    #[test]
671    fn test_update_one_annotation_flag() -> Result<(), PdfiumError> {
672        let pdfium = test_bind_to_pdfium();
673        let mut document = pdfium.load_pdf_from_file("test/form-test.pdf", None)?;
674        let mut page = document.pages_mut().first()?;
675        let mut annotation = page
676            .annotations_mut()
677            .iter()
678            .find(|annotation| annotation.as_form_field().is_some())
679            .unwrap();
680        let widget = annotation.as_widget_annotation_mut().unwrap();
681
682        assert_eq!(widget.is_editable(), true);
683        assert_eq!(widget.is_locked(), false);
684        assert_eq!(widget.is_read_only(), false);
685        assert_eq!(widget.is_printed(), true);
686        assert_eq!(widget.is_hidden(), false);
687        assert_eq!(widget.is_invisible_if_unsupported(), false);
688
689        widget.set_is_editable(false)?;
690        assert_eq!(widget.is_editable(), false);
691
692        widget.set_is_locked(true)?;
693        assert_eq!(widget.is_locked(), true);
694
695        widget.set_is_read_only(true)?;
696        assert_eq!(widget.is_read_only(), true);
697
698        widget.set_is_printed(false)?;
699        assert_eq!(widget.is_printed(), false);
700
701        widget.set_is_hidden(true)?;
702        assert_eq!(widget.is_hidden(), true);
703
704        widget.set_is_invisible_if_unsupported(true)?;
705        assert_eq!(widget.is_invisible_if_unsupported(), true);
706
707        assert_eq!(widget.is_editable(), false);
708        assert_eq!(widget.is_locked(), true);
709        assert_eq!(widget.is_read_only(), true);
710        assert_eq!(widget.is_printed(), false);
711        assert_eq!(widget.is_hidden(), true);
712        assert_eq!(widget.is_invisible_if_unsupported(), true);
713
714        Ok(())
715    }
716}