1use crate::crypto::DecryptionTarget;
4use crate::filter::Filter;
5use crate::object;
6use crate::object::Dict;
7use crate::object::Name;
8use crate::object::dict::keys::{DECODE_PARMS, DP, F, FILTER, LENGTH, TYPE};
9use crate::object::{Array, ObjectIdentifier};
10use crate::object::{Object, ObjectLike};
11use crate::reader::Reader;
12use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
13use crate::sync::Arc;
14use crate::util::OptionLog;
15use alloc::borrow::Cow;
16use alloc::vec::Vec;
17use core::fmt::{Debug, Formatter};
18use log::warn;
19use smallvec::SmallVec;
20
21#[derive(Clone)]
22struct StreamInner<'a> {
23 dict: Dict<'a>,
24 filters: SmallVec<[Filter; 2]>,
25 filter_params: SmallVec<[Dict<'a>; 2]>,
26 data: &'a [u8],
27 stream_byte_limit: u64,
31}
32
33#[derive(Clone)]
35pub struct Stream<'a>(Arc<StreamInner<'a>>);
36
37impl PartialEq for Stream<'_> {
38 fn eq(&self, other: &Self) -> bool {
39 self.0.dict == other.0.dict && self.0.data == other.0.data
40 }
41}
42
43#[derive(Clone, PartialEq, Default)]
45pub struct ImageDecodeParams {
46 pub is_indexed: bool,
48 pub bpc: Option<u8>,
50 pub num_components: Option<u8>,
52 pub target_dimension: Option<(u32, u32)>,
56 pub width: u32,
58 pub height: u32,
60 pub pixel_limit: Option<u32>,
69}
70
71impl<'a> Stream<'a> {
72 pub(crate) fn new(data: &'a [u8], dict: Dict<'a>, stream_byte_limit: u64) -> Self {
73 let mut collected_filters = SmallVec::new();
74 let mut collected_params = SmallVec::new();
75
76 if let Some(filter) = dict
77 .get::<Name>(F)
78 .or_else(|| dict.get::<Name>(FILTER))
79 .and_then(Filter::from_name)
80 {
81 let params = dict
82 .get::<Dict<'_>>(DP)
83 .or_else(|| dict.get::<Dict<'_>>(DECODE_PARMS))
84 .unwrap_or_default();
85
86 collected_filters.push(filter);
87 collected_params.push(params);
88 } else if let Some(filters) = dict
89 .get::<Array<'_>>(F)
90 .or_else(|| dict.get::<Array<'_>>(FILTER))
91 {
92 let filters = filters.iter::<Name>().map(Filter::from_name);
93 let mut params = dict
94 .get::<Array<'_>>(DP)
95 .or_else(|| dict.get::<Array<'_>>(DECODE_PARMS))
96 .map(|a| a.iter::<Object<'_>>());
97
98 for filter in filters {
99 let params = params
100 .as_mut()
101 .and_then(|p| p.next())
102 .and_then(|p| p.into_dict())
103 .unwrap_or_default();
104
105 if let Some(filter) = filter {
106 collected_filters.push(filter);
107 collected_params.push(params);
108 }
109 }
110 }
111
112 Self(Arc::new(StreamInner {
113 dict,
114 filters: collected_filters,
115 filter_params: collected_params,
116 data,
117 stream_byte_limit,
118 }))
119 }
120
121 pub fn raw_data(&self) -> Cow<'a, [u8]> {
125 let ctx = self.0.dict.ctx();
126
127 if ctx.xref().needs_decryption(ctx)
128 && self
129 .0
130 .dict
131 .get::<object::String>(TYPE)
132 .map(|t| t.as_ref() != b"XRef")
133 .unwrap_or(true)
134 {
135 if let Some(obj_id) = self.0.dict.obj_id() {
138 Cow::Owned(
139 ctx.xref()
140 .decrypt(obj_id, self.0.data, DecryptionTarget::Stream)
141 .unwrap_or_default(),
142 )
143 } else {
144 Cow::Borrowed(self.0.data)
145 }
146 } else {
147 Cow::Borrowed(self.0.data)
148 }
149 }
150
151 pub fn dict(&self) -> &Dict<'a> {
153 &self.0.dict
154 }
155
156 pub fn obj_id(&self) -> Option<ObjectIdentifier> {
160 self.0.dict.obj_id()
161 }
162
163 pub fn filters(&self) -> &[Filter] {
165 &self.0.filters
166 }
167
168 pub fn decoded(&self) -> Result<Vec<u8>, DecodeFailure> {
173 self.decoded_image(&ImageDecodeParams::default())
174 .map(|r| r.data)
175 }
176
177 pub fn decoded_image(
180 &self,
181 image_params: &ImageDecodeParams,
182 ) -> Result<FilterResult, DecodeFailure> {
183 let limit_in_force = self.0.dict.ctx().load_limits().image_pixel_limit();
185 let image_params = &ImageDecodeParams {
186 pixel_limit: limit_in_force,
187 ..image_params.clone()
188 };
189
190 if let Some(limit) = limit_in_force
191 && image_params.width > 0
192 && image_params.height > 0
193 {
194 let pixels =
195 u64::from(image_params.width).saturating_mul(u64::from(image_params.height));
196 if pixels > u64::from(limit) {
197 warn!("image pixel count {pixels} exceeds limit {limit}, stopping image decode");
198 return Err(DecodeFailure::ImageDecode);
199 }
200 }
201
202 let data = self.raw_data();
203
204 let mut current: Option<FilterResult> = None;
205
206 for (filter, params) in self.0.filters.iter().zip(self.0.filter_params.iter()) {
207 let new = filter.apply(
208 current.as_ref().map(|c| c.data.as_ref()).unwrap_or(&data),
209 params.clone(),
210 image_params,
211 )?;
212 current = Some(new);
213 }
214
215 let result = current.unwrap_or(FilterResult {
216 data: data.to_vec(),
217 image_data: None,
218 });
219
220 let limit = self.0.stream_byte_limit;
221 if limit != u64::MAX {
222 let observed = result.data.len() as u64;
223 if observed > limit {
224 warn!("decoded stream size {observed} exceeds limit {limit}, stopping decode");
225 return Err(DecodeFailure::StreamTooLarge { observed, limit });
226 }
227 }
228
229 Ok(result)
230 }
231}
232
233impl Debug for Stream<'_> {
234 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
235 write!(f, "Stream (len: {:?})", self.0.data.len())
236 }
237}
238
239impl Skippable for Stream<'_> {
240 fn skip(_: &mut Reader<'_>, _: bool) -> Option<()> {
241 warn!("attempted to skip a stream object");
243
244 None
245 }
246}
247
248impl<'a> Readable<'a> for Stream<'a> {
249 fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
250 let dict = r.read_with_context::<Dict<'_>>(ctx)?;
251
252 if dict.contains_key(F) {
253 warn!("encountered stream referencing external file, which is unsupported");
254
255 return None;
256 }
257
258 let stream_byte_limit = ctx.load_limits().stream_byte_limit().unwrap_or(u64::MAX);
259 let offset = r.offset();
260 parse_proper(r, &dict, stream_byte_limit)
261 .or_else(|| {
262 warn!("failed to parse stream, trying to parse it manually");
263 crate::leniency::emit(crate::leniency::STREAM_PARSE_FALLBACK);
264 r.jump(offset);
265 parse_fallback(r, &dict, stream_byte_limit)
266 })
267 .error_none("was unable to manually parse the stream")
268 }
269}
270
271#[derive(Debug, Copy, Clone)]
272pub enum DecodeFailure {
274 ImageDecode,
276 StreamDecode,
278 Decryption,
280 Unknown,
282 StreamTooLarge {
289 observed: u64,
291 limit: u64,
293 },
294}
295
296#[derive(Debug, Copy, Clone)]
298pub enum ImageColorSpace {
299 Gray,
301 Rgb,
303 RgbFromYCbCr,
313 Cmyk,
315 Unknown(u8),
317}
318
319pub struct ImageData {
321 pub alpha: Option<Vec<u8>>,
323 pub color_space: Option<ImageColorSpace>,
325 pub bits_per_component: u8,
327 pub width: u32,
329 pub height: u32,
331}
332
333pub struct FilterResult {
335 pub data: Vec<u8>,
337 pub image_data: Option<ImageData>,
339}
340
341impl FilterResult {
342 pub(crate) fn from_data(data: Vec<u8>) -> Self {
343 Self {
344 data,
345 image_data: None,
346 }
347 }
348}
349
350fn parse_proper<'a>(
351 r: &mut Reader<'a>,
352 dict: &Dict<'a>,
353 stream_byte_limit: u64,
354) -> Option<Stream<'a>> {
355 let length = dict.get::<u32>(LENGTH)?;
356
357 r.skip_white_spaces_and_comments();
358 r.forward_tag(b"stream")?;
359 while r.peek_byte().is_some_and(|b| b == b' ' || b == b'\t') {
363 r.forward();
364 }
365 r.forward_tag(b"\n")
366 .or_else(|| r.forward_tag(b"\r\n"))
367 .or_else(|| r.forward_tag(b"\r"))?;
368 let data = r.read_bytes(length as usize)?;
369 r.skip_white_spaces();
370 r.forward_tag(b"endstream")?;
371
372 Some(Stream::new(data, dict.clone(), stream_byte_limit))
373}
374
375fn parse_fallback<'a>(
376 r: &mut Reader<'a>,
377 dict: &Dict<'a>,
378 stream_byte_limit: u64,
379) -> Option<Stream<'a>> {
380 while r.forward_tag(b"stream").is_none() {
381 r.read_byte()?;
382 }
383
384 while r.peek_byte().is_some_and(|b| b == b' ' || b == b'\t') {
387 r.forward();
388 }
389 r.forward_tag(b"\n")
390 .or_else(|| r.forward_tag(b"\r\n"))
391 .or_else(|| r.forward_tag(b"\r"))?;
393
394 let data_start = r.tail()?;
395 let start = r.offset();
396
397 loop {
398 if r.peek_byte()?.is_ascii_whitespace() || r.peek_tag(b"endstream").is_some() {
399 let length = r.offset() - start;
400 let data = data_start.get(..length)?;
401
402 r.skip_white_spaces();
403
404 if r.forward_tag(b"endstream").is_none() {
407 continue;
408 }
409
410 let stream = Stream::new(data, dict.clone(), stream_byte_limit);
411
412 return Some(stream);
414 } else {
415 r.read_byte()?;
416 }
417 }
418}
419
420impl<'a> TryFrom<Object<'a>> for Stream<'a> {
421 type Error = ();
422
423 fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
424 match value {
425 Object::Stream(s) => Ok(s),
426 _ => Err(()),
427 }
428 }
429}
430
431impl<'a> ObjectLike<'a> for Stream<'a> {}
432
433#[cfg(test)]
434mod tests {
435 use crate::object::Stream;
436 use crate::pdf::PdfLoadLimits;
437 use crate::reader::Reader;
438 use crate::reader::{ReaderContext, ReaderExt};
439
440 use super::DecodeFailure;
441
442 #[test]
443 fn stream() {
444 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
445 let mut r = Reader::new(data);
446 let stream = r
447 .read_with_context::<Stream<'_>>(&ReaderContext::dummy())
448 .unwrap();
449
450 assert_eq!(stream.0.data, b"abcdefghij");
451 }
452
453 #[test]
455 fn decoded_no_limit() {
456 let data = b"<< /Length 5 >> stream\nhello\nendstream";
457 let mut r = Reader::new(data);
458 let stream = r
459 .read_with_context::<Stream<'_>>(&ReaderContext::dummy())
460 .unwrap();
461
462 let decoded = stream.decoded().unwrap();
463 assert_eq!(decoded, b"hello");
464 }
465
466 #[test]
468 fn decoded_exceeds_byte_limit() {
469 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
471 let limits = PdfLoadLimits::new().max_stream_bytes(5);
472 let ctx = ReaderContext::dummy_with_limits(limits);
473 let mut r = Reader::new(data);
474 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
475
476 match stream.decoded() {
477 Err(DecodeFailure::StreamTooLarge { observed, limit }) => {
478 assert_eq!(observed, 10);
479 assert_eq!(limit, 5);
480 }
481 other => panic!("expected StreamTooLarge, got {other:?}"),
482 }
483 }
484
485 #[test]
487 fn decoded_at_byte_limit_succeeds() {
488 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
489 let limits = PdfLoadLimits::new().max_stream_bytes(10);
490 let ctx = ReaderContext::dummy_with_limits(limits);
491 let mut r = Reader::new(data);
492 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
493
494 let decoded = stream.decoded().unwrap();
495 assert_eq!(decoded, b"abcdefghij");
496 }
497
498 #[test]
505 fn flate_decode_exceeds_limit() {
506 let compressed: &[u8] = &[
508 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
509 ];
510 let length = compressed.len();
511 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
512 pdf.extend_from_slice(compressed);
513 pdf.extend_from_slice(b"\nendstream");
514
515 let limits = PdfLoadLimits::new().max_stream_bytes(20); let ctx = ReaderContext::dummy_with_limits(limits);
517 let mut r = Reader::new(&pdf);
518 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
519
520 match stream.decoded() {
521 Err(DecodeFailure::StreamTooLarge { .. }) => {}
522 other => panic!("expected StreamTooLarge, got {other:?}"),
523 }
524 }
525
526 #[test]
529 fn flate_decode_within_limit() {
530 let compressed: &[u8] = &[
531 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
532 ];
533 let length = compressed.len();
534 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
535 pdf.extend_from_slice(compressed);
536 pdf.extend_from_slice(b"\nendstream");
537
538 let limits = PdfLoadLimits::new().max_stream_bytes(50);
539 let ctx = ReaderContext::dummy_with_limits(limits);
540 let mut r = Reader::new(&pdf);
541 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
542
543 let decoded = stream.decoded().unwrap();
544 assert_eq!(decoded.len(), 50);
545 assert!(decoded.iter().all(|&b| b == 0));
546 }
547
548 #[test]
550 fn flate_decode_limit_one_below() {
551 let compressed: &[u8] = &[
552 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
553 ];
554 let length = compressed.len();
555 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
556 pdf.extend_from_slice(compressed);
557 pdf.extend_from_slice(b"\nendstream");
558
559 let limits = PdfLoadLimits::new().max_stream_bytes(49); let ctx = ReaderContext::dummy_with_limits(limits);
561 let mut r = Reader::new(&pdf);
562 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
563
564 match stream.decoded() {
565 Err(DecodeFailure::StreamTooLarge { .. }) => {}
566 other => panic!("expected StreamTooLarge, got {other:?}"),
567 }
568 }
569
570 #[test]
583 fn the_pixel_limit_reaches_the_filter_that_needs_it() {
584 use super::ImageDecodeParams;
585
586 let data = b"<< /Length 2 /Filter /CCITTFaxDecode /DecodeParms << /K 0 /Columns 8 /Rows 1 >> >> stream\n\x35\x14\nendstream";
588 let limits = PdfLoadLimits::new().max_image_pixels(4);
589 let mut r = Reader::new(data);
590 let stream = r
591 .read_with_context::<Stream<'_>>(&ReaderContext::dummy_with_limits(limits))
592 .unwrap();
593
594 let params = ImageDecodeParams {
597 width: 1,
598 height: 1,
599 ..Default::default()
600 };
601
602 let produced = stream
603 .decoded_image(¶ms)
604 .ok()
605 .and_then(|res| res.image_data)
606 .map(|i| u64::from(i.width) * u64::from(i.height));
607 assert!(
608 produced.is_none_or(|pixels| pixels <= 4),
609 "produced {produced:?} pixels under a limit of 4"
610 );
611 }
612}