#![forbid(unsafe_code)]
use crate::common::{
buffer::SliceWriter, errors::EncodeError, traits::BarcodeEncoder, types::Encoded,
};
const ITF_TABLE: [[bool; 5]; 10] = [
[false, false, true, true, false], [true, false, false, false, true], [false, true, false, false, true], [true, true, false, false, false], [false, false, true, false, true], [true, false, true, false, false], [false, true, true, false, false], [false, false, false, true, true], [true, false, false, true, false], [false, true, false, true, false], ];
pub struct Itf;
impl BarcodeEncoder for Itf {
type Input = str;
fn encode_into(input: &str, buf: &mut [bool]) -> Result<Encoded, EncodeError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(EncodeError::InvalidInput("ITF input must not be empty"));
}
if !trimmed.chars().all(|c| c.is_ascii_digit()) {
return Err(EncodeError::InvalidInput(
"ITF input must contain digits only",
));
}
let pad = !trimmed.len().is_multiple_of(2);
let len = encode_bars(trimmed.as_bytes(), pad, buf)?;
Ok(Encoded::Linear { len, height: 50 })
}
fn symbology_name() -> &'static str {
"ITF"
}
}
fn encode_bars(digits: &[u8], pad: bool, buf: &mut [bool]) -> Result<usize, EncodeError> {
let pad = pad as usize;
let total = digits.len() + pad;
let digit = |i: usize| -> usize {
if i < pad {
0
} else {
(digits[i - pad] - b'0') as usize
}
};
let mut w = SliceWriter::new(buf);
w.push(true)?;
w.push(false)?;
w.push(true)?;
w.push(false)?;
let mut i = 0;
while i + 1 < total {
let p1 = &ITF_TABLE[digit(i)];
let p2 = &ITF_TABLE[digit(i + 1)];
for j in 0..5 {
w.push_run(true, if p1[j] { 3 } else { 1 })?; w.push_run(false, if p2[j] { 3 } else { 1 })?; }
i += 2;
}
w.push_run(true, 3)?; w.push(false)?; w.push(true)?;
Ok(w.len())
}
#[cfg(test)]
mod tests {
use super::*;
fn encode<'a>(input: &str, buf: &'a mut [bool]) -> &'a [bool] {
match Itf::encode_into(input, buf).unwrap() {
Encoded::Linear { len, .. } => &buf[..len],
_ => panic!("expected linear"),
}
}
#[test]
fn test_encode_even_digits() {
let mut buf = [false; 512];
assert!(!encode("12345678", &mut buf).is_empty());
}
#[test]
fn test_encode_odd_digits_padded() {
let mut buf_odd = [false; 512];
let mut buf_even = [false; 512];
let odd = encode("1234567", &mut buf_odd);
let even = encode("01234567", &mut buf_even);
assert_eq!(odd, even);
}
#[test]
fn test_invalid_characters() {
let mut buf = [false; 512];
assert!(Itf::encode_into("1234A678", &mut buf).is_err());
}
#[test]
fn test_empty_input() {
let mut buf = [false; 512];
assert!(Itf::encode_into("", &mut buf).is_err());
}
#[test]
fn test_buffer_too_small() {
let mut buf = [false; 8];
assert_eq!(
Itf::encode_into("12345678", &mut buf),
Err(EncodeError::BufferTooSmall)
);
}
#[test]
fn test_symbology_name() {
assert_eq!(Itf::symbology_name(), "ITF");
}
#[test]
fn test_bar_length_two_digits() {
let mut buf = [false; 512];
assert_eq!(encode("12", &mut buf).len(), 27);
}
#[cfg(feature = "alloc")]
#[test]
fn test_svg_output() {
let svg = Itf::encode("1234").unwrap().to_svg_string();
assert!(svg.starts_with("<svg "));
}
}