oxidize_pdf/objects/
stream.rs

1#[cfg(feature = "compression")]
2use crate::error::{PdfError, Result};
3use crate::objects::Dictionary;
4
5#[derive(Debug, Clone)]
6pub struct Stream {
7    dictionary: Dictionary,
8    data: Vec<u8>,
9}
10
11impl Stream {
12    pub fn new(data: Vec<u8>) -> Self {
13        let mut dictionary = Dictionary::new();
14        dictionary.set("Length", data.len() as i64);
15
16        Self { dictionary, data }
17    }
18
19    pub fn with_dictionary(dictionary: Dictionary, data: Vec<u8>) -> Self {
20        let mut dict = dictionary;
21        dict.set("Length", data.len() as i64);
22
23        Self {
24            dictionary: dict,
25            data,
26        }
27    }
28
29    pub fn dictionary(&self) -> &Dictionary {
30        &self.dictionary
31    }
32
33    pub fn dictionary_mut(&mut self) -> &mut Dictionary {
34        &mut self.dictionary
35    }
36
37    pub fn data(&self) -> &[u8] {
38        &self.data
39    }
40
41    pub fn data_mut(&mut self) -> &mut Vec<u8> {
42        &mut self.data
43    }
44
45    pub fn set_filter(&mut self, filter: &str) {
46        self.dictionary
47            .set("Filter", crate::objects::Object::Name(filter.to_string()));
48    }
49
50    pub fn set_decode_params(&mut self, params: Dictionary) {
51        self.dictionary.set("DecodeParms", params);
52    }
53
54    #[cfg(feature = "compression")]
55    pub fn compress_flate(&mut self) -> Result<()> {
56        use flate2::write::ZlibEncoder;
57        use flate2::Compression;
58        use std::io::Write;
59
60        let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
61        encoder
62            .write_all(&self.data)
63            .map_err(|e| PdfError::CompressionError(e.to_string()))?;
64        let compressed = encoder
65            .finish()
66            .map_err(|e| PdfError::CompressionError(e.to_string()))?;
67
68        self.data = compressed;
69        self.dictionary.set("Length", self.data.len() as i64);
70        self.set_filter("FlateDecode");
71
72        Ok(())
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::objects::Object;
80
81    #[test]
82    fn test_stream_new() {
83        let data = vec![1, 2, 3, 4, 5];
84        let stream = Stream::new(data.clone());
85
86        assert_eq!(stream.data(), &data);
87        assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(5)));
88    }
89
90    #[test]
91    fn test_stream_with_dictionary() {
92        let mut dict = Dictionary::new();
93        dict.set("Type", "XObject");
94        dict.set("Subtype", "Image");
95
96        let data = vec![0xFF, 0xD8, 0xFF];
97        let stream = Stream::with_dictionary(dict, data.clone());
98
99        assert_eq!(stream.data(), &data);
100        assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(3)));
101        assert_eq!(
102            stream.dictionary().get("Type"),
103            Some(&Object::String("XObject".to_string()))
104        );
105        assert_eq!(
106            stream.dictionary().get("Subtype"),
107            Some(&Object::String("Image".to_string()))
108        );
109    }
110
111    #[test]
112    fn test_stream_accessors() {
113        let data = vec![10, 20, 30];
114        let stream = Stream::new(data);
115
116        // Test dictionary accessor
117        let dict = stream.dictionary();
118        assert_eq!(dict.get("Length"), Some(&Object::Integer(3)));
119
120        // Test data accessor
121        assert_eq!(stream.data(), &[10, 20, 30]);
122    }
123
124    #[test]
125    fn test_stream_mutators() {
126        let data = vec![1, 2, 3];
127        let mut stream = Stream::new(data);
128
129        // Test dictionary_mut
130        stream.dictionary_mut().set("CustomKey", "CustomValue");
131        assert_eq!(
132            stream.dictionary().get("CustomKey"),
133            Some(&Object::String("CustomValue".to_string()))
134        );
135
136        // Test data_mut
137        stream.data_mut().push(4);
138        assert_eq!(stream.data(), &[1, 2, 3, 4]);
139
140        // Note: Length is not automatically updated when using data_mut
141        assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(3)));
142    }
143
144    #[test]
145    fn test_set_filter() {
146        let mut stream = Stream::new(vec![1, 2, 3]);
147
148        stream.set_filter("FlateDecode");
149        assert_eq!(
150            stream.dictionary().get("Filter"),
151            Some(&Object::Name("FlateDecode".to_string()))
152        );
153
154        stream.set_filter("ASCII85Decode");
155        assert_eq!(
156            stream.dictionary().get("Filter"),
157            Some(&Object::Name("ASCII85Decode".to_string()))
158        );
159    }
160
161    #[test]
162    fn test_set_decode_params() {
163        let mut stream = Stream::new(vec![1, 2, 3]);
164
165        let mut params = Dictionary::new();
166        params.set("Predictor", 12);
167        params.set("Colors", 1);
168        params.set("BitsPerComponent", 8);
169        params.set("Columns", 100);
170
171        stream.set_decode_params(params.clone());
172
173        if let Some(Object::Dictionary(decode_params)) = stream.dictionary().get("DecodeParms") {
174            assert_eq!(decode_params.get("Predictor"), Some(&Object::Integer(12)));
175            assert_eq!(decode_params.get("Colors"), Some(&Object::Integer(1)));
176            assert_eq!(
177                decode_params.get("BitsPerComponent"),
178                Some(&Object::Integer(8))
179            );
180            assert_eq!(decode_params.get("Columns"), Some(&Object::Integer(100)));
181        } else {
182            panic!("Expected DecodeParms to be a Dictionary");
183        }
184    }
185
186    #[test]
187    fn test_empty_stream() {
188        let stream = Stream::new(vec![]);
189
190        assert_eq!(stream.data(), &[] as &[u8]);
191        assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(0)));
192    }
193
194    #[test]
195    fn test_large_stream() {
196        let data: Vec<u8> = (0..1000).map(|i| (i % 256) as u8).collect();
197        let stream = Stream::new(data);
198
199        assert_eq!(stream.data().len(), 1000);
200        assert_eq!(
201            stream.dictionary().get("Length"),
202            Some(&Object::Integer(1000))
203        );
204    }
205
206    #[test]
207    #[cfg(feature = "compression")]
208    fn test_compress_flate() {
209        // Use a longer, more repetitive string that will compress well
210        let original_data = "Hello, this is a test string that should be compressed! "
211            .repeat(10)
212            .into_bytes();
213        let mut stream = Stream::new(original_data.clone());
214
215        // Compress the stream
216        let result = stream.compress_flate();
217        assert!(result.is_ok());
218
219        // Check that the stream has been modified
220        assert_ne!(stream.data(), &original_data);
221
222        // For very small data, compression might not always reduce size due to headers
223        // So we just check that it's different
224
225        // Check that the filter has been set
226        assert_eq!(
227            stream.dictionary().get("Filter"),
228            Some(&Object::Name("FlateDecode".to_string()))
229        );
230
231        // Check that the length has been updated
232        assert_eq!(
233            stream.dictionary().get("Length"),
234            Some(&Object::Integer(stream.data().len() as i64))
235        );
236    }
237
238    #[test]
239    #[cfg(feature = "compression")]
240    fn test_compress_flate_empty() {
241        let mut stream = Stream::new(vec![]);
242
243        let result = stream.compress_flate();
244        assert!(result.is_ok());
245
246        // Even empty data should have some compressed bytes (header/trailer)
247        assert!(!stream.data().is_empty());
248
249        assert_eq!(
250            stream.dictionary().get("Filter"),
251            Some(&Object::Name("FlateDecode".to_string()))
252        );
253    }
254
255    #[test]
256    fn test_stream_with_existing_length() {
257        // Test that Length is properly updated even if dictionary already has it
258        let mut dict = Dictionary::new();
259        dict.set("Length", 999); // Wrong length
260        dict.set("Type", "XObject");
261
262        let data = vec![1, 2, 3, 4, 5];
263        let stream = Stream::with_dictionary(dict, data);
264
265        // Length should be corrected to actual data length
266        assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(5)));
267    }
268}