use anyd::codes::pdf417::{EcLevel, Pdf417Decoder, Pdf417Encoder};
use anyd::output::Encoding;
use anyd::segment::Segment;
use anyd::traits::{Decode, Encode};
fn level(n: u8) -> EcLevel {
EcLevel::new(n).unwrap()
}
fn assert_lossless(segments: Vec<Segment>, ec: EcLevel) {
let enc = Pdf417Encoder::new();
let symbol = enc.build(segments.clone(), ec).unwrap();
let encoding = enc.encode(&symbol).unwrap();
let decoded = Pdf417Decoder::new().decode(&encoding).unwrap();
assert_eq!(
decoded.segments, symbol.segments,
"segments differ after decode"
);
assert_eq!(decoded.meta, symbol.meta, "meta differs after decode");
let reencoded = enc.encode(&decoded).unwrap();
assert_eq!(reencoded, encoding, "re-encoding is not byte-identical");
}
#[test]
fn text_all_levels() {
for l in 0..=4 {
assert_lossless(
vec![Segment::alphanumeric(
b"PDF417 Text Compaction: Mixed 123 $tuff!".to_vec(),
)],
level(l),
);
}
}
#[test]
fn numeric_all_levels() {
for l in 0..=4 {
assert_lossless(
vec![Segment::numeric(b"00213298174000123456789".to_vec())],
level(l),
);
}
}
#[test]
fn byte_all_levels() {
for l in 0..=4 {
assert_lossless(
vec![Segment::byte((0u8..=250).collect::<Vec<u8>>())],
level(l),
);
}
}
#[test]
fn byte_utf8() {
assert_lossless(
vec![Segment::byte("héllo wörld — 日本語 🦀".as_bytes().to_vec())],
level(2),
);
}
#[test]
fn mixed_segments_preserve_boundaries() {
let segments = vec![
Segment::numeric(b"12345".to_vec()),
Segment::alphanumeric(b"ABCdef".to_vec()),
Segment::byte(b"\x00\x01\xff\xfe raw".to_vec()),
Segment::numeric(b"9876543210".to_vec()),
];
assert_lossless(segments, level(3));
}
#[test]
fn numeric_odd_remainders() {
assert_lossless(vec![Segment::numeric(b"7".to_vec())], level(1));
assert_lossless(vec![Segment::numeric(b"42".to_vec())], level(1));
assert_lossless(vec![Segment::numeric(b"100".to_vec())], level(1));
let long: Vec<u8> = (0..100).map(|i| b'0' + (i % 10) as u8).collect();
assert_lossless(vec![Segment::numeric(long)], level(2));
}
#[test]
fn large_payload_multiple_columns() {
let data = vec![b'A'; 400];
assert_lossless(vec![Segment::alphanumeric(data)], level(3));
}
#[test]
fn explicit_columns() {
let enc = Pdf417Encoder::new();
let symbol = enc
.build_sized(
vec![Segment::alphanumeric(b"FIXED WIDTH".to_vec())],
level(2),
Some(5),
)
.unwrap();
let encoding = enc.encode(&symbol).unwrap();
let Encoding::Matrix(m) = &encoding else {
panic!("expected matrix");
};
assert_eq!(m.width(), 17 * 5 + 69);
let decoded = Pdf417Decoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.segments, symbol.segments);
assert_eq!(decoded.meta, symbol.meta);
assert_eq!(enc.encode(&decoded).unwrap(), encoding);
}
#[test]
fn survives_correctable_errors() {
let enc = Pdf417Encoder::new();
let symbol = enc
.build(
vec![Segment::alphanumeric(
b"RESILIENT PDF417 PAYLOAD 2026".to_vec(),
)],
level(5),
)
.unwrap();
let Encoding::Matrix(mut m) = enc.encode(&symbol).unwrap() else {
panic!("expected matrix");
};
let rows = m.height() / 3;
for y in 0..rows.min(8) {
let x0 = 34; for j in 0..17 {
for dy in 0..3 {
let v = m.get(x0 + j, y * 3 + dy);
m.set(x0 + j, y * 3 + dy, !v);
}
}
}
let decoded = Pdf417Decoder::new().decode(&Encoding::Matrix(m)).unwrap();
assert_eq!(
decoded.text().as_deref(),
Some("RESILIENT PDF417 PAYLOAD 2026")
);
}
#[test]
fn text_convenience_roundtrip() {
let enc = Pdf417Encoder::new();
let symbol = enc
.build_text("https://github.com/KarpelesLab/anydcode", level(2))
.unwrap();
let encoding = enc.encode(&symbol).unwrap();
let decoded = Pdf417Decoder::new().decode(&encoding).unwrap();
assert_eq!(
decoded.text().as_deref(),
Some("https://github.com/KarpelesLab/anydcode")
);
assert_eq!(enc.encode(&decoded).unwrap(), encoding);
}
#[test]
fn reference_codewords_via_public_api() {
let enc = Pdf417Encoder::new();
let symbol = enc
.build_sized(
vec![Segment::alphanumeric(b"ABCD".to_vec())],
level(0),
Some(1),
)
.unwrap();
let Encoding::Matrix(m) = enc.encode(&symbol).unwrap() else {
panic!("expected matrix");
};
let decoded = Pdf417Decoder::new().decode(&Encoding::Matrix(m)).unwrap();
assert_eq!(decoded.text().as_deref(), Some("ABCD"));
}