#![no_std]
#![warn(missing_docs)]
#![feature(const_mut_refs)]
mod tables;
use tables::{HL_TO_LL, M_PDF417_VARIANTS, M_PDF417_VARIANTS_COUNT, M_PDF417_RAP, M_PDF417_SIDE, M_PDF417_CENTER};
pub mod ecc;
pub mod high_level;
pub use high_level::*;
pub use tables::{get_variant, find_variant, variant_dim};
const START: u32 = 0b11111111010101000;
const END: u32 = 0b111111101000101001;
pub const START_PATTERN_LEN: u8 = 17;
pub const END_PATTERN_LEN: u8 = 18;
pub const MIN_ROWS: u8 = 3;
pub const MAX_ROWS: u8 = 90;
pub const MIN_COLS: u8 = 1;
pub const MAX_COLS: u8 = 30;
pub type PDF417Config = (u8, u8, (u32, u32), bool);
pub trait RenderTarget {
type State;
fn begin(&self, config: PDF417Config) -> Self::State;
fn row_start(&mut self, state: &mut Self::State);
fn row_end(&mut self, state: &mut Self::State);
fn append_bits(&mut self, state: &mut Self::State, value: u32, count: u8);
#[allow(unused_variables)]
fn end(&mut self, state: Self::State) {}
}
#[derive(Debug, Default)]
pub struct BoolSliceRenderConfig {
i: usize,
row_start: usize,
scale: (u32, u32),
inverted: bool
}
impl RenderTarget for [bool] {
type State = BoolSliceRenderConfig;
fn begin(&self, (_, _, scale, inverted): PDF417Config) -> Self::State {
BoolSliceRenderConfig { scale, inverted, ..Default::default() }
}
fn row_start(&mut self, state: &mut Self::State) {
state.row_start = state.i;
}
fn row_end(&mut self, state: &mut Self::State) {
let w = state.scale.1 as usize;
if w > 1 {
let mut i = state.i;
let len = state.i - state.row_start;
for _ in 0..(w - 1) {
self.copy_within((state.row_start)..(state.row_start+len), i);
i += len;
}
state.i = i;
}
}
fn append_bits(&mut self, state: &mut Self::State, mut value: u32, count: u8) {
if state.inverted {
value = !value;
}
let w = state.scale.0 as usize;
let i = &mut state.i;
let mut mask = 1 << (count as u32 - 1);
for _ in 0..count {
self[(*i)..(*i + w)].fill((value & mask) != 0);
*i += w;
mask >>= 1;
}
}
}
#[derive(Debug, Default)]
struct BitShifter {
cursor: usize,
bit: u8
}
impl BitShifter {
#[inline(always)]
pub fn shift(&mut self, storage: &mut [u8], v: bool) {
if v { storage[self.cursor] |= 1 << (7 - self.bit); }
self.bit += 1;
if self.bit == 8 {
self.cursor += 1;
self.bit = 0;
}
}
#[inline(always)]
pub fn skip(&mut self) {
self.cursor += 1;
self.bit = 0;
}
#[inline(always)]
pub fn move_to(&mut self, cursor: usize) {
self.cursor = cursor;
self.bit = 0;
}
}
#[derive(Debug, Default)]
pub struct ByteSliceRenderConfig {
bs: BitShifter,
row_start: usize,
inverted: bool,
scale: (u32, u32)
}
impl RenderTarget for [u8] {
type State = ByteSliceRenderConfig;
fn begin(&self, (_, _, scale, inverted): PDF417Config) -> Self::State {
ByteSliceRenderConfig { scale, inverted, ..Default::default() }
}
fn row_start(&mut self, state: &mut Self::State) {
state.row_start = state.bs.cursor;
}
fn row_end(&mut self, state: &mut Self::State) {
if state.bs.bit > 0 {
state.bs.skip();
}
let h = state.scale.1;
if h > 1 {
let mut i = state.bs.cursor;
let j = state.row_start;
let len = i - j;
for _ in 0..(h - 1) {
self.copy_within(j..(j+len), i);
i += len;
}
state.bs.move_to(i);
}
}
fn append_bits(&mut self, state: &mut Self::State, mut value: u32, mut count: u8) {
if state.inverted {
value = !value;
}
let w = state.scale.0 as usize;
while count > 0 {
count -= 1;
let is_set = (value >> count) & 1 == 1;
for _ in 0..w {
state.bs.shift(self, is_set);
}
}
}
}
const LEADING_ONE: u32 = 1 << 16;
macro_rules! cw {
($tb:ident, $val:expr) => {
LEADING_ONE + HL_TO_LL[$tb * 929 + $val as usize] as u32
}
}
#[macro_export]
macro_rules! pdf417_width {
($cols:expr) => {
pdf417_width!($cols, 1);
};
($cols:expr, $scale_x:expr) => {
pdf417_width!($cols, $scale_x, false);
};
($cols:expr, $scale_x:expr, $truncated:expr) => {
if $truncated {
(pdf417::START_PATTERN_LEN as usize + 17 + $cols as usize * 17 + 1)
* $scale_x as usize
} else {
(pdf417::START_PATTERN_LEN as usize + 17 + $cols as usize * 17 + 17 + pdf417::END_PATTERN_LEN as usize)
* $scale_x as usize
}
};
}
#[macro_export]
macro_rules! pdf417_height {
($rows:expr) => {
pdf417_height!($rows, 1);
};
($rows:expr, $scale_y:expr) => {
$rows as usize * $scale_y as usize
};
}
#[derive(Debug, Clone)]
pub struct PDF417<'a> {
codewords: &'a [u16],
rows: u8,
cols: u8,
level: u8,
scale: (u32, u32),
truncated: bool,
inverted: bool
}
impl<'a> PDF417<'a> {
pub const fn new(codewords: &'a [u16], rows: u8, cols: u8, level: u8) -> Self {
assert!(rows >= MIN_ROWS && rows <= MAX_ROWS, "The number of rows must be between 3 and 90");
assert!(cols >= MIN_COLS && cols <= MAX_COLS, "The number of columns must be between 1 and 30");
assert!(codewords.len() == (rows as usize * cols as usize),
"The data will not fit in the provided configuration");
assert!(level < 9, "ECC level must be between 0 and 8 inclusive");
PDF417 { codewords, rows, cols, level, scale: (1, 1), truncated: false, inverted: false }
}
pub const fn is_truncated(&self) -> bool {
self.truncated
}
pub const fn truncated(mut self, truncated: bool) -> Self {
self.truncated = truncated;
self
}
pub const fn set_truncated(&mut self, truncated: bool) -> &mut Self {
self.truncated = truncated;
self
}
pub const fn scale(&self) -> (u32, u32) {
self.scale
}
pub const fn scaled(mut self, scale: (u32, u32)) -> Self {
assert!(scale.0 != 0 && scale.1 != 0, "scale cannot be zero");
self.scale = scale;
self
}
pub const fn set_scaled(&mut self, scale: (u32, u32)) -> &mut Self {
assert!(scale.0 != 0 && scale.1 != 0, "scale cannot be zero");
self.scale = scale;
self
}
pub const fn is_inverted(&self) -> bool {
self.inverted
}
pub const fn inverted(mut self, inverted: bool) -> Self {
self.inverted = inverted;
self
}
pub const fn set_inverted(&mut self, inverted: bool) -> &mut Self {
self.inverted = inverted;
self
}
pub const fn rows(&self) -> u8 {
self.rows
}
pub const fn cols(&self) -> u8 {
self.cols
}
pub const fn with_dimensions(mut self, (rows, cols): (u8, u8)) -> Self {
assert!(rows >= MIN_ROWS && rows <= MAX_ROWS, "The number of rows must be between 3 and 90");
assert!(cols >= MIN_COLS && cols <= MAX_COLS, "The number of columns must be between 1 and 30");
assert!(self.codewords.len() == (rows as usize * cols as usize),
"The data will not fit in the provided configuration");
self.rows = rows;
self.cols = cols;
self
}
pub const fn set_dimensions(&mut self, (rows, cols): (u8, u8)) -> &mut Self {
assert!(rows >= MIN_ROWS && rows <= MAX_ROWS, "The number of rows must be between 3 and 90");
assert!(cols >= MIN_COLS && cols <= MAX_COLS, "The number of columns must be between 1 and 30");
assert!(self.codewords.len() == (rows as usize * cols as usize),
"The data will not fit in the provided configuration");
self.rows = rows;
self.cols = cols;
self
}
pub fn render<Target: RenderTarget + ?Sized>(&self, storage: &mut Target) {
let (rows, cols) = (self.rows as u32, self.cols as u32);
let rows_val = (rows - 1) / 3;
let cols_val = cols - 1;
let level_val = self.level as u32 * 3 + (rows - 1) % 3;
let mut table = 0;
let mut state = storage.begin((self.rows, self.cols, self.scale, self.inverted));
for row in 0..rows {
storage.row_start(&mut state);
storage.append_bits(&mut state, START, START_PATTERN_LEN);
let cw = (row / 3) * 30 + match table {
0 => rows_val,
1 => level_val,
2 => cols_val,
_ => unreachable!()
};
storage.append_bits(&mut state, cw!(table, cw), 17);
for col in 0..cols {
let cw = self.codewords[(row * cols + col) as usize];
storage.append_bits(&mut state, cw!(table, cw), 17);
}
if self.truncated {
storage.append_bits(&mut state, 1, 1);
} else {
let cw = (row / 3) * 30 + match table {
0 => cols_val,
1 => rows_val,
2 => level_val,
_ => unreachable!()
};
storage.append_bits(&mut state, cw!(table, cw), 17);
storage.append_bits(&mut state, END, END_PATTERN_LEN);
}
storage.row_end(&mut state);
if table == 2 { table = 0; } else { table += 1 };
}
storage.end(state);
}
}
#[macro_export]
macro_rules! m_pdf417_width {
($cols:expr) => {
m_pdf417_width!($cols, 1);
};
($cols:expr, $scale_x:expr) => {
(10 + $cols as usize * 17 + ($cols as usize / 3) * 10 + 10 + 1)
* $scale_x as usize
};
}
#[macro_export]
macro_rules! m_pdf417_height {
($rows:expr) => {
m_pdf417_height!($rows, 2);
};
($rows:expr, $scale_y:expr) => {
$rows as usize * $scale_y as usize
};
}
#[derive(Debug, Clone)]
pub struct MicroPDF417<'a> {
codewords: &'a [u16],
variant: u8,
scale: (u32, u32),
inverted: bool
}
impl<'a> MicroPDF417<'a> {
pub const fn new(codewords: &'a [u16], variant: u8) -> Self {
assert!(variant <= 34);
MicroPDF417 { codewords, variant, scale: (1, 2), inverted: false }
}
pub const fn scale(&self) -> (u32, u32) {
self.scale
}
pub const fn scaled(mut self, scale: (u32, u32)) -> Self {
assert!(scale.0 != 0 && scale.1 != 0, "scale cannot be zero");
self.scale = scale;
self
}
pub const fn set_scaled(&mut self, scale: (u32, u32)) -> &mut Self {
assert!(scale.0 != 0 && scale.1 != 0, "scale cannot be zero");
self.scale = scale;
self
}
pub const fn is_inverted(&self) -> bool {
self.inverted
}
pub const fn inverted(mut self, inverted: bool) -> Self {
self.inverted = inverted;
self
}
pub const fn set_inverted(&mut self, inverted: bool) -> &mut Self {
self.inverted = inverted;
self
}
pub const fn rows(&self) -> u8 {
M_PDF417_VARIANTS[1 * M_PDF417_VARIANTS_COUNT + self.variant as usize] as u8
}
pub const fn cols(&self) -> u8 {
M_PDF417_VARIANTS[0 * M_PDF417_VARIANTS_COUNT + self.variant as usize] as u8
}
pub const fn variant(&self) -> u8 {
self.variant
}
pub fn render<Target: RenderTarget + ?Sized>(&self, storage: &mut Target) {
let variant = self.variant as usize;
let (cols, rows, mut left, mut center, mut right, mut table) = (
self.cols() as usize,
self.rows() as usize,
M_PDF417_RAP[0 * M_PDF417_VARIANTS_COUNT + variant] as usize - 1,
M_PDF417_RAP[1 * M_PDF417_VARIANTS_COUNT + variant] as usize - 1,
M_PDF417_RAP[2 * M_PDF417_VARIANTS_COUNT + variant] as usize - 1,
M_PDF417_RAP[3 * M_PDF417_VARIANTS_COUNT + variant] as usize,
);
let mut state = storage.begin((rows as u8, cols as u8, self.scale, self.inverted));
for row in 0..rows {
storage.row_start(&mut state);
storage.append_bits(&mut state, M_PDF417_SIDE[left] as u32, 10);
let mut col = 0;
while col < cols && col < 2 {
storage.append_bits(&mut state, cw!(table, self.codewords[row * cols + col]), 17);
col += 1;
}
if col < cols {
storage.append_bits(&mut state, M_PDF417_CENTER[center] as u32, 10);
while col < cols {
storage.append_bits(&mut state, cw!(table, self.codewords[row * cols + col]), 17);
col += 1;
}
}
storage.append_bits(&mut state, ((M_PDF417_SIDE[right] as u32) << 1) | 1, 11);
storage.row_end(&mut state);
if left == 51 { left = 0; } else { left += 1; }
if center == 51 { center = 0; } else { center += 1; }
if right == 51 { right = 0; } else { right += 1; }
if table == 2 { table = 0; } else { table += 1; }
}
storage.end(state);
}
}
#[cfg(test)]
mod tests {
use super::RenderTarget;
#[test]
fn test_append_bits_to_bool_slice() {
let mut t = [false; 13];
let mut state = t.begin((3, 1, (1, 1), false));
t.append_bits(&mut state, 0b110001, 6);
t.append_bits(&mut state, 0b11, 2);
t.append_bits(&mut state, 0b00111, 5);
assert_eq!(&t, &[true, true, false, false, false, true, true, true, false, false, true, true, true]);
}
#[test]
fn test_append_bits_to_byte_slice() {
let mut t = [0u8; 5];
let mut state = t.begin((3, 1, (1, 1), false));
t.append_bits(&mut state, 0b10101010_10101010_1, 17);
t.append_bits(&mut state, 0b1110001_110001, 13);
t.append_bits(&mut state, 0b11, 2);
t.append_bits(&mut state, 0b0000111, 7);
assert_eq!(&t, &[0b10101010, 0b10101010, 0b11110001, 0b11000111, 0b00001110]);
}
#[test]
fn test_append_bits_to_bool_slice_scaled() {
let mut t = [false; 6 * 3 * 2];
let mut state = t.begin((3, 1, (3, 2), false));
t.row_start(&mut state);
t.append_bits(&mut state, 0b110001, 6);
assert_eq!(&t[..(t.len()/2)], &[true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, true, true, true], "Testing X scale");
t.row_end(&mut state);
assert_eq!(&t[(t.len()/2)..], &[true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, true, true, true],"Testing Y scale");
}
#[test]
fn test_append_bits_to_byte_slice_scaled() {
let mut t = [0u8; (8 * 3 * 2) / 8];
let mut state = t.begin((3, 1, (3, 2), false));
t.row_start(&mut state);
t.append_bits(&mut state, 0b01000111, 8);
assert_eq!(&t[..(t.len()/2)], &[0b00011100, 0b00000001, 0b11111111], "Testing X scale");
t.row_end(&mut state);
assert_eq!(&t[(t.len()/2)..], &[0b00011100, 0b00000001, 0b11111111], "Testing Y scale");
}
}