1pub mod attachment;
5pub mod attachments;
6pub mod bookmark;
7pub mod bookmarks;
8pub mod catalog;
9pub mod fonts;
10pub mod form;
11pub mod metadata;
12pub mod page;
13pub mod pages;
14pub mod permissions;
15pub mod signature;
16pub mod signatures;
17
18use crate::bindgen::FPDF_DOCUMENT;
19use crate::error::PdfiumError;
20use crate::error::PdfiumInternalError;
21use crate::pdf::document::attachments::PdfAttachments;
22use crate::pdf::document::bookmarks::PdfBookmarks;
23use crate::pdf::document::catalog::PdfCatalog;
24use crate::pdf::document::fonts::PdfFonts;
25use crate::pdf::document::form::PdfForm;
26use crate::pdf::document::metadata::PdfMetadata;
27use crate::pdf::document::pages::PdfPages;
28use crate::pdf::document::permissions::PdfPermissions;
29use crate::pdf::document::signatures::PdfSignatures;
30use crate::pdfium::PdfiumLibraryBindingsAccessor;
31use crate::utils::files::get_pdfium_file_writer_from_writer;
32use crate::utils::files::FpdfFileAccessExt;
33use std::fmt::{Debug, Formatter};
34use std::io::Cursor;
35use std::io::Write;
36
37#[cfg(not(target_arch = "wasm32"))]
38use std::fs::File;
39
40use std::marker::PhantomData;
41#[cfg(not(target_arch = "wasm32"))]
42use std::path::Path;
43
44#[cfg(target_arch = "wasm32")]
45use js_sys::{Array, Uint8Array};
46
47#[cfg(target_arch = "wasm32")]
48use wasm_bindgen::JsValue;
49
50#[cfg(target_arch = "wasm32")]
51use web_sys::Blob;
52
53#[cfg(doc)]
58struct Blob;
59
60#[derive(Debug, Copy, Clone, PartialEq)]
64pub enum PdfDocumentVersion {
65 Unset,
68
69 Pdf1_0,
71
72 Pdf1_1,
74
75 Pdf1_2,
77
78 Pdf1_3,
80
81 Pdf1_4,
83
84 Pdf1_5,
86
87 Pdf1_6,
89
90 Pdf1_7,
95
96 Pdf2_0,
98
99 Other(i32),
104}
105
106impl PdfDocumentVersion {
107 pub const DEFAULT_VERSION: PdfDocumentVersion = PdfDocumentVersion::Pdf1_7;
109
110 #[inline]
111 pub(crate) fn from_pdfium(version: i32) -> Self {
112 match version {
113 10 => PdfDocumentVersion::Pdf1_0,
114 11 => PdfDocumentVersion::Pdf1_1,
115 12 => PdfDocumentVersion::Pdf1_2,
116 13 => PdfDocumentVersion::Pdf1_3,
117 14 => PdfDocumentVersion::Pdf1_4,
118 15 => PdfDocumentVersion::Pdf1_5,
119 16 => PdfDocumentVersion::Pdf1_6,
120 17 => PdfDocumentVersion::Pdf1_7,
121 20 => PdfDocumentVersion::Pdf2_0,
122 _ => PdfDocumentVersion::Other(version),
123 }
124 }
125
126 #[inline]
127 pub(crate) fn as_pdfium(&self) -> Option<i32> {
128 match self {
129 PdfDocumentVersion::Pdf1_0 => Some(10),
130 PdfDocumentVersion::Pdf1_1 => Some(11),
131 PdfDocumentVersion::Pdf1_2 => Some(12),
132 PdfDocumentVersion::Pdf1_3 => Some(13),
133 PdfDocumentVersion::Pdf1_4 => Some(14),
134 PdfDocumentVersion::Pdf1_5 => Some(15),
135 PdfDocumentVersion::Pdf1_6 => Some(16),
136 PdfDocumentVersion::Pdf1_7 => Some(17),
137 PdfDocumentVersion::Pdf2_0 => Some(20),
138 PdfDocumentVersion::Other(value) => Some(*value),
139 PdfDocumentVersion::Unset => None,
140 }
141 }
142}
143
144pub struct PdfDocument<'a> {
159 handle: FPDF_DOCUMENT,
160 output_version: Option<PdfDocumentVersion>,
161 attachments: PdfAttachments<'a>,
162 bookmarks: PdfBookmarks<'a>,
163 catalog: PdfCatalog<'a>,
164 form: Option<PdfForm<'a>>,
165 fonts: PdfFonts<'a>,
166 metadata: PdfMetadata<'a>,
167 pages: PdfPages<'a>,
168 permissions: PdfPermissions<'a>,
169 signatures: PdfSignatures<'a>,
170 source_byte_buffer: Option<Vec<u8>>,
171
172 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
173 file_access_reader: Option<Box<FpdfFileAccessExt<'a>>>,
175
176 lifetime: PhantomData<&'a FPDF_DOCUMENT>,
177}
178
179impl<'a> PdfDocument<'a> {
180 #[inline]
181 pub(crate) fn from_pdfium(handle: FPDF_DOCUMENT) -> Self {
182 let form = PdfForm::from_pdfium(handle);
183
184 let pages = PdfPages::from_pdfium(handle, form.as_ref().map(|form| form.handle()));
185
186 PdfDocument {
187 handle,
188 output_version: None,
189 attachments: PdfAttachments::from_pdfium(handle),
190 bookmarks: PdfBookmarks::from_pdfium(handle),
191 catalog: PdfCatalog::from_pdfium(handle),
192 form,
193 fonts: PdfFonts::from_pdfium(handle),
194 metadata: PdfMetadata::from_pdfium(handle),
195 pages,
196 permissions: PdfPermissions::from_pdfium(handle),
197 signatures: PdfSignatures::from_pdfium(handle),
198 source_byte_buffer: None,
199 file_access_reader: None,
200 lifetime: PhantomData,
201 }
202 }
203
204 #[inline]
206 pub(crate) fn handle(&self) -> FPDF_DOCUMENT {
207 self.handle
208 }
209
210 #[inline]
213 pub(crate) fn set_source_byte_buffer(&mut self, bytes: Vec<u8>) {
214 self.source_byte_buffer = Some(bytes);
215 }
216
217 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
220 #[inline]
222 pub(crate) fn set_file_access_reader(&mut self, reader: Box<FpdfFileAccessExt<'a>>) {
223 self.file_access_reader = Some(reader);
224 }
225
226 pub fn version(&self) -> PdfDocumentVersion {
228 let mut version = 0;
229
230 if unsafe {
231 self.bindings()
232 .FPDF_GetFileVersion(self.handle, &mut version)
233 } != 0
234 {
235 PdfDocumentVersion::from_pdfium(version)
236 } else {
237 PdfDocumentVersion::Unset
238 }
239 }
240
241 pub fn set_version(&mut self, version: PdfDocumentVersion) {
243 self.output_version = Some(version);
244 }
245
246 #[inline]
248 pub fn attachments(&self) -> &PdfAttachments<'_> {
249 &self.attachments
250 }
251
252 #[inline]
254 pub fn attachments_mut(&mut self) -> &mut PdfAttachments<'a> {
255 &mut self.attachments
256 }
257
258 #[inline]
260 pub fn bookmarks(&self) -> &PdfBookmarks<'_> {
261 &self.bookmarks
262 }
263
264 #[inline]
266 pub fn catalog(&self) -> &PdfCatalog<'_> {
267 &self.catalog
268 }
269
270 #[inline]
272 pub fn catalog_mut(&mut self) -> &mut PdfCatalog<'a> {
273 &mut self.catalog
274 }
275
276 #[inline]
278 pub fn form(&self) -> Option<&PdfForm<'_>> {
279 self.form.as_ref()
280 }
281
282 #[inline]
284 pub fn fonts(&self) -> &PdfFonts<'_> {
285 &self.fonts
286 }
287
288 #[inline]
290 pub fn fonts_mut(&mut self) -> &mut PdfFonts<'a> {
291 &mut self.fonts
292 }
293
294 #[inline]
296 pub fn metadata(&self) -> &PdfMetadata<'_> {
297 &self.metadata
298 }
299
300 #[inline]
302 pub fn pages(&self) -> &PdfPages<'a> {
303 &self.pages
304 }
305
306 #[inline]
308 pub fn pages_mut(&mut self) -> &mut PdfPages<'a> {
309 &mut self.pages
310 }
311
312 #[inline]
314 pub fn permissions(&self) -> &PdfPermissions<'_> {
315 &self.permissions
316 }
317
318 #[inline]
320 pub fn signatures(&self) -> &PdfSignatures<'_> {
321 &self.signatures
322 }
323
324 pub fn save_to_writer<W: Write + 'static>(&self, writer: &mut W) -> Result<(), PdfiumError> {
326 let flags = 0;
332
333 let mut pdfium_file_writer = get_pdfium_file_writer_from_writer(writer);
334
335 let result = match self.output_version {
336 Some(version) => unsafe {
337 self.bindings().FPDF_SaveWithVersion(
338 self.handle,
339 pdfium_file_writer.as_fpdf_file_write_mut_ptr(),
340 flags,
341 version.as_pdfium().unwrap_or_else(|| {
342 PdfDocumentVersion::DEFAULT_VERSION.as_pdfium().unwrap()
343 }),
344 )
345 },
346 None => unsafe {
347 self.bindings().FPDF_SaveAsCopy(
348 self.handle,
349 pdfium_file_writer.as_fpdf_file_write_mut_ptr(),
350 flags,
351 )
352 },
353 };
354
355 match self.bindings().is_true(result) {
356 true => {
357 pdfium_file_writer.flush().map_err(PdfiumError::IoError)
360 }
361 false => {
362 Err(PdfiumError::PdfiumLibraryInternalError(
365 PdfiumInternalError::Unknown,
366 ))
367 }
368 }
369 }
370
371 #[cfg(not(target_arch = "wasm32"))]
380 pub fn save_to_file(&self, path: &(impl AsRef<Path> + ?Sized)) -> Result<(), PdfiumError> {
381 self.save_to_writer(&mut File::create(path).map_err(PdfiumError::IoError)?)
382 }
383
384 pub fn save_to_bytes(&self) -> Result<Vec<u8>, PdfiumError> {
386 let mut cursor = Cursor::new(Vec::new());
387
388 self.save_to_writer(&mut cursor)?;
389
390 Ok(cursor.into_inner())
391 }
392
393 #[cfg(any(doc, target_arch = "wasm32"))]
397 pub fn save_to_blob(&self) -> Result<Blob, PdfiumError> {
398 let bytes = self.save_to_bytes()?;
399
400 let array = Uint8Array::new_with_length(bytes.len() as u32);
401
402 array.copy_from(bytes.as_slice());
403
404 let blob =
405 Blob::new_with_u8_array_sequence(&JsValue::from(Array::of1(&JsValue::from(array))))
406 .map_err(|_| PdfiumError::JsSysErrorConstructingBlobFromBytes)?;
407
408 Ok(blob)
409 }
410}
411
412impl<'a> Drop for PdfDocument<'a> {
413 #[inline]
416 fn drop(&mut self) {
417 self.form = None;
422 unsafe {
423 self.bindings().FPDF_CloseDocument(self.handle);
424 }
425 }
426}
427
428impl<'a> Debug for PdfDocument<'a> {
429 #[inline]
430 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431 f.debug_struct("PdfDocument")
432 .field("FPDF_DOCUMENT", &format!("{:?}", self.handle))
433 .finish()
434 }
435}
436
437impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfDocument<'a> {}
438
439#[cfg(feature = "thread_safe")]
440unsafe impl<'a> Send for PdfDocument<'a> {}
441
442#[cfg(feature = "thread_safe")]
443unsafe impl<'a> Sync for PdfDocument<'a> {}