use std::ffi::CString;
use std::os::raw::c_char;
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
#[repr(C)]
pub struct DxpdfBuffer {
pub data: *mut u8,
pub len: usize,
pub cap: usize,
}
impl DxpdfBuffer {
fn empty() -> Self {
DxpdfBuffer {
data: ptr::null_mut(),
len: 0,
cap: 0,
}
}
fn from_vec(mut bytes: Vec<u8>) -> Self {
let buf = DxpdfBuffer {
data: bytes.as_mut_ptr(),
len: bytes.len(),
cap: bytes.capacity(),
};
std::mem::forget(bytes);
buf
}
}
fn set_error(out_error: *mut *mut c_char, message: &str) {
if out_error.is_null() {
return;
}
let sanitized: String = message.chars().filter(|&c| c != '\0').collect();
let c_string = CString::new(sanitized).expect("NUL bytes were just filtered out");
unsafe {
*out_error = c_string.into_raw();
}
}
fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
if let Some(s) = payload.downcast_ref::<&str>() {
(*s).to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic".to_string()
}
}
#[no_mangle]
pub unsafe extern "C" fn dxpdf_convert(
docx_ptr: *const u8,
docx_len: usize,
image_dpi: f32,
out_pdf: *mut DxpdfBuffer,
out_error: *mut *mut c_char,
) -> i32 {
if !out_pdf.is_null() {
unsafe {
*out_pdf = DxpdfBuffer::empty();
}
}
if !out_error.is_null() {
unsafe {
*out_error = ptr::null_mut();
}
}
if docx_ptr.is_null() {
set_error(out_error, "dxpdf_convert: docx_ptr is null");
return -1;
}
let docx_bytes = unsafe { std::slice::from_raw_parts(docx_ptr, docx_len) };
let options = crate::RenderOptions::default().with_image_dpi(image_dpi);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
crate::convert_with_options(docx_bytes, &options)
}));
match result {
Ok(Ok(pdf_bytes)) => {
if !out_pdf.is_null() {
unsafe {
*out_pdf = DxpdfBuffer::from_vec(pdf_bytes);
}
}
0
}
Ok(Err(e)) => {
set_error(out_error, &e.to_string());
1
}
Err(panic_payload) => {
let message = panic_message(panic_payload.as_ref());
set_error(out_error, &format!("dxpdf: internal error: {message}"));
2
}
}
}
#[no_mangle]
pub unsafe extern "C" fn dxpdf_free_buffer(buf: DxpdfBuffer) {
if buf.data.is_null() {
return;
}
unsafe {
drop(Vec::from_raw_parts(buf.data, buf.len, buf.cap));
}
}
#[no_mangle]
pub unsafe extern "C" fn dxpdf_free_error(err: *mut c_char) {
if err.is_null() {
return;
}
unsafe {
drop(CString::from_raw(err));
}
}
#[no_mangle]
pub extern "C" fn dxpdf_default_image_dpi() -> f32 {
crate::DEFAULT_IMAGE_DPI
}
#[no_mangle]
pub extern "C" fn dxpdf_min_image_dpi() -> f32 {
crate::MIN_IMAGE_DPI
}