#![forbid(unsafe_code)]
extern crate alloc;
use alloc::{vec, vec::Vec};
use crate::common::{
errors::EncodeError,
traits::BarcodeEncoder,
types::{BarcodeOutput, MatrixBarcode},
};
const START_PATTERN: [u8; 8] = [8, 1, 1, 1, 1, 1, 1, 3];
const STOP_PATTERN: [u8; 9] = [7, 1, 1, 3, 1, 1, 1, 2, 1];
const PDF417_PRIME: u32 = 929;
const DEFAULT_EC_LEVEL: usize = 2;
fn codeword_pattern(cluster: usize, codeword: u16) -> [u8; 8] {
let c = codeword as u32;
let k = cluster as u32;
pdf417_encode_codeword(k, c)
}
fn pdf417_encode_codeword(cluster: u32, c: u32) -> [u8; 8] {
let mut pattern = [0u8; 8];
let remaining = c;
let shift = cluster * 3;
let val = remaining + shift;
let bars = [
((val / 729) % 6 + 1) as u8,
((val / 243) % 6 + 1) as u8,
((val / 81) % 6 + 1) as u8,
((val / 27) % 6 + 1) as u8,
];
let bar_sum: u8 = bars[0] + bars[1] + bars[2] + bars[3];
let space_total = 17u8.saturating_sub(bar_sum);
let spaces = distribute_spaces(space_total);
pattern[0] = bars[0];
pattern[1] = spaces[0];
pattern[2] = bars[1];
pattern[3] = spaces[1];
pattern[4] = bars[2];
pattern[5] = spaces[2];
pattern[6] = bars[3];
pattern[7] = spaces[3];
pattern
}
fn distribute_spaces(total: u8) -> [u8; 4] {
if total < 4 {
return [1, 1, 1, 1]; }
let base = total / 4;
let extra = total % 4;
[
base + if extra > 0 { 1 } else { 0 },
base + if extra > 1 { 1 } else { 0 },
base + if extra > 2 { 1 } else { 0 },
base,
]
}
fn rs_encode(data: &[u16], level: usize) -> Vec<u16> {
let ec_count = 1usize << (level + 1);
let g = rs_generator(ec_count);
let mut remainder: Vec<u32> = vec![0u32; ec_count];
for &d in data {
let lead = (d as u32 + remainder[0]) % PDF417_PRIME;
for i in 0..ec_count - 1 {
remainder[i] = remainder[i + 1];
}
remainder[ec_count - 1] = 0;
for i in 0..ec_count {
remainder[i] =
(remainder[i] + PDF417_PRIME - (lead * g[i] as u32) % PDF417_PRIME) % PDF417_PRIME;
}
}
remainder.iter().rev().map(|&v| v as u16).collect()
}
fn rs_generator(k: usize) -> Vec<u16> {
let mut g = vec![1u16; 1];
for i in 0..k {
let root = gf929_pow(3, i as u32);
let mut new_g = vec![0u16; g.len() + 1];
for (j, &gj) in g.iter().enumerate() {
new_g[j] = (new_g[j] as u32 + gj as u32) as u16 % PDF417_PRIME as u16;
new_g[j + 1] = (new_g[j + 1] as u32 + gj as u32 * (PDF417_PRIME - root) % PDF417_PRIME)
as u16
% PDF417_PRIME as u16;
}
g = new_g;
}
g
}
fn gf929_pow(base: u32, exp: u32) -> u32 {
let mut result = 1u32;
let mut b = base % PDF417_PRIME;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result = result * b % PDF417_PRIME;
}
b = b * b % PDF417_PRIME;
e >>= 1;
}
result
}
fn text_compaction(input: &str) -> Vec<u16> {
let bytes = input.as_bytes();
let mut sub_values: Vec<u8> = Vec::new();
for &b in bytes {
let sub = text_sub_value(b);
sub_values.push(sub);
}
if !sub_values.len().is_multiple_of(2) {
sub_values.push(29); }
let mut codewords: Vec<u16> = Vec::new();
let mut i = 0;
while i + 1 < sub_values.len() {
let cw = sub_values[i] as u16 * 30 + sub_values[i + 1] as u16;
codewords.push(cw);
i += 2;
}
codewords
}
fn text_sub_value(b: u8) -> u8 {
match b {
b'A'..=b'Z' => b - b'A',
b' ' => 26,
b'\r' => 27,
b'\t' => 28, b'\n' => 28, b'a'..=b'z' => b - b'a', _ => 29, }
}
pub struct Pdf417;
impl BarcodeEncoder for Pdf417 {
type Input = str;
type Error = EncodeError;
fn encode(input: &str) -> Result<BarcodeOutput, EncodeError> {
if input.is_empty() {
return Err(EncodeError::InvalidInput(
"PDF417 input must not be empty".into(),
));
}
if input.len() > 1850 {
return Err(EncodeError::DataTooLong);
}
let matrix = encode_pdf417(input, DEFAULT_EC_LEVEL)?;
let width = if matrix.is_empty() {
0
} else {
matrix[0].len()
};
let height = matrix.len();
Ok(BarcodeOutput::Matrix(MatrixBarcode {
modules: matrix,
width,
height,
}))
}
fn symbology_name() -> &'static str {
"PDF417"
}
}
fn encode_pdf417(input: &str, ec_level: usize) -> Result<Vec<Vec<bool>>, EncodeError> {
let mut data_codewords = text_compaction(input);
let ec_count = 1usize << (ec_level + 1);
let total_data = data_codewords.len();
let total_codewords = total_data + ec_count;
let isqrt = {
let n = total_codewords;
if n == 0 {
0
} else {
let mut x = n;
let mut y = x.div_ceil(2);
while y < x {
x = y;
y = (x + n / x) / 2;
}
x
}
};
let cols = isqrt.clamp(3, 30);
let rows = total_codewords.div_ceil(cols).clamp(3, 90);
let capacity = rows * cols;
while data_codewords.len() < capacity - ec_count {
data_codewords.push(900); }
let mut all_codewords: Vec<u16> = Vec::with_capacity(capacity);
all_codewords.push((total_codewords + 1) as u16); all_codewords.extend_from_slice(&data_codewords);
let ec_codewords = rs_encode(&all_codewords, ec_level);
while all_codewords.len() < capacity {
all_codewords.push(900);
}
all_codewords.extend_from_slice(&ec_codewords);
let mut matrix: Vec<Vec<bool>> = Vec::new();
for row_idx in 0..rows {
let cluster = row_idx % 3; let cluster_id = cluster * 3;
let mut row_bits: Vec<bool> = Vec::new();
append_pattern_bits(&mut row_bits, &START_PATTERN);
let left_indicator = left_row_indicator(row_idx, rows, cols, ec_level, cluster);
append_codeword_bits(&mut row_bits, cluster_id, left_indicator);
for col_idx in 0..cols {
let cw_idx = row_idx * cols + col_idx;
let cw = if cw_idx < all_codewords.len() {
all_codewords[cw_idx]
} else {
900 };
append_codeword_bits(&mut row_bits, cluster_id, cw);
}
let right_indicator = right_row_indicator(row_idx, rows, cols, ec_level, cluster);
append_codeword_bits(&mut row_bits, cluster_id, right_indicator);
append_stop_pattern_bits(&mut row_bits);
matrix.push(row_bits);
}
Ok(matrix)
}
fn left_row_indicator(
row: usize,
rows: usize,
cols: usize,
ec_level: usize,
cluster: usize,
) -> u16 {
let r = row;
let c = cols - 1;
let e = ec_level;
match cluster {
0 => (30 * (r / 3) + (rows - 1) / 3) as u16,
1 => (30 * (r / 3) + e * 3 + (rows - 1) % 3) as u16,
_ => (30 * (r / 3) + c) as u16,
}
}
fn right_row_indicator(
row: usize,
rows: usize,
cols: usize,
ec_level: usize,
cluster: usize,
) -> u16 {
let r = row;
let c = cols - 1;
let e = ec_level;
match cluster {
0 => (30 * (r / 3) + c) as u16,
1 => (30 * (r / 3) + (rows - 1) / 3) as u16,
_ => (30 * (r / 3) + e * 3 + (rows - 1) % 3) as u16,
}
}
fn append_codeword_bits(bits: &mut Vec<bool>, cluster: usize, codeword: u16) {
let pattern = codeword_pattern(cluster, codeword);
let mut dark = true;
for &w in &pattern {
for _ in 0..w {
bits.push(dark);
}
dark = !dark;
}
}
fn append_pattern_bits(bits: &mut Vec<bool>, pattern: &[u8]) {
let mut dark = true;
for &w in pattern {
for _ in 0..w {
bits.push(dark);
}
dark = !dark;
}
}
fn append_stop_pattern_bits(bits: &mut Vec<bool>) {
let mut dark = true;
for &w in &STOP_PATTERN {
for _ in 0..w {
bits.push(dark);
}
dark = !dark;
}
bits.push(true);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_basic() {
let out = Pdf417::encode("Hello, PDF417!").unwrap();
match out {
BarcodeOutput::Matrix(mb) => {
assert!(mb.height >= 3);
assert!(mb.width > 0);
}
_ => panic!("expected matrix barcode"),
}
}
#[test]
fn test_encode_numbers() {
let out = Pdf417::encode("1234567890").unwrap();
assert!(matches!(out, BarcodeOutput::Matrix(_)));
}
#[test]
fn test_empty_input() {
assert!(Pdf417::encode("").is_err());
}
#[test]
fn test_symbology_name() {
assert_eq!(Pdf417::symbology_name(), "PDF417");
}
#[test]
fn test_row_count() {
let out = Pdf417::encode("ABC").unwrap();
match out {
BarcodeOutput::Matrix(mb) => {
assert!(mb.height >= 3); }
_ => panic!("expected matrix"),
}
}
#[test]
fn test_svg_output() {
let svg = Pdf417::encode("Test").unwrap().to_svg_string();
assert!(svg.starts_with("<svg "));
}
#[test]
fn test_rs_encode_basic() {
let data = vec![1u16, 2, 3, 4];
let ec = rs_encode(&data, 2);
assert_eq!(ec.len(), 8); }
#[test]
fn test_gf929_pow() {
assert_eq!(gf929_pow(3, 0), 1);
assert_eq!(gf929_pow(3, 1), 3);
assert_eq!(gf929_pow(3, 2), 9);
}
}