pub mod attachment;
pub mod attachments;
pub mod bookmark;
pub mod bookmarks;
pub mod fonts;
pub mod form;
pub mod metadata;
pub mod page;
pub mod pages;
pub mod permissions;
pub mod signature;
pub mod signatures;
use crate::bindgen::FPDF_DOCUMENT;
use crate::error::PdfiumError;
use crate::error::PdfiumInternalError;
use crate::pdf::document::attachments::PdfAttachments;
use crate::pdf::document::bookmarks::PdfBookmarks;
use crate::pdf::document::fonts::PdfFonts;
use crate::pdf::document::form::PdfForm;
use crate::pdf::document::metadata::PdfMetadata;
use crate::pdf::document::pages::PdfPages;
use crate::pdf::document::permissions::PdfPermissions;
use crate::pdf::document::signatures::PdfSignatures;
use crate::pdfium::PdfiumLibraryBindingsAccessor;
use crate::utils::files::get_pdfium_file_writer_from_writer;
use crate::utils::files::FpdfFileAccessExt;
use std::fmt::{Debug, Formatter};
use std::io::Cursor;
use std::io::Write;
#[cfg(not(target_arch = "wasm32"))]
use std::fs::File;
use std::marker::PhantomData;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
#[cfg(target_arch = "wasm32")]
use js_sys::{Array, Uint8Array};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;
#[cfg(target_arch = "wasm32")]
use web_sys::Blob;
#[cfg(doc)]
struct Blob;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PdfDocumentVersion {
Unset,
Pdf1_0,
Pdf1_1,
Pdf1_2,
Pdf1_3,
Pdf1_4,
Pdf1_5,
Pdf1_6,
Pdf1_7,
Pdf2_0,
Other(i32),
}
impl PdfDocumentVersion {
pub const DEFAULT_VERSION: PdfDocumentVersion = PdfDocumentVersion::Pdf1_7;
#[inline]
pub(crate) fn from_pdfium(version: i32) -> Self {
match version {
10 => PdfDocumentVersion::Pdf1_0,
11 => PdfDocumentVersion::Pdf1_1,
12 => PdfDocumentVersion::Pdf1_2,
13 => PdfDocumentVersion::Pdf1_3,
14 => PdfDocumentVersion::Pdf1_4,
15 => PdfDocumentVersion::Pdf1_5,
16 => PdfDocumentVersion::Pdf1_6,
17 => PdfDocumentVersion::Pdf1_7,
20 => PdfDocumentVersion::Pdf2_0,
_ => PdfDocumentVersion::Other(version),
}
}
#[inline]
pub(crate) fn as_pdfium(&self) -> Option<i32> {
match self {
PdfDocumentVersion::Pdf1_0 => Some(10),
PdfDocumentVersion::Pdf1_1 => Some(11),
PdfDocumentVersion::Pdf1_2 => Some(12),
PdfDocumentVersion::Pdf1_3 => Some(13),
PdfDocumentVersion::Pdf1_4 => Some(14),
PdfDocumentVersion::Pdf1_5 => Some(15),
PdfDocumentVersion::Pdf1_6 => Some(16),
PdfDocumentVersion::Pdf1_7 => Some(17),
PdfDocumentVersion::Pdf2_0 => Some(20),
PdfDocumentVersion::Other(value) => Some(*value),
PdfDocumentVersion::Unset => None,
}
}
}
pub struct PdfDocument<'a> {
handle: FPDF_DOCUMENT,
output_version: Option<PdfDocumentVersion>,
attachments: PdfAttachments<'a>,
bookmarks: PdfBookmarks<'a>,
form: Option<PdfForm<'a>>,
fonts: PdfFonts<'a>,
metadata: PdfMetadata<'a>,
pages: PdfPages<'a>,
permissions: PdfPermissions<'a>,
signatures: PdfSignatures<'a>,
source_byte_buffer: Option<Vec<u8>>,
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
file_access_reader: Option<Box<FpdfFileAccessExt<'a>>>,
lifetime: PhantomData<&'a FPDF_DOCUMENT>,
}
impl<'a> PdfDocument<'a> {
#[inline]
pub(crate) fn from_pdfium(handle: FPDF_DOCUMENT) -> Self {
let form = PdfForm::from_pdfium(handle);
let pages = PdfPages::from_pdfium(handle, form.as_ref().map(|form| form.handle()));
PdfDocument {
handle,
output_version: None,
attachments: PdfAttachments::from_pdfium(handle),
bookmarks: PdfBookmarks::from_pdfium(handle),
form,
fonts: PdfFonts::from_pdfium(handle),
metadata: PdfMetadata::from_pdfium(handle),
pages,
permissions: PdfPermissions::from_pdfium(handle),
signatures: PdfSignatures::from_pdfium(handle),
source_byte_buffer: None,
file_access_reader: None,
lifetime: PhantomData,
}
}
#[inline]
pub(crate) fn handle(&self) -> FPDF_DOCUMENT {
self.handle
}
#[inline]
pub(crate) fn set_source_byte_buffer(&mut self, bytes: Vec<u8>) {
self.source_byte_buffer = Some(bytes);
}
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
#[inline]
pub(crate) fn set_file_access_reader(&mut self, reader: Box<FpdfFileAccessExt<'a>>) {
self.file_access_reader = Some(reader);
}
pub fn version(&self) -> PdfDocumentVersion {
let mut version = 0;
if unsafe {
self.bindings()
.FPDF_GetFileVersion(self.handle, &mut version)
} != 0
{
PdfDocumentVersion::from_pdfium(version)
} else {
PdfDocumentVersion::Unset
}
}
pub fn set_version(&mut self, version: PdfDocumentVersion) {
self.output_version = Some(version);
}
#[inline]
pub fn attachments(&self) -> &PdfAttachments<'_> {
&self.attachments
}
#[inline]
pub fn attachments_mut(&mut self) -> &mut PdfAttachments<'a> {
&mut self.attachments
}
#[inline]
pub fn bookmarks(&self) -> &PdfBookmarks<'_> {
&self.bookmarks
}
#[inline]
pub fn form(&self) -> Option<&PdfForm<'_>> {
self.form.as_ref()
}
#[inline]
pub fn fonts(&self) -> &PdfFonts<'_> {
&self.fonts
}
#[inline]
pub fn fonts_mut(&mut self) -> &mut PdfFonts<'a> {
&mut self.fonts
}
#[inline]
pub fn metadata(&self) -> &PdfMetadata<'_> {
&self.metadata
}
#[inline]
pub fn pages(&self) -> &PdfPages<'a> {
&self.pages
}
#[inline]
pub fn pages_mut(&mut self) -> &mut PdfPages<'a> {
&mut self.pages
}
#[inline]
pub fn permissions(&self) -> &PdfPermissions<'_> {
&self.permissions
}
#[inline]
pub fn signatures(&self) -> &PdfSignatures<'_> {
&self.signatures
}
pub fn save_to_writer<W: Write + 'static>(&self, writer: &mut W) -> Result<(), PdfiumError> {
let flags = 0;
let mut pdfium_file_writer = get_pdfium_file_writer_from_writer(writer);
let result = match self.output_version {
Some(version) => unsafe {
self.bindings().FPDF_SaveWithVersion(
self.handle,
pdfium_file_writer.as_fpdf_file_write_mut_ptr(),
flags,
version.as_pdfium().unwrap_or_else(|| {
PdfDocumentVersion::DEFAULT_VERSION.as_pdfium().unwrap()
}),
)
},
None => unsafe {
self.bindings().FPDF_SaveAsCopy(
self.handle,
pdfium_file_writer.as_fpdf_file_write_mut_ptr(),
flags,
)
},
};
match self.bindings().is_true(result) {
true => {
pdfium_file_writer.flush().map_err(PdfiumError::IoError)
}
false => {
Err(PdfiumError::PdfiumLibraryInternalError(
PdfiumInternalError::Unknown,
))
}
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn save_to_file(&self, path: &(impl AsRef<Path> + ?Sized)) -> Result<(), PdfiumError> {
self.save_to_writer(&mut File::create(path).map_err(PdfiumError::IoError)?)
}
pub fn save_to_bytes(&self) -> Result<Vec<u8>, PdfiumError> {
let mut cursor = Cursor::new(Vec::new());
self.save_to_writer(&mut cursor)?;
Ok(cursor.into_inner())
}
#[cfg(any(doc, target_arch = "wasm32"))]
pub fn save_to_blob(&self) -> Result<Blob, PdfiumError> {
let bytes = self.save_to_bytes()?;
let array = Uint8Array::new_with_length(bytes.len() as u32);
array.copy_from(bytes.as_slice());
let blob =
Blob::new_with_u8_array_sequence(&JsValue::from(Array::of1(&JsValue::from(array))))
.map_err(|_| PdfiumError::JsSysErrorConstructingBlobFromBytes)?;
Ok(blob)
}
}
impl<'a> Drop for PdfDocument<'a> {
#[inline]
fn drop(&mut self) {
self.form = None;
unsafe {
self.bindings().FPDF_CloseDocument(self.handle);
}
}
}
impl<'a> Debug for PdfDocument<'a> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PdfDocument")
.field("FPDF_DOCUMENT", &format!("{:?}", self.handle))
.finish()
}
}
impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfDocument<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfDocument<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfDocument<'a> {}