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