Skip to main content

pdfium_render/pdf/document/
permissions.rs

1//! Defines the [PdfPermissions] collection, containing information on the permissions
2//! and security handlers set for a single `PdfDocument`.
3
4use crate::bindgen::FPDF_DOCUMENT;
5use crate::error::PdfiumError;
6use crate::pdfium::PdfiumLibraryBindingsAccessor;
7use bitflags::bitflags;
8use std::marker::PhantomData;
9use std::os::raw::c_int;
10
11#[cfg(doc)]
12use crate::pdf::document::PdfDocument;
13
14bitflags! {
15    struct FpdfPermissions: u32 {
16        const RESERVED_BIT_1 =                          0b00000000000000000000000000000001;
17        const RESERVED_BIT_2 =                          0b00000000000000000000000000000010;
18        const CAN_PRINT_BIT_3 =                         0b00000000000000000000000000000100;
19        const CAN_MODIFY_BIT_4 =                        0b00000000000000000000000000001000;
20        const CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_5 =     0b00000000000000000000000000010000;
21        const CAN_ANNOTATE_AND_FORM_FILL_BIT_6 =        0b00000000000000000000000000100000;
22        const RESERVED_BIT_7 =                          0b00000000000000000000000001000000;
23        const RESERVED_BIT_8 =                          0b00000000000000000000000010000000;
24        const V3_CAN_FORM_FILL_BIT_9 =                  0b00000000000000000000000100000000;
25        const V3_CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_10 = 0b00000000000000000000001000000000;
26        const V3_CAN_ASSEMBLE_DOCUMENT_BIT_11 =         0b00000000000000000000010000000000;
27        const V3_CAN_PRINT_HIGH_QUALITY_BIT_12 =        0b00000000000000000000100000000000;
28    }
29}
30
31/// The revision of the standard security handler for a single [PdfDocument].
32#[derive(Copy, Clone, Debug, PartialEq)]
33pub enum PdfSecurityHandlerRevision {
34    Unprotected,
35    Revision2,
36    Revision3,
37    Revision4,
38}
39
40impl PdfSecurityHandlerRevision {
41    pub(crate) fn from_pdfium(value: c_int) -> Option<Self> {
42        match value {
43            -1 => Some(PdfSecurityHandlerRevision::Unprotected),
44            2 => Some(PdfSecurityHandlerRevision::Revision2),
45            3 => Some(PdfSecurityHandlerRevision::Revision3),
46            4 => Some(PdfSecurityHandlerRevision::Revision4),
47            _ => None,
48        }
49    }
50}
51
52/// The collection of document permissions and security handler settings for a single [PdfDocument].
53///
54/// Note that Pdfium currently only offers support for reading the existing permissions of a
55/// document. It does not support changing existing permissions or adding new permissions to
56/// a document.
57pub struct PdfPermissions<'a> {
58    document_handle: FPDF_DOCUMENT,
59    lifetime: PhantomData<&'a FPDF_DOCUMENT>,
60}
61
62impl<'a> PdfPermissions<'a> {
63    #[inline]
64    pub(crate) fn from_pdfium(document_handle: FPDF_DOCUMENT) -> Self {
65        Self {
66            document_handle,
67            lifetime: PhantomData,
68        }
69    }
70
71    /// Returns the raw permissions bitflags for the containing [PdfDocument].
72    #[inline]
73    fn get_permissions_bits(&self) -> FpdfPermissions {
74        FpdfPermissions::from_bits_truncate(unsafe {
75            self.bindings().FPDF_GetDocPermissions(self.document_handle)
76        } as u32)
77    }
78
79    /// Returns the revision of the standard security handler used by the containing [PdfDocument].
80    /// As of PDF version 1.7, possible revision numbers are 2, 3, or 4.
81    pub fn security_handler_revision(&self) -> Result<PdfSecurityHandlerRevision, PdfiumError> {
82        PdfSecurityHandlerRevision::from_pdfium(unsafe {
83            self.bindings()
84                .FPDF_GetSecurityHandlerRevision(self.document_handle)
85        })
86        .ok_or(PdfiumError::UnknownPdfSecurityHandlerRevision)
87    }
88
89    /// Returns `true` if the containing [PdfDocument] can be printed to a representation
90    /// from which a faithful digital copy of the original content could be recovered.
91    pub fn can_print_high_quality(&self) -> Result<bool, PdfiumError> {
92        let permissions = self.get_permissions_bits();
93
94        let result = match self.security_handler_revision()? {
95            PdfSecurityHandlerRevision::Unprotected => true,
96            PdfSecurityHandlerRevision::Revision2 => {
97                permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3)
98            }
99            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
100                permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3)
101                    && permissions.contains(FpdfPermissions::V3_CAN_PRINT_HIGH_QUALITY_BIT_12)
102            }
103        };
104
105        Ok(result)
106    }
107
108    /// Returns `true` if the containing [PdfDocument] can be only be printed to a low-level
109    /// representation of the appearance of the document, possibly of degraded quality,
110    /// from which a faithful digital copy of the original content could _not_ be recovered.
111    pub fn can_print_only_low_quality(&self) -> Result<bool, PdfiumError> {
112        let permissions = self.get_permissions_bits();
113
114        let result = match self.security_handler_revision()? {
115            PdfSecurityHandlerRevision::Unprotected | PdfSecurityHandlerRevision::Revision2 => {
116                false
117            }
118            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
119                permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3)
120                    && !permissions.contains(FpdfPermissions::V3_CAN_PRINT_HIGH_QUALITY_BIT_12)
121            }
122        };
123
124        Ok(result)
125    }
126
127    /// Returns `true` if the containing [PdfDocument] can be _assembled_; that is, the
128    /// document can have pages inserted, rotated, or deleted, can have bookmarks created,
129    /// or can have thumbnail page images created.
130    pub fn can_assemble_document(&self) -> Result<bool, PdfiumError> {
131        let permissions = self.get_permissions_bits();
132
133        let result = match self.security_handler_revision()? {
134            PdfSecurityHandlerRevision::Unprotected => true,
135            PdfSecurityHandlerRevision::Revision2 => {
136                permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4)
137            }
138            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
139                permissions.contains(FpdfPermissions::V3_CAN_ASSEMBLE_DOCUMENT_BIT_11)
140            }
141        };
142
143        Ok(result)
144    }
145
146    /// Returns `true` if the containing [PdfDocument] allows general modification of
147    /// the document contents.
148    ///
149    /// For security handler revisions 3 and later, general document modification can be disabled
150    /// while still allowing modification of annotations and interactive form fields.
151    pub fn can_modify_document_content(&self) -> Result<bool, PdfiumError> {
152        let permissions = self.get_permissions_bits();
153
154        let result = match self.security_handler_revision()? {
155            PdfSecurityHandlerRevision::Unprotected => true,
156            _ => permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4),
157        };
158
159        Ok(result)
160    }
161
162    /// Returns `true` if the containing [PdfDocument] permits text and graphics to be extracted.
163    pub fn can_extract_text_and_graphics(&self) -> Result<bool, PdfiumError> {
164        let permissions = self.get_permissions_bits();
165
166        let result = match self.security_handler_revision()? {
167            PdfSecurityHandlerRevision::Unprotected => true,
168            PdfSecurityHandlerRevision::Revision2 => {
169                permissions.contains(FpdfPermissions::CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_5)
170            }
171            // TODO: AJRC - 27/5/22 - what operations are permitted by bit 10 but prevented by bit 5?
172            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
173                permissions.contains(FpdfPermissions::V3_CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_10)
174            }
175        };
176
177        Ok(result)
178    }
179
180    /// Returns `true` if the containing [PdfDocument] permits any existing form fields,
181    /// including signature fields, to be filled in by a user.
182    pub fn can_fill_existing_interactive_form_fields(&self) -> Result<bool, PdfiumError> {
183        let permissions = self.get_permissions_bits();
184
185        let result = match self.security_handler_revision()? {
186            PdfSecurityHandlerRevision::Unprotected => true,
187            PdfSecurityHandlerRevision::Revision2 => {
188                permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6)
189            }
190            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
191                permissions.contains(FpdfPermissions::V3_CAN_FORM_FILL_BIT_9)
192            }
193        };
194
195        Ok(result)
196    }
197
198    /// Returns `true` if the containing [PdfDocument] allows the creation of new form fields,
199    /// including new signature fields.
200    pub fn can_create_new_interactive_form_fields(&self) -> Result<bool, PdfiumError> {
201        let permissions = self.get_permissions_bits();
202
203        let result = match self.security_handler_revision()? {
204            PdfSecurityHandlerRevision::Unprotected => true,
205            _ => {
206                permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4)
207                    && permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6)
208            }
209        };
210
211        Ok(result)
212    }
213
214    /// Returns `true` if the containing [PdfDocument] allows the addition or modification
215    /// of text annotations.
216    pub fn can_add_or_modify_text_annotations(&self) -> Result<bool, PdfiumError> {
217        let permissions = self.get_permissions_bits();
218
219        let result = match self.security_handler_revision()? {
220            PdfSecurityHandlerRevision::Unprotected => true,
221            _ => permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6),
222        };
223
224        Ok(result)
225    }
226}
227
228impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPermissions<'a> {}
229
230#[cfg(feature = "thread_safe")]
231unsafe impl<'a> Send for PdfPermissions<'a> {}
232
233#[cfg(feature = "thread_safe")]
234unsafe impl<'a> Sync for PdfPermissions<'a> {}