oxidize_pdf/objects/
stream.rs1#[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 let dict = stream.dictionary();
118 assert_eq!(dict.get("Length"), Some(&Object::Integer(3)));
119
120 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 stream.dictionary_mut().set("CustomKey", "CustomValue");
131 assert_eq!(
132 stream.dictionary().get("CustomKey"),
133 Some(&Object::String("CustomValue".to_string()))
134 );
135
136 stream.data_mut().push(4);
138 assert_eq!(stream.data(), &[1, 2, 3, 4]);
139
140 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.clone());
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 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 let result = stream.compress_flate();
217 assert!(result.is_ok());
218
219 assert_ne!(stream.data(), &original_data);
221
222 assert_eq!(
227 stream.dictionary().get("Filter"),
228 Some(&Object::Name("FlateDecode".to_string()))
229 );
230
231 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 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 let mut dict = Dictionary::new();
259 dict.set("Length", 999); dict.set("Type", "XObject");
261
262 let data = vec![1, 2, 3, 4, 5];
263 let stream = Stream::with_dictionary(dict, data);
264
265 assert_eq!(stream.dictionary().get("Length"), Some(&Object::Integer(5)));
267 }
268}