katana-document-viewer 0.1.4

KatanA document viewer artifact, render evaluation, and export foundation.
Documentation
use crate::export_surface::DocumentSurface;
use image::codecs::jpeg::JpegEncoder;
use image::codecs::png::PngEncoder;
use image::{ColorType, ImageEncoder};

pub(crate) struct ImageExportPayloadFactory;

impl ImageExportPayloadFactory {
    pub(crate) fn create_png(surface: &DocumentSurface) -> Result<Vec<u8>, String> {
        let mut bytes = Vec::new();
        let encoder = PngEncoder::new(&mut bytes);
        encoder
            .write_image(
                surface.image.as_raw(),
                surface.image.width(),
                surface.image.height(),
                ColorType::Rgba8.into(),
            )
            .map_err(png_encoding_error)?;
        Ok(bytes)
    }

    pub(crate) fn create_jpeg(surface: &DocumentSurface) -> Result<Vec<u8>, String> {
        let mut rgb = Vec::with_capacity(
            (surface.image.width() * surface.image.height() * RGB_CHANNELS_PER_PIXEL) as usize,
        );
        for pixel in surface.image.pixels() {
            rgb.extend_from_slice(&[pixel[0], pixel[1], pixel[2]]);
        }
        let mut bytes = Vec::new();
        let mut encoder = JpegEncoder::new_with_quality(&mut bytes, JPEG_QUALITY);
        encoder
            .encode(
                &rgb,
                surface.image.width(),
                surface.image.height(),
                ColorType::Rgb8.into(),
            )
            .map_err(jpeg_encoding_error)?;
        Ok(bytes)
    }
}

const RGB_CHANNELS_PER_PIXEL: u32 = 3;
const JPEG_QUALITY: u8 = 90;

fn png_encoding_error(error: image::ImageError) -> String {
    format!("PNG encoding failed: {error}")
}

fn jpeg_encoding_error(error: image::ImageError) -> String {
    format!("JPEG encoding failed: {error}")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn image_encoding_errors_include_format_name() {
        let png = png_encoding_error(image::ImageError::IoError(std::io::Error::other("png")));
        let jpeg = jpeg_encoding_error(image::ImageError::IoError(std::io::Error::other("jpeg")));

        assert!(png.contains("PNG encoding failed"));
        assert!(jpeg.contains("JPEG encoding failed"));
    }
}