use crate::bindgen::{FPDF_ATTACHMENT, FPDF_WCHAR};
use crate::error::PdfiumError;
use crate::pdfium::PdfiumLibraryBindingsAccessor;
use crate::utils::mem::create_byte_buffer;
use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
use std::io::Write;
use std::marker::PhantomData;
use std::os::raw::{c_ulong, c_void};
#[cfg(not(target_arch = "wasm32"))]
use {std::fs::File, std::path::Path};
#[cfg(target_arch = "wasm32")]
use {
js_sys::{Array, Uint8Array},
wasm_bindgen::JsValue,
web_sys::Blob,
};
#[cfg(doc)]
struct Blob;
#[cfg(doc)]
use crate::pdf::document::PdfDocument;
pub struct PdfAttachment<'a> {
handle: FPDF_ATTACHMENT,
lifetime: PhantomData<&'a FPDF_ATTACHMENT>,
}
impl<'a> PdfAttachment<'a> {
#[inline]
pub(crate) fn from_pdfium(handle: FPDF_ATTACHMENT) -> Self {
PdfAttachment {
handle,
lifetime: PhantomData,
}
}
pub fn name(&self) -> String {
let buffer_length = unsafe {
self.bindings()
.FPDFAttachment_GetName(self.handle, std::ptr::null_mut(), 0)
};
if buffer_length == 0 {
return String::new();
}
let mut buffer = create_byte_buffer(buffer_length as usize);
let result = unsafe {
self.bindings().FPDFAttachment_GetName(
self.handle,
buffer.as_mut_ptr() as *mut FPDF_WCHAR,
buffer_length,
)
};
assert_eq!(result, buffer_length);
get_string_from_pdfium_utf16le_bytes(buffer).unwrap_or_default()
}
pub fn len(&self) -> usize {
let mut out_buflen: c_ulong = 0;
if self.bindings().is_true(unsafe {
self.bindings().FPDFAttachment_GetFile(
self.handle,
std::ptr::null_mut(),
0,
&mut out_buflen,
)
}) {
out_buflen as usize
} else {
0
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn save_to_bytes(&self) -> Result<Vec<u8>, PdfiumError> {
let mut out_buflen: c_ulong = 0;
if self.bindings().is_true(unsafe {
self.bindings().FPDFAttachment_GetFile(
self.handle,
std::ptr::null_mut(),
0,
&mut out_buflen,
)
}) {
let buffer_length = out_buflen;
let mut buffer = create_byte_buffer(buffer_length as usize);
let result = unsafe {
self.bindings().FPDFAttachment_GetFile(
self.handle,
buffer.as_mut_ptr() as *mut c_void,
buffer_length,
&mut out_buflen,
)
};
assert!(self.bindings().is_true(result));
assert_eq!(buffer_length, out_buflen);
Ok(buffer)
} else {
Err(PdfiumError::NoDataInAttachment)
}
}
pub fn save_to_writer<W: Write>(&self, writer: &mut W) -> Result<(), PdfiumError> {
self.save_to_bytes().and_then(|bytes| {
writer
.write_all(bytes.as_slice())
.map_err(PdfiumError::IoError)
})
}
#[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)?)
}
#[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> PdfiumLibraryBindingsAccessor<'a> for PdfAttachment<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfAttachment<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfAttachment<'a> {}