From 87cea537c9a808a436532fc56d468e5111f1821c Mon Sep 17 00:00:00 2001 From: oqpvc <5766455+oqpvc@users.noreply.github.com> Date: Sat, 7 Dec 2024 02:03:52 +0300 Subject: [PATCH] create error images for faulty decodes --- Cargo.toml | 1 + src/image_container.rs | 55 ++++++++++++++++++----------------- src/image_helpers.rs | 35 ++++++++++++++++++++++ src/lib.rs | 2 +- src/main.rs | 3 -- src/typst_helpers.rs | 4 +-- src/webapp/generate_report.rs | 2 +- 7 files changed, 69 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d60e32b..2f50fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ time = "0.3.36" ttf-parser = "0.25.0" typst-pdf = "0.12.0" typst-render = "0.12.0" +ab_glyph = "0.2.29" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] rayon = "1.10.0" diff --git a/src/image_container.rs b/src/image_container.rs index 1f83106..331df95 100644 --- a/src/image_container.rs +++ b/src/image_container.rs @@ -1,4 +1,4 @@ -use crate::image_helpers::{binary_image_from_image, fax_to_grayimage}; +use crate::image_helpers::{binary_image_from_image, create_error_image, fax_to_grayimage}; use image::{DynamicImage, GrayImage, ImageDecoder}; use pdf::any::AnySync; @@ -129,9 +129,7 @@ impl ImageContainer for PdfContainer { fn get_page(&mut self, n: usize) -> Result> { let file = &self.pdf_file; let resolver = file.resolver(); - let page = file.get_page(n as u32)?; - let image = page.resources()?.xobjects.iter().find_map(|(_name, &r)| { resolver.get(r).ok().and_then(|o| match *o { XObject::Image(ref im) => Some(im.clone()), @@ -141,35 +139,40 @@ impl ImageContainer for PdfContainer { if let Some(img) = image { let (image_data, filter) = img.raw_image_data(&resolver).unwrap(); - let grayimage: Result> = match filter - { - Some(pdf::enc::StreamFilter::DCTDecode(_)) => Ok(binary_image_from_image( - image::load_from_memory_with_format(&image_data, image::ImageFormat::Jpeg)?, - )), - - Some(pdf::enc::StreamFilter::FlateDecode(_)) => Ok(binary_image_from_image( - image::load_from_memory_with_format(&image_data, image::ImageFormat::Png)?, - )), - + let result = match filter { + Some(pdf::enc::StreamFilter::DCTDecode(_)) => { + match image::load_from_memory_with_format(&image_data, image::ImageFormat::Jpeg) + { + Ok(img) => Ok(binary_image_from_image(img)), + Err(e) => Ok(create_error_image(&format!( + "Failed to decode JPEG on page {}: {}", + n + 1, + e + ))), + } + } + Some(pdf::enc::StreamFilter::FlateDecode(_)) => { + match image::load_from_memory_with_format(&image_data, image::ImageFormat::Png) + { + Ok(img) => Ok(binary_image_from_image(img)), + Err(e) => Ok(create_error_image(&format!( + "Failed to decode PNG on page {}: {}", + n + 1, + e + ))), + } + } Some(pdf::enc::StreamFilter::CCITTFaxDecode(_)) => { Ok(fax_to_grayimage(&image_data, img.width, img.height)) } - - _ => Err(std::boxed::Box::new(image::ImageError::Unsupported( - image::error::UnsupportedError::from_format_and_kind( - image::error::ImageFormatHint::Unknown, - image::error::UnsupportedErrorKind::GenericFeature( - "Unsupported feature".to_string(), - ), - ), + _ => Ok(create_error_image(&format!( + "Could not decode image on page {}: Unsupported format", + n + 1 ))), }; - grayimage + result } else { - Err(std::boxed::Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "Page has no image", - ))) + Ok(create_error_image(&format!("No image on page {}", n + 1))) } } } diff --git a/src/image_helpers.rs b/src/image_helpers.rs index 80e1967..b86ab8b 100644 --- a/src/image_helpers.rs +++ b/src/image_helpers.rs @@ -3,6 +3,7 @@ use fax::decoder; use fax::Color; use image::{DynamicImage, GrayImage, ImageReader, Luma, RgbImage}; use imageproc::drawing; + use std::path::Path; /// This is the computation of the Kapur level using equ. (18) in @@ -158,3 +159,37 @@ pub fn rgb_to_egui_color_image(image: &RgbImage) -> egui::ColorImage { pixels, } } + +pub fn create_error_image(error_text: &str) -> GrayImage { + // Create a new 400x300 grayscale image + let mut image = GrayImage::new(800, 300); + + // Fill with light gray background + for pixel in image.pixels_mut() { + *pixel = image::Luma([240u8]); + } + + // Load font from binary data embedded in the executable + let font_data = crate::typst_helpers::BIOLINUM_BOLD; + let font = ab_glyph::FontArc::try_from_slice(font_data).expect("Error loading font"); + + // Configure font scale (size) + let scale = ab_glyph::PxScale::from(30.0); + + // Calculate text position + let x = 20; // Padding from left + let y = 150; // Vertically centered + + // Draw the error text + imageproc::drawing::draw_text_mut( + &mut image, + image::Luma([50u8]), // Dark gray text + x, + y, + scale, + &font, + error_text, + ); + + image +} diff --git a/src/lib.rs b/src/lib.rs index 63d7754..1400e28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ pub fn generate_reports_for_image_container( let scan = Scan { image: img }; let template_scan = TemplateScan::new(template, scan); let report = template_scan - .generate_image_report(key, &format!("page{}", idx + turn * chunksize)); + .generate_image_report(key, &format!("page{}", idx + turn * chunksize + 1)); report.save_to_file(&out_prefix); let file_name = report.save_filename(&"".to_string()); (file_name, report.sid, report.score) diff --git a/src/main.rs b/src/main.rs index 0385163..2d789bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,7 +121,6 @@ fn main() -> Result<(), std::boxed::Box> { csv_report = generate_reports_for_image_container(&mut container, &t, &k, outpath) .expect("error generating report"); - println!("{}", csv_report); } Some("tif") | Some("tiff") => { let buffer = std::io::BufReader::new( @@ -209,8 +208,6 @@ fn main() -> Result<(), std::boxed::Box> { &template, ); - // println!("{:#?}", document); - let pdf = typst_pdf::pdf(&document, &typst_pdf::PdfOptions::default()).expect("bla"); let _ = std::fs::write(format!("{}.pdf", outprefix), pdf); diff --git a/src/typst_helpers.rs b/src/typst_helpers.rs index 9c6c01d..02cc422 100644 --- a/src/typst_helpers.rs +++ b/src/typst_helpers.rs @@ -13,8 +13,8 @@ use crate::point::Point; use crate::template::{Box, Question, Template}; // hardcoding these for easy wasm support -const BIOLINUM: &[u8] = include_bytes!("../assets/linux-biolinum.regular.ttf"); -const BIOLINUM_BOLD: &[u8] = include_bytes!("../assets/linux-biolinum.bold.ttf"); +pub const BIOLINUM: &[u8] = include_bytes!("../assets/linux-biolinum.regular.ttf"); +pub const BIOLINUM_BOLD: &[u8] = include_bytes!("../assets/linux-biolinum.bold.ttf"); #[derive(Debug)] enum BubbleType { diff --git a/src/webapp/generate_report.rs b/src/webapp/generate_report.rs index feb8f5a..8720a2e 100644 --- a/src/webapp/generate_report.rs +++ b/src/webapp/generate_report.rs @@ -276,7 +276,7 @@ impl GenerateReport { let template_scan = TemplateScan::new(&template, scan); template_scan.generate_image_report( &key, - &format!("page{}", idx + turn * chunksize), + &format!("page{}", idx + turn * chunksize + 1), ) }) .collect();