use std::ffi::CString;
use std::sync::Once;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::{Mutex, MutexGuard, OnceLock};
use crate::document::Document;
use crate::error::PdfiumError;
use crate::ffi;
static INIT: Once = Once::new();
#[cfg(not(target_arch = "wasm32"))]
fn pdfium_lock() -> &'static Mutex<()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
}
pub struct Library {
#[cfg(not(target_arch = "wasm32"))]
_guard: MutexGuard<'static, ()>,
#[cfg(target_arch = "wasm32")]
_private: (),
}
impl Library {
pub fn init() -> Library {
#[cfg(not(target_arch = "wasm32"))]
{
pdfium_sys::dynamic::load_default().expect("failed to load pdfium shared library");
let guard = pdfium_lock()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
INIT.call_once(|| unsafe { ffi!(FPDF_InitLibrary()) });
Library { _guard: guard }
}
#[cfg(target_arch = "wasm32")]
{
INIT.call_once(|| unsafe { ffi!(FPDF_InitLibrary()) });
Library { _private: () }
}
}
pub fn load_document(
&self,
path: &str,
password: Option<&str>,
) -> Result<Document<'_>, PdfiumError> {
let c_path = CString::new(path).map_err(|_| PdfiumError::FileNotFound)?;
let c_password = password
.map(|p| CString::new(p).map_err(|_| PdfiumError::OperationFailed))
.transpose()?;
let handle = unsafe {
ffi!(FPDF_LoadDocument(
c_path.as_ptr(),
c_password.as_ref().map_or(std::ptr::null(), |p| p.as_ptr()),
))
};
if handle.is_null() {
return Err(PdfiumError::from_last_error());
}
Ok(Document {
handle,
_lib: std::marker::PhantomData,
})
}
pub fn load_document_from_bytes(
&self,
data: &[u8],
password: Option<&str>,
) -> Result<Document<'_>, PdfiumError> {
let c_password = password
.map(|p| CString::new(p).map_err(|_| PdfiumError::OperationFailed))
.transpose()?;
let handle = unsafe {
ffi!(FPDF_LoadMemDocument(
data.as_ptr() as *const std::ffi::c_void,
data.len() as i32,
c_password.as_ref().map_or(std::ptr::null(), |p| p.as_ptr()),
))
};
if handle.is_null() {
return Err(PdfiumError::from_last_error());
}
Ok(Document {
handle,
_lib: std::marker::PhantomData,
})
}
}