1pub mod fonts;
5pub mod form;
6pub mod metadata;
7pub mod page;
8pub mod pages;
9pub mod permissions;
10
11use crate::bindgen::FPDF_DOCUMENT;
12use crate::bindings::PdfiumLibraryBindings;
13use crate::error::PdfiumError;
14use crate::error::PdfiumInternalError;
15use crate::pdf::document::fonts::PdfFonts;
16use crate::pdf::document::form::PdfForm;
17use crate::pdf::document::metadata::PdfMetadata;
18use crate::pdf::document::page::index_cache::PdfPageIndexCache;
19use crate::pdf::document::pages::PdfPages;
20use crate::pdf::document::permissions::PdfPermissions;
21use crate::utils::files::FpdfFileAccessExt;
22use crate::utils::files::get_pdfium_file_writer_from_writer;
23use std::fmt::{Debug, Formatter};
24use std::io::Cursor;
25use std::io::Write;
26
27#[cfg(not(target_arch = "wasm32"))]
28use std::fs::File;
29
30#[cfg(not(target_arch = "wasm32"))]
31use std::path::Path;
32
33#[cfg(target_arch = "wasm32")]
34use js_sys::{Array, Uint8Array};
35
36#[cfg(target_arch = "wasm32")]
37use wasm_bindgen::JsValue;
38
39#[cfg(target_arch = "wasm32")]
40use web_sys::Blob;
41
42#[cfg(doc)]
43struct Blob;
44
45#[derive(Debug, Copy, Clone, PartialEq)]
49pub enum PdfDocumentVersion {
50 Unset,
53
54 Pdf1_0,
56
57 Pdf1_1,
59
60 Pdf1_2,
62
63 Pdf1_3,
65
66 Pdf1_4,
68
69 Pdf1_5,
71
72 Pdf1_6,
74
75 Pdf1_7,
80
81 Pdf2_0,
83
84 Other(i32),
89}
90
91impl PdfDocumentVersion {
92 pub const DEFAULT_VERSION: PdfDocumentVersion = PdfDocumentVersion::Pdf1_7;
94
95 #[inline]
96 pub(crate) fn from_pdfium(version: i32) -> Self {
97 match version {
98 10 => PdfDocumentVersion::Pdf1_0,
99 11 => PdfDocumentVersion::Pdf1_1,
100 12 => PdfDocumentVersion::Pdf1_2,
101 13 => PdfDocumentVersion::Pdf1_3,
102 14 => PdfDocumentVersion::Pdf1_4,
103 15 => PdfDocumentVersion::Pdf1_5,
104 16 => PdfDocumentVersion::Pdf1_6,
105 17 => PdfDocumentVersion::Pdf1_7,
106 20 => PdfDocumentVersion::Pdf2_0,
107 _ => PdfDocumentVersion::Other(version),
108 }
109 }
110
111 #[inline]
112 pub(crate) fn as_pdfium(&self) -> Option<i32> {
113 match self {
114 PdfDocumentVersion::Pdf1_0 => Some(10),
115 PdfDocumentVersion::Pdf1_1 => Some(11),
116 PdfDocumentVersion::Pdf1_2 => Some(12),
117 PdfDocumentVersion::Pdf1_3 => Some(13),
118 PdfDocumentVersion::Pdf1_4 => Some(14),
119 PdfDocumentVersion::Pdf1_5 => Some(15),
120 PdfDocumentVersion::Pdf1_6 => Some(16),
121 PdfDocumentVersion::Pdf1_7 => Some(17),
122 PdfDocumentVersion::Pdf2_0 => Some(20),
123 PdfDocumentVersion::Other(value) => Some(*value),
124 PdfDocumentVersion::Unset => None,
125 }
126 }
127}
128
129pub struct PdfDocument<'a> {
140 handle: FPDF_DOCUMENT,
141 output_version: Option<PdfDocumentVersion>,
142 form: Option<PdfForm<'a>>,
143 fonts: PdfFonts<'a>,
144 metadata: PdfMetadata<'a>,
145 pages: PdfPages<'a>,
146 permissions: PdfPermissions<'a>,
147 bindings: &'a dyn PdfiumLibraryBindings,
148 source_byte_buffer: Option<Vec<u8>>,
149
150 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
151 file_access_reader: Option<Box<FpdfFileAccessExt<'a>>>,
152}
153
154impl<'a> PdfDocument<'a> {
155 #[inline]
156 pub(crate) fn from_pdfium(handle: FPDF_DOCUMENT, bindings: &'a dyn PdfiumLibraryBindings) -> Self {
157 let form = PdfForm::from_pdfium(handle, bindings);
158
159 let pages = PdfPages::from_pdfium(handle, form.as_ref().map(|form| form.handle()), bindings);
160
161 PdfDocument {
162 handle,
163 output_version: None,
164 form,
165 fonts: PdfFonts::from_pdfium(handle, bindings),
166 metadata: PdfMetadata::from_pdfium(handle, bindings),
167 pages,
168 permissions: PdfPermissions::from_pdfium(handle, bindings),
169 bindings,
170 source_byte_buffer: None,
171 file_access_reader: None,
172 }
173 }
174
175 #[inline]
177 pub(crate) fn handle(&self) -> FPDF_DOCUMENT {
178 self.handle
179 }
180
181 #[inline]
183 pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
184 self.bindings
185 }
186
187 #[inline]
190 pub(crate) fn set_source_byte_buffer(&mut self, bytes: Vec<u8>) {
191 self.source_byte_buffer = Some(bytes);
192 }
193
194 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
197 #[inline]
198 pub(crate) fn set_file_access_reader(&mut self, reader: Box<FpdfFileAccessExt<'a>>) {
199 self.file_access_reader = Some(reader);
200 }
201
202 pub fn version(&self) -> PdfDocumentVersion {
204 let mut version = 0;
205
206 if self.bindings.FPDF_GetFileVersion(self.handle, &mut version) != 0 {
207 PdfDocumentVersion::from_pdfium(version)
208 } else {
209 PdfDocumentVersion::Unset
210 }
211 }
212
213 pub fn set_version(&mut self, version: PdfDocumentVersion) {
215 self.output_version = Some(version);
216 }
217
218 pub fn is_tagged(&self) -> bool {
220 self.bindings.is_true(self.bindings.FPDFCatalog_IsTagged(self.handle))
221 }
222
223 #[inline]
225 pub fn form(&self) -> Option<&PdfForm<'_>> {
226 self.form.as_ref()
227 }
228
229 #[inline]
231 pub fn fonts(&self) -> &PdfFonts<'_> {
232 &self.fonts
233 }
234
235 #[inline]
237 pub fn fonts_mut(&mut self) -> &mut PdfFonts<'a> {
238 &mut self.fonts
239 }
240
241 #[inline]
243 pub fn metadata(&self) -> &PdfMetadata<'_> {
244 &self.metadata
245 }
246
247 #[inline]
249 pub fn pages(&self) -> &PdfPages<'a> {
250 &self.pages
251 }
252
253 #[inline]
255 pub fn pages_mut(&mut self) -> &mut PdfPages<'a> {
256 &mut self.pages
257 }
258
259 #[inline]
261 pub fn permissions(&self) -> &PdfPermissions<'_> {
262 &self.permissions
263 }
264
265 pub fn save_to_writer<W: Write + 'static>(&self, writer: &mut W) -> Result<(), PdfiumError> {
267 let flags = 0;
273
274 let mut pdfium_file_writer = get_pdfium_file_writer_from_writer(writer);
275
276 let result = match self.output_version {
277 Some(version) => self.bindings.FPDF_SaveWithVersion(
278 self.handle,
279 pdfium_file_writer.as_fpdf_file_write_mut_ptr(),
280 flags,
281 version
282 .as_pdfium()
283 .unwrap_or_else(|| PdfDocumentVersion::DEFAULT_VERSION.as_pdfium().unwrap()),
284 ),
285 None => self
286 .bindings
287 .FPDF_SaveAsCopy(self.handle, pdfium_file_writer.as_fpdf_file_write_mut_ptr(), flags),
288 };
289
290 match self.bindings.is_true(result) {
291 true => pdfium_file_writer.flush().map_err(PdfiumError::IoError),
292 false => Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown)),
293 }
294 }
295
296 #[cfg(not(target_arch = "wasm32"))]
305 pub fn save_to_file(&self, path: &(impl AsRef<Path> + ?Sized)) -> Result<(), PdfiumError> {
306 self.save_to_writer(&mut File::create(path).map_err(PdfiumError::IoError)?)
307 }
308
309 pub fn save_to_bytes(&self) -> Result<Vec<u8>, PdfiumError> {
311 let mut cursor = Cursor::new(Vec::new());
312
313 self.save_to_writer(&mut cursor)?;
314
315 Ok(cursor.into_inner())
316 }
317
318 #[cfg(any(doc, target_arch = "wasm32"))]
322 pub fn save_to_blob(&self) -> Result<Blob, PdfiumError> {
323 let bytes = self.save_to_bytes()?;
324
325 let array = Uint8Array::new_with_length(bytes.len() as u32);
326
327 array.copy_from(bytes.as_slice());
328
329 let blob = Blob::new_with_u8_array_sequence(&JsValue::from(Array::of1(&JsValue::from(array))))
330 .map_err(|_| PdfiumError::JsSysErrorConstructingBlobFromBytes)?;
331
332 Ok(blob)
333 }
334}
335
336impl<'a> Drop for PdfDocument<'a> {
337 #[inline]
340 fn drop(&mut self) {
341 self.form = None;
342
343 PdfPageIndexCache::clear_document(self.handle);
344
345 self.bindings.FPDF_CloseDocument(self.handle);
346 }
347}
348
349impl<'a> Debug for PdfDocument<'a> {
350 #[inline]
351 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
352 f.debug_struct("PdfDocument")
353 .field("FPDF_DOCUMENT", &format!("{:?}", self.handle))
354 .finish()
355 }
356}