Skip to main content

pdf_min/
basic.rs

1use crate::*;
2use format_bytes::write_bytes as wb;
3use std::collections::BTreeSet;
4
5/// Low level PDF writing.
6pub struct BasicPdfWriter {
7    /// Output buffer.
8    pub b: Vec<u8>,
9    /// Offset in file of each object.
10    pub xref: Vec<usize>,
11    /// For compressing streams.
12    pub comp: flate3::Compressor,
13    /// Suppresses compression.
14    pub nocomp: bool,
15}
16
17impl Default for BasicPdfWriter {
18    fn default() -> Self {
19        let mut b = Vec::new();
20        b.extend_from_slice(b"%PDF-1.4\n");
21        Self {
22            b,
23            xref: Vec::new(),
24            comp: flate3::Compressor::default(),
25            nocomp: false,
26        }
27    }
28}
29
30impl BasicPdfWriter {
31    /// Allocate PDF object number.
32    pub fn obj(&mut self) -> usize {
33        self.xref.push(0);
34        self.xref.len()
35    }
36
37    /// Start definition of PDF object.
38    pub fn start(&mut self, obj_num: usize) {
39        self.xref[obj_num - 1] = self.b.len();
40        let _ = wb!(&mut self.b, b"{} 0 obj\n", obj_num);
41    }
42
43    /// Allocate PDF object and start definition.
44    pub fn begin(&mut self) -> usize {
45        let obj = self.obj();
46        self.start(obj);
47        obj
48    }
49
50    /// End definition of PDF object.
51    pub fn end(&mut self) {
52        self.b.extend_from_slice(b"\nendobj\n");
53    }
54
55    /// Finish the PDF by writing a list of pages.
56    pub fn finish(&mut self, pages: &[Page], title: &[u8]) {
57        let mut kids = Vec::new();
58        let pagesobj = self.obj();
59        for p in pages {
60            let contentobj = self.stream(&p.os);
61            let pageobj = self.begin();
62            let _ = wb!(&mut kids, b"{} 0 R ", pageobj);
63            let _ = wb!(
64                &mut self.b,
65                b"<</Type/Page/Parent {} 0 R/MediaBox[0 0 {} {}]/Contents {} 0 R/Resources <<",
66                pagesobj,
67                p.width,
68                p.height,
69                contentobj
70            );
71            self.resource_set(&p.fonts, b"/Font", b"/F");
72            self.resource_set(&p.xobjs, b"/XObject", b"/X");
73            self.b.extend_from_slice(b" >> >>");
74            self.end();
75        }
76        self.start(pagesobj);
77        let _ = wb!(
78            &mut self.b,
79            b"<</Type/Pages/Count {}/Kids[{}]>>",
80            pages.len(),
81            kids
82        );
83        self.end();
84
85        let cat = self.begin();
86        let _ = wb!(&mut self.b, b"<</Type/Catalog/Pages {} 0 R>>", pagesobj);
87        self.end();
88
89        let info = self.begin();
90        let _ = wb!(&mut self.b, b"<</Title({})>>", title);
91        self.end();
92
93        let startxref = self.b.len();
94        let xc = self.xref.len() + 1;
95        let _ = wb!(&mut self.b, b"xref\n0 {}\n0000000000 65535 f\n", xc);
96
97        for i in 0..self.xref.len() {
98            let x = decimal(self.xref[i], 10);
99            let _ = wb!(&mut self.b, b"{} 00000 n\n", x);
100        }
101
102        let _ = wb!(
103            &mut self.b,
104            b"trailer\n<</Size {}/Root {} 0 R/Info {} 0 R>>",
105            xc,
106            cat,
107            info
108        );
109        let _ = wb!(&mut self.b, b"\nstartxref\n{}\n%%EOF\n", startxref);
110    }
111
112    fn resource_set(&mut self, s: &BTreeSet<usize>, n1: &[u8], n2: &[u8]) {
113        if !s.is_empty() {
114            let _ = wb!(&mut self.b, b"{}<<", n1);
115            for i in s {
116                let _ = wb!(&mut self.b, b"{}{} {} 0 R", n2, i, i);
117            }
118            self.b.extend_from_slice(b">>");
119        }
120    }
121
122    /// Output a stream (possibly compressed ), result is obj number.
123    pub fn stream(&mut self, data: &[u8]) -> usize {
124        let obj = self.begin();
125        if self.nocomp {
126            let _ = wb!(&mut self.b, b"<</Length {}>>stream\n", data.len());
127            self.b.extend_from_slice(data);
128        } else {
129            let cb: Vec<u8> = self.comp.deflate(data);
130            let _ = wb!(
131                &mut self.b,
132                b"<</Filter/FlateDecode/Length {}>>stream\n",
133                cb.len()
134            );
135            self.b.extend_from_slice(&cb);
136        }
137        self.b.extend_from_slice(b"\nendstream");
138        self.end();
139        obj
140    }
141}
142
143/// Format x as decimal padded to length n with zeros.
144fn decimal(mut x: usize, mut n: usize) -> Vec<u8> {
145    let mut result = vec![b'0'; n];
146    while x != 0 {
147        n -= 1;
148        result[n] = b'0' + (x % 10) as u8;
149        x /= 10;
150    }
151    result
152}