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!(
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 let dict = stream.dictionary();
124 assert_eq!(dict.get("Length"), Some(&Object::Integer(3)));
125
126 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 stream.dictionary_mut().set("CustomKey", "CustomValue");
137 assert_eq!(
138 stream.dictionary().get("CustomKey"),
139 Some(&Object::String("CustomValue".to_string()))
140 );
141
142 stream.data_mut().push(4);
144 assert_eq!(stream.data(), &[1, 2, 3, 4]);
145
146 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 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 let result = stream.compress_flate();
224 assert!(result.is_ok());
225
226 assert_ne!(stream.data(), &original_data);
228
229 assert_eq!(
234 stream.dictionary().get("Filter"),
235 Some(&Object::Name("FlateDecode".to_string()))
236 );
237
238 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 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 let mut dict = Dictionary::new();
266 dict.set("Length", 999); dict.set("Type", "XObject");
268
269 let data = vec![1, 2, 3, 4, 5];
270 let stream = Stream::with_dictionary(dict, data);
271
272 assert_eq!(
274 stream.dictionary().get("Length"),
275 Some(&Object::Integer(5))
276 );
277 }
278}