hayro_syntax/object/
stream.rs1use crate::filter::Filter;
4use crate::object::Dict;
5use crate::object::Name;
6use crate::object::dict::keys::{DECODE_PARMS, DP, F, FILTER, LENGTH};
7use crate::object::{Array, ObjectIdentifier};
8use crate::object::{Object, ObjectLike};
9use crate::reader::{Readable, Reader, ReaderContext, Skippable};
10use crate::util::OptionLog;
11use log::{info, warn};
12use std::fmt::{Debug, Formatter};
13
14#[derive(Clone, PartialEq)]
16pub struct Stream<'a> {
17 dict: Dict<'a>,
18 data: &'a [u8],
19}
20
21#[derive(Clone, PartialEq, Default)]
23pub struct ImageDecodeParams {
24 pub is_indexed: bool,
26}
27
28impl<'a> Stream<'a> {
29 pub fn raw_data(&self) -> &'a [u8] {
31 self.data
32 }
33
34 pub fn dict(&self) -> &Dict<'a> {
36 &self.dict
37 }
38
39 pub fn obj_id(&self) -> ObjectIdentifier {
41 self.dict.obj_id().unwrap()
42 }
43
44 pub fn decoded(&self) -> Result<Vec<u8>, DecodeFailure> {
49 self.decoded_image(&ImageDecodeParams::default())
50 .map(|r| r.data)
51 }
52
53 pub fn decoded_image(
56 &self,
57 image_params: &ImageDecodeParams,
58 ) -> Result<FilterResult, DecodeFailure> {
59 if let Some(filter) = self
60 .dict
61 .get::<Name>(F)
62 .or_else(|| self.dict.get::<Name>(FILTER))
63 .and_then(|n| Filter::from_name(n))
64 {
65 let params = self
66 .dict
67 .get::<Dict>(DP)
68 .or_else(|| self.dict.get::<Dict>(DECODE_PARMS));
69
70 filter.apply(self.data, params.clone().unwrap_or_default(), image_params)
71 } else if let Some(filters) = self
72 .dict
73 .get::<Array>(F)
74 .or_else(|| self.dict.get::<Array>(FILTER))
75 {
76 let filters = filters
77 .iter::<Name>()
78 .map(|n| Filter::from_name(n))
79 .collect::<Option<Vec<_>>>()
80 .ok_or(DecodeFailure::Unknown)?;
81 let params: Vec<_> = self
82 .dict
83 .get::<Array>(DP)
84 .or_else(|| self.dict.get::<Array>(DECODE_PARMS))
85 .map(|a| a.iter::<Object>().collect())
86 .unwrap_or_default();
87
88 let mut current: Option<FilterResult> = None;
89
90 for (i, filter) in filters.iter().enumerate() {
91 let params = params.get(i).and_then(|p| p.clone().cast::<Dict>());
92
93 let new = filter.apply(
94 current
95 .as_ref()
96 .map(|c| c.data.as_ref())
97 .unwrap_or(self.data),
98 params.clone().unwrap_or_default(),
99 image_params,
100 )?;
101 current = Some(new);
102 }
103
104 Ok(current.unwrap_or(FilterResult {
105 data: self.data.to_vec(),
106 image_data: None,
107 }))
108 } else {
109 Ok(FilterResult {
110 data: self.data.to_vec(),
111 image_data: None,
112 })
113 }
114 }
115
116 pub(crate) fn from_raw(data: &'a [u8], dict: Dict<'a>) -> Self {
117 Self { dict, data }
118 }
119}
120
121impl Debug for Stream<'_> {
122 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
123 write!(f, "Stream (len: {:?})", self.data.len())
124 }
125}
126
127impl Skippable for Stream<'_> {
128 fn skip(_: &mut Reader<'_>, _: bool) -> Option<()> {
129 warn!("attempted to skip a stream object");
131
132 None
133 }
134}
135
136impl<'a> Readable<'a> for Stream<'a> {
137 fn read(r: &mut Reader<'a>, ctx: ReaderContext<'a>) -> Option<Self> {
138 let dict = r.read_with_context::<Dict>(ctx)?;
139
140 if dict.contains_key(F) {
141 warn!("encountered stream referencing external file, which is unsupported");
142
143 return None;
144 }
145
146 let offset = r.offset();
147 parse_proper(r, &dict)
148 .or_else(|| {
149 warn!("failed to parse stream, trying to parse it manually");
150
151 r.jump(offset);
152 parse_fallback(r, &dict)
153 })
154 .error_none("was unable to manually parse the stream")
155 }
156}
157
158#[derive(Debug, Copy, Clone)]
159pub enum DecodeFailure {
161 ImageDecode,
163 StreamDecode,
165 JpxImage,
167 Unknown,
169}
170
171#[derive(Debug, Copy, Clone)]
173pub enum ImageColorSpace {
174 Gray,
176 Rgb,
178 Cmyk,
180}
181
182pub struct ImageData {
184 pub alpha: Option<Vec<u8>>,
186 pub color_space: ImageColorSpace,
188 pub bits_per_component: u8,
190}
191
192pub struct FilterResult {
194 pub data: Vec<u8>,
196 pub image_data: Option<ImageData>,
198}
199
200impl FilterResult {
201 pub(crate) fn from_data(data: Vec<u8>) -> Self {
202 Self {
203 data,
204 image_data: None,
205 }
206 }
207}
208
209fn parse_proper<'a>(r: &mut Reader<'a>, dict: &Dict<'a>) -> Option<Stream<'a>> {
210 let length = dict.get::<u32>(LENGTH)?;
211
212 r.skip_white_spaces_and_comments();
213 r.forward_tag(b"stream")?;
214 r.forward_tag(b"\n")
215 .or_else(|| r.forward_tag(b"\r\n"))
216 .or_else(|| r.forward_tag(b"\r"))?;
217 let data = r.read_bytes(length as usize)?;
218 r.skip_white_spaces();
219 r.forward_tag(b"endstream")?;
220
221 Some(Stream {
222 data,
223 dict: dict.clone(),
224 })
225}
226
227fn parse_fallback<'a>(r: &mut Reader<'a>, dict: &Dict<'a>) -> Option<Stream<'a>> {
228 while r.forward_tag(b"stream").is_none() {
229 r.read_byte()?;
230 }
231
232 r.forward_tag(b"\n").or_else(|| r.forward_tag(b"\r\n"))?;
233
234 let data_start = r.tail()?;
235 let start = r.offset();
236
237 loop {
238 if r.peek_byte()?.is_ascii_whitespace() || r.peek_tag(b"endstream").is_some() {
239 let length = r.offset() - start;
240 let data = data_start.get(..length)?;
241
242 r.skip_white_spaces();
243
244 if r.forward_tag(b"endstream").is_none() {
247 continue;
248 }
249
250 let stream = Stream {
251 data,
252 dict: dict.clone(),
253 };
254
255 if stream.decoded().is_ok() {
257 info!("managed to reconstruct the stream");
258
259 return Some(stream);
261 }
262 } else {
263 r.read_byte()?;
264 }
265 }
266}
267
268impl<'a> TryFrom<Object<'a>> for Stream<'a> {
269 type Error = ();
270
271 fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
272 match value {
273 Object::Stream(s) => Ok(s),
274 _ => Err(()),
275 }
276 }
277}
278
279impl<'a> ObjectLike<'a> for Stream<'a> {}
280
281#[cfg(test)]
282mod tests {
283 use crate::object::Stream;
284 use crate::reader::{Reader, ReaderContext};
285
286 #[test]
287 fn stream() {
288 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
289 let mut r = Reader::new(data);
290 let stream = r
291 .read_with_context::<Stream>(ReaderContext::dummy())
292 .unwrap();
293
294 assert_eq!(stream.data, b"abcdefghij");
295 }
296}