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}