pub(crate) mod annotations;
pub(crate) mod forms;
pub(crate) mod hierarchy;
pub(crate) mod images;
pub(crate) mod metadata;
pub(crate) mod table;
pub(crate) mod text;
use crate::Result;
use crate::error::XbergError;
pub(crate) fn guard_oxide_panic<T, E>(
op: impl FnOnce() -> std::result::Result<T, E>,
on_panic: impl FnOnce(String) -> E,
) -> std::result::Result<T, E> {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(op)) {
Ok(result) => result,
Err(payload) => Err(on_panic(panic_message(payload.as_ref()))),
}
}
fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
if let Some(s) = payload.downcast_ref::<&'static str>() {
(*s).to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic".to_string()
}
}
pub(crate) struct OxideDocument {
pub doc: pdf_oxide::PdfDocument,
}
impl OxideDocument {
#[cfg(test)]
pub(crate) fn open_bytes(bytes: &[u8]) -> Result<Self> {
Self::open_bytes_with_passwords(bytes, &[])
}
pub(crate) fn open_bytes_with_passwords(bytes: &[u8], passwords: &[String]) -> Result<Self> {
let doc = pdf_oxide::PdfDocument::from_bytes(bytes.to_vec()).map_err(|e| XbergError::Parsing {
message: format!("pdf_oxide: failed to load bytes: {e}"),
source: None,
})?;
let opened = doc.authenticate(b"").unwrap_or(false);
if !opened {
let mut authenticated = false;
for password in passwords {
if doc.authenticate(password.as_bytes()).unwrap_or(false) {
authenticated = true;
break;
}
}
if !authenticated {
return Err(XbergError::Parsing {
message: if passwords.is_empty() {
"PDF is encrypted and requires a password; set pdf_options.passwords".to_string()
} else {
"PDF is encrypted and none of the supplied passwords authenticated".to_string()
},
source: None,
});
}
}
Ok(Self { doc })
}
}