Skip to main content

pdfkit/
page.rs

1use std::ptr;
2
3use crate::annotation::PdfAnnotation;
4use crate::error::{PdfKitError, Result};
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::selection::PdfSelection;
8use crate::types::{DisplayBox, PdfPageImageInitializationOptions, PdfPoint, PdfRect};
9use crate::util::{c_string, take_string};
10
11#[derive(Debug, Clone)]
12pub struct PdfPage {
13    handle: ObjectHandle,
14}
15
16impl PdfPage {
17    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
18        Self { handle }
19    }
20
21    pub fn new() -> Result<Self> {
22        let mut out_page = ptr::null_mut();
23        let mut out_error = ptr::null_mut();
24        let status = unsafe { ffi::pdf_page_new(&mut out_page, &mut out_error) };
25        crate::util::status_result(status, out_error)?;
26        Ok(Self::from_handle(crate::util::required_handle(
27            out_page, "PDFPage",
28        )?))
29    }
30
31    pub fn from_image_data(
32        image_data: &[u8],
33        options: &PdfPageImageInitializationOptions,
34    ) -> Result<Self> {
35        let options_json = serde_json::to_string(options).map_err(|error| {
36            PdfKitError::new(
37                ffi::status::FRAMEWORK,
38                format!("failed to encode PDFPage image initialization options: {error}"),
39            )
40        })?;
41        let options_json = c_string(&options_json)?;
42        let mut out_page = ptr::null_mut();
43        let mut out_error = ptr::null_mut();
44        let status = unsafe {
45            ffi::pdf_page_new_with_image_data(
46                image_data.as_ptr(),
47                image_data.len(),
48                options_json.as_ptr(),
49                &mut out_page,
50                &mut out_error,
51            )
52        };
53        crate::util::status_result(status, out_error)?;
54        Ok(Self::from_handle(crate::util::required_handle(
55            out_page, "PDFPage",
56        )?))
57    }
58
59    #[must_use]
60    pub fn label(&self) -> Option<String> {
61        take_string(unsafe { ffi::pdf_page_label_string(self.handle.as_ptr()) })
62    }
63
64    #[must_use]
65    pub fn string(&self) -> Option<String> {
66        take_string(unsafe { ffi::pdf_page_string(self.handle.as_ptr()) })
67    }
68
69    #[must_use]
70    pub fn number_of_characters(&self) -> usize {
71        unsafe { ffi::pdf_page_number_of_characters(self.handle.as_ptr()) as usize }
72    }
73
74    #[must_use]
75    pub fn rotation(&self) -> i32 {
76        unsafe { ffi::pdf_page_rotation(self.handle.as_ptr()) }
77    }
78
79    pub fn set_rotation(&self, rotation: i32) -> Result<()> {
80        let mut out_error = ptr::null_mut();
81        let status =
82            unsafe { ffi::pdf_page_set_rotation(self.handle.as_ptr(), rotation, &mut out_error) };
83        crate::util::status_result(status, out_error)
84    }
85
86    #[must_use]
87    pub fn bounds(&self, display_box: DisplayBox) -> PdfRect {
88        let mut x = 0.0_f64;
89        let mut y = 0.0_f64;
90        let mut width = 0.0_f64;
91        let mut height = 0.0_f64;
92        unsafe {
93            ffi::pdf_page_bounds(
94                self.handle.as_ptr(),
95                display_box.as_raw(),
96                &mut x,
97                &mut y,
98                &mut width,
99                &mut height,
100            );
101        }
102        PdfRect {
103            x,
104            y,
105            width,
106            height,
107        }
108    }
109
110    pub fn set_bounds(&self, display_box: DisplayBox, bounds: PdfRect) -> Result<()> {
111        let mut out_error = ptr::null_mut();
112        let status = unsafe {
113            ffi::pdf_page_set_bounds(
114                self.handle.as_ptr(),
115                display_box.as_raw(),
116                bounds.x,
117                bounds.y,
118                bounds.width,
119                bounds.height,
120                &mut out_error,
121            )
122        };
123        crate::util::status_result(status, out_error)
124    }
125
126    #[must_use]
127    pub fn document(&self) -> Option<crate::document::PdfDocument> {
128        let ptr = unsafe { ffi::pdf_page_document(self.handle.as_ptr()) };
129        unsafe { ObjectHandle::from_retained_ptr(ptr) }
130            .map(crate::document::PdfDocument::from_handle)
131    }
132
133    #[must_use]
134    pub fn annotation_count(&self) -> usize {
135        unsafe { ffi::pdf_page_annotation_count(self.handle.as_ptr()) as usize }
136    }
137
138    #[must_use]
139    pub fn annotation(&self, index: usize) -> Option<PdfAnnotation> {
140        let ptr = unsafe { ffi::pdf_page_annotation_at(self.handle.as_ptr(), index as u64) };
141        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfAnnotation::from_handle)
142    }
143
144    #[must_use]
145    pub fn annotations(&self) -> Vec<PdfAnnotation> {
146        (0..self.annotation_count())
147            .filter_map(|index| self.annotation(index))
148            .collect()
149    }
150
151    pub fn add_annotation(&self, annotation: &PdfAnnotation) -> Result<()> {
152        let mut out_error = ptr::null_mut();
153        let status = unsafe {
154            ffi::pdf_page_add_annotation(
155                self.handle.as_ptr(),
156                annotation.as_handle_ptr(),
157                &mut out_error,
158            )
159        };
160        crate::util::status_result(status, out_error)
161    }
162
163    pub fn remove_annotation(&self, annotation: &PdfAnnotation) -> Result<()> {
164        let mut out_error = ptr::null_mut();
165        let status = unsafe {
166            ffi::pdf_page_remove_annotation(
167                self.handle.as_ptr(),
168                annotation.as_handle_ptr(),
169                &mut out_error,
170            )
171        };
172        crate::util::status_result(status, out_error)
173    }
174
175    #[must_use]
176    pub fn annotation_at_point(&self, point: PdfPoint) -> Option<PdfAnnotation> {
177        let ptr =
178            unsafe { ffi::pdf_page_annotation_at_point(self.handle.as_ptr(), point.x, point.y) };
179        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfAnnotation::from_handle)
180    }
181
182    #[must_use]
183    pub fn selection_for_range(&self, location: usize, length: usize) -> Option<PdfSelection> {
184        let ptr = unsafe {
185            ffi::pdf_page_selection_for_range(self.handle.as_ptr(), location as u64, length as u64)
186        };
187        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
188    }
189
190    #[must_use]
191    pub fn selection_for_rect(&self, rect: PdfRect) -> Option<PdfSelection> {
192        let ptr = unsafe {
193            ffi::pdf_page_selection_for_rect(
194                self.handle.as_ptr(),
195                rect.x,
196                rect.y,
197                rect.width,
198                rect.height,
199            )
200        };
201        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
202    }
203
204    #[must_use]
205    pub fn selection_for_word_at_point(&self, point: PdfPoint) -> Option<PdfSelection> {
206        let ptr = unsafe {
207            ffi::pdf_page_selection_for_word_at_point(self.handle.as_ptr(), point.x, point.y)
208        };
209        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
210    }
211
212    #[must_use]
213    pub fn selection_for_line_at_point(&self, point: PdfPoint) -> Option<PdfSelection> {
214        let ptr = unsafe {
215            ffi::pdf_page_selection_for_line_at_point(self.handle.as_ptr(), point.x, point.y)
216        };
217        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
218    }
219
220    #[must_use]
221    pub fn selection_from_points(&self, start: PdfPoint, end: PdfPoint) -> Option<PdfSelection> {
222        let ptr = unsafe {
223            ffi::pdf_page_selection_from_points(
224                self.handle.as_ptr(),
225                start.x,
226                start.y,
227                end.x,
228                end.y,
229            )
230        };
231        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
232    }
233
234    #[must_use]
235    pub fn character_bounds_at(&self, index: usize) -> PdfRect {
236        let mut x = 0.0_f64;
237        let mut y = 0.0_f64;
238        let mut width = 0.0_f64;
239        let mut height = 0.0_f64;
240        unsafe {
241            ffi::pdf_page_character_bounds_at(
242                self.handle.as_ptr(),
243                index as u64,
244                &mut x,
245                &mut y,
246                &mut width,
247                &mut height,
248            );
249        }
250        PdfRect {
251            x,
252            y,
253            width,
254            height,
255        }
256    }
257
258    #[must_use]
259    pub fn character_index_at_point(&self, point: PdfPoint) -> Option<usize> {
260        let index = unsafe {
261            ffi::pdf_page_character_index_at_point(self.handle.as_ptr(), point.x, point.y)
262        };
263        (index != i64::MAX)
264            .then_some(index)
265            .and_then(|index| usize::try_from(index).ok())
266    }
267
268    #[must_use]
269    pub fn displays_annotations(&self) -> bool {
270        unsafe { ffi::pdf_page_displays_annotations(self.handle.as_ptr()) != 0 }
271    }
272
273    pub fn set_displays_annotations(&self, value: bool) {
274        unsafe { ffi::pdf_page_set_displays_annotations(self.handle.as_ptr(), i32::from(value)) };
275    }
276
277    pub(crate) fn as_handle_ptr(&self) -> *mut core::ffi::c_void {
278        self.handle.as_ptr()
279    }
280}