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")
70 .finish_non_exhaustive()
71 }
72}
73
74fn duplicate_string(value: Option<String>) -> *mut c_char {
75 value
76 .and_then(|value| CString::new(value).ok())
77 .map_or(ptr::null_mut(), |value| unsafe {
78 libc::strdup(value.as_ptr())
79 })
80}
81
82unsafe fn delegate_state(context: *mut c_void) -> Option<&'static mut DelegateState> {
86 context.cast::<DelegateState>().as_mut()
87}
88
89unsafe extern "C" fn pdf_document_delegate_notification_trampoline(
94 context: *mut c_void,
95 raw_notification: i32,
96) {
97 let _ = catch_unwind(AssertUnwindSafe(|| {
98 let Some(state) = (unsafe { delegate_state(context) }) else {
100 return;
101 };
102 let Some(notification) = PdfDocumentNotification::from_raw(raw_notification) else {
103 return;
104 };
105 state.delegate.handle_notification(notification);
106 }));
107}
108
109unsafe extern "C" fn pdf_document_delegate_match_trampoline(
115 context: *mut c_void,
116 selection_handle: *mut c_void,
117) {
118 let _ = catch_unwind(AssertUnwindSafe(|| {
119 let Some(state) = (unsafe { delegate_state(context) }) else {
121 return;
122 };
123 let Some(handle) = (unsafe { ObjectHandle::from_retained_ptr(selection_handle) }) else {
124 return;
125 };
126 state
127 .delegate
128 .did_match_string(PdfSelection::from_handle(handle));
129 }));
130}
131
132unsafe extern "C" fn pdf_document_delegate_page_class_name_trampoline(
137 context: *mut c_void,
138) -> *mut c_char {
139 catch_unwind(AssertUnwindSafe(|| {
140 let Some(state) = (unsafe { delegate_state(context) }) else {
142 return ptr::null_mut();
143 };
144 duplicate_string(state.delegate.page_class_name())
145 }))
146 .unwrap_or(ptr::null_mut())
147}
148
149unsafe extern "C" fn pdf_document_delegate_annotation_class_name_trampoline(
155 context: *mut c_void,
156 annotation_type: *const c_char,
157) -> *mut c_char {
158 catch_unwind(AssertUnwindSafe(|| {
159 let Some(state) = (unsafe { delegate_state(context) }) else {
161 return ptr::null_mut();
162 };
163 let Some(annotation_type) = (!annotation_type.is_null()).then(|| unsafe {
164 CStr::from_ptr(annotation_type)
166 .to_string_lossy()
167 .into_owned()
168 }) else {
169 return ptr::null_mut();
170 };
171 duplicate_string(state.delegate.annotation_class_name(&annotation_type))
172 }))
173 .unwrap_or(ptr::null_mut())
174}