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::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(document_handle: FPDF_DOCUMENT, bindings: &'a dyn PdfiumLibraryBindings) -> Self {
64        Self {
65            document_handle,
66            bindings,
67        }
68    }
69
70    /// Returns the [PdfiumLibraryBindings] used by this [PdfPermissions] collection.
71    #[inline]
72    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
73        self.bindings
74    }
75
76    /// Returns the raw permissions bitflags for the containing [PdfDocument].
77    #[inline]
78    fn get_permissions_bits(&self) -> FpdfPermissions {
79        #[allow(clippy::unnecessary_cast)]
80        FpdfPermissions::from_bits_truncate(self.bindings().FPDF_GetDocPermissions(self.document_handle) as u32)
81    }
82
83    /// Returns the revision of the standard security handler used by the containing [PdfDocument].
84    /// As of PDF version 1.7, possible revision numbers are 2, 3, or 4.
85    pub fn security_handler_revision(&self) -> Result<PdfSecurityHandlerRevision, PdfiumError> {
86        PdfSecurityHandlerRevision::from_pdfium(self.bindings().FPDF_GetSecurityHandlerRevision(self.document_handle))
87            .ok_or(PdfiumError::UnknownPdfSecurityHandlerRevision)
88    }
89
90    /// Returns `true` if the containing [PdfDocument] can be printed to a representation
91    /// from which a faithful digital copy of the original content could be recovered.
92    pub fn can_print_high_quality(&self) -> Result<bool, PdfiumError> {
93        let permissions = self.get_permissions_bits();
94
95        let result = match self.security_handler_revision()? {
96            PdfSecurityHandlerRevision::Unprotected => true,
97            PdfSecurityHandlerRevision::Revision2 => permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3),
98            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
99                permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3)
100                    && permissions.contains(FpdfPermissions::V3_CAN_PRINT_HIGH_QUALITY_BIT_12)
101            }
102        };
103
104        Ok(result)
105    }
106
107    /// Returns `true` if the containing [PdfDocument] can be only be printed to a low-level
108    /// representation of the appearance of the document, possibly of degraded quality,
109    /// from which a faithful digital copy of the original content could _not_ be recovered.
110    pub fn can_print_only_low_quality(&self) -> Result<bool, PdfiumError> {
111        let permissions = self.get_permissions_bits();
112
113        let result = match self.security_handler_revision()? {
114            PdfSecurityHandlerRevision::Unprotected | PdfSecurityHandlerRevision::Revision2 => false,
115            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
116                permissions.contains(FpdfPermissions::CAN_PRINT_BIT_3)
117                    && !permissions.contains(FpdfPermissions::V3_CAN_PRINT_HIGH_QUALITY_BIT_12)
118            }
119        };
120
121        Ok(result)
122    }
123
124    /// Returns `true` if the containing [PdfDocument] can be _assembled_; that is, the
125    /// document can have pages inserted, rotated, or deleted, can have bookmarks created,
126    /// or can have thumbnail page images created.
127    pub fn can_assemble_document(&self) -> Result<bool, PdfiumError> {
128        let permissions = self.get_permissions_bits();
129
130        let result = match self.security_handler_revision()? {
131            PdfSecurityHandlerRevision::Unprotected => true,
132            PdfSecurityHandlerRevision::Revision2 => permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4),
133            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
134                permissions.contains(FpdfPermissions::V3_CAN_ASSEMBLE_DOCUMENT_BIT_11)
135            }
136        };
137
138        Ok(result)
139    }
140
141    /// Returns `true` if the containing [PdfDocument] allows general modification of
142    /// the document contents.
143    ///
144    /// For security handler revisions 3 and later, general document modification can be disabled
145    /// while still allowing modification of annotations and interactive form fields.
146    pub fn can_modify_document_content(&self) -> Result<bool, PdfiumError> {
147        let permissions = self.get_permissions_bits();
148
149        let result = match self.security_handler_revision()? {
150            PdfSecurityHandlerRevision::Unprotected => true,
151            _ => permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4),
152        };
153
154        Ok(result)
155    }
156
157    /// Returns `true` if the containing [PdfDocument] permits text and graphics to be extracted.
158    pub fn can_extract_text_and_graphics(&self) -> Result<bool, PdfiumError> {
159        let permissions = self.get_permissions_bits();
160
161        let result = match self.security_handler_revision()? {
162            PdfSecurityHandlerRevision::Unprotected => true,
163            PdfSecurityHandlerRevision::Revision2 => {
164                permissions.contains(FpdfPermissions::CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_5)
165            }
166            // ~keep TODO: AJRC - 27/5/22 - what operations are permitted by bit 10 but prevented by bit 5?
167            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
168                permissions.contains(FpdfPermissions::V3_CAN_EXTRACT_TEXT_AND_GRAPHICS_BIT_10)
169            }
170        };
171
172        Ok(result)
173    }
174
175    /// Returns `true` if the containing [PdfDocument] permits any existing form fields,
176    /// including signature fields, to be filled in by a user.
177    pub fn can_fill_existing_interactive_form_fields(&self) -> Result<bool, PdfiumError> {
178        let permissions = self.get_permissions_bits();
179
180        let result = match self.security_handler_revision()? {
181            PdfSecurityHandlerRevision::Unprotected => true,
182            PdfSecurityHandlerRevision::Revision2 => {
183                permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6)
184            }
185            PdfSecurityHandlerRevision::Revision3 | PdfSecurityHandlerRevision::Revision4 => {
186                permissions.contains(FpdfPermissions::V3_CAN_FORM_FILL_BIT_9)
187            }
188        };
189
190        Ok(result)
191    }
192
193    /// Returns `true` if the containing [PdfDocument] allows the creation of new form fields,
194    /// including new signature fields.
195    pub fn can_create_new_interactive_form_fields(&self) -> Result<bool, PdfiumError> {
196        let permissions = self.get_permissions_bits();
197
198        let result = match self.security_handler_revision()? {
199            PdfSecurityHandlerRevision::Unprotected => true,
200            _ => {
201                permissions.contains(FpdfPermissions::CAN_MODIFY_BIT_4)
202                    && permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6)
203            }
204        };
205
206        Ok(result)
207    }
208
209    /// Returns `true` if the containing [PdfDocument] allows the addition or modification
210    /// of text annotations.
211    pub fn can_add_or_modify_text_annotations(&self) -> Result<bool, PdfiumError> {
212        let permissions = self.get_permissions_bits();
213
214        let result = match self.security_handler_revision()? {
215            PdfSecurityHandlerRevision::Unprotected => true,
216            _ => permissions.contains(FpdfPermissions::CAN_ANNOTATE_AND_FORM_FILL_BIT_6),
217        };
218
219        Ok(result)
220    }
221}