use anyd::codes::pdf417::{MicroPdf417Decoder, MicroPdf417Encoder};
use anyd::output::{BitMatrix, Encoding};
use anyd::segment::Segment;
use anyd::traits::{Decode, Encode};
#[allow(clippy::type_complexity)]
const REFERENCE: &[(&str, usize, &[&str])] = &[
(
"A",
1,
&[
"11001000101000011000110010011100110101",
"11101000101111101011101111011110110101",
"11101100101100011111001001011110010101",
"11001100101000011000110010011100010101",
"11011100101111101000101100011000010101",
"11011110101100010010111110011000110101",
"11001110101001111011110100011000100101",
"11101110101111011001100100011100100101",
"11100110101001111110011101011110100101",
"11110110101110001010111000011110101101",
"11110010101011111100010011011110101001",
],
),
(
"ABCDEFGH",
2,
&[
"1100100010100001100011001001111010101111000011001000101",
"1110100010110101111110111101111101000100110011101000101",
"1110110010101101100111100001100011111001001011101100101",
"1100110010100001100011001001000011000110010011001100101",
"1101110010100011011111101101111001000001001011011100101",
"1101111010110011110100011001000111100001010011011110101",
"1100111010100000101100110001110111001000001011001110101",
"1110111010111110011000101001110100110100000011101110101",
],
),
(
"Wikipedia",
3,
&[
"1100100010100001100011001001011001110110011101110011001110010001001110011001000101",
"1110100010110010111100001101001001110111111010001011101100101110001000011101000101",
"1110110010110000111010111001001101110101100001001110001000010001001111011101100101",
"1100110010110000100010001101000101110110011010000010001001100111011000011001100101",
"1101110010100010000111101001000100110101111101100111101101101111000001011011100101",
"1101111010100011010001110001000110110101110000100011001010100000111100011011110101",
],
),
(
"123456789012",
4,
&[
"110100111010111111010011000111111010101110001001110110100110001111101001111101001100111011010010001",
"110100110010000010101111000101000010011110001001110100110001111100100101100011111001001011010110001",
"110100010011100011000100110101110111101100001001100100100011001111011101000111011110110011010111001",
"110100011011100010110000010111100101000000101001100110111111001110010101111110111000101011010111101",
],
),
];
fn row_strings(matrix: &BitMatrix, num_rows: usize) -> Vec<String> {
let step = matrix.height() / num_rows;
(0..num_rows)
.map(|r| {
let y = r * step;
(0..matrix.width())
.map(|x| if matrix.get(x, y) { '1' } else { '0' })
.collect()
})
.collect()
}
#[test]
fn matches_zint_reference_bitmaps() {
let enc = MicroPdf417Encoder::new();
for &(data, cols, expected) in REFERENCE {
let segment = if data.bytes().all(|b| b.is_ascii_digit()) {
Segment::numeric(data.as_bytes().to_vec())
} else {
Segment::alphanumeric(data.as_bytes().to_vec())
};
let symbol = enc
.build_sized(vec![segment], Some(cols))
.unwrap_or_else(|e| panic!("build {data:?} cols {cols}: {e:?}"));
let Encoding::Matrix(m) = enc.encode(&symbol).unwrap() else {
panic!("expected a matrix");
};
let got = row_strings(&m, expected.len());
assert_eq!(
got.len(),
expected.len(),
"row count differs for {data:?} cols {cols}"
);
for (i, (g, e)) in got.iter().zip(expected).enumerate() {
assert_eq!(
g, e,
"row {i} differs from zint for {data:?} cols {cols}\n got: {g}\n exp: {e}"
);
}
}
}
fn assert_lossless(segments: Vec<Segment>, columns: Option<usize>) {
let enc = MicroPdf417Encoder::new();
let symbol = enc.build_sized(segments.clone(), columns).unwrap();
let encoding = enc.encode(&symbol).unwrap();
let decoded = MicroPdf417Decoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.segments, symbol.segments, "segments differ");
assert_eq!(decoded.meta, symbol.meta, "meta differs");
let re = enc.encode(&decoded).unwrap();
assert_eq!(encoding, re, "re-encode is not identical");
}
#[test]
fn roundtrip_text_all_columns() {
for cols in 1..=4 {
assert_lossless(
vec![Segment::alphanumeric(b"Hello, MicroPDF417!".to_vec())],
Some(cols),
);
}
}
#[test]
fn roundtrip_numeric_all_columns() {
for cols in 1..=4 {
assert_lossless(
vec![Segment::numeric(b"9876543210123456".to_vec())],
Some(cols),
);
}
}
#[test]
fn roundtrip_byte_all_columns() {
for cols in 1..=4 {
assert_lossless(
vec![Segment::byte(vec![0u8, 1, 2, 3, 127, 128, 254, 255])],
Some(cols),
);
}
}
#[test]
fn roundtrip_mixed_segments_autosize() {
assert_lossless(
vec![
Segment::alphanumeric(b"PART".to_vec()),
Segment::numeric(b"00427".to_vec()),
Segment::byte(vec![0xDE, 0xAD, 0xBE, 0xEF]),
],
None,
);
}
#[test]
fn autosize_grows_with_data() {
let enc = MicroPdf417Encoder::new();
let small = enc.build_text("Hi").unwrap();
let large = enc
.build_text("The quick brown fox jumps over the lazy dog 0123456789")
.unwrap();
let Encoding::Matrix(sm) = enc.encode(&small).unwrap() else {
panic!("matrix");
};
let Encoding::Matrix(lm) = enc.encode(&large).unwrap() else {
panic!("matrix");
};
assert!(
sm.width() * sm.height() <= lm.width() * lm.height(),
"larger payload should not yield a smaller symbol"
);
assert_eq!(
MicroPdf417Decoder::new().decode_matrix(&lm).unwrap().text(),
large.text()
);
}