1use std::collections::BTreeMap;
2use std::io::{Seek, Write};
3
4use crate::error::PdfResult;
5use crate::object::*;
6use crate::parser::Document;
7
8pub struct Serializer {
9 deterministic: bool,
10}
11
12impl Serializer {
13 pub fn new() -> Self {
14 Self {
15 deterministic: false,
16 }
17 }
18
19 pub fn with_deterministic(deterministic: bool) -> Self {
22 Self { deterministic }
23 }
24
25 pub fn serialize(&self, doc: &Document, writer: &mut (impl Write + Seek)) -> PdfResult<()> {
26 writeln!(writer, "%PDF-1.7")?;
27 writer.write_all(b"%\xe2\xcf\xd3\xe2\n")?;
28
29 let mut offsets: BTreeMap<u32, u64> = BTreeMap::new();
30 for (id, obj) in doc.objects() {
31 offsets.insert(id.number, writer.stream_position()?);
32 writeln!(writer, "{} {} obj", id.number, id.generation)?;
33 self.serialize_object(obj, writer)?;
34 writeln!(writer)?;
38 writeln!(writer, "endobj")?;
39 writeln!(writer)?;
40 }
41
42 let xref_offset = writer.stream_position()?;
43 let max_number = doc.objects().keys().map(|id| id.number).max().unwrap_or(0);
44 let size = max_number + 1;
45
46 writeln!(writer, "xref")?;
47 writeln!(writer, "0 {}", size)?;
48 for n in 0..=max_number {
49 if let Some(offset) = offsets.get(&n) {
53 writeln!(writer, "{:010} 00000 n ", offset)?;
54 } else {
55 writeln!(writer, "0000000000 65535 f ")?;
56 }
57 }
58
59 writeln!(writer, "trailer")?;
60 write!(
61 writer,
62 "<< /Size {} /Root {}",
63 size,
64 doc.catalog().unwrap_or(ObjectId::new(1, 0))
65 )?;
66 if let Some(info) = doc.info() {
67 write!(writer, " /Info {}", info)?;
68 }
69 writeln!(writer, " >>")?;
70 writeln!(writer, "startxref")?;
71 writeln!(writer, "{}", xref_offset)?;
72 writeln!(writer, "%%EOF")?;
73 Ok(())
74 }
75
76 fn serialize_object(&self, obj: &PdfObject, writer: &mut impl Write) -> PdfResult<()> {
77 match obj {
78 PdfObject::Null => write!(writer, "null")?,
79 PdfObject::Boolean(b) => write!(writer, "{}", b)?,
80 PdfObject::Integer(i) => write!(writer, "{}", i)?,
81 PdfObject::Real(r) => {
82 if r.is_finite() && r.fract() == 0.0 {
88 write!(writer, "{r:.1}")?;
89 } else {
90 write!(writer, "{r}")?;
91 }
92 }
93 PdfObject::Name(n) => write!(writer, "{}", n)?,
94 PdfObject::String(s) => write!(writer, "{}", s)?,
95 PdfObject::Array(a) => {
96 write!(writer, "[")?;
97 for (i, item) in a.0.iter().enumerate() {
98 if i > 0 {
99 write!(writer, " ")?;
100 }
101 self.serialize_object(item, writer)?;
102 }
103 write!(writer, "]")?;
104 }
105 PdfObject::Dictionary(d) => {
106 writeln!(writer, "<<")?;
107 if self.deterministic {
108 let mut entries: Vec<(&PdfName, &PdfObject)> = d.iter().collect();
110 entries.sort_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str()));
111 for (key, value) in entries {
112 write!(writer, "{} ", key)?;
113 self.serialize_object(value, writer)?;
114 writeln!(writer)?;
115 }
116 } else {
117 for (key, value) in d.iter() {
118 write!(writer, "{} ", key)?;
119 self.serialize_object(value, writer)?;
120 writeln!(writer)?;
121 }
122 }
123 write!(writer, ">>")?;
124 }
125 PdfObject::Stream(s) => {
126 let mut dict = s.dictionary.clone();
130 dict.insert("Length", PdfObject::Integer(s.data.len() as i64));
131 self.serialize_object(&PdfObject::Dictionary(dict), writer)?;
132 writeln!(writer)?;
133 writeln!(writer, "stream")?;
134 writer.write_all(&s.data)?;
135 writeln!(writer)?;
136 write!(writer, "endstream")?;
137 }
138 PdfObject::Reference(id) => write!(writer, "{}", id)?,
139 }
140 Ok(())
141 }
142}
143
144impl Default for Serializer {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use std::io::Cursor;
153
154 use super::*;
155 use crate::{ObjectId, PdfDictionary, PdfDocument, PdfName, PdfObject};
156
157 #[test]
158 fn xref_entries_are_exactly_20_bytes() {
159 let mut doc = PdfDocument::new();
163 doc.set_catalog(ObjectId::new(1, 0));
164 let mut catalog = PdfDictionary::new();
165 catalog.insert("Type", PdfObject::Name(PdfName::new("Catalog")));
166 doc.add_object(ObjectId::new(1, 0), PdfObject::Dictionary(catalog));
167
168 let mut buf = Cursor::new(Vec::new());
169 Serializer::new()
170 .serialize(&doc, &mut buf)
171 .expect("serialize");
172 let bytes = buf.into_inner();
173
174 let start = bytes
176 .windows(b"startxref".len())
177 .position(|w| w == b"startxref")
178 .expect("startxref present");
179 let xref = &bytes[..start];
180
181 let mut entry_count = 0;
182 for line in xref.split(|&b| b == b'\n') {
183 let is_entry = line.len() == 19
186 && line[..10].iter().all(u8::is_ascii_digit)
187 && (line.ends_with(b"n ") || line.ends_with(b"f "));
188 if is_entry {
189 entry_count += 1;
190 }
191 }
192 assert!(
193 entry_count >= 2,
194 "expected at least two 20-byte xref entries, found {entry_count}"
195 );
196 }
197}