1use std::{ffi::CString, path::Path, slice};
2
3use crate::{ObjectStreamMode, QPdf, Result, StreamDataMode, StreamDecodeLevel};
4
5#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, PartialOrd)]
7pub enum PrintPermission {
8 #[default]
9 Full,
10 Low,
11 None,
12}
13
14impl From<PrintPermission> for qpdf_sys::qpdf_r3_print_e {
15 fn from(value: PrintPermission) -> Self {
16 match value {
17 PrintPermission::Full => qpdf_sys::qpdf_r3_print_e_qpdf_r3p_full,
18 PrintPermission::Low => qpdf_sys::qpdf_r3_print_e_qpdf_r3p_low,
19 PrintPermission::None => qpdf_sys::qpdf_r3_print_e_qpdf_r3p_none,
20 }
21 }
22}
23
24#[derive(Debug, Default, Clone, Eq, PartialEq)]
26pub struct EncryptionParamsR2 {
27 pub user_password: String,
28 pub owner_password: String,
29 pub allow_print: bool,
30 pub allow_modify: bool,
31 pub allow_extract: bool,
32 pub allow_annotate: bool,
33}
34
35#[derive(Debug, Default, Clone, Eq, PartialEq)]
38pub struct EncryptionParamsR3 {
39 pub user_password: String,
40 pub owner_password: String,
41 pub allow_accessibility: bool,
42 pub allow_extract: bool,
43 pub allow_assemble: bool,
44 pub allow_annotate_and_form: bool,
45 pub allow_form_filling: bool,
46 pub allow_modify_other: bool,
47 pub allow_print: PrintPermission,
48}
49
50#[derive(Debug, Default, Clone, Eq, PartialEq)]
53pub struct EncryptionParamsR4 {
54 pub user_password: String,
55 pub owner_password: String,
56 pub allow_accessibility: bool,
57 pub allow_extract: bool,
58 pub allow_assemble: bool,
59 pub allow_annotate_and_form: bool,
60 pub allow_form_filling: bool,
61 pub allow_modify_other: bool,
62 pub allow_print: PrintPermission,
63 pub encrypt_metadata: bool,
64 pub use_aes: bool,
65}
66
67#[derive(Debug, Default, Clone, Eq, PartialEq)]
70pub struct EncryptionParamsR6 {
71 pub user_password: String,
72 pub owner_password: String,
73 pub allow_accessibility: bool,
74 pub allow_extract: bool,
75 pub allow_assemble: bool,
76 pub allow_annotate_and_form: bool,
77 pub allow_form_filling: bool,
78 pub allow_modify_other: bool,
79 pub allow_print: PrintPermission,
80 pub encrypt_metadata: bool,
81}
82
83#[derive(Debug, Clone, Eq, PartialEq)]
85pub enum EncryptionParams {
86 #[cfg(feature = "legacy")]
88 R2(EncryptionParamsR2),
89 #[cfg(feature = "legacy")]
91 R3(EncryptionParamsR3),
92 #[cfg(feature = "legacy")]
94 R4(EncryptionParamsR4),
95 R6(EncryptionParamsR6),
97}
98
99pub struct QPdfWriter {
101 owner: QPdf,
102 compress_streams: Option<bool>,
103 preserve_unreferenced_objects: Option<bool>,
104 normalize_content: Option<bool>,
105 preserve_encryption: Option<bool>,
106 linearize: Option<bool>,
107 static_id: Option<bool>,
108 deterministic_id: Option<bool>,
109 min_pdf_version: Option<String>,
110 force_pdf_version: Option<String>,
111 stream_decode_level: Option<StreamDecodeLevel>,
112 object_stream_mode: Option<ObjectStreamMode>,
113 stream_data_mode: Option<StreamDataMode>,
114 encryption_params: Option<EncryptionParams>,
115}
116
117impl QPdfWriter {
118 pub(crate) fn new(owner: QPdf) -> Self {
119 QPdfWriter {
120 owner,
121 compress_streams: None,
122 preserve_unreferenced_objects: None,
123 normalize_content: None,
124 preserve_encryption: None,
125 linearize: None,
126 static_id: None,
127 deterministic_id: None,
128 min_pdf_version: None,
129 force_pdf_version: None,
130 stream_decode_level: None,
131 object_stream_mode: None,
132 stream_data_mode: None,
133 encryption_params: None,
134 }
135 }
136
137 fn process_params(&self) -> Result<()> {
138 unsafe {
139 if let Some(compress_streams) = self.compress_streams {
140 qpdf_sys::qpdf_set_compress_streams(self.owner.inner(), compress_streams.into());
141 }
142
143 if let Some(preserve_unreferenced_objects) = self.preserve_unreferenced_objects {
144 qpdf_sys::qpdf_set_preserve_unreferenced_objects(
145 self.owner.inner(),
146 preserve_unreferenced_objects.into(),
147 );
148 }
149
150 if let Some(normalize_content) = self.normalize_content {
151 qpdf_sys::qpdf_set_content_normalization(self.owner.inner(), normalize_content.into());
152 }
153
154 if let Some(preserve_encryption) = self.preserve_encryption {
155 qpdf_sys::qpdf_set_preserve_encryption(self.owner.inner(), preserve_encryption.into());
156 }
157
158 if let Some(linearize) = self.linearize {
159 qpdf_sys::qpdf_set_linearization(self.owner.inner(), linearize.into());
160 }
161
162 if let Some(static_id) = self.static_id {
163 qpdf_sys::qpdf_set_static_ID(self.owner.inner(), static_id.into());
164 }
165
166 if let Some(deterministic_id) = self.deterministic_id {
167 qpdf_sys::qpdf_set_deterministic_ID(self.owner.inner(), deterministic_id.into());
168 }
169
170 if let Some(stream_decode_level) = self.stream_decode_level {
171 qpdf_sys::qpdf_set_decode_level(self.owner.inner(), stream_decode_level.as_qpdf_enum());
172 }
173
174 if let Some(object_stream_mode) = self.object_stream_mode {
175 qpdf_sys::qpdf_set_object_stream_mode(self.owner.inner(), object_stream_mode.as_qpdf_enum());
176 }
177
178 if let Some(stream_data_mode) = self.stream_data_mode {
179 qpdf_sys::qpdf_set_stream_data_mode(self.owner.inner(), stream_data_mode.as_qpdf_enum());
180 }
181
182 if let Some(ref version) = self.min_pdf_version {
183 let version = CString::new(version.as_str())?;
184 self.owner
185 .wrap_ffi_call(|| qpdf_sys::qpdf_set_minimum_pdf_version(self.owner.inner(), version.as_ptr()))?;
186 }
187 if let Some(ref version) = self.force_pdf_version {
188 let version = CString::new(version.as_str())?;
189 self.owner
190 .wrap_ffi_call(|| qpdf_sys::qpdf_force_pdf_version(self.owner.inner(), version.as_ptr()))?;
191 }
192 if let Some(ref params) = self.encryption_params {
193 self.set_encryption_params(params)?;
194 }
195 }
196 Ok(())
197 }
198
199 fn set_encryption_params(&self, params: &EncryptionParams) -> Result<()> {
200 match params {
201 #[cfg(feature = "legacy")]
202 EncryptionParams::R2(r2) => {
203 let user_password = CString::new(r2.user_password.as_str())?;
204 let owner_password = CString::new(r2.owner_password.as_str())?;
205 unsafe {
206 self.owner.wrap_ffi_call(|| {
207 qpdf_sys::qpdf_set_r2_encryption_parameters_insecure(
208 self.owner.inner(),
209 user_password.as_ptr(),
210 owner_password.as_ptr(),
211 r2.allow_print.into(),
212 r2.allow_modify.into(),
213 r2.allow_extract.into(),
214 r2.allow_annotate.into(),
215 )
216 })?;
217 }
218 }
219 #[cfg(feature = "legacy")]
220 EncryptionParams::R3(r3) => {
221 let user_password = CString::new(r3.user_password.as_str())?;
222 let owner_password = CString::new(r3.owner_password.as_str())?;
223 unsafe {
224 self.owner.wrap_ffi_call(|| {
225 qpdf_sys::qpdf_set_r3_encryption_parameters_insecure(
226 self.owner.inner(),
227 user_password.as_ptr(),
228 owner_password.as_ptr(),
229 r3.allow_accessibility.into(),
230 r3.allow_extract.into(),
231 r3.allow_assemble.into(),
232 r3.allow_annotate_and_form.into(),
233 r3.allow_form_filling.into(),
234 r3.allow_modify_other.into(),
235 r3.allow_print.into(),
236 )
237 })?;
238 }
239 }
240 #[cfg(feature = "legacy")]
241 EncryptionParams::R4(r4) => {
242 let user_password = CString::new(r4.user_password.as_str())?;
243 let owner_password = CString::new(r4.owner_password.as_str())?;
244 unsafe {
245 self.owner.wrap_ffi_call(|| {
246 qpdf_sys::qpdf_set_r4_encryption_parameters_insecure(
247 self.owner.inner(),
248 user_password.as_ptr(),
249 owner_password.as_ptr(),
250 r4.allow_accessibility.into(),
251 r4.allow_extract.into(),
252 r4.allow_assemble.into(),
253 r4.allow_annotate_and_form.into(),
254 r4.allow_form_filling.into(),
255 r4.allow_modify_other.into(),
256 r4.allow_print.into(),
257 r4.encrypt_metadata.into(),
258 r4.use_aes.into(),
259 )
260 })?;
261 }
262 }
263 EncryptionParams::R6(r6) => {
264 let user_password = CString::new(r6.user_password.as_str())?;
265 let owner_password = CString::new(r6.owner_password.as_str())?;
266 unsafe {
267 self.owner.wrap_ffi_call(|| {
268 qpdf_sys::qpdf_set_r6_encryption_parameters2(
269 self.owner.inner(),
270 user_password.as_ptr(),
271 owner_password.as_ptr(),
272 r6.allow_accessibility.into(),
273 r6.allow_extract.into(),
274 r6.allow_assemble.into(),
275 r6.allow_annotate_and_form.into(),
276 r6.allow_form_filling.into(),
277 r6.allow_modify_other.into(),
278 r6.allow_print.into(),
279 r6.encrypt_metadata.into(),
280 )
281 })?;
282 }
283 }
284 }
285 Ok(())
286 }
287
288 pub fn write<P>(&self, path: P) -> Result<()>
290 where
291 P: AsRef<Path>,
292 {
293 let filename = CString::new(path.as_ref().to_string_lossy().as_ref())?;
294
295 let inner = self.owner.inner();
296
297 self.owner
298 .wrap_ffi_call(|| unsafe { qpdf_sys::qpdf_init_write(inner, filename.as_ptr()) })?;
299
300 self.process_params()?;
301
302 self.owner.wrap_ffi_call(|| unsafe { qpdf_sys::qpdf_write(inner) })
303 }
304
305 pub fn write_to_memory(&self) -> Result<Vec<u8>> {
307 let inner = self.owner.inner();
308 self.owner
309 .wrap_ffi_call(|| unsafe { qpdf_sys::qpdf_init_write_memory(inner) })?;
310
311 self.process_params()?;
312
313 self.owner.wrap_ffi_call(|| unsafe { qpdf_sys::qpdf_write(inner) })?;
314
315 let buffer = unsafe { qpdf_sys::qpdf_get_buffer(inner) };
316 let buffer_len = unsafe { qpdf_sys::qpdf_get_buffer_length(inner) };
317
318 unsafe { Ok(slice::from_raw_parts(buffer, buffer_len as _).to_vec()) }
319 }
320
321 pub fn compress_streams(&mut self, flag: bool) -> &mut Self {
323 self.compress_streams = Some(flag);
324 self
325 }
326
327 pub fn minimum_pdf_version(&mut self, version: &str) -> &mut Self {
329 self.min_pdf_version = Some(version.to_owned());
330 self
331 }
332
333 pub fn force_pdf_version(&mut self, version: &str) -> &mut Self {
335 self.force_pdf_version = Some(version.to_owned());
336 self
337 }
338
339 pub fn stream_decode_level(&mut self, level: StreamDecodeLevel) -> &mut Self {
341 self.stream_decode_level = Some(level);
342 self
343 }
344
345 pub fn object_stream_mode(&mut self, mode: ObjectStreamMode) -> &mut Self {
347 self.object_stream_mode = Some(mode);
348 self
349 }
350
351 pub fn stream_data_mode(&mut self, mode: StreamDataMode) -> &mut Self {
353 self.stream_data_mode = Some(mode);
354 self
355 }
356
357 pub fn preserve_unreferenced_objects(&mut self, flag: bool) -> &mut Self {
359 self.preserve_unreferenced_objects = Some(flag);
360 self
361 }
362
363 pub fn normalize_content(&mut self, flag: bool) -> &mut Self {
365 self.normalize_content = Some(flag);
366 self
367 }
368
369 pub fn preserve_encryption(&mut self, flag: bool) -> &mut Self {
371 self.preserve_encryption = Some(flag);
372 self
373 }
374
375 pub fn linearize(&mut self, flag: bool) -> &mut Self {
377 self.linearize = Some(flag);
378 self
379 }
380
381 pub fn static_id(&mut self, flag: bool) -> &mut Self {
383 self.static_id = Some(flag);
384 self
385 }
386
387 pub fn deterministic_id(&mut self, flag: bool) -> &mut Self {
389 self.deterministic_id = Some(flag);
390 self
391 }
392
393 pub fn encryption_params(&mut self, params: EncryptionParams) -> &mut Self {
395 self.encryption_params = Some(params);
396 self
397 }
398}