use jbig2enc::encoder::encode_generic;
use jbig2enc::wire::{
SEGMENT_END_OF_FILE, SEGMENT_END_OF_PAGE, SEGMENT_IMM_GENERIC_REGION, SEGMENT_PAGE_INFORMATION,
};
use leptonica::{Pix, PixMut, PixelDepth};
fn white_1bpp(width: u32, height: u32) -> Pix {
PixMut::new(width, height, PixelDepth::Bit1).unwrap().into()
}
fn black_1bpp(width: u32, height: u32) -> Pix {
let mut pm = PixMut::new(width, height, PixelDepth::Bit1).unwrap();
pm.set_all_arbitrary(1).unwrap();
pm.into()
}
fn striped_1bpp(width: u32, height: u32) -> Pix {
let mut pm = PixMut::new(width, height, PixelDepth::Bit1).unwrap();
for y in 0..height {
if y % 2 == 0 {
let row = pm.row_data_mut(y);
for word in row.iter_mut() {
*word = 0xFFFF_FFFF;
}
}
}
pm.into()
}
fn segment_type_at(data: &[u8], offset: usize) -> u8 {
data[offset + 4] & 0x3F
}
fn segment_data_length_at(data: &[u8], offset: usize) -> u32 {
u32::from_be_bytes([
data[offset + 7],
data[offset + 8],
data[offset + 9],
data[offset + 10],
])
}
#[test]
fn rejects_non_1bpp_image() {
let pix = PixMut::new(32, 32, PixelDepth::Bit8).unwrap().into();
let result = encode_generic(&pix, true, 0, 0, false);
assert!(result.is_err());
}
#[test]
fn full_headers_structure_white_32x32() {
let pix = white_1bpp(32, 32);
let output = encode_generic(&pix, true, 300, 300, false).unwrap();
assert_eq!(
&output[0..8],
&[0x97, 0x4a, 0x42, 0x32, 0x0d, 0x0a, 0x1a, 0x0a]
);
assert_eq!(output[8], 0x01); assert_eq!(&output[9..13], &[0x00, 0x00, 0x00, 0x01]);
assert_eq!(segment_type_at(&output, 13), SEGMENT_PAGE_INFORMATION);
assert_eq!(segment_data_length_at(&output, 13), 19);
assert_eq!(&output[24..28], &[0x00, 0x00, 0x00, 0x20]); assert_eq!(&output[28..32], &[0x00, 0x00, 0x00, 0x20]); assert_eq!(&output[32..36], &[0x00, 0x00, 0x01, 0x2C]); assert_eq!(&output[36..40], &[0x00, 0x00, 0x01, 0x2C]); assert_eq!(output[40], 0x01);
assert_eq!(segment_type_at(&output, 43), SEGMENT_IMM_GENERIC_REGION);
let genreg_data_len = segment_data_length_at(&output, 43);
assert!(genreg_data_len >= 26);
assert_eq!(&output[54..58], &[0x00, 0x00, 0x00, 0x20]); assert_eq!(&output[58..62], &[0x00, 0x00, 0x00, 0x20]); assert_eq!(output[71], 0x00);
let arith_data_len = (genreg_data_len - 26) as usize;
let end_of_page_offset = 54 + 26 + arith_data_len;
assert_eq!(
segment_type_at(&output, end_of_page_offset),
SEGMENT_END_OF_PAGE
);
assert_eq!(segment_data_length_at(&output, end_of_page_offset), 0);
let end_of_file_offset = end_of_page_offset + 11;
assert_eq!(
segment_type_at(&output, end_of_file_offset),
SEGMENT_END_OF_FILE
);
assert_eq!(segment_data_length_at(&output, end_of_file_offset), 0);
assert_eq!(output.len(), end_of_file_offset + 11);
}
#[test]
fn segment_numbers_sequential() {
let pix = white_1bpp(32, 32);
let output = encode_generic(&pix, true, 0, 0, false).unwrap();
assert_eq!(&output[13..17], &[0x00, 0x00, 0x00, 0x00]);
assert_eq!(&output[43..47], &[0x00, 0x00, 0x00, 0x01]);
let genreg_data_len = segment_data_length_at(&output, 43) as usize;
let eop_offset = 54 + genreg_data_len;
assert_eq!(
&output[eop_offset..eop_offset + 4],
&[0x00, 0x00, 0x00, 0x02]
);
let eof_offset = eop_offset + 11;
assert_eq!(
&output[eof_offset..eof_offset + 4],
&[0x00, 0x00, 0x00, 0x03]
);
}
#[test]
fn no_headers_structure() {
let pix = white_1bpp(32, 32);
let output = encode_generic(&pix, false, 300, 300, false).unwrap();
assert_eq!(segment_type_at(&output, 0), SEGMENT_PAGE_INFORMATION);
assert_eq!(segment_data_length_at(&output, 0), 19);
assert_eq!(segment_type_at(&output, 30), SEGMENT_IMM_GENERIC_REGION);
let genreg_data_len = segment_data_length_at(&output, 30) as usize;
let expected_total = 30 + 11 + genreg_data_len;
assert_eq!(output.len(), expected_total);
}
#[test]
fn tpgdon_flag_set() {
let pix = white_1bpp(32, 32);
let output = encode_generic(&pix, true, 0, 0, true).unwrap();
assert_eq!(output[71], 0x08);
}
#[test]
fn tpgd_produces_different_output() {
let pix = white_1bpp(64, 64);
let without_tpgd = encode_generic(&pix, false, 0, 0, false).unwrap();
let with_tpgd = encode_generic(&pix, false, 0, 0, true).unwrap();
assert_ne!(without_tpgd, with_tpgd);
}
#[test]
fn uses_pix_resolution_when_zero() {
let mut pm = PixMut::new(32, 32, PixelDepth::Bit1).unwrap();
pm.set_resolution(150, 200);
let pix: Pix = pm.into();
let output = encode_generic(&pix, true, 0, 0, false).unwrap();
assert_eq!(&output[32..36], &[0x00, 0x00, 0x00, 0x96]); assert_eq!(&output[36..40], &[0x00, 0x00, 0x00, 0xC8]); }
#[test]
fn explicit_resolution_overrides_pix() {
let mut pm = PixMut::new(32, 32, PixelDepth::Bit1).unwrap();
pm.set_resolution(150, 200);
let pix: Pix = pm.into();
let output = encode_generic(&pix, true, 72, 72, false).unwrap();
assert_eq!(&output[32..36], &[0x00, 0x00, 0x00, 0x48]); assert_eq!(&output[36..40], &[0x00, 0x00, 0x00, 0x48]);
}
#[test]
fn non_32_aligned_width() {
let pix = white_1bpp(33, 10);
let output = encode_generic(&pix, false, 0, 0, false).unwrap();
assert_eq!(&output[41..45], &[0x00, 0x00, 0x00, 0x21]); assert_eq!(&output[45..49], &[0x00, 0x00, 0x00, 0x0A]); }
#[test]
fn all_black_image() {
let pix = black_1bpp(32, 32);
let result = encode_generic(&pix, false, 0, 0, false);
assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.is_empty());
}
#[test]
fn striped_image_with_tpgd() {
let pix = striped_1bpp(64, 64);
let result = encode_generic(&pix, false, 0, 0, true);
assert!(result.is_ok());
}
#[test]
fn minimal_1x1_image() {
let pix = white_1bpp(1, 1);
let result = encode_generic(&pix, true, 0, 0, false);
assert!(result.is_ok());
}