Skip to main content

justpdf_core/writer/
mod.rs

1pub mod clean;
2pub mod compress;
3pub mod document;
4pub mod encode;
5pub mod linearize;
6pub mod modify;
7pub mod object_stream;
8pub mod page;
9pub mod serialize;
10
11pub use clean::{clean_objects, CleanStats};
12pub use compress::{compress_pdf, analyze_pdf, CompressOptions, CompressStats, AnalyzeResult};
13pub use document::{DocumentBuilder, embed_jpeg, embed_png};
14pub use encode::{encode_flate, make_stream};
15pub use linearize::linearize as linearize_pdf;
16pub use modify::{merge_documents, incremental_save, DocumentModifier};
17pub use object_stream::pack_object_streams;
18pub use page::PageBuilder;
19pub use serialize::{serialize_pdf, serialize_pdf_encrypted};
20
21use crate::error::Result;
22use crate::object::{IndirectRef, PdfObject};
23
24use std::path::Path;
25
26/// Low-level PDF writer that accumulates indirect objects and serializes them
27/// into a valid PDF byte stream.
28pub struct PdfWriter {
29    /// Stored indirect objects: (object number, object).
30    pub objects: Vec<(u32, PdfObject)>,
31    /// Next available object number.
32    pub(crate) next_obj_num: u32,
33    /// PDF version as (major, minor), e.g. (1, 7).
34    pub version: (u8, u8),
35}
36
37impl PdfWriter {
38    /// Create a new writer with default PDF 1.7 version.
39    pub fn new() -> Self {
40        Self {
41            objects: Vec::new(),
42            next_obj_num: 1,
43            version: (1, 7),
44        }
45    }
46
47    /// Allocate the next object number without adding an object.
48    /// Useful for forward references.
49    pub fn alloc_object_num(&mut self) -> u32 {
50        let num = self.next_obj_num;
51        self.next_obj_num += 1;
52        num
53    }
54
55    /// Add an object, assigning it the next available object number.
56    /// Returns an `IndirectRef` that can be used to reference this object.
57    pub fn add_object(&mut self, obj: PdfObject) -> IndirectRef {
58        let num = self.alloc_object_num();
59        self.objects.push((num, obj));
60        IndirectRef {
61            obj_num: num,
62            gen_num: 0,
63        }
64    }
65
66    /// Set (or replace) an object at a specific object number.
67    /// If an object with this number already exists, it is replaced.
68    pub fn set_object(&mut self, obj_num: u32, obj: PdfObject) {
69        if let Some(entry) = self.objects.iter_mut().find(|(n, _)| *n == obj_num) {
70            entry.1 = obj;
71        } else {
72            self.objects.push((obj_num, obj));
73        }
74    }
75
76    /// Serialize all objects into a complete PDF byte stream.
77    pub fn write_to_bytes(&self, catalog_ref: &IndirectRef) -> Result<Vec<u8>> {
78        serialize_pdf(&self.objects, self.version, catalog_ref, None)
79    }
80
81    /// Serialize and write to a file.
82    pub fn write_to_file(&self, path: &Path, catalog_ref: &IndirectRef) -> Result<()> {
83        let bytes = self.write_to_bytes(catalog_ref)?;
84        std::fs::write(path, bytes)?;
85        Ok(())
86    }
87}
88
89impl Default for PdfWriter {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::object::PdfObject;
99
100    #[test]
101    fn test_add_object_increments_refs() {
102        let mut writer = PdfWriter::new();
103
104        let ref1 = writer.add_object(PdfObject::Integer(1));
105        assert_eq!(ref1.obj_num, 1);
106        assert_eq!(ref1.gen_num, 0);
107
108        let ref2 = writer.add_object(PdfObject::Integer(2));
109        assert_eq!(ref2.obj_num, 2);
110        assert_eq!(ref2.gen_num, 0);
111
112        let ref3 = writer.add_object(PdfObject::Integer(3));
113        assert_eq!(ref3.obj_num, 3);
114        assert_eq!(ref3.gen_num, 0);
115
116        assert_eq!(writer.objects.len(), 3);
117    }
118
119    #[test]
120    fn test_alloc_object_num() {
121        let mut writer = PdfWriter::new();
122        let num = writer.alloc_object_num();
123        assert_eq!(num, 1);
124
125        let ref1 = writer.add_object(PdfObject::Null);
126        assert_eq!(ref1.obj_num, 2);
127    }
128
129    #[test]
130    fn test_set_object_replaces() {
131        let mut writer = PdfWriter::new();
132        let r = writer.add_object(PdfObject::Integer(10));
133        writer.set_object(r.obj_num, PdfObject::Integer(20));
134
135        assert_eq!(writer.objects.len(), 1);
136        assert_eq!(writer.objects[0].1, PdfObject::Integer(20));
137    }
138
139    #[test]
140    fn test_set_object_new() {
141        let mut writer = PdfWriter::new();
142        writer.set_object(5, PdfObject::Integer(50));
143        assert_eq!(writer.objects.len(), 1);
144        assert_eq!(writer.objects[0].0, 5);
145    }
146}