use super::document::CPDF_DOCS;
use super::{clear_error, set_error};
use crate::cpdf::bookmarks::{self, Bookmark};
use crate::ffi::Handle;
use std::ffi::{CStr, c_char, c_int};
macro_rules! get_doc_clone {
($handle:expr) => {
match CPDF_DOCS.get($handle) {
Some(arc) => arc.lock().unwrap().clone(),
None => {
set_error(1, &format!("invalid document handle: {}", $handle));
return 0;
}
}
};
}
macro_rules! get_doc_ref {
($handle:expr) => {
match CPDF_DOCS.get($handle) {
Some(arc) => arc,
None => {
set_error(1, &format!("invalid document handle: {}", $handle));
return std::ptr::null_mut();
}
}
};
}
macro_rules! ok_or_error {
($result:expr) => {
match $result {
Ok(doc) => CPDF_DOCS.insert(doc),
Err(e) => {
set_error(1, &e.to_string());
0
}
}
};
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_getBookmarksJSON(doc: Handle, len_out: *mut usize) -> *mut u8 {
clear_error();
let d = get_doc_ref!(doc);
let guard = d.lock().unwrap();
match bookmarks::get_bookmarks_json(&guard) {
Ok(json) => {
let bytes = json.into_bytes();
let len = bytes.len();
let ptr = unsafe {
let layout = std::alloc::Layout::from_size_align_unchecked(len, 1);
let p = std::alloc::alloc(layout);
if p.is_null() {
set_error(1, "allocation failed");
return std::ptr::null_mut();
}
std::ptr::copy_nonoverlapping(bytes.as_ptr(), p, len);
p
};
if !len_out.is_null() {
unsafe { *len_out = len };
}
ptr
}
Err(e) => {
set_error(1, &e.to_string());
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_setBookmarksJSON(doc: Handle, data: *const u8, len: usize) -> Handle {
clear_error();
let d = get_doc_clone!(doc);
if data.is_null() || len == 0 {
set_error(1, "cpdf_setBookmarksJSON: null or empty data");
return 0;
}
let slice = unsafe { std::slice::from_raw_parts(data, len) };
let json = match std::str::from_utf8(slice) {
Ok(s) => s,
Err(e) => {
set_error(1, &format!("invalid UTF-8: {e}"));
return 0;
}
};
ok_or_error!(bookmarks::set_bookmarks_from_json(d, json))
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_addBookmark(
doc: Handle,
level: c_int,
page: c_int,
title: *const c_char,
is_open: c_int,
) -> Handle {
clear_error();
let d = get_doc_clone!(doc);
let t = if title.is_null() {
""
} else {
unsafe { CStr::from_ptr(title) }.to_str().unwrap_or("")
};
let bm = Bookmark {
level: level.max(0) as usize,
page: page.max(1) as usize,
title: t.to_owned(),
open: is_open != 0,
};
ok_or_error!(bookmarks::add_bookmark(d, bm))
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_removeBookmarks(doc: Handle) -> Handle {
clear_error();
let d = get_doc_clone!(doc);
ok_or_error!(bookmarks::remove_bookmarks(d))
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_bookmarksOpenToLevel(doc: Handle, level: c_int) -> Handle {
clear_error();
let d = get_doc_clone!(doc);
ok_or_error!(bookmarks::bookmarks_open_to_level(d, level.max(0) as usize))
}
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_addBookmarkTitle(doc: Handle, title: *const c_char) -> Handle {
clear_error();
let d = get_doc_clone!(doc);
let t = if title.is_null() {
""
} else {
unsafe { CStr::from_ptr(title) }.to_str().unwrap_or("")
};
ok_or_error!(bookmarks::add_bookmark_title(d, t))
}