1use std::path::Path;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::outline::PdfOutline;
8use crate::page::PdfPage;
9use crate::selection::PdfSelection;
10use crate::types::{PdfDocumentAttributes, PdfDocumentInfo, PdfPoint};
11use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};
12
13#[derive(Debug, Clone)]
14pub struct PdfDocument {
15 handle: ObjectHandle,
16}
17
18impl PdfDocument {
19 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
20 Self { handle }
21 }
22
23 pub fn new() -> Result<Self> {
24 let mut out_document = ptr::null_mut();
25 let mut out_error = ptr::null_mut();
26 let status = unsafe { ffi::pdf_document_new(&mut out_document, &mut out_error) };
27 crate::util::status_result(status, out_error)?;
28 Ok(Self::from_handle(required_handle(
29 out_document,
30 "PDFDocument",
31 )?))
32 }
33
34 pub fn from_url(path: impl AsRef<Path>) -> Result<Self> {
35 let path = path_to_c_string(path.as_ref())?;
36 let mut out_document = ptr::null_mut();
37 let mut out_error = ptr::null_mut();
38 let status = unsafe {
39 ffi::pdf_document_new_with_url(path.as_ptr(), &mut out_document, &mut out_error)
40 };
41 crate::util::status_result(status, out_error)?;
42 Ok(Self::from_handle(required_handle(
43 out_document,
44 "PDFDocument",
45 )?))
46 }
47
48 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
49 let mut out_document = ptr::null_mut();
50 let mut out_error = ptr::null_mut();
51 let status = unsafe {
52 ffi::pdf_document_new_with_data(
53 bytes.as_ptr(),
54 bytes.len(),
55 &mut out_document,
56 &mut out_error,
57 )
58 };
59 crate::util::status_result(status, out_error)?;
60 Ok(Self::from_handle(required_handle(
61 out_document,
62 "PDFDocument",
63 )?))
64 }
65
66 pub fn info(&self) -> Result<PdfDocumentInfo> {
67 parse_json(
68 unsafe { ffi::pdf_document_info_json(self.handle.as_ptr()) },
69 "PDFDocument",
70 )
71 }
72
73 pub fn attributes(&self) -> Result<PdfDocumentAttributes> {
74 parse_json(
75 unsafe { ffi::pdf_document_attributes_json(self.handle.as_ptr()) },
76 "PDFDocument attributes",
77 )
78 }
79
80 #[must_use]
81 pub fn string(&self) -> Option<String> {
82 take_string(unsafe { ffi::pdf_document_string(self.handle.as_ptr()) })
83 }
84
85 #[must_use]
86 pub fn page_count(&self) -> usize {
87 unsafe { ffi::pdf_document_page_count(self.handle.as_ptr()) as usize }
88 }
89
90 #[must_use]
91 pub fn page(&self, index: usize) -> Option<PdfPage> {
92 let ptr = unsafe { ffi::pdf_document_page_at(self.handle.as_ptr(), index as u64) };
93 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfPage::from_handle)
94 }
95
96 #[must_use]
97 pub fn pages(&self) -> Vec<PdfPage> {
98 (0..self.page_count())
99 .filter_map(|index| self.page(index))
100 .collect()
101 }
102
103 #[must_use]
104 pub fn page_index(&self, page: &PdfPage) -> Option<usize> {
105 let index = unsafe { ffi::pdf_document_index_for_page(self.handle.as_ptr(), page.as_handle_ptr()) };
106 (index != u64::MAX).then_some(index as usize)
107 }
108
109 #[must_use]
110 pub fn outline_root(&self) -> Option<PdfOutline> {
111 let ptr = unsafe { ffi::pdf_document_outline_root(self.handle.as_ptr()) };
112 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfOutline::from_handle)
113 }
114
115 pub fn set_outline_root(&self, outline: Option<&PdfOutline>) -> Result<()> {
116 let mut out_error = ptr::null_mut();
117 let status = unsafe {
118 ffi::pdf_document_set_outline_root(
119 self.handle.as_ptr(),
120 outline.map_or(ptr::null_mut(), PdfOutline::as_handle_ptr),
121 &mut out_error,
122 )
123 };
124 crate::util::status_result(status, out_error)
125 }
126
127 #[must_use]
128 pub fn outline_item_for_selection(&self, selection: &PdfSelection) -> Option<PdfOutline> {
129 let ptr = unsafe {
130 ffi::pdf_document_outline_item_for_selection(
131 self.handle.as_ptr(),
132 selection.as_handle_ptr(),
133 )
134 };
135 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfOutline::from_handle)
136 }
137
138 #[must_use]
139 pub fn selection_for_entire_document(&self) -> Option<PdfSelection> {
140 let ptr = unsafe { ffi::pdf_document_selection_for_entire_document(self.handle.as_ptr()) };
141 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
142 }
143
144 #[must_use]
145 pub fn selection_from_page_points(
146 &self,
147 start_page: &PdfPage,
148 start_point: PdfPoint,
149 end_page: &PdfPage,
150 end_point: PdfPoint,
151 ) -> Option<PdfSelection> {
152 let ptr = unsafe {
153 ffi::pdf_document_selection_from_pages_points(
154 self.handle.as_ptr(),
155 start_page.as_handle_ptr(),
156 start_point.x,
157 start_point.y,
158 end_page.as_handle_ptr(),
159 end_point.x,
160 end_point.y,
161 )
162 };
163 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
164 }
165
166 #[must_use]
167 pub fn selection_from_page_characters(
168 &self,
169 start_page: &PdfPage,
170 start_character: usize,
171 end_page: &PdfPage,
172 end_character: usize,
173 ) -> Option<PdfSelection> {
174 let ptr = unsafe {
175 ffi::pdf_document_selection_from_pages_characters(
176 self.handle.as_ptr(),
177 start_page.as_handle_ptr(),
178 start_character as u64,
179 end_page.as_handle_ptr(),
180 end_character as u64,
181 )
182 };
183 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(PdfSelection::from_handle)
184 }
185
186 pub fn unlock(&self, password: &str) -> Result<bool> {
187 let password = c_string(password)?;
188 Ok(unsafe { ffi::pdf_document_unlock(self.handle.as_ptr(), password.as_ptr()) != 0 })
189 }
190
191 pub fn write_to_url(&self, path: impl AsRef<Path>) -> Result<()> {
192 let path = path_to_c_string(path.as_ref())?;
193 let mut out_error = ptr::null_mut();
194 let status = unsafe {
195 ffi::pdf_document_write_to_url(self.handle.as_ptr(), path.as_ptr(), &mut out_error)
196 };
197 crate::util::status_result(status, out_error)
198 }
199
200 pub fn insert_page(&self, page: &PdfPage, index: usize) -> Result<()> {
201 let mut out_error = ptr::null_mut();
202 let status = unsafe {
203 ffi::pdf_document_insert_page(
204 self.handle.as_ptr(),
205 page.as_handle_ptr(),
206 index as u64,
207 &mut out_error,
208 )
209 };
210 crate::util::status_result(status, out_error)
211 }
212
213 pub fn remove_page(&self, index: usize) -> Result<()> {
214 let mut out_error = ptr::null_mut();
215 let status = unsafe {
216 ffi::pdf_document_remove_page_at(self.handle.as_ptr(), index as u64, &mut out_error)
217 };
218 crate::util::status_result(status, out_error)
219 }
220
221 pub fn exchange_pages(&self, index_a: usize, index_b: usize) -> Result<()> {
222 let mut out_error = ptr::null_mut();
223 let status = unsafe {
224 ffi::pdf_document_exchange_pages(
225 self.handle.as_ptr(),
226 index_a as u64,
227 index_b as u64,
228 &mut out_error,
229 )
230 };
231 crate::util::status_result(status, out_error)
232 }
233
234 pub(crate) fn as_handle_ptr(&self) -> *mut core::ffi::c_void {
235 self.handle.as_ptr()
236 }
237}