use crate::error::PdfResult;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StreamFilter {
ASCIIHex,
ASCII85,
Flate,
LZW,
RunLength,
CCITTFax,
JBIG2,
DCT,
JPX,
}
pub struct StreamDecoder;
impl StreamDecoder {
pub fn new() -> Self {
Self
}
pub fn decode(&self, data: &[u8], filter: StreamFilter) -> PdfResult<Vec<u8>> {
self.decode_with_limit(data, filter, usize::MAX)
}
pub fn decode_with_limit(
&self,
data: &[u8],
filter: StreamFilter,
limit: usize,
) -> PdfResult<Vec<u8>> {
match filter {
StreamFilter::Flate => self.decode_flate(data, limit),
_ => Err(crate::error::PdfError::NotImplemented(format!(
"filter {:?} not yet implemented",
filter
))),
}
}
fn decode_flate(&self, data: &[u8], limit: usize) -> PdfResult<Vec<u8>> {
use std::io::Read;
let mut decoder = flate2::read::ZlibDecoder::new(data);
let mut result = Vec::new();
decoder
.by_ref()
.take(limit.saturating_add(1) as u64)
.read_to_end(&mut result)?;
Ok(result)
}
}
impl Default for StreamDecoder {
fn default() -> Self {
Self::new()
}
}
pub struct StreamEncoder {
compressor: Option<(u32, flate2::Compress)>,
}
impl StreamEncoder {
pub fn new() -> Self {
Self { compressor: None }
}
pub fn encode(&mut self, data: &[u8], filter: StreamFilter) -> PdfResult<Vec<u8>> {
self.encode_with_compression(data, filter, 6)
}
pub fn encode_with_compression(
&mut self,
data: &[u8],
filter: StreamFilter,
level: u32,
) -> PdfResult<Vec<u8>> {
let level = level.clamp(0, 9);
match filter {
StreamFilter::Flate => self.encode_flate(data, level),
_ => Err(crate::error::PdfError::NotImplemented(format!(
"filter {:?} not yet implemented",
filter
))),
}
}
fn encode_flate(&mut self, data: &[u8], level: u32) -> PdfResult<Vec<u8>> {
if self
.compressor
.as_ref()
.map(|(l, _)| *l != level)
.unwrap_or(true)
{
self.compressor = Some((
level,
flate2::Compress::new(flate2::Compression::new(level), true),
));
}
let (_, compressor) = self.compressor.as_mut().expect("initialized above");
compressor.reset();
let mut out = Vec::with_capacity(data.len().saturating_add(data.len() / 2).max(128));
loop {
let before = compressor.total_out();
match compressor.compress_vec(data, &mut out, flate2::FlushCompress::Finish) {
Ok(flate2::Status::StreamEnd) => break,
Ok(_) | Err(_) => {
if compressor.total_out() == before {
return Err(crate::error::PdfError::NotImplemented(
"flate compression made no progress".to_string(),
));
}
out.reserve(data.len().max(64));
}
}
}
Ok(out)
}
}
impl Default for StreamEncoder {
fn default() -> Self {
Self::new()
}
}