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}