use crate::{Dictionary, Document, Error, Object, ObjectId, Result}; #[derive(Debug, Clone)] pub struct IncrementalDocument { /// The raw data for the files read from input. bytes_documents: Vec, /// The combined result of `bytes_documents`. /// Do not edit this document as it will not be saved. prev_documents: Document, /// A new document appended to the previously loaded file. pub new_document: Document, } impl IncrementalDocument { /// Create new PDF document. pub fn new() -> Self { Self { bytes_documents: Vec::new(), prev_documents: Document::new(), new_document: Document::new(), } } /// Create new `IncrementalDocument` from the bytes and document. /// /// The function expects the bytes and previous document to match. /// If they do not match exactly this might result in broken PDFs. pub fn create_from(prev_bytes: Vec, prev_documents: Document) -> Self { Self { bytes_documents: prev_bytes, new_document: Document::new_from_prev(&prev_documents), prev_documents, } } /// Get the structure of the previous documents (all prev incremental updates combined.) pub fn get_prev_documents(&self) -> &Document { &self.prev_documents } /// Get the bytes of the previous documents. pub fn get_prev_documents_bytes(&self) -> &[u8] { &self.bytes_documents } /// Clone Object from previous document to new document. /// If the object already exists nothing is done. /// /// This function can be used to clone an object so it can be changed in the incremental updates. pub fn opt_clone_object_to_new_document(&mut self, object_id: ObjectId) -> Result<()> { if !self.new_document.has_object(object_id) { let old_object = self.prev_documents.get_object(object_id)?; self.new_document.set_object(object_id, old_object.clone()); } Ok(()) } /// Get the pages resources. (only in new document) /// /// Get Object that has the key `Resources`. fn get_or_create_resources_mut(&mut self, page_id: ObjectId) -> Result<&mut Object> { let page = self .new_document .get_object_mut(page_id) .and_then(Object::as_dict_mut)?; if page.has(b"Resources") { if let Ok(_res_id) = page.get(b"Resources").and_then(Object::as_reference) { // Find and return referenced object. // Note: This returns an error because we can not have 2 mut barrow for `*self`. // self.get_object_mut(res_id) Err(Error::ObjectNotFound) } else { // It exists and is not a reference. page.get_mut(b"Resources") } } else { // "Resources" key does not exist, So create it. page.set("Resources", Dictionary::new()); page.get_mut(b"Resources") } } /// Get the pages resources. /// /// Get Object that has the key `Resources`. pub fn get_or_create_resources(&mut self, page_id: ObjectId) -> Result<&mut Object> { self.opt_clone_object_to_new_document(page_id)?; let resources_id = { let page = self.new_document.get_object(page_id).and_then(Object::as_dict)?; if page.has(b"Resources") { page.get(b"Resources").and_then(Object::as_reference).ok() } else { None } }; match resources_id { Some(res_id) => { self.opt_clone_object_to_new_document(res_id)?; self.new_document.get_object_mut(res_id) } None => self.get_or_create_resources_mut(page_id), } } /// Add XObject to a page. /// /// Get Object that has the key `Resources -> XObject`. pub fn add_xobject>>( &mut self, page_id: ObjectId, xobject_name: N, xobject_id: ObjectId, ) -> Result<()> { if let Ok(resources) = self.get_or_create_resources(page_id).and_then(Object::as_dict_mut) { if !resources.has(b"XObject") { resources.set("XObject", Dictionary::new()); } let mut xobjects = resources.get_mut(b"XObject")?; if let Object::Reference(xobjects_ref_id) = xobjects { let mut xobjects_id = *xobjects_ref_id; while let Object::Reference(id) = self.new_document.get_object(xobjects_id)? { xobjects_id = *id; } xobjects = self.new_document.get_object_mut(xobjects_id)?; } let xobjects = Object::as_dict_mut(xobjects)?; xobjects.set(xobject_name, Object::Reference(xobject_id)); } Ok(()) } /// Add Graphics State to a page. /// /// Get Object that has the key `Resources -> ExtGState`. pub fn add_graphics_state>>( &mut self, page_id: ObjectId, gs_name: N, gs_id: ObjectId, ) -> Result<()> { if let Ok(resources) = self.get_or_create_resources(page_id).and_then(Object::as_dict_mut) { if !resources.has(b"ExtGState") { resources.set("ExtGState", Dictionary::new()); } let states = resources.get_mut(b"ExtGState").and_then(Object::as_dict_mut)?; states.set(gs_name, Object::Reference(gs_id)); } Ok(()) } } impl Default for IncrementalDocument { fn default() -> Self { Self::new() } }