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 {
15 fn handle_notification(&mut self, _notification: PdfDocumentNotification) {}
17
18 fn did_match_string(&mut self, _instance: PdfSelection) {}
20
21 fn page_class_name(&mut self) -> Option<String> {
23 None
24 }
25
26 fn annotation_class_name(&mut self, _annotation_type: &str) -> Option<String> {
28 None
29 }
30}
31
32struct DelegateState {
33 delegate: Box<dyn PdfDocumentDelegate>,
34}
35
36pub struct PdfDocumentDelegateHandle {
38 handle: ObjectHandle,
39 _state: Box<DelegateState>,
40}
41
42impl PdfDocumentDelegateHandle {
43 pub fn new(delegate: impl PdfDocumentDelegate) -> Result<Self> {
45 let mut state = Box::new(DelegateState {
46 delegate: Box::new(delegate),
47 });
48 let context = ptr::addr_of_mut!(*state).cast::<c_void>();
49 let mut out_delegate = ptr::null_mut();
50 let mut out_error = ptr::null_mut();
51 let status = unsafe {
52 ffi::pdf_document_delegate_new(
53 context,
54 Some(pdf_document_delegate_notification_trampoline),
55 Some(pdf_document_delegate_match_trampoline),
56 Some(pdf_document_delegate_page_class_name_trampoline),
57 Some(pdf_document_delegate_annotation_class_name_trampoline),
58 &mut out_delegate,
59 &mut out_error,
60 )
61 };
62 crate::util::status_result(status, out_error)?;
63 Ok(Self {
64 handle: crate::util::required_handle(out_delegate, "PDFDocumentDelegate")?,
65 _state: state,
66 })
67 }
68
69 pub(crate) fn as_handle_ptr(&self) -> *mut c_void {
70 self.handle.as_ptr()
71 }
72}
73
74impl fmt::Debug for PdfDocumentDelegateHandle {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("PdfDocumentDelegateHandle")
77 .finish_non_exhaustive()
78 }
79}
80
81fn duplicate_string(value: Option<String>) -> *mut c_char {
82 value
83 .and_then(|value| CString::new(value).ok())
84 .map_or(ptr::null_mut(), |value| unsafe {
85 libc::strdup(value.as_ptr())
86 })
87}
88
89unsafe fn delegate_state(context: *mut c_void) -> Option<&'static mut DelegateState> {
93 context.cast::<DelegateState>().as_mut()
94}
95
96unsafe extern "C" fn pdf_document_delegate_notification_trampoline(
101 context: *mut c_void,
102 raw_notification: i32,
103) {
104 let _ = catch_unwind(AssertUnwindSafe(|| {
105 let Some(state) = (unsafe { delegate_state(context) }) else {
107 return;
108 };
109 let Some(notification) = PdfDocumentNotification::from_raw(raw_notification) else {
110 return;
111 };
112 state.delegate.handle_notification(notification);
113 }));
114}
115
116unsafe extern "C" fn pdf_document_delegate_match_trampoline(
122 context: *mut c_void,
123 selection_handle: *mut c_void,
124) {
125 let _ = catch_unwind(AssertUnwindSafe(|| {
126 let Some(state) = (unsafe { delegate_state(context) }) else {
128 return;
129 };
130 let Some(handle) = (unsafe { ObjectHandle::from_retained_ptr(selection_handle) }) else {
131 return;
132 };
133 state
134 .delegate
135 .did_match_string(PdfSelection::from_handle(handle));
136 }));
137}
138
139unsafe extern "C" fn pdf_document_delegate_page_class_name_trampoline(
144 context: *mut c_void,
145) -> *mut c_char {
146 catch_unwind(AssertUnwindSafe(|| {
147 let Some(state) = (unsafe { delegate_state(context) }) else {
149 return ptr::null_mut();
150 };
151 duplicate_string(state.delegate.page_class_name())
152 }))
153 .unwrap_or(ptr::null_mut())
154}
155
156unsafe extern "C" fn pdf_document_delegate_annotation_class_name_trampoline(
162 context: *mut c_void,
163 annotation_type: *const c_char,
164) -> *mut c_char {
165 catch_unwind(AssertUnwindSafe(|| {
166 let Some(state) = (unsafe { delegate_state(context) }) else {
168 return ptr::null_mut();
169 };
170 let Some(annotation_type) = (!annotation_type.is_null()).then(|| unsafe {
171 CStr::from_ptr(annotation_type)
173 .to_string_lossy()
174 .into_owned()
175 }) else {
176 return ptr::null_mut();
177 };
178 duplicate_string(state.delegate.annotation_class_name(&annotation_type))
179 }))
180 .unwrap_or(ptr::null_mut())
181}