1#![deny(missing_docs)]
8#![warn(clippy::all, clippy::nursery, clippy::pedantic, clippy::cargo)]
9
10use bitflags::Flags;
11use log::{debug, error, warn};
12use lopdf::{
13 Document, EncryptionState, EncryptionVersion, Error, Permissions, Result as PdfResult,
14};
15
16pub trait PdfPerm {
18 fn permissions(&self) -> Permissions;
20 fn set_permissions(&mut self, permissions: Permissions) -> PdfResult<()>;
30}
31
32impl PdfPerm for Document {
33 fn permissions(&self) -> Permissions {
34 self.encryption_state
35 .as_ref()
36 .map(EncryptionState::permissions)
37 .unwrap_or_default()
38 }
39 fn set_permissions(&mut self, permissions: Permissions) -> PdfResult<()> {
40 if self.is_encrypted() {
41 error!("Does not support setting permissions on encrypted documents");
42 return Err(Error::AlreadyEncrypted);
43 }
44
45 if permissions == Permissions::default() {
46 debug!("Skipping encryption since permissions are default");
47 return Ok(());
48 }
49
50 let version = EncryptionVersion::V1 {
51 document: self,
52 owner_password: "",
53 user_password: "",
54 permissions,
55 };
56 let state: EncryptionState = version.try_into()?;
57 debug!("Encryption state: {state:?}");
58 self.encrypt(&state)?;
59
60 Ok(())
61 }
62}
63
64pub trait ShortFlags: Flags + Copy {
67 const SHORT_FLAGS: &'static [char];
70
71 fn from_char(c: char) -> Option<Self> {
74 if c == '*' {
75 return Some(Self::all());
76 }
77 let index = Self::SHORT_FLAGS.iter().position(|&flag| flag == c)?;
78 Some(*Self::FLAGS.get(index)?.value())
79 }
80 #[must_use]
82 fn from_str(s: &str) -> Self {
83 let mut flags = Self::empty();
84 for c in s.chars() {
85 if let Some(flag) = Self::from_char(c) {
86 flags.insert(flag);
87 } else {
88 warn!("Invalid short flag: {c}");
89 }
90 }
91 flags
92 }
93 fn apply_modification(&mut self, modification: &str) {
95 let (first, rest) = modification.split_at(1);
96 let flags_mod = Self::from_str(rest);
97 match first {
98 "+" => self.insert(flags_mod), "-" => self.remove(flags_mod), "=" => *self = flags_mod, _ => warn!("Invalid modification indicator: {modification}"),
102 }
103 }
104 fn summary(&self) -> String {
106 let mut summary = String::with_capacity(Self::SHORT_FLAGS.len());
107 for (short, flag) in Self::SHORT_FLAGS.iter().zip(Self::FLAGS) {
108 if self.contains(*flag.value()) {
109 summary.push(*short);
110 } else {
111 summary.push('-');
112 }
113 }
114 summary
115 }
116}
117
118impl ShortFlags for Permissions {
119 const SHORT_FLAGS: &'static [char] = &['p', 'm', 'c', 'a', 'f', 'x', 's', 'q'];
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use lopdf::Object;
126
127 fn create_test_document() -> Document {
129 let mut doc = Document::new();
131
132 doc.trailer.set(
133 "ID",
134 Object::Array(vec![
135 Object::string_literal(b"ABC"),
136 Object::string_literal(b"DEF"),
137 ]),
138 );
139
140 doc
141 }
142
143 #[test]
144 fn test_permissions() {
145 let mut doc = create_test_document();
146 assert_eq!(doc.permissions(), Permissions::default());
147
148 let pma_permissions = Permissions::from_str("pma");
149 doc.set_permissions(pma_permissions).unwrap();
150
151 let mut buffer = Vec::new();
152 doc.save_to(&mut buffer).unwrap();
153
154 let doc = Document::load_mem(&buffer).unwrap();
155 assert_eq!(doc.permissions(), pma_permissions);
156 }
157
158 #[test]
160 fn test_from_str_1() {
161 let permissions = Permissions::from_str("pmc");
162 let expected = Permissions::PRINTABLE | Permissions::MODIFIABLE | Permissions::COPYABLE;
163 assert_eq!(permissions, expected);
164 }
165
166 #[test]
167 fn test_from_str_2() {
168 let permissions = Permissions::from_str("*");
169 let expected = Permissions::all();
170 assert_eq!(permissions, expected);
171 }
172
173 #[test]
174 fn test_apply_modification() {
175 let mut permissions = Permissions::empty();
176 permissions.apply_modification("+p");
177 assert_eq!(permissions, Permissions::PRINTABLE);
178 permissions.apply_modification("-p");
179 assert_eq!(permissions, Permissions::empty());
180 permissions.apply_modification("=m");
181 assert_eq!(permissions, Permissions::MODIFIABLE);
182 }
183}