Skip to main content

pdf_lib_rs/core/document/
pdf_trailer.rs

1use std::fmt;
2use crate::core::objects::pdf_object::PdfObjectTrait;
3use crate::core::syntax::CharCodes;
4use crate::utils::copy_string_into_buffer;
5
6/// PDF trailer — `startxref` offset and `%%EOF` marker.
7#[derive(Debug, Clone)]
8pub struct PdfTrailer {
9    last_xref_offset: String,
10}
11
12impl PdfTrailer {
13    pub fn for_last_cross_ref_section_offset(offset: u64) -> Self {
14        PdfTrailer {
15            last_xref_offset: offset.to_string(),
16        }
17    }
18}
19
20impl fmt::Display for PdfTrailer {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(f, "startxref\n{}\n%%EOF", self.last_xref_offset)
23    }
24}
25
26impl PdfObjectTrait for PdfTrailer {
27    fn size_in_bytes(&self) -> usize {
28        // "startxref\n" (10) + offset + "\n%%EOF" (6) = 16 + offset.len
29        16 + self.last_xref_offset.len()
30    }
31
32    fn copy_bytes_into(&self, buffer: &mut [u8], offset: usize) -> usize {
33        let initial_offset = offset;
34        let mut off = offset;
35
36        // "startxref\n"
37        for &b in b"startxref" {
38            buffer[off] = b;
39            off += 1;
40        }
41        buffer[off] = CharCodes::Newline;
42        off += 1;
43
44        off += copy_string_into_buffer(&self.last_xref_offset, buffer, off);
45
46        // "\n%%EOF"
47        buffer[off] = CharCodes::Newline;
48        off += 1;
49        buffer[off] = CharCodes::Percent;
50        off += 1;
51        buffer[off] = CharCodes::Percent;
52        off += 1;
53        buffer[off] = CharCodes::UpperE;
54        off += 1;
55        buffer[off] = CharCodes::UpperO;
56        off += 1;
57        buffer[off] = CharCodes::UpperF;
58        off += 1;
59
60        off - initial_offset
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use crate::utils::typed_array_for;
68
69    #[test]
70    fn can_be_converted_to_string() {
71        assert_eq!(
72            PdfTrailer::for_last_cross_ref_section_offset(799).to_string(),
73            "startxref\n799\n%%EOF"
74        );
75    }
76
77    #[test]
78    fn can_provide_size_in_bytes() {
79        assert_eq!(
80            PdfTrailer::for_last_cross_ref_section_offset(1919).size_in_bytes(),
81            20
82        );
83    }
84
85    #[test]
86    fn can_be_serialized() {
87        let mut buffer = vec![b' '; 21];
88        let trailer = PdfTrailer::for_last_cross_ref_section_offset(1);
89        trailer.copy_bytes_into(&mut buffer, 3);
90        assert_eq!(buffer, typed_array_for("   startxref\n1\n%%EOF "));
91    }
92}