rust_pdf_box/pd/
document.rs1use crate::cos::document::COSDocument;
2use crate::cos::object::{COSArray, ObjectId};
3use crate::cos::parser::Parser;
4use crate::cos::writer::Writer;
5use crate::error::{PdfError, PdfResult};
6use crate::pd::catalog::PDDocumentCatalog;
7use crate::pd::info::PDDocumentInformation;
8use crate::pd::page::PDPage;
9use std::fs::File;
10use std::io::{Read, Write};
11use std::path::Path;
12
13#[derive(Debug, Clone)]
15pub struct PDDocument {
16 pub cos_doc: COSDocument,
17 pub catalog: PDDocumentCatalog,
18 pub pages: Vec<PDPage>,
19 pub info: PDDocumentInformation,
20}
21
22impl Default for PDDocument {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl PDDocument {
29 pub fn new() -> Self {
31 let mut cos_doc = COSDocument::new();
32 let catalog = PDDocumentCatalog::new(&mut cos_doc);
33
34 let info = PDDocumentInformation::new();
35 let info_id = cos_doc.add_object(info.dict.clone());
36 cos_doc.trailer.set("Info", info_id);
37
38 Self {
39 cos_doc,
40 catalog,
41 pages: Vec::new(),
42 info,
43 }
44 }
45
46 pub fn load_from_bytes(bytes: &[u8]) -> PdfResult<Self> {
48 let parser = Parser::new(bytes);
49 let cos_doc = parser.parse()?;
50 let catalog = PDDocumentCatalog::from_doc(&cos_doc)?;
51
52 let page_ids = catalog.collect_page_ids(&cos_doc);
54 let mut pages = Vec::with_capacity(page_ids.len());
55
56 for id in page_ids {
57 if let Some(obj) = cos_doc.get_object(id) {
58 if let Some(dict) = obj.as_dict() {
59 pages.push(PDPage::from_dict(dict.clone(), id));
60 }
61 }
62 }
63
64 let info = if let Some(info_dict) = cos_doc.get_info() {
65 PDDocumentInformation::from_dict(info_dict.clone())
66 } else {
67 PDDocumentInformation::new()
68 };
69
70 Ok(Self {
71 cos_doc,
72 catalog,
73 pages,
74 info,
75 })
76 }
77
78 pub fn load_from_file(path: impl AsRef<Path>) -> PdfResult<Self> {
80 let mut file = File::open(path)?;
81 let mut bytes = Vec::new();
82 file.read_to_end(&mut bytes)?;
83 Self::load_from_bytes(&bytes)
84 }
85
86 pub fn page_count(&self) -> usize {
88 self.pages.len()
89 }
90
91 pub fn pages(&self) -> &[PDPage] {
93 &self.pages
94 }
95
96 pub fn pages_mut(&mut self) -> &mut [PDPage] {
98 &mut self.pages
99 }
100
101 pub fn get_page(&self, index: usize) -> PdfResult<&PDPage> {
103 self.pages.get(index).ok_or_else(|| {
104 PdfError::PageOperation(format!(
105 "Page index {index} out of bounds (count {})",
106 self.pages.len()
107 ))
108 })
109 }
110
111 pub fn get_page_mut(&mut self, index: usize) -> PdfResult<&mut PDPage> {
113 let count = self.pages.len();
114 self.pages.get_mut(index).ok_or_else(|| {
115 PdfError::PageOperation(format!("Page index {index} out of bounds (count {count})"))
116 })
117 }
118
119 pub fn add_page(&mut self, mut page: PDPage) -> ObjectId {
121 page.sync_resources();
122 let page_id = if let Some(id) = page.page_id {
123 self.cos_doc.set_object(id, page.dict.clone());
124 id
125 } else {
126 let id = self.cos_doc.allocate_id();
127 page.page_id = Some(id);
128 page.dict.set("Parent", self.catalog.pages_id);
129 self.cos_doc.set_object(id, page.dict.clone());
130 id
131 };
132
133 self.pages.push(page);
134 self.update_page_tree();
135 page_id
136 }
137
138 pub fn insert_page(&mut self, index: usize, mut page: PDPage) -> ObjectId {
140 page.sync_resources();
141 let page_id = if let Some(id) = page.page_id {
142 self.cos_doc.set_object(id, page.dict.clone());
143 id
144 } else {
145 let id = self.cos_doc.allocate_id();
146 page.page_id = Some(id);
147 page.dict.set("Parent", self.catalog.pages_id);
148 self.cos_doc.set_object(id, page.dict.clone());
149 id
150 };
151
152 if index >= self.pages.len() {
153 self.pages.push(page);
154 } else {
155 self.pages.insert(index, page);
156 }
157
158 self.update_page_tree();
159 page_id
160 }
161
162 pub fn remove_page(&mut self, index: usize) -> PdfResult<()> {
164 if index >= self.pages.len() {
165 return Err(PdfError::PageOperation(format!(
166 "Cannot remove page at index {index}, total pages {}",
167 self.pages.len()
168 )));
169 }
170 let removed = self.pages.remove(index);
171 if let Some(id) = removed.page_id {
172 self.cos_doc.objects.remove(&id);
173 self.cos_doc.dirty_objects.remove(&id);
174 }
175 self.update_page_tree();
176 Ok(())
177 }
178
179 fn update_page_tree(&mut self) {
181 let mut kids = COSArray::new();
182 for p in &self.pages {
183 if let Some(id) = p.page_id {
184 kids.push(id);
185 }
186 }
187
188 if let Some(pages_obj) = self.cos_doc.get_object_mut(self.catalog.pages_id) {
189 if let Some(dict) = pages_obj.as_dict_mut() {
190 dict.set("Kids", kids);
191 dict.set("Count", self.pages.len() as i64);
192 }
193 }
194 }
195
196 pub fn sync(&mut self) {
198 for page in &mut self.pages {
199 page.sync_resources();
200 if let Some(id) = page.page_id {
201 self.cos_doc.set_object(id, page.dict.clone());
202 }
203 }
204
205 let info_id = if let Some(id) = self.cos_doc.trailer.get_reference("Info") {
206 id
207 } else {
208 let id = self.cos_doc.allocate_id();
209 self.cos_doc.trailer.set("Info", id);
210 id
211 };
212 self.cos_doc.set_object(info_id, self.info.dict.clone());
213 self.update_page_tree();
214 }
215
216 pub fn save_to_writer<W: Write>(&mut self, writer: W) -> PdfResult<()> {
218 self.sync();
219 let mut cos_writer = Writer::new(writer);
220 cos_writer.write_document(&self.cos_doc)
221 }
222
223 pub fn save_to_file(&mut self, path: impl AsRef<Path>) -> PdfResult<()> {
225 let file = File::create(path)?;
226 self.save_to_writer(file)
227 }
228
229 pub fn save_incremental<W: Write>(
231 &mut self,
232 prev_bytes: &[u8],
233 mut writer: W,
234 ) -> PdfResult<()> {
235 self.sync();
236 writer.write_all(prev_bytes)?;
237
238 let parser = Parser::new(prev_bytes);
240 let prev_startxref = parser.find_startxref().unwrap_or(0);
241
242 let mut cos_writer = Writer::with_offset(writer, prev_bytes.len());
243 cos_writer.write_incremental_update(&self.cos_doc, prev_startxref)
244 }
245
246 pub fn stamp(&self) -> crate::stamp::Stamper {
248 crate::stamp::Stamper::new()
249 }
250
251 pub fn sign(&mut self, signer: crate::sign::Signer) -> PdfResult<Vec<u8>> {
253 signer.sign_document(self)
254 }
255}