Skip to main content

lopdf/
incremental_document.rs

1use crate::{Dictionary, Document, Object, ObjectId, Result};
2
3#[derive(Debug, Clone)]
4pub struct IncrementalDocument {
5    /// The raw data for the files read from input.
6    bytes_documents: Vec<u8>,
7
8    /// The combined result of `bytes_documents`.
9    /// Do not edit this document as it will not be saved.
10    prev_documents: Document,
11
12    /// A new document appended to the previously loaded file.
13    pub new_document: Document,
14}
15
16impl IncrementalDocument {
17    /// Create new PDF document.
18    pub fn new() -> Self {
19        Self {
20            bytes_documents: Vec::new(),
21            prev_documents: Document::new(),
22            new_document: Document::new(),
23        }
24    }
25
26    /// Create new `IncrementalDocument` from the bytes and document.
27    ///
28    /// The function expects the bytes and previous document to match.
29    /// If they do not match exactly this might result in broken PDFs.
30    pub fn create_from(prev_bytes: Vec<u8>, prev_documents: Document) -> Self {
31        Self {
32            bytes_documents: prev_bytes,
33            new_document: Document::new_from_prev(&prev_documents),
34            prev_documents,
35        }
36    }
37
38    /// Get the structure of the previous documents (all prev incremental updates combined.)
39    pub fn get_prev_documents(&self) -> &Document {
40        &self.prev_documents
41    }
42
43    /// Get the bytes of the previous documents.
44    pub fn get_prev_documents_bytes(&self) -> &[u8] {
45        &self.bytes_documents
46    }
47
48    /// Clone Object from previous document to new document.
49    /// If the object already exists nothing is done.
50    ///
51    /// This function can be used to clone an object so it can be changed in the incremental updates.
52    pub fn opt_clone_object_to_new_document(&mut self, object_id: ObjectId) -> Result<()> {
53        if !self.new_document.has_object(object_id) {
54            let old_object = self.prev_documents.get_object(object_id)?;
55            self.new_document.set_object(object_id, old_object.clone());
56        }
57        Ok(())
58    }
59
60    /// Get the page's resource dictionary (only in new document).
61    ///
62    /// Get Object that has the key `Resources`.
63    pub fn get_or_create_resources(&mut self, page_id: ObjectId) -> Result<&mut Object> {
64        self.opt_clone_object_to_new_document(page_id)?;
65        let resources_id = {
66            let page = self.new_document.get_object(page_id).and_then(Object::as_dict)?;
67            if page.has(b"Resources") {
68                page.get(b"Resources").and_then(Object::as_reference).ok()
69            } else {
70                None
71            }
72        };
73        if let Some(res_id) = resources_id {
74            self.opt_clone_object_to_new_document(res_id)?;
75            return self.new_document.get_object_mut(res_id);
76        }
77        let page = self
78            .new_document
79            .get_object_mut(page_id)
80            .and_then(Object::as_dict_mut)?;
81        if !page.has(b"Resources") {
82            page.set(b"Resources", Dictionary::new());
83        }
84        page.get_mut(b"Resources")
85    }
86
87    /// Add XObject to a page.
88    ///
89    /// Get Object that has the key `Resources -> XObject`.
90    pub fn add_xobject<N: Into<Vec<u8>>>(
91        &mut self, page_id: ObjectId, xobject_name: N, xobject_id: ObjectId,
92    ) -> Result<()> {
93        if let Ok(resources) = self.get_or_create_resources(page_id).and_then(Object::as_dict_mut) {
94            if !resources.has(b"XObject") {
95                resources.set("XObject", Dictionary::new());
96            }
97            let mut xobjects = resources.get_mut(b"XObject")?;
98            if let Object::Reference(xobjects_ref_id) = xobjects {
99                let mut xobjects_id = *xobjects_ref_id;
100                while let Object::Reference(id) = self.new_document.get_object(xobjects_id)? {
101                    xobjects_id = *id;
102                }
103                xobjects = self.new_document.get_object_mut(xobjects_id)?;
104            }
105            let xobjects = Object::as_dict_mut(xobjects)?;
106            xobjects.set(xobject_name, Object::Reference(xobject_id));
107        }
108        Ok(())
109    }
110
111    /// Add Graphics State to a page.
112    ///
113    /// Get Object that has the key `Resources -> ExtGState`.
114    pub fn add_graphics_state<N: Into<Vec<u8>>>(
115        &mut self, page_id: ObjectId, gs_name: N, gs_id: ObjectId,
116    ) -> Result<()> {
117        if let Ok(resources) = self.get_or_create_resources(page_id).and_then(Object::as_dict_mut) {
118            if !resources.has(b"ExtGState") {
119                resources.set("ExtGState", Dictionary::new());
120            }
121            let states = resources.get_mut(b"ExtGState").and_then(Object::as_dict_mut)?;
122            states.set(gs_name, Object::Reference(gs_id));
123        }
124        Ok(())
125    }
126}
127
128impl Default for IncrementalDocument {
129    fn default() -> Self {
130        Self::new()
131    }
132}