Skip to main content

pdfkit/
document.rs

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}