pdfkit/
document_delegate.rs1use std::ffi::{CStr, CString};
2use std::fmt;
3use std::os::raw::{c_char, c_void};
4use std::panic::{catch_unwind, AssertUnwindSafe};
5use std::ptr;
6
7use crate::error::Result;
8use crate::ffi;
9use crate::handle::ObjectHandle;
10use crate::notifications::PdfDocumentNotification;
11use crate::selection::PdfSelection;
12
13pub trait PdfDocumentDelegate: 'static {
14 fn handle_notification(&mut self, _notification: PdfDocumentNotification) {}
15
16 fn did_match_string(&mut self, _instance: PdfSelection) {}
17
18 fn page_class_name(&mut self) -> Option<String> {
19 None
20 }
21
22 fn annotation_class_name(&mut self, _annotation_type: &str) -> Option<String> {
23 None
24 }
25}
26
27struct DelegateState {
28 delegate: Box<dyn PdfDocumentDelegate>,
29}
30
31pub struct PdfDocumentDelegateHandle {
32 handle: ObjectHandle,
33 _state: Box<DelegateState>,
34}
35
36impl PdfDocumentDelegateHandle {
37 pub fn new(delegate: impl PdfDocumentDelegate) -> Result<Self> {
38 let mut state = Box::new(DelegateState {
39 delegate: Box::new(delegate),
40 });
41 let context = ptr::addr_of_mut!(*state).cast::<c_void>();
42 let mut out_delegate = ptr::null_mut();
43 let mut out_error = ptr::null_mut();
44 let status = unsafe {
45 ffi::pdf_document_delegate_new(
46 context,
47 Some(pdf_document_delegate_notification_trampoline),
48 Some(pdf_document_delegate_match_trampoline),
49 Some(pdf_document_delegate_page_class_name_trampoline),
50 Some(pdf_document_delegate_annotation_class_name_trampoline),
51 &mut out_delegate,
52 &mut out_error,
53 )
54 };
55 crate::util::status_result(status, out_error)?;
56 Ok(Self {
57 handle: crate::util::required_handle(out_delegate, "PDFDocumentDelegate")?,
58 _state: state,
59 })
60 }
61
62 pub(crate) fn as_handle_ptr(&self) -> *mut c_void {
63 self.handle.as_ptr()
64 }
65}
66
67impl fmt::Debug for PdfDocumentDelegateHandle {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.debug_struct("PdfDocumentDelegateHandle").finish_non_exhaustive()
70 }
71}
72
73fn duplicate_string(value: Option<String>) -> *mut c_char {
74 value.and_then(|value| CString::new(value).ok()).map_or(ptr::null_mut(), |value| unsafe {
75 libc::strdup(value.as_ptr())
76 })
77}
78
79unsafe fn delegate_state(context: *mut c_void) -> Option<&'static mut DelegateState> {
80 context.cast::<DelegateState>().as_mut()
81}
82
83unsafe extern "C" fn pdf_document_delegate_notification_trampoline(
84 context: *mut c_void,
85 raw_notification: i32,
86) {
87 let _ = catch_unwind(AssertUnwindSafe(|| {
88 let Some(state) = (unsafe { delegate_state(context) }) else {
89 return;
90 };
91 let Some(notification) = PdfDocumentNotification::from_raw(raw_notification) else {
92 return;
93 };
94 state.delegate.handle_notification(notification);
95 }));
96}
97
98unsafe extern "C" fn pdf_document_delegate_match_trampoline(
99 context: *mut c_void,
100 selection_handle: *mut c_void,
101) {
102 let _ = catch_unwind(AssertUnwindSafe(|| {
103 let Some(state) = (unsafe { delegate_state(context) }) else {
104 return;
105 };
106 let Some(handle) = (unsafe { ObjectHandle::from_retained_ptr(selection_handle) }) else {
107 return;
108 };
109 state.delegate.did_match_string(PdfSelection::from_handle(handle));
110 }));
111}
112
113unsafe extern "C" fn pdf_document_delegate_page_class_name_trampoline(
114 context: *mut c_void,
115) -> *mut c_char {
116 catch_unwind(AssertUnwindSafe(|| {
117 let Some(state) = (unsafe { delegate_state(context) }) else {
118 return ptr::null_mut();
119 };
120 duplicate_string(state.delegate.page_class_name())
121 }))
122 .unwrap_or(ptr::null_mut())
123}
124
125unsafe extern "C" fn pdf_document_delegate_annotation_class_name_trampoline(
126 context: *mut c_void,
127 annotation_type: *const c_char,
128) -> *mut c_char {
129 catch_unwind(AssertUnwindSafe(|| {
130 let Some(state) = (unsafe { delegate_state(context) }) else {
131 return ptr::null_mut();
132 };
133 let Some(annotation_type) = (!annotation_type.is_null())
134 .then(|| unsafe { CStr::from_ptr(annotation_type).to_string_lossy().into_owned() })
135 else {
136 return ptr::null_mut();
137 };
138 duplicate_string(state.delegate.annotation_class_name(&annotation_type))
139 }))
140 .unwrap_or(ptr::null_mut())
141}