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)]
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 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 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 #[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 #[must_use]
70 pub fn string(&self) -> Option<String> {
71 take_string(unsafe { ffi::pdf_page_string(self.handle.as_ptr()) })
72 }
73
74 #[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 #[must_use]
82 pub fn rotation(&self) -> i32 {
83 unsafe { ffi::pdf_page_rotation(self.handle.as_ptr()) }
84 }
85
86 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 #[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 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 #[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 #[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 #[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 #[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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[must_use]
294 pub fn displays_annotations(&self) -> bool {
295 unsafe { ffi::pdf_page_displays_annotations(self.handle.as_ptr()) != 0 }
296 }
297
298 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}