Skip to main content

pdf_objects/
stream.rs

1use std::io::Read;
2
3use flate2::read::ZlibDecoder;
4
5use crate::error::{PdfError, PdfResult};
6use crate::types::{PdfStream, PdfValue};
7
8pub fn decode_stream(stream: &PdfStream) -> PdfResult<Vec<u8>> {
9    match stream.dict.get("Filter") {
10        None => Ok(stream.data.clone()),
11        Some(PdfValue::Name(name)) if name == "FlateDecode" => inflate(stream.data.as_slice()),
12        Some(PdfValue::Array(filters)) if filters.len() == 1 => match filters.first() {
13            Some(PdfValue::Name(name)) if name == "FlateDecode" => inflate(stream.data.as_slice()),
14            _ => Err(PdfError::Unsupported(
15                "only a single FlateDecode filter is supported".to_string(),
16            )),
17        },
18        Some(_) => Err(PdfError::Unsupported(
19            "unsupported stream filter configuration".to_string(),
20        )),
21    }
22}
23
24/// Maximum decompressed stream size (256 MiB). Prevents decompression bombs from
25/// exhausting memory in WASM or native contexts.
26const MAX_DECOMPRESSED_SIZE: u64 = 256 * 1024 * 1024;
27
28fn inflate(data: &[u8]) -> PdfResult<Vec<u8>> {
29    let decoder = ZlibDecoder::new(data);
30    let mut output = Vec::new();
31    decoder
32        .take(MAX_DECOMPRESSED_SIZE + 1)
33        .read_to_end(&mut output)
34        .map_err(|error| PdfError::Corrupt(format!("failed to decode flate stream: {error}")))?;
35    if output.len() as u64 > MAX_DECOMPRESSED_SIZE {
36        return Err(PdfError::Corrupt(
37            "decompressed stream exceeds maximum allowed size".to_string(),
38        ));
39    }
40    Ok(output)
41}