Skip to main content

pdf_lib_rs/core/objects/
pdf_raw_stream.rs

1use std::fmt;
2use crate::core::syntax::CharCodes;
3use crate::utils::array_as_string;
4use super::pdf_dict::PdfDict;
5use super::pdf_name::PdfName;
6use super::pdf_number::PdfNumber;
7use super::pdf_object::{PdfObject, PdfObjectTrait};
8
9/// A PDF stream with raw byte contents.
10#[derive(Debug, Clone)]
11pub struct PdfRawStream {
12    pub dict: PdfDict,
13    pub contents: Vec<u8>,
14}
15
16impl PdfRawStream {
17    pub fn of(dict: PdfDict, contents: Vec<u8>) -> Self {
18        PdfRawStream { dict, contents }
19    }
20
21    pub fn as_uint8_array(&self) -> Vec<u8> {
22        self.contents.clone()
23    }
24
25    pub fn get_contents_string(&self) -> String {
26        array_as_string(&self.contents)
27    }
28
29    #[allow(dead_code)]
30    fn update_dict(&mut self) {
31        self.dict.set(
32            PdfName::length(),
33            PdfObject::Number(PdfNumber::of(self.contents.len() as f64)),
34        );
35    }
36}
37
38impl PartialEq for PdfRawStream {
39    fn eq(&self, _other: &Self) -> bool {
40        false
41    }
42}
43
44impl fmt::Display for PdfRawStream {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        write!(
47            f,
48            "{}\nstream\n{}\nendstream",
49            self.dict,
50            self.get_contents_string()
51        )
52    }
53}
54
55impl PdfObjectTrait for PdfRawStream {
56    fn size_in_bytes(&self) -> usize {
57        // dict + "\nstream\n" + contents + "\nendstream"
58        self.dict.size_in_bytes() + self.contents.len() + 18
59    }
60
61    fn copy_bytes_into(&self, buffer: &mut [u8], offset: usize) -> usize {
62        let initial_offset = offset;
63        let mut off = offset;
64
65        // Write dict
66        off += self.dict.copy_bytes_into(buffer, off);
67
68        // \nstream\n
69        buffer[off] = CharCodes::Newline;
70        off += 1;
71        for &b in b"stream" {
72            buffer[off] = b;
73            off += 1;
74        }
75        buffer[off] = CharCodes::Newline;
76        off += 1;
77
78        // Contents
79        buffer[off..off + self.contents.len()].copy_from_slice(&self.contents);
80        off += self.contents.len();
81
82        // \nendstream
83        buffer[off] = CharCodes::Newline;
84        off += 1;
85        for &b in b"endstream" {
86            buffer[off] = b;
87            off += 1;
88        }
89
90        off - initial_offset
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn can_be_constructed() {
100        let dict = PdfDict::new();
101        let contents = vec![1, 2, 3, 4, 5];
102        let stream = PdfRawStream::of(dict, contents.clone());
103        assert_eq!(stream.contents, contents);
104    }
105
106    #[test]
107    fn can_get_contents() {
108        let dict = PdfDict::new();
109        let stream = PdfRawStream::of(dict, b"hello".to_vec());
110        assert_eq!(stream.get_contents_string(), "hello");
111        assert_eq!(stream.as_uint8_array(), b"hello".to_vec());
112    }
113
114    #[test]
115    fn can_be_serialized() {
116        let dict = PdfDict::new();
117        let stream = PdfRawStream::of(dict, b"data".to_vec());
118        let size = stream.size_in_bytes();
119        let mut buffer = vec![0u8; size];
120        stream.copy_bytes_into(&mut buffer, 0);
121        let result = String::from_utf8(buffer).unwrap();
122        assert!(result.contains("stream\n"));
123        assert!(result.contains("data"));
124        assert!(result.contains("endstream"));
125    }
126}