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!(
88            stream.dictionary().get("Length"),
89            Some(&Object::Integer(5))
90        );
91    }
92
93    #[test]
94    fn test_stream_with_dictionary() {
95        let mut dict = Dictionary::new();
96        dict.set("Type", "XObject");
97        dict.set("Subtype", "Image");
98        
99        let data = vec![0xFF, 0xD8, 0xFF];
100        let stream = Stream::with_dictionary(dict, data.clone());
101        
102        assert_eq!(stream.data(), &data);
103        assert_eq!(
104            stream.dictionary().get("Length"),
105            Some(&Object::Integer(3))
106        );
107        assert_eq!(
108            stream.dictionary().get("Type"),
109            Some(&Object::String("XObject".to_string()))
110        );
111        assert_eq!(
112            stream.dictionary().get("Subtype"),
113            Some(&Object::String("Image".to_string()))
114        );
115    }
116
117    #[test]
118    fn test_stream_accessors() {
119        let data = vec![10, 20, 30];
120        let stream = Stream::new(data);
121        
122        // Test dictionary accessor
123        let dict = stream.dictionary();
124        assert_eq!(dict.get("Length"), Some(&Object::Integer(3)));
125        
126        // Test data accessor
127        assert_eq!(stream.data(), &[10, 20, 30]);
128    }
129
130    #[test]
131    fn test_stream_mutators() {
132        let data = vec![1, 2, 3];
133        let mut stream = Stream::new(data);
134        
135        // Test dictionary_mut
136        stream.dictionary_mut().set("CustomKey", "CustomValue");
137        assert_eq!(
138            stream.dictionary().get("CustomKey"),
139            Some(&Object::String("CustomValue".to_string()))
140        );
141        
142        // Test data_mut
143        stream.data_mut().push(4);
144        assert_eq!(stream.data(), &[1, 2, 3, 4]);
145        
146        // Note: Length is not automatically updated when using data_mut
147        assert_eq!(
148            stream.dictionary().get("Length"),
149            Some(&Object::Integer(3))
150        );
151    }
152
153    #[test]
154    fn test_set_filter() {
155        let mut stream = Stream::new(vec![1, 2, 3]);
156        
157        stream.set_filter("FlateDecode");
158        assert_eq!(
159            stream.dictionary().get("Filter"),
160            Some(&Object::Name("FlateDecode".to_string()))
161        );
162        
163        stream.set_filter("ASCII85Decode");
164        assert_eq!(
165            stream.dictionary().get("Filter"),
166            Some(&Object::Name("ASCII85Decode".to_string()))
167        );
168    }
169
170    #[test]
171    fn test_set_decode_params() {
172        let mut stream = Stream::new(vec![1, 2, 3]);
173        
174        let mut params = Dictionary::new();
175        params.set("Predictor", 12);
176        params.set("Colors", 1);
177        params.set("BitsPerComponent", 8);
178        params.set("Columns", 100);
179        
180        stream.set_decode_params(params.clone());
181        
182        if let Some(Object::Dictionary(decode_params)) = stream.dictionary().get("DecodeParms") {
183            assert_eq!(decode_params.get("Predictor"), Some(&Object::Integer(12)));
184            assert_eq!(decode_params.get("Colors"), Some(&Object::Integer(1)));
185            assert_eq!(decode_params.get("BitsPerComponent"), Some(&Object::Integer(8)));
186            assert_eq!(decode_params.get("Columns"), Some(&Object::Integer(100)));
187        } else {
188            panic!("Expected DecodeParms to be a Dictionary");
189        }
190    }
191
192    #[test]
193    fn test_empty_stream() {
194        let stream = Stream::new(vec![]);
195        
196        assert_eq!(stream.data(), &[] as &[u8]);
197        assert_eq!(
198            stream.dictionary().get("Length"),
199            Some(&Object::Integer(0))
200        );
201    }
202
203    #[test]
204    fn test_large_stream() {
205        let data: Vec<u8> = (0..1000).map(|i| (i % 256) as u8).collect();
206        let stream = Stream::new(data.clone());
207        
208        assert_eq!(stream.data().len(), 1000);
209        assert_eq!(
210            stream.dictionary().get("Length"),
211            Some(&Object::Integer(1000))
212        );
213    }
214
215    #[test]
216    #[cfg(feature = "compression")]
217    fn test_compress_flate() {
218        // Use a longer, more repetitive string that will compress well
219        let original_data = "Hello, this is a test string that should be compressed! ".repeat(10).into_bytes();
220        let mut stream = Stream::new(original_data.clone());
221        
222        // Compress the stream
223        let result = stream.compress_flate();
224        assert!(result.is_ok());
225        
226        // Check that the stream has been modified
227        assert_ne!(stream.data(), &original_data);
228        
229        // For very small data, compression might not always reduce size due to headers
230        // So we just check that it's different
231        
232        // Check that the filter has been set
233        assert_eq!(
234            stream.dictionary().get("Filter"),
235            Some(&Object::Name("FlateDecode".to_string()))
236        );
237        
238        // Check that the length has been updated
239        assert_eq!(
240            stream.dictionary().get("Length"),
241            Some(&Object::Integer(stream.data().len() as i64))
242        );
243    }
244
245    #[test]
246    #[cfg(feature = "compression")]
247    fn test_compress_flate_empty() {
248        let mut stream = Stream::new(vec![]);
249        
250        let result = stream.compress_flate();
251        assert!(result.is_ok());
252        
253        // Even empty data should have some compressed bytes (header/trailer)
254        assert!(!stream.data().is_empty());
255        
256        assert_eq!(
257            stream.dictionary().get("Filter"),
258            Some(&Object::Name("FlateDecode".to_string()))
259        );
260    }
261
262    #[test]
263    fn test_stream_with_existing_length() {
264        // Test that Length is properly updated even if dictionary already has it
265        let mut dict = Dictionary::new();
266        dict.set("Length", 999); // Wrong length
267        dict.set("Type", "XObject");
268        
269        let data = vec![1, 2, 3, 4, 5];
270        let stream = Stream::with_dictionary(dict, data);
271        
272        // Length should be corrected to actual data length
273        assert_eq!(
274            stream.dictionary().get("Length"),
275            Some(&Object::Integer(5))
276        );
277    }
278}