Skip to main content

pdf_objects/
serializer.rs

1use std::fmt::Write;
2
3use crate::types::{PdfDictionary, PdfFile, PdfObject, PdfString, PdfValue};
4
5pub fn serialize_pdf(file: &PdfFile) -> Vec<u8> {
6    let mut output = Vec::new();
7    output.extend_from_slice(
8        format!("%PDF-{}\n%\u{00FF}\u{00FF}\u{00FF}\u{00FF}\n", file.version).as_bytes(),
9    );
10
11    let mut offsets = std::collections::BTreeMap::new();
12    for (object_ref, object) in &file.objects {
13        let offset = output.len();
14        offsets.insert(object_ref.object_number, offset);
15        output.extend_from_slice(
16            format!(
17                "{} {} obj\n",
18                object_ref.object_number, object_ref.generation
19            )
20            .as_bytes(),
21        );
22        match object {
23            PdfObject::Value(value) => {
24                output.extend_from_slice(serialize_value(value).as_bytes());
25                output.extend_from_slice(b"\nendobj\n");
26            }
27            PdfObject::Stream(stream) => {
28                let mut dict = stream.dict.clone();
29                dict.insert(
30                    "Length".to_string(),
31                    PdfValue::Integer(stream.data.len() as i64),
32                );
33                output.extend_from_slice(serialize_dictionary(&dict).as_bytes());
34                output.extend_from_slice(b"\nstream\n");
35                output.extend_from_slice(&stream.data);
36                if !stream.data.ends_with(b"\n") {
37                    output.push(b'\n');
38                }
39                output.extend_from_slice(b"endstream\nendobj\n");
40            }
41        }
42    }
43
44    let startxref = output.len();
45    let size = file.max_object_number + 1;
46    output.extend_from_slice(format!("xref\n0 {}\n", size).as_bytes());
47    output.extend_from_slice(b"0000000000 65535 f \n");
48    for object_number in 1..=file.max_object_number {
49        if let Some(offset) = offsets.get(&object_number).copied() {
50            output.extend_from_slice(format!("{offset:010} 00000 n \n").as_bytes());
51        } else {
52            output.extend_from_slice(b"0000000000 65535 f \n");
53        }
54    }
55
56    let mut trailer = file.trailer.clone();
57    trailer.insert("Size".to_string(), PdfValue::Integer(size as i64));
58    trailer.remove("Prev");
59    trailer.remove("XRefStm");
60    output.extend_from_slice(b"trailer\n");
61    output.extend_from_slice(serialize_dictionary(&trailer).as_bytes());
62    output.extend_from_slice(format!("\nstartxref\n{startxref}\n%%EOF\n").as_bytes());
63    output
64}
65
66pub fn serialize_value(value: &PdfValue) -> String {
67    match value {
68        PdfValue::Null => "null".to_string(),
69        PdfValue::Bool(value) => value.to_string(),
70        PdfValue::Integer(value) => value.to_string(),
71        PdfValue::Number(value) => {
72            if value.fract() == 0.0 {
73                format!("{:.0}", value)
74            } else {
75                let mut number = format!("{value:.6}");
76                while number.contains('.') && number.ends_with('0') {
77                    number.pop();
78                }
79                if number.ends_with('.') {
80                    number.pop();
81                }
82                number
83            }
84        }
85        PdfValue::Name(name) => {
86            let mut encoded = String::from("/");
87            for byte in name.bytes() {
88                if byte == b'#'
89                    || byte <= b' '
90                    || byte >= 0x7F
91                    || matches!(
92                        byte,
93                        b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
94                    )
95                {
96                    encoded.push_str(&format!("#{:02X}", byte));
97                } else {
98                    encoded.push(byte as char);
99                }
100            }
101            encoded
102        }
103        PdfValue::String(string) => serialize_string(string),
104        PdfValue::Array(values) => format!(
105            "[{}]",
106            values
107                .iter()
108                .map(serialize_value)
109                .collect::<Vec<_>>()
110                .join(" ")
111        ),
112        PdfValue::Dictionary(dictionary) => serialize_dictionary(dictionary),
113        PdfValue::Reference(object_ref) => {
114            format!("{} {} R", object_ref.object_number, object_ref.generation)
115        }
116    }
117}
118
119pub fn serialize_dictionary(dictionary: &PdfDictionary) -> String {
120    let mut output = String::from("<<");
121    for (key, value) in dictionary {
122        write!(output, "/{} {}", key, serialize_value(value))
123            .expect("string writes should succeed");
124        output.push(' ');
125    }
126    output.push_str(">>");
127    output
128}
129
130pub fn serialize_string(string: &PdfString) -> String {
131    let mut output = String::from("(");
132    for byte in &string.0 {
133        match byte {
134            b'(' | b')' | b'\\' => {
135                output.push('\\');
136                output.push(*byte as char);
137            }
138            b'\n' => output.push_str("\\n"),
139            b'\r' => output.push_str("\\r"),
140            b'\t' => output.push_str("\\t"),
141            0x08 => output.push_str("\\b"),
142            0x0C => output.push_str("\\f"),
143            byte if byte.is_ascii_graphic() || *byte == b' ' => output.push(*byte as char),
144            other => output.push_str(&format!("\\{:03o}", other)),
145        }
146    }
147    output.push(')');
148    output
149}