hayro-syntax 0.4.0

A low-level crate for reading PDF files.
Documentation
use crate::bit_reader::{BitSize, BitWriter};
use crate::filter::FilterResult;
use crate::object::stream::{ImageColorSpace, ImageData, ImageDecodeParams};
use jpeg2k::DecodeParameters;

impl ImageColorSpace {
    fn num_components(&self) -> u8 {
        match self {
            ImageColorSpace::Gray => 1,
            ImageColorSpace::Rgb => 3,
            ImageColorSpace::Cmyk => 4,
        }
    }
}

pub(crate) fn decode(data: &[u8], params: &ImageDecodeParams) -> Option<FilterResult> {
    use crate::object::stream::ImageColorSpace;

    let mut jpx_params = DecodeParameters::new();

    if params.is_indexed {
        jpx_params = jpx_params.ignore_pclr_cmap_cdef();
    }

    let image = jpeg2k::Image::from_bytes_with(data, jpx_params).ok()?;
    let width = image.width();
    let height = image.height();
    let components = image.components();
    let bpc = params.bpc.unwrap_or(
        components
            .iter()
            .fold(u32::MIN, |max, c| max.max(c.precision())) as u8,
    );
    let cs = match components.iter().filter(|c| !c.is_alpha()).count() {
        1 => ImageColorSpace::Gray,
        3 => ImageColorSpace::Rgb,
        4 => ImageColorSpace::Cmyk,
        _ => return None,
    };
    let alpha = components
        .iter()
        .flat_map(|c| if c.is_alpha() { Some(c) } else { None })
        .next()
        .map(|c| c.data_u8().collect::<Vec<_>>());
    let mut components_iters = image
        .components()
        .iter()
        .flat_map(|c| {
            if c.is_alpha() {
                None
            } else {
                Some(c.data_u8())
            }
        })
        .collect::<Vec<_>>();
    let mut buf = vec![];

    'outer: loop {
        for iter in &mut components_iters {
            if let Some(n) = iter.next() {
                buf.push(n);
            } else {
                break 'outer;
            }
        }
    }

    buf = if bpc == 8 {
        buf
    } else {
        scale(buf.as_slice(), bpc, cs.num_components(), width, height)?
    };

    Some(FilterResult {
        data: buf,
        image_data: Some(ImageData {
            alpha,
            color_space: cs,
            bits_per_component: bpc,
        }),
    })
}

fn scale(
    data: &[u8],
    bit_per_component: u8,
    num_components: u8,
    width: u32,
    height: u32,
) -> Option<Vec<u8>> {
    let mut input = vec![0; ((width + 1) * num_components as u32 * height) as usize];
    let bit_size = BitSize::from_u8(bit_per_component)?;
    let mut writer = BitWriter::new(&mut input, bit_size)?;

    let old_max = ((1 << 8) - 1) as f32;
    let new_max = ((1 << bit_per_component) - 1) as f32;

    for bytes in data.chunks_exact(num_components as usize * width as usize) {
        for byte in bytes {
            let scaled = ((*byte as f32 / old_max) * new_max) as u16;
            writer.write(scaled)?;
        }

        writer.align();
    }

    let final_pos = writer.cur_pos();
    input.truncate(final_pos);

    Some(input)
}