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_shared(&self) -> Result<crate::sync::Arc<Vec<u8>>, DecodeFailure> {
184 match self.obj_id() {
185 Some(id) => self
186 .0
187 .dict
188 .ctx()
189 .xref()
190 .decoded_stream(id, || self.decoded()),
191 None => self.decoded().map(crate::sync::Arc::new),
192 }
193 }
194
195 pub fn decoded_image(
198 &self,
199 image_params: &ImageDecodeParams,
200 ) -> Result<FilterResult, DecodeFailure> {
201 let limit_in_force = self.0.dict.ctx().load_limits().image_pixel_limit();
203 let image_params = &ImageDecodeParams {
204 pixel_limit: limit_in_force,
205 ..image_params.clone()
206 };
207
208 if let Some(limit) = limit_in_force
209 && image_params.width > 0
210 && image_params.height > 0
211 {
212 let pixels =
213 u64::from(image_params.width).saturating_mul(u64::from(image_params.height));
214 if pixels > u64::from(limit) {
215 warn!("image pixel count {pixels} exceeds limit {limit}, stopping image decode");
216 return Err(DecodeFailure::ImageDecode);
217 }
218 }
219
220 let data = self.raw_data();
221
222 let mut current: Option<FilterResult> = None;
223
224 for (filter, params) in self.0.filters.iter().zip(self.0.filter_params.iter()) {
225 let new = filter.apply(
226 current.as_ref().map(|c| c.data.as_ref()).unwrap_or(&data),
227 params.clone(),
228 image_params,
229 )?;
230 current = Some(new);
231 }
232
233 let result = current.unwrap_or(FilterResult {
234 data: data.to_vec(),
235 image_data: None,
236 });
237
238 let limit = self.0.stream_byte_limit;
239 if limit != u64::MAX {
240 let observed = result.data.len() as u64;
241 if observed > limit {
242 warn!("decoded stream size {observed} exceeds limit {limit}, stopping decode");
243 return Err(DecodeFailure::StreamTooLarge { observed, limit });
244 }
245 }
246
247 Ok(result)
248 }
249}
250
251impl Debug for Stream<'_> {
252 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
253 write!(f, "Stream (len: {:?})", self.0.data.len())
254 }
255}
256
257impl Skippable for Stream<'_> {
258 fn skip(_: &mut Reader<'_>, _: bool) -> Option<()> {
259 warn!("attempted to skip a stream object");
261
262 None
263 }
264}
265
266impl<'a> Readable<'a> for Stream<'a> {
267 fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
268 let dict = r.read_with_context::<Dict<'_>>(ctx)?;
269
270 if dict.contains_key(F) {
271 warn!("encountered stream referencing external file, which is unsupported");
272
273 return None;
274 }
275
276 let stream_byte_limit = ctx.load_limits().stream_byte_limit().unwrap_or(u64::MAX);
277 let offset = r.offset();
278 parse_proper(r, &dict, stream_byte_limit)
279 .or_else(|| {
280 warn!("failed to parse stream, trying to parse it manually");
281 crate::leniency::emit(crate::leniency::STREAM_PARSE_FALLBACK);
282 r.jump(offset);
283 parse_fallback(r, &dict, stream_byte_limit)
284 })
285 .error_none("was unable to manually parse the stream")
286 }
287}
288
289#[derive(Debug, Copy, Clone)]
290pub enum DecodeFailure {
292 ImageDecode,
294 StreamDecode,
296 Decryption,
298 Unknown,
300 StreamTooLarge {
307 observed: u64,
309 limit: u64,
311 },
312}
313
314#[derive(Debug, Copy, Clone)]
316pub enum ImageColorSpace {
317 Gray,
319 Rgb,
321 RgbFromYCbCr,
331 Cmyk,
333 Unknown(u8),
335}
336
337pub struct ImageData {
339 pub alpha: Option<Vec<u8>>,
341 pub color_space: Option<ImageColorSpace>,
343 pub bits_per_component: u8,
345 pub width: u32,
347 pub height: u32,
349}
350
351pub struct FilterResult {
353 pub data: Vec<u8>,
355 pub image_data: Option<ImageData>,
357}
358
359impl FilterResult {
360 pub(crate) fn from_data(data: Vec<u8>) -> Self {
361 Self {
362 data,
363 image_data: None,
364 }
365 }
366}
367
368fn parse_proper<'a>(
369 r: &mut Reader<'a>,
370 dict: &Dict<'a>,
371 stream_byte_limit: u64,
372) -> Option<Stream<'a>> {
373 let length = dict.get::<u32>(LENGTH)?;
374
375 r.skip_white_spaces_and_comments();
376 r.forward_tag(b"stream")?;
377 while r.peek_byte().is_some_and(|b| b == b' ' || b == b'\t') {
381 r.forward();
382 }
383 r.forward_tag(b"\n")
384 .or_else(|| r.forward_tag(b"\r\n"))
385 .or_else(|| r.forward_tag(b"\r"))?;
386 let data = r.read_bytes(length as usize)?;
387 r.skip_white_spaces();
388 r.forward_tag(b"endstream")?;
389
390 Some(Stream::new(data, dict.clone(), stream_byte_limit))
391}
392
393fn parse_fallback<'a>(
394 r: &mut Reader<'a>,
395 dict: &Dict<'a>,
396 stream_byte_limit: u64,
397) -> Option<Stream<'a>> {
398 while r.forward_tag(b"stream").is_none() {
399 r.read_byte()?;
400 }
401
402 while r.peek_byte().is_some_and(|b| b == b' ' || b == b'\t') {
405 r.forward();
406 }
407 r.forward_tag(b"\n")
408 .or_else(|| r.forward_tag(b"\r\n"))
409 .or_else(|| r.forward_tag(b"\r"))?;
411
412 let data_start = r.tail()?;
413 let start = r.offset();
414
415 loop {
416 if r.peek_byte()?.is_ascii_whitespace() || r.peek_tag(b"endstream").is_some() {
417 let length = r.offset() - start;
418 let data = data_start.get(..length)?;
419
420 r.skip_white_spaces();
421
422 if r.forward_tag(b"endstream").is_none() {
425 continue;
426 }
427
428 let stream = Stream::new(data, dict.clone(), stream_byte_limit);
429
430 return Some(stream);
432 } else {
433 r.read_byte()?;
434 }
435 }
436}
437
438impl<'a> TryFrom<Object<'a>> for Stream<'a> {
439 type Error = ();
440
441 fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
442 match value {
443 Object::Stream(s) => Ok(s),
444 _ => Err(()),
445 }
446 }
447}
448
449impl<'a> ObjectLike<'a> for Stream<'a> {}
450
451#[cfg(test)]
452mod tests {
453 use crate::object::Stream;
454 use crate::pdf::PdfLoadLimits;
455 use crate::reader::Reader;
456 use crate::reader::{ReaderContext, ReaderExt};
457
458 use super::DecodeFailure;
459
460 #[test]
461 fn stream() {
462 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
463 let mut r = Reader::new(data);
464 let stream = r
465 .read_with_context::<Stream<'_>>(&ReaderContext::dummy())
466 .unwrap();
467
468 assert_eq!(stream.0.data, b"abcdefghij");
469 }
470
471 #[test]
473 fn decoded_no_limit() {
474 let data = b"<< /Length 5 >> stream\nhello\nendstream";
475 let mut r = Reader::new(data);
476 let stream = r
477 .read_with_context::<Stream<'_>>(&ReaderContext::dummy())
478 .unwrap();
479
480 let decoded = stream.decoded().unwrap();
481 assert_eq!(decoded, b"hello");
482 }
483
484 #[test]
486 fn decoded_exceeds_byte_limit() {
487 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
489 let limits = PdfLoadLimits::new().max_stream_bytes(5);
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 match stream.decoded() {
495 Err(DecodeFailure::StreamTooLarge { observed, limit }) => {
496 assert_eq!(observed, 10);
497 assert_eq!(limit, 5);
498 }
499 other => panic!("expected StreamTooLarge, got {other:?}"),
500 }
501 }
502
503 #[test]
505 fn decoded_at_byte_limit_succeeds() {
506 let data = b"<< /Length 10 >> stream\nabcdefghij\nendstream";
507 let limits = PdfLoadLimits::new().max_stream_bytes(10);
508 let ctx = ReaderContext::dummy_with_limits(limits);
509 let mut r = Reader::new(data);
510 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
511
512 let decoded = stream.decoded().unwrap();
513 assert_eq!(decoded, b"abcdefghij");
514 }
515
516 #[test]
523 fn flate_decode_exceeds_limit() {
524 let compressed: &[u8] = &[
526 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
527 ];
528 let length = compressed.len();
529 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
530 pdf.extend_from_slice(compressed);
531 pdf.extend_from_slice(b"\nendstream");
532
533 let limits = PdfLoadLimits::new().max_stream_bytes(20); let ctx = ReaderContext::dummy_with_limits(limits);
535 let mut r = Reader::new(&pdf);
536 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
537
538 match stream.decoded() {
539 Err(DecodeFailure::StreamTooLarge { .. }) => {}
540 other => panic!("expected StreamTooLarge, got {other:?}"),
541 }
542 }
543
544 #[test]
547 fn flate_decode_within_limit() {
548 let compressed: &[u8] = &[
549 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
550 ];
551 let length = compressed.len();
552 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
553 pdf.extend_from_slice(compressed);
554 pdf.extend_from_slice(b"\nendstream");
555
556 let limits = PdfLoadLimits::new().max_stream_bytes(50);
557 let ctx = ReaderContext::dummy_with_limits(limits);
558 let mut r = Reader::new(&pdf);
559 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
560
561 let decoded = stream.decoded().unwrap();
562 assert_eq!(decoded.len(), 50);
563 assert!(decoded.iter().all(|&b| b == 0));
564 }
565
566 #[test]
568 fn flate_decode_limit_one_below() {
569 let compressed: &[u8] = &[
570 0x78, 0x9c, 0x63, 0x60, 0x20, 0x15, 0x00, 0x00, 0x00, 0x32, 0x00, 0x01,
571 ];
572 let length = compressed.len();
573 let mut pdf = format!("<< /Length {length} /Filter /FlateDecode >> stream\n").into_bytes();
574 pdf.extend_from_slice(compressed);
575 pdf.extend_from_slice(b"\nendstream");
576
577 let limits = PdfLoadLimits::new().max_stream_bytes(49); let ctx = ReaderContext::dummy_with_limits(limits);
579 let mut r = Reader::new(&pdf);
580 let stream = r.read_with_context::<Stream<'_>>(&ctx).unwrap();
581
582 match stream.decoded() {
583 Err(DecodeFailure::StreamTooLarge { .. }) => {}
584 other => panic!("expected StreamTooLarge, got {other:?}"),
585 }
586 }
587
588 #[test]
601 fn the_pixel_limit_reaches_the_filter_that_needs_it() {
602 use super::ImageDecodeParams;
603
604 let data = b"<< /Length 2 /Filter /CCITTFaxDecode /DecodeParms << /K 0 /Columns 8 /Rows 1 >> >> stream\n\x35\x14\nendstream";
606 let limits = PdfLoadLimits::new().max_image_pixels(4);
607 let mut r = Reader::new(data);
608 let stream = r
609 .read_with_context::<Stream<'_>>(&ReaderContext::dummy_with_limits(limits))
610 .unwrap();
611
612 let params = ImageDecodeParams {
615 width: 1,
616 height: 1,
617 ..Default::default()
618 };
619
620 let produced = stream
621 .decoded_image(¶ms)
622 .ok()
623 .and_then(|res| res.image_data)
624 .map(|i| u64::from(i.width) * u64::from(i.height));
625 assert!(
626 produced.is_none_or(|pixels| pixels <= 4),
627 "produced {produced:?} pixels under a limit of 4"
628 );
629 }
630}