use rxing::{BarcodeFormat, DecodeHints};
use super::error::AamvaError;
pub fn decode_pdf417_from_image_bytes(bytes: &[u8]) -> Result<String, AamvaError> {
let luma = image::load_from_memory(bytes)
.map_err(|e| AamvaError::ImageDecode(e.to_string()))?
.to_luma8();
let (width, height) = (luma.width(), luma.height());
decode_pdf417_from_luma8(width, height, luma.into_raw())
}
pub fn decode_pdf417_from_luma8(
width: u32,
height: u32,
luma: Vec<u8>,
) -> Result<String, AamvaError> {
let expected = (width as usize)
.checked_mul(height as usize)
.ok_or_else(|| AamvaError::ImageDecode(format!("dimensions {width}x{height} overflow")))?;
if luma.len() != expected {
return Err(AamvaError::PdfDecode(format!(
"luma buffer length {} does not match width*height={}",
luma.len(),
expected
)));
}
let mut hints = DecodeHints::default();
hints.TryHarder = Some(true);
let result = rxing::helpers::detect_in_luma_with_hints(
luma,
width,
height,
Some(BarcodeFormat::PDF_417),
&mut hints,
)
.map_err(map_rxing_err)?;
Ok(result.getText().to_string())
}
fn map_rxing_err(e: rxing::Exceptions) -> AamvaError {
match e {
rxing::Exceptions::NotFoundException(_) => AamvaError::BarcodeNotFound,
other => AamvaError::PdfDecode(other.to_string()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use image::{GrayImage, ImageFormat, Luma};
use rxing::pdf417::PDF417Writer;
use rxing::{EncodeHints, Writer};
use std::io::Cursor;
fn encode_pdf417_to_luma(text: &str, width: i32, height: i32) -> (u32, u32, Vec<u8>) {
let writer = PDF417Writer::default();
let matrix = writer
.encode_with_hints(
text,
&BarcodeFormat::PDF_417,
width,
height,
&EncodeHints::default(),
)
.expect("PDF417 encode");
let w = matrix.getWidth();
let h = matrix.getHeight();
let mut img = GrayImage::from_pixel(w, h, Luma([255u8]));
for y in 0..h {
for x in 0..w {
if matrix.get(x, y) {
img.put_pixel(x, y, Luma([0u8]));
}
}
}
(w, h, img.into_raw())
}
#[test]
fn roundtrip_simple_text_via_luma8() {
let original = "HELLO PDF417";
let (w, h, buf) = encode_pdf417_to_luma(original, 400, 120);
let decoded = decode_pdf417_from_luma8(w, h, buf).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn roundtrip_via_png_bytes() {
let original = "AAMVA-TEST";
let (w, h, buf) = encode_pdf417_to_luma(original, 400, 120);
let img = GrayImage::from_raw(w, h, buf).unwrap();
let mut png_bytes = Cursor::new(Vec::new());
img.write_to(&mut png_bytes, ImageFormat::Png).unwrap();
let decoded = decode_pdf417_from_image_bytes(png_bytes.get_ref()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn luma_dimensions_mismatch_is_rejected() {
let err = decode_pdf417_from_luma8(10, 10, vec![0u8; 50]).unwrap_err();
assert!(matches!(err, AamvaError::PdfDecode(_)));
}
}