#![forbid(unsafe_code)]
use crate::common::{
buffer::SliceWriter, errors::EncodeError, traits::BarcodeEncoder, types::Encoded,
};
pub(crate) const MAX_DATA: usize = 512;
pub(crate) const MAX_SYMBOLS: usize = MAX_DATA + 3;
pub(crate) const PATTERNS: &[[u8; 6]; 107] = &[
[2, 1, 2, 2, 2, 2], [2, 2, 2, 1, 2, 2], [2, 2, 2, 2, 2, 1], [1, 2, 1, 2, 2, 3], [1, 2, 1, 3, 2, 2], [1, 3, 1, 2, 2, 2], [1, 2, 2, 2, 1, 3], [1, 2, 2, 3, 1, 2], [1, 3, 2, 2, 1, 2], [2, 2, 1, 2, 1, 3], [2, 2, 1, 3, 1, 2], [2, 3, 1, 2, 1, 2], [1, 1, 2, 2, 3, 2], [1, 2, 2, 1, 3, 2], [1, 2, 2, 2, 3, 1], [1, 1, 3, 2, 2, 2], [1, 2, 3, 1, 2, 2], [1, 2, 3, 2, 2, 1], [2, 2, 3, 2, 1, 1], [2, 2, 1, 1, 3, 2], [2, 2, 1, 2, 3, 1], [2, 1, 3, 2, 1, 2], [2, 2, 3, 1, 1, 2], [3, 1, 2, 1, 3, 1], [3, 1, 1, 2, 2, 2], [3, 2, 1, 1, 2, 2], [3, 2, 1, 2, 2, 1], [3, 1, 2, 2, 1, 2], [3, 2, 2, 1, 1, 2], [3, 2, 2, 2, 1, 1], [2, 1, 2, 1, 2, 3], [2, 1, 2, 3, 2, 1], [2, 3, 2, 1, 2, 1], [1, 1, 1, 3, 2, 3], [1, 3, 1, 1, 2, 3], [1, 3, 1, 3, 2, 1], [1, 1, 2, 3, 1, 3], [1, 3, 2, 1, 1, 3], [1, 3, 2, 3, 1, 1], [2, 1, 1, 3, 1, 3], [2, 3, 1, 1, 1, 3], [2, 3, 1, 3, 1, 1], [1, 1, 2, 1, 3, 3], [1, 1, 2, 3, 3, 1], [1, 3, 2, 1, 3, 1], [1, 1, 3, 1, 2, 3], [1, 1, 3, 3, 2, 1], [1, 3, 3, 1, 2, 1], [3, 1, 3, 1, 2, 1], [2, 1, 1, 3, 3, 1], [2, 3, 1, 1, 3, 1], [2, 1, 3, 1, 1, 3], [2, 1, 3, 3, 1, 1], [2, 1, 3, 1, 3, 1], [3, 1, 1, 1, 2, 3], [3, 1, 1, 3, 2, 1], [3, 3, 1, 1, 2, 1], [3, 1, 2, 1, 1, 3], [3, 1, 2, 3, 1, 1], [3, 3, 2, 1, 1, 1], [3, 1, 4, 1, 1, 1], [2, 2, 1, 4, 1, 1], [4, 3, 1, 1, 1, 1], [1, 1, 1, 2, 2, 4], [1, 1, 1, 4, 2, 2], [1, 2, 1, 1, 2, 4], [1, 2, 1, 4, 2, 1], [1, 4, 1, 1, 2, 2], [1, 4, 1, 2, 2, 1], [1, 1, 2, 2, 1, 4], [1, 1, 2, 4, 1, 2], [1, 2, 2, 1, 1, 4], [1, 2, 2, 4, 1, 1], [1, 4, 2, 1, 1, 2], [1, 4, 2, 2, 1, 1], [2, 4, 1, 2, 1, 1], [2, 2, 1, 1, 1, 4], [4, 1, 3, 1, 1, 1], [2, 4, 1, 1, 1, 2], [1, 3, 4, 1, 1, 1], [1, 1, 1, 2, 4, 2], [1, 2, 1, 1, 4, 2], [1, 2, 1, 2, 4, 1], [1, 1, 4, 2, 1, 2], [1, 2, 4, 1, 1, 2], [1, 2, 4, 2, 1, 1], [4, 1, 1, 2, 1, 2], [4, 2, 1, 1, 1, 2], [4, 2, 1, 2, 1, 1], [2, 1, 2, 1, 4, 1], [2, 1, 4, 1, 2, 1], [4, 1, 2, 1, 2, 1], [1, 1, 1, 1, 4, 3], [1, 1, 1, 3, 4, 1], [1, 3, 1, 1, 4, 1], [1, 1, 4, 1, 1, 3], [1, 1, 4, 3, 1, 1], [4, 1, 1, 1, 1, 3], [4, 1, 1, 3, 1, 1], [1, 1, 3, 1, 4, 1], [1, 1, 4, 1, 3, 1], [3, 1, 1, 1, 4, 1], [4, 1, 1, 1, 3, 1], [2, 1, 1, 4, 1, 2], [2, 1, 1, 2, 1, 4], [2, 1, 1, 2, 3, 2], [2, 3, 3, 1, 1, 1], ];
pub(crate) const STOP_TERMINATION: u8 = 2;
pub(crate) const START_A: u8 = 103;
pub(crate) const START_B: u8 = 104;
pub(crate) const START_C: u8 = 105;
pub(crate) const STOP: u8 = 106;
pub(crate) const FNC1: u8 = 102;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Subset {
A,
B,
C,
}
fn best_subset(input: &[u8]) -> Result<Subset, EncodeError> {
if input.len() >= 2
&& input.len().is_multiple_of(2)
&& input.iter().all(|&b| b.is_ascii_digit())
{
return Ok(Subset::C);
}
if input.iter().any(|&b| b < 0x20 || b == 0x7F) {
if input.iter().all(|&b| b <= 0x5F || b == 0x7F) {
return Ok(Subset::A);
}
return Err(EncodeError::InvalidInput(
"input contains characters not encodable in Code 128A",
));
}
if input.iter().all(|&b| (0x20..=0x7E).contains(&b)) {
return Ok(Subset::B);
}
Err(EncodeError::InvalidInput(
"input contains characters outside the Code 128 character set",
))
}
fn symbol_value_a(byte: u8) -> u8 {
if byte <= 0x1F { byte + 64 } else { byte - 0x20 }
}
fn symbol_value_b(byte: u8) -> u8 {
byte - 0x20
}
pub struct Code128;
impl BarcodeEncoder for Code128 {
type Input = str;
fn encode_into(input: &str, buf: &mut [bool]) -> Result<Encoded, EncodeError> {
if input.is_empty() {
return Err(EncodeError::InvalidInput(
"Code 128 input must not be empty",
));
}
let bytes = input.as_bytes();
if bytes.len() > MAX_DATA {
return Err(EncodeError::DataTooLong);
}
let subset = best_subset(bytes)?;
let mut symbols = [0u8; MAX_SYMBOLS];
let mut n = 0;
symbols[n] = match subset {
Subset::A => START_A,
Subset::B => START_B,
Subset::C => START_C,
};
n += 1;
match subset {
Subset::A => {
for &b in bytes {
symbols[n] = symbol_value_a(b);
n += 1;
}
}
Subset::B => {
for &b in bytes {
symbols[n] = symbol_value_b(b);
n += 1;
}
}
Subset::C => {
let mut i = 0;
while i + 1 < bytes.len() {
let tens = bytes[i] - b'0';
let units = bytes[i + 1] - b'0';
symbols[n] = tens * 10 + units;
n += 1;
i += 2;
}
}
}
symbols[n] = compute_check(&symbols[..n]);
n += 1;
symbols[n] = STOP;
n += 1;
let len = symbols_to_bars(&symbols[..n], buf)?;
Ok(Encoded::Linear { len, height: 50 })
}
fn symbology_name() -> &'static str {
"Code 128"
}
}
pub(crate) fn compute_check(symbols: &[u8]) -> u8 {
let start_val = symbols[0] as u32;
let weighted: u32 = symbols[1..]
.iter()
.enumerate()
.map(|(i, &s)| (i as u32 + 1) * s as u32)
.sum();
((start_val + weighted) % 103) as u8
}
pub(crate) fn symbols_to_bars(symbols: &[u8], buf: &mut [bool]) -> Result<usize, EncodeError> {
let mut w = SliceWriter::new(buf);
for &sym in symbols.iter() {
let is_stop = sym == STOP;
let pattern = &PATTERNS[sym as usize];
let mut dark = true;
for &width in pattern.iter() {
w.push_run(dark, width as usize)?;
dark = !dark;
}
if is_stop {
w.push_run(true, STOP_TERMINATION as usize)?;
}
}
Ok(w.len())
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_len(input: &str) -> usize {
let mut buf = [false; 4096];
match Code128::encode_into(input, &mut buf).unwrap() {
Encoded::Linear { len, .. } => len,
_ => panic!("expected linear"),
}
}
#[test]
fn test_encode_subset_b_basic() {
assert_eq!(encode_len("Hello"), 7 * 11 + 13);
}
#[test]
fn test_encode_subset_c() {
assert_eq!(encode_len("123456"), 5 * 11 + 13);
}
#[test]
fn test_encode_subset_a_control() {
assert!(encode_len("\x07ABC") > 0);
}
#[test]
fn test_empty_input_error() {
let mut buf = [false; 4096];
assert!(Code128::encode_into("", &mut buf).is_err());
}
#[test]
fn test_invalid_high_byte() {
let mut buf = [false; 4096];
assert!(Code128::encode_into("caf\u{00E9}", &mut buf).is_err());
}
#[test]
fn test_buffer_too_small() {
let mut buf = [false; 16];
assert_eq!(
Code128::encode_into("Hello", &mut buf),
Err(EncodeError::BufferTooSmall)
);
}
#[test]
fn test_check_computation() {
assert!(encode_len("PJJ123C") > 0);
}
#[test]
fn test_symbology_name() {
assert_eq!(Code128::symbology_name(), "Code 128");
}
#[cfg(feature = "alloc")]
#[test]
fn test_svg_output() {
let svg = Code128::encode("Test").unwrap().to_svg_string();
assert!(svg.starts_with("<svg "));
}
}