Skip to main content

pdfluent_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
67                .new_document
68                .get_object(page_id)
69                .and_then(Object::as_dict)?;
70            if page.has(b"Resources") {
71                page.get(b"Resources").and_then(Object::as_reference).ok()
72            } else {
73                None
74            }
75        };
76        if let Some(res_id) = resources_id {
77            self.opt_clone_object_to_new_document(res_id)?;
78            return self.new_document.get_object_mut(res_id);
79        }
80        let page = self
81            .new_document
82            .get_object_mut(page_id)
83            .and_then(Object::as_dict_mut)?;
84        if !page.has(b"Resources") {
85            page.set(b"Resources", Dictionary::new());
86        }
87        page.get_mut(b"Resources")
88    }
89
90    /// Add XObject to a page.
91    ///
92    /// Get Object that has the key `Resources -> XObject`.
93    pub fn add_xobject<N: Into<Vec<u8>>>(
94        &mut self,
95        page_id: ObjectId,
96        xobject_name: N,
97        xobject_id: ObjectId,
98    ) -> Result<()> {
99        if let Ok(resources) = self
100            .get_or_create_resources(page_id)
101            .and_then(Object::as_dict_mut)
102        {
103            if !resources.has(b"XObject") {
104                resources.set("XObject", Dictionary::new());
105            }
106            let mut xobjects = resources.get_mut(b"XObject")?;
107            if let Object::Reference(xobjects_ref_id) = xobjects {
108                let mut xobjects_id = *xobjects_ref_id;
109                while let Object::Reference(id) = self.new_document.get_object(xobjects_id)? {
110                    xobjects_id = *id;
111                }
112                xobjects = self.new_document.get_object_mut(xobjects_id)?;
113            }
114            let xobjects = Object::as_dict_mut(xobjects)?;
115            xobjects.set(xobject_name, Object::Reference(xobject_id));
116        }
117        Ok(())
118    }
119
120    /// Add Graphics State to a page.
121    ///
122    /// Get Object that has the key `Resources -> ExtGState`.
123    pub fn add_graphics_state<N: Into<Vec<u8>>>(
124        &mut self,
125        page_id: ObjectId,
126        gs_name: N,
127        gs_id: ObjectId,
128    ) -> Result<()> {
129        if let Ok(resources) = self
130            .get_or_create_resources(page_id)
131            .and_then(Object::as_dict_mut)
132        {
133            if !resources.has(b"ExtGState") {
134                resources.set("ExtGState", Dictionary::new());
135            }
136            let states = resources
137                .get_mut(b"ExtGState")
138                .and_then(Object::as_dict_mut)?;
139            states.set(gs_name, Object::Reference(gs_id));
140        }
141        Ok(())
142    }
143}
144
145impl Default for IncrementalDocument {
146    fn default() -> Self {
147        Self::new()
148    }
149}