use crate::enhanced::html_to_pdf::{
HtmlToPdfOptions, Margins, PageSize, html_file_to_pdf, html_to_pdf,
};
use crate::ffi::Handle;
use std::ffi::CStr;
use std::os::raw::c_char;
pub type HtmlToPdfOptionsHandle = Handle;
pub const MP_HTML_SUCCESS: i32 = 0;
pub const MP_HTML_ERROR_INVALID_PARAM: i32 = -1;
pub const MP_HTML_ERROR_CONVERSION: i32 = -2;
pub const MP_HTML_ERROR_NOT_FOUND: i32 = -3;
pub const MP_HTML_ERROR_IO: i32 = -4;
pub const MP_PAGE_SIZE_LETTER: i32 = 0;
pub const MP_PAGE_SIZE_LEGAL: i32 = 1;
pub const MP_PAGE_SIZE_A3: i32 = 2;
pub const MP_PAGE_SIZE_A4: i32 = 3;
pub const MP_PAGE_SIZE_A5: i32 = 4;
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_create() -> HtmlToPdfOptionsHandle {
let options = Box::new(HtmlToPdfOptions::default());
Box::into_raw(options) as Handle
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_free(handle: HtmlToPdfOptionsHandle) {
if handle != 0 {
unsafe {
let _ = Box::from_raw(handle as *mut HtmlToPdfOptions);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_set_page_size(
handle: HtmlToPdfOptionsHandle,
page_size: i32,
) -> i32 {
if handle == 0 {
return MP_HTML_ERROR_INVALID_PARAM;
}
let options = unsafe { &mut *(handle as *mut HtmlToPdfOptions) };
let size = match page_size {
MP_PAGE_SIZE_LETTER => PageSize::Letter,
MP_PAGE_SIZE_LEGAL => PageSize::Legal,
MP_PAGE_SIZE_A3 => PageSize::A3,
MP_PAGE_SIZE_A4 => PageSize::A4,
MP_PAGE_SIZE_A5 => PageSize::A5,
_ => return MP_HTML_ERROR_INVALID_PARAM,
};
let (w, h) = size.dimensions();
options.page_width = w;
options.page_height = h;
MP_HTML_SUCCESS
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_set_page_size_custom(
handle: HtmlToPdfOptionsHandle,
width: f32,
height: f32,
) -> i32 {
if handle == 0 {
return MP_HTML_ERROR_INVALID_PARAM;
}
let options = unsafe { &mut *(handle as *mut HtmlToPdfOptions) };
options.page_width = width;
options.page_height = height;
MP_HTML_SUCCESS
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_set_margins(
handle: HtmlToPdfOptionsHandle,
top: f32,
right: f32,
bottom: f32,
left: f32,
) -> i32 {
if handle == 0 {
return MP_HTML_ERROR_INVALID_PARAM;
}
let options = unsafe { &mut *(handle as *mut HtmlToPdfOptions) };
options.margins = Margins {
top,
right,
bottom,
left,
};
MP_HTML_SUCCESS
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_set_default_font(
handle: HtmlToPdfOptionsHandle,
family: *const c_char,
) -> i32 {
if handle == 0 || family.is_null() {
return MP_HTML_ERROR_INVALID_PARAM;
}
let options = unsafe { &mut *(handle as *mut HtmlToPdfOptions) };
let family_str = unsafe { CStr::from_ptr(family) }.to_string_lossy();
options.default_font_family = family_str.to_string();
MP_HTML_SUCCESS
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_set_default_font_size(
handle: HtmlToPdfOptionsHandle,
size: f32,
) -> i32 {
if handle == 0 {
return MP_HTML_ERROR_INVALID_PARAM;
}
let options = unsafe { &mut *(handle as *mut HtmlToPdfOptions) };
options.default_font_size = size;
MP_HTML_SUCCESS
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_to_pdf(
html: *const c_char,
output_path: *const c_char,
options: HtmlToPdfOptionsHandle,
) -> i32 {
if html.is_null() || output_path.is_null() {
return MP_HTML_ERROR_INVALID_PARAM;
}
let html_str = unsafe { CStr::from_ptr(html) }.to_string_lossy();
let output_str = unsafe { CStr::from_ptr(output_path) }.to_string_lossy();
let opts = if options == 0 {
HtmlToPdfOptions::default()
} else {
unsafe { &*(options as *const HtmlToPdfOptions) }.clone()
};
match html_to_pdf(&html_str, &output_str, &opts) {
Ok(()) => MP_HTML_SUCCESS,
Err(_) => MP_HTML_ERROR_CONVERSION,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_file_to_pdf(
html_path: *const c_char,
output_path: *const c_char,
options: HtmlToPdfOptionsHandle,
) -> i32 {
if html_path.is_null() || output_path.is_null() {
return MP_HTML_ERROR_INVALID_PARAM;
}
let html_path_str = unsafe { CStr::from_ptr(html_path) }.to_string_lossy();
let output_str = unsafe { CStr::from_ptr(output_path) }.to_string_lossy();
let opts = if options == 0 {
HtmlToPdfOptions::default()
} else {
unsafe { &*(options as *const HtmlToPdfOptions) }.clone()
};
match html_file_to_pdf(&html_path_str, &output_str, &opts) {
Ok(()) => MP_HTML_SUCCESS,
Err(e) => {
if e.to_string().contains("not found") {
MP_HTML_ERROR_NOT_FOUND
} else {
MP_HTML_ERROR_CONVERSION
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_get_page_width(handle: HtmlToPdfOptionsHandle) -> f32 {
if handle == 0 {
return 0.0;
}
let options = unsafe { &*(handle as *const HtmlToPdfOptions) };
options.page_width
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_get_page_height(handle: HtmlToPdfOptionsHandle) -> f32 {
if handle == 0 {
return 0.0;
}
let options = unsafe { &*(handle as *const HtmlToPdfOptions) };
options.page_height
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_get_content_width(handle: HtmlToPdfOptionsHandle) -> f32 {
if handle == 0 {
return 0.0;
}
let options = unsafe { &*(handle as *const HtmlToPdfOptions) };
options.content_width()
}
#[unsafe(no_mangle)]
pub extern "C" fn mp_html_options_get_content_height(handle: HtmlToPdfOptionsHandle) -> f32 {
if handle == 0 {
return 0.0;
}
let options = unsafe { &*(handle as *const HtmlToPdfOptions) };
options.content_height()
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
#[test]
fn test_options_create_free() {
let handle = mp_html_options_create();
assert_ne!(handle, 0);
mp_html_options_free(handle);
}
#[test]
fn test_options_page_size() {
let handle = mp_html_options_create();
assert_eq!(
mp_html_options_set_page_size(handle, MP_PAGE_SIZE_A4),
MP_HTML_SUCCESS
);
let width = mp_html_options_get_page_width(handle);
assert!((width - 595.28).abs() < 0.01);
mp_html_options_free(handle);
}
#[test]
fn test_options_margins() {
let handle = mp_html_options_create();
assert_eq!(
mp_html_options_set_margins(handle, 72.0, 72.0, 72.0, 72.0),
MP_HTML_SUCCESS
);
let content_width = mp_html_options_get_content_width(handle);
assert!((content_width - (612.0 - 144.0)).abs() < 0.01);
mp_html_options_free(handle);
}
#[test]
fn test_options_default_font() {
let handle = mp_html_options_create();
let font = CString::new("Times-Roman").unwrap();
assert_eq!(
mp_html_options_set_default_font(handle, font.as_ptr()),
MP_HTML_SUCCESS
);
mp_html_options_free(handle);
}
#[test]
fn test_options_default_font_size() {
let handle = mp_html_options_create();
assert_eq!(
mp_html_options_set_default_font_size(handle, 14.0),
MP_HTML_SUCCESS
);
mp_html_options_free(handle);
}
#[test]
fn test_html_to_pdf_basic() {
let html = CString::new("<html><body><h1>Hello</h1></body></html>").unwrap();
let output = CString::new("/tmp/test_html_v2_output.pdf").unwrap();
let result = mp_html_to_pdf(html.as_ptr(), output.as_ptr(), 0);
assert_eq!(result, MP_HTML_SUCCESS);
let _ = std::fs::remove_file("/tmp/test_html_v2_output.pdf");
}
#[test]
fn test_null_params() {
assert_eq!(
mp_html_options_set_page_size(0, MP_PAGE_SIZE_A4),
MP_HTML_ERROR_INVALID_PARAM
);
assert_eq!(
mp_html_to_pdf(std::ptr::null(), std::ptr::null(), 0),
MP_HTML_ERROR_INVALID_PARAM
);
}
#[test]
fn test_options_free_null() {
mp_html_options_free(0);
}
#[test]
fn test_options_invalid_page_size() {
let handle = mp_html_options_create();
assert_eq!(
mp_html_options_set_page_size(handle, 99),
MP_HTML_ERROR_INVALID_PARAM
);
mp_html_options_free(handle);
}
#[test]
fn test_options_set_page_size_custom_invalid() {
assert_eq!(
mp_html_options_set_page_size_custom(0, 612.0, 792.0),
MP_HTML_ERROR_INVALID_PARAM
);
}
#[test]
fn test_options_set_margins_invalid() {
assert_eq!(
mp_html_options_set_margins(0, 72.0, 72.0, 72.0, 72.0),
MP_HTML_ERROR_INVALID_PARAM
);
}
#[test]
fn test_options_all_page_sizes() {
let handle = mp_html_options_create();
assert_eq!(
mp_html_options_set_page_size(handle, MP_PAGE_SIZE_LETTER),
MP_HTML_SUCCESS
);
assert_eq!(
mp_html_options_set_page_size(handle, MP_PAGE_SIZE_LEGAL),
MP_HTML_SUCCESS
);
assert_eq!(
mp_html_options_set_page_size(handle, MP_PAGE_SIZE_A3),
MP_HTML_SUCCESS
);
assert_eq!(
mp_html_options_set_page_size(handle, MP_PAGE_SIZE_A5),
MP_HTML_SUCCESS
);
mp_html_options_free(handle);
}
}