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