1use crate::*;
2use format_bytes::write_bytes as wb;
3use std::collections::BTreeSet;
4
5pub struct BasicPdfWriter {
7 pub b: Vec<u8>,
9 pub xref: Vec<usize>,
11 pub comp: flate3::Compressor,
13 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 pub fn obj(&mut self) -> usize {
33 self.xref.push(0);
34 self.xref.len()
35 }
36
37 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 pub fn begin(&mut self) -> usize {
45 let obj = self.obj();
46 self.start(obj);
47 obj
48 }
49
50 pub fn end(&mut self) {
52 self.b.extend_from_slice(b"\nendobj\n");
53 }
54
55 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 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
143fn 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}