use crate::ffi::colorspace::FZ_COLORSPACE_GRAY;
use crate::ffi::pixmap::Pixmap;
use crate::ffi::{Handle, PIXMAPS};
use std::ffi::{CStr, CString, c_char};
use std::ptr;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BarcodeType {
#[default]
None = 0,
Aztec = 1,
Codabar = 2,
Code39 = 3,
Code93 = 4,
Code128 = 5,
DataBar = 6,
DataBarExpanded = 7,
DataMatrix = 8,
Ean8 = 9,
Ean13 = 10,
Itf = 11,
MaxiCode = 12,
Pdf417 = 13,
QrCode = 14,
UpcA = 15,
UpcE = 16,
MicroQrCode = 17,
RmQrCode = 18,
DxFilmEdge = 19,
DataBarLimited = 20,
}
impl BarcodeType {
pub fn from_i32(value: i32) -> Self {
match value {
0 => BarcodeType::None,
1 => BarcodeType::Aztec,
2 => BarcodeType::Codabar,
3 => BarcodeType::Code39,
4 => BarcodeType::Code93,
5 => BarcodeType::Code128,
6 => BarcodeType::DataBar,
7 => BarcodeType::DataBarExpanded,
8 => BarcodeType::DataMatrix,
9 => BarcodeType::Ean8,
10 => BarcodeType::Ean13,
11 => BarcodeType::Itf,
12 => BarcodeType::MaxiCode,
13 => BarcodeType::Pdf417,
14 => BarcodeType::QrCode,
15 => BarcodeType::UpcA,
16 => BarcodeType::UpcE,
17 => BarcodeType::MicroQrCode,
18 => BarcodeType::RmQrCode,
19 => BarcodeType::DxFilmEdge,
20 => BarcodeType::DataBarLimited,
_ => BarcodeType::None,
}
}
pub fn to_string(self) -> &'static str {
match self {
BarcodeType::None => "none",
BarcodeType::Aztec => "aztec",
BarcodeType::Codabar => "codabar",
BarcodeType::Code39 => "code39",
BarcodeType::Code93 => "code93",
BarcodeType::Code128 => "code128",
BarcodeType::DataBar => "databar",
BarcodeType::DataBarExpanded => "databarexpanded",
BarcodeType::DataMatrix => "datamatrix",
BarcodeType::Ean8 => "ean8",
BarcodeType::Ean13 => "ean13",
BarcodeType::Itf => "itf",
BarcodeType::MaxiCode => "maxicode",
BarcodeType::Pdf417 => "pdf417",
BarcodeType::QrCode => "qrcode",
BarcodeType::UpcA => "upca",
BarcodeType::UpcE => "upce",
BarcodeType::MicroQrCode => "microqrcode",
BarcodeType::RmQrCode => "rmqrcode",
BarcodeType::DxFilmEdge => "dxfilmedge",
BarcodeType::DataBarLimited => "databarlimited",
}
}
pub fn from_string(s: &str) -> Self {
match s.to_lowercase().as_str() {
"aztec" => BarcodeType::Aztec,
"codabar" => BarcodeType::Codabar,
"code39" | "code-39" | "code_39" => BarcodeType::Code39,
"code93" | "code-93" | "code_93" => BarcodeType::Code93,
"code128" | "code-128" | "code_128" => BarcodeType::Code128,
"databar" | "rss14" | "rss-14" => BarcodeType::DataBar,
"databarexpanded" | "rssexpanded" => BarcodeType::DataBarExpanded,
"datamatrix" | "data-matrix" | "data_matrix" | "dm" => BarcodeType::DataMatrix,
"ean8" | "ean-8" | "ean_8" => BarcodeType::Ean8,
"ean13" | "ean-13" | "ean_13" => BarcodeType::Ean13,
"itf" | "interleaved2of5" | "i2of5" => BarcodeType::Itf,
"maxicode" => BarcodeType::MaxiCode,
"pdf417" | "pdf-417" => BarcodeType::Pdf417,
"qrcode" | "qr" | "qr-code" | "qr_code" => BarcodeType::QrCode,
"upca" | "upc-a" | "upc_a" => BarcodeType::UpcA,
"upce" | "upc-e" | "upc_e" => BarcodeType::UpcE,
"microqrcode" | "micro-qr" | "microqr" => BarcodeType::MicroQrCode,
"rmqrcode" | "rmqr" | "rectangular-qr" => BarcodeType::RmQrCode,
"dxfilmedge" | "dx-film-edge" => BarcodeType::DxFilmEdge,
"databarlimited" | "rsslimited" => BarcodeType::DataBarLimited,
_ => BarcodeType::None,
}
}
pub fn is_2d(self) -> bool {
matches!(
self,
BarcodeType::Aztec
| BarcodeType::DataMatrix
| BarcodeType::MaxiCode
| BarcodeType::Pdf417
| BarcodeType::QrCode
| BarcodeType::MicroQrCode
| BarcodeType::RmQrCode
)
}
pub fn is_1d(self) -> bool {
!self.is_2d() && self != BarcodeType::None
}
pub fn default_size(self) -> i32 {
if self.is_2d() { 4 } else { 2 }
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EcLevel {
#[default]
L = 0,
M = 1,
Q = 2,
H = 3,
}
impl EcLevel {
pub fn from_i32(value: i32) -> Self {
match value {
0 => EcLevel::L,
1 => EcLevel::M,
2 => EcLevel::Q,
3 | _ => EcLevel::H,
}
}
}
fn generate_qr_matrix(data: &str, size: i32, ec_level: EcLevel) -> Option<Vec<Vec<bool>>> {
let data_bytes = data.as_bytes();
if data_bytes.is_empty() || data_bytes.len() > 2953 {
return None;
}
let version = calculate_qr_version(data_bytes.len(), ec_level);
let modules = version * 4 + 17;
let mut matrix = vec![vec![false; modules as usize]; modules as usize];
add_finder_pattern(&mut matrix, 0, 0);
add_finder_pattern(&mut matrix, modules - 7, 0);
add_finder_pattern(&mut matrix, 0, modules - 7);
for i in 8..modules - 8 {
matrix[6][i as usize] = i % 2 == 0;
matrix[i as usize][6] = i % 2 == 0;
}
if version >= 2 {
let align_pos = modules - 7;
add_alignment_pattern(&mut matrix, align_pos - 2, align_pos - 2);
}
encode_data_to_matrix(&mut matrix, data_bytes, version);
apply_mask(&mut matrix, 0);
Some(matrix)
}
fn calculate_qr_version(data_len: usize, _ec_level: EcLevel) -> i32 {
if data_len <= 17 {
1
} else if data_len <= 32 {
2
} else if data_len <= 53 {
3
} else if data_len <= 78 {
4
} else if data_len <= 106 {
5
} else if data_len <= 134 {
6
} else {
(data_len as i32 / 20).min(40)
}
}
fn add_finder_pattern(matrix: &mut [Vec<bool>], x: i32, y: i32) {
for dy in 0..7 {
for dx in 0..7 {
let is_border = dx == 0 || dx == 6 || dy == 0 || dy == 6;
let is_inner = (2..=4).contains(&dx) && (2..=4).contains(&dy);
matrix[(y + dy) as usize][(x + dx) as usize] = is_border || is_inner;
}
}
}
fn add_alignment_pattern(matrix: &mut [Vec<bool>], x: i32, y: i32) {
for dy in 0..5 {
for dx in 0..5 {
let is_border = dx == 0 || dx == 4 || dy == 0 || dy == 4;
let is_center = dx == 2 && dy == 2;
if (y + dy) < matrix.len() as i32 && (x + dx) < matrix[0].len() as i32 {
matrix[(y + dy) as usize][(x + dx) as usize] = is_border || is_center;
}
}
}
}
fn encode_data_to_matrix(matrix: &mut [Vec<bool>], data: &[u8], _version: i32) {
let modules = matrix.len();
let mut bit_pos = 0;
let mut col = modules - 1;
while col > 0 {
if col == 6 {
col -= 1; }
for row in (0..modules).rev() {
for c in [col, col.saturating_sub(1)] {
if !is_function_module(matrix, row, c) {
let byte_idx = bit_pos / 8;
let bit_idx = 7 - (bit_pos % 8);
let bit = if byte_idx < data.len() {
(data[byte_idx] >> bit_idx) & 1 == 1
} else {
false
};
matrix[row][c] = bit;
bit_pos += 1;
}
}
}
col = col.saturating_sub(2);
}
}
fn is_function_module(matrix: &[Vec<bool>], row: usize, col: usize) -> bool {
let n = matrix.len();
if (row < 9 && col < 9) || (row < 9 && col >= n - 8) || (row >= n - 8 && col < 9) {
return true;
}
if row == 6 || col == 6 {
return true;
}
false
}
fn apply_mask(matrix: &mut [Vec<bool>], _mask_pattern: u8) {
let n = matrix.len();
for row in 0..n {
for col in 0..n {
if !is_function_module(matrix, row, col) && (row + col) % 2 == 0 {
matrix[row][col] = !matrix[row][col];
}
}
}
}
fn generate_1d_barcode(data: &str, barcode_type: BarcodeType) -> Option<Vec<bool>> {
match barcode_type {
BarcodeType::Code128 => generate_code128(data),
BarcodeType::Code39 => generate_code39(data),
BarcodeType::Ean13 => generate_ean13(data),
BarcodeType::Ean8 => generate_ean8(data),
BarcodeType::UpcA => generate_upca(data),
_ => generate_code128(data), }
}
fn generate_code128(data: &str) -> Option<Vec<bool>> {
let patterns: [u16; 107] = [
0b11011001100, 0b11001101100, 0b11001100110, 0b10010011000, 0b10010001100, 0b10001001100, 0b10011001000, 0b10011000100, 0b10001100100, 0b11001001000, 0b11001000100, 0b11000100100, 0b10110011100, 0b10011011100, 0b10011001110, 0b10111001100, 0b10011101100, 0b10011100110, 0b11001110010, 0b11001011100, 0b11001001110, 0b11011100100, 0b11001110100, 0b11101101110, 0b11101001100, 0b11100101100, 0b11100100110, 0b11101100100, 0b11100110100, 0b11100110010, 0b11011011000, 0b11011000110, 0b11000110110, 0b10100011000, 0b10001011000, 0b10001000110, 0b10110001000, 0b10001101000, 0b10001100010, 0b11010001000, 0b11000101000, 0b11000100010, 0b10110111000, 0b10110001110, 0b10001101110, 0b10111011000, 0b10111000110, 0b10001110110, 0b11101110110, 0b11010001110, 0b11000101110, 0b11011101000, 0b11011100010, 0b11011101110, 0b11101011000, 0b11101000110, 0b11100010110, 0b11101101000, 0b11101100010, 0b11100011010, 0b11101111010, 0b11001000010, 0b11110001010, 0b10100110000, 0b10100001100, 0b10010110000, 0b10010000110, 0b10000101100, 0b10000100110, 0b10110010000, 0b10110000100, 0b10011010000, 0b10011000010, 0b10000110100, 0b10000110010, 0b11000010010, 0b11001010000, 0b11110111010, 0b11000010100, 0b10001111010, 0b10100111100, 0b10010111100, 0b10010011110, 0b10111100100, 0b10011110100, 0b10011110010, 0b11110100100, 0b11110010100, 0b11110010010, 0b11011011110, 0b11011110110, 0b11110110110, 0b10101111000, 0b10100011110, 0b10001011110, 0b10111101000, 0b10111100010, 0b11110101000, 0b11110100010, 0b10111011110, 0b10111101110, 0b11101011110, 0b11110101110, 0b11010000100, 0b11010010000, 0b11010011100, 0b1100011101011, ];
let mut result = Vec::new();
for _ in 0..10 {
result.push(false);
}
let start = patterns[104];
for i in (0..11).rev() {
result.push((start >> i) & 1 == 1);
}
let mut checksum = 104;
for (pos, c) in data.chars().enumerate() {
let value = if c >= ' ' && c <= '~' {
(c as usize) - 32
} else {
0
};
let pattern = patterns[value];
for i in (0..11).rev() {
result.push((pattern >> i) & 1 == 1);
}
checksum += value * (pos + 1);
}
let check_pattern = patterns[checksum % 103];
for i in (0..11).rev() {
result.push((check_pattern >> i) & 1 == 1);
}
let stop = patterns[106];
for i in (0..13).rev() {
result.push((stop >> i) & 1 == 1);
}
for _ in 0..10 {
result.push(false);
}
Some(result)
}
fn generate_code39(data: &str) -> Option<Vec<bool>> {
let chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*";
let patterns: [u16; 44] = [
0b101001101101, 0b110100101011, 0b101100101011, 0b110110010101, 0b101001101011, 0b110100110101, 0b101100110101, 0b101001011011, 0b110100101101, 0b101100101101, 0b110101001011, 0b101101001011, 0b110110100101, 0b101011001011, 0b110101100101, 0b101101100101, 0b101010011011, 0b110101001101, 0b101101001101, 0b101011001101, 0b110101010011, 0b101101010011, 0b110110101001, 0b101011010011, 0b110101101001, 0b101101101001, 0b101010110011, 0b110101011001, 0b101101011001, 0b101011011001, 0b110010101011, 0b100110101011, 0b110011010101, 0b100101101011, 0b110010110101, 0b100110110101, 0b100101011011, 0b110010101101, 0b100110101101, 0b100100100101, 0b100100101001, 0b100101001001, 0b101001001001, 0b100101101101, ];
let mut result = Vec::new();
for _ in 0..10 {
result.push(false);
}
let start = patterns[43];
for i in (0..12).rev() {
result.push((start >> i) & 1 == 1);
}
result.push(false);
for c in data.to_uppercase().chars() {
if let Some(idx) = chars.find(c) {
let pattern = patterns[idx];
for i in (0..12).rev() {
result.push((pattern >> i) & 1 == 1);
}
result.push(false); }
}
for i in (0..12).rev() {
result.push((start >> i) & 1 == 1);
}
for _ in 0..10 {
result.push(false);
}
Some(result)
}
fn generate_ean13(data: &str) -> Option<Vec<bool>> {
let digits: Vec<u8> = data
.chars()
.filter_map(|c| c.to_digit(10).map(|d| d as u8))
.collect();
if digits.len() < 12 {
return None;
}
let l_codes: [u8; 10] = [
0b0001101, 0b0011001, 0b0010011, 0b0111101, 0b0100011, 0b0110001, 0b0101111, 0b0111011, 0b0110111, 0b0001011, ];
let r_codes: [u8; 10] = [
0b1110010, 0b1100110, 0b1101100, 0b1000010, 0b1011100, 0b1001110, 0b1010000, 0b1000100, 0b1001000, 0b1110100, ];
let mut result = Vec::new();
for _ in 0..9 {
result.push(false);
}
result.push(true);
result.push(false);
result.push(true);
for &d in &digits[1..7] {
let code = l_codes[d as usize];
for i in (0..7).rev() {
result.push((code >> i) & 1 == 1);
}
}
result.push(false);
result.push(true);
result.push(false);
result.push(true);
result.push(false);
for &d in &digits[7..13.min(digits.len())] {
let code = r_codes[d as usize];
for i in (0..7).rev() {
result.push((code >> i) & 1 == 1);
}
}
result.push(true);
result.push(false);
result.push(true);
for _ in 0..9 {
result.push(false);
}
Some(result)
}
fn generate_ean8(data: &str) -> Option<Vec<bool>> {
let digits: Vec<u8> = data
.chars()
.filter_map(|c| c.to_digit(10).map(|d| d as u8))
.collect();
if digits.len() < 7 {
return None;
}
let l_codes: [u8; 10] = [
0b0001101, 0b0011001, 0b0010011, 0b0111101, 0b0100011, 0b0110001, 0b0101111, 0b0111011,
0b0110111, 0b0001011,
];
let r_codes: [u8; 10] = [
0b1110010, 0b1100110, 0b1101100, 0b1000010, 0b1011100, 0b1001110, 0b1010000, 0b1000100,
0b1001000, 0b1110100,
];
let mut result = Vec::new();
for _ in 0..7 {
result.push(false);
}
result.push(true);
result.push(false);
result.push(true);
for &d in &digits[0..4] {
let code = l_codes[d as usize];
for i in (0..7).rev() {
result.push((code >> i) & 1 == 1);
}
}
result.push(false);
result.push(true);
result.push(false);
result.push(true);
result.push(false);
for &d in &digits[4..8.min(digits.len())] {
let code = r_codes[d as usize];
for i in (0..7).rev() {
result.push((code >> i) & 1 == 1);
}
}
result.push(true);
result.push(false);
result.push(true);
for _ in 0..7 {
result.push(false);
}
Some(result)
}
fn generate_upca(data: &str) -> Option<Vec<bool>> {
let padded = format!("0{}", data);
generate_ean13(&padded)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_string_from_barcode_type(barcode_type: i32) -> *const c_char {
static NAMES: [&str; 21] = [
"none\0",
"aztec\0",
"codabar\0",
"code39\0",
"code93\0",
"code128\0",
"databar\0",
"databarexpanded\0",
"datamatrix\0",
"ean8\0",
"ean13\0",
"itf\0",
"maxicode\0",
"pdf417\0",
"qrcode\0",
"upca\0",
"upce\0",
"microqrcode\0",
"rmqrcode\0",
"dxfilmedge\0",
"databarlimited\0",
];
let idx = (barcode_type as usize).min(20);
NAMES[idx].as_ptr().cast()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_type_from_string(str_ptr: *const c_char) -> i32 {
if str_ptr.is_null() {
return BarcodeType::None as i32;
}
let s = unsafe { CStr::from_ptr(str_ptr) };
if let Ok(name) = s.to_str() {
BarcodeType::from_string(name) as i32
} else {
BarcodeType::None as i32
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_barcode_pixmap(
_ctx: Handle,
barcode_type: i32,
value: *const c_char,
size: i32,
ec_level: i32,
quiet: i32,
_hrt: i32,
) -> Handle {
if value.is_null() {
return 0;
}
let data = match unsafe { CStr::from_ptr(value) }.to_str() {
Ok(s) => s,
Err(_) => return 0,
};
let bt = BarcodeType::from_i32(barcode_type);
let module_size = if size > 0 { size } else { bt.default_size() };
let ec = EcLevel::from_i32(ec_level);
let add_quiet = quiet != 0;
if bt.is_2d() {
let matrix = match bt {
BarcodeType::QrCode | BarcodeType::MicroQrCode => generate_qr_matrix(data, size, ec),
_ => generate_qr_matrix(data, size, ec), };
if let Some(matrix) = matrix {
let modules = matrix.len();
let quiet_zone = if add_quiet { 4 } else { 0 };
let total_size = (modules + quiet_zone * 2) as i32 * module_size;
let pixmap = Pixmap::new(FZ_COLORSPACE_GRAY, total_size, total_size, false);
let handle = PIXMAPS.insert(pixmap);
if let Some(pix) = PIXMAPS.get(handle) {
let mut guard = pix.lock().unwrap();
let samples = guard.samples_mut();
for sample in samples.iter_mut() {
*sample = 255;
}
let stride = total_size as usize;
for (row, matrix_row) in matrix.iter().enumerate() {
for (col, &is_dark) in matrix_row.iter().enumerate() {
if is_dark {
let x_start = ((col + quiet_zone) as i32 * module_size) as usize;
let y_start = ((row + quiet_zone) as i32 * module_size) as usize;
for dy in 0..module_size as usize {
for dx in 0..module_size as usize {
let idx = (y_start + dy) * stride + (x_start + dx);
if idx < samples.len() {
samples[idx] = 0; }
}
}
}
}
}
}
return handle;
}
} else {
let pattern = generate_1d_barcode(data, bt);
if let Some(pattern) = pattern {
let bar_height = 50 * module_size;
let width = pattern.len() as i32 * module_size;
let pixmap = Pixmap::new(FZ_COLORSPACE_GRAY, width, bar_height, false);
let handle = PIXMAPS.insert(pixmap);
if let Some(pix) = PIXMAPS.get(handle) {
let mut guard = pix.lock().unwrap();
let samples = guard.samples_mut();
for sample in samples.iter_mut() {
*sample = 255;
}
let stride = width as usize;
for (col, &is_bar) in pattern.iter().enumerate() {
if is_bar {
let x_start = (col as i32 * module_size) as usize;
for y in 0..bar_height as usize {
for dx in 0..module_size as usize {
let idx = y * stride + x_start + dx;
if idx < samples.len() {
samples[idx] = 0; }
}
}
}
}
}
return handle;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_barcode_image(
ctx: Handle,
barcode_type: i32,
value: *const c_char,
size: i32,
ec_level: i32,
quiet: i32,
hrt: i32,
) -> Handle {
fz_new_barcode_pixmap(ctx, barcode_type, value, size, ec_level, quiet, hrt)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_is_2d(barcode_type: i32) -> i32 {
if BarcodeType::from_i32(barcode_type).is_2d() {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_is_1d(barcode_type: i32) -> i32 {
if BarcodeType::from_i32(barcode_type).is_1d() {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_default_size(barcode_type: i32) -> i32 {
BarcodeType::from_i32(barcode_type).default_size()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_type_count() -> i32 {
21 }
#[unsafe(no_mangle)]
pub extern "C" fn fz_decode_barcode_from_pixmap(
_ctx: Handle,
type_out: *mut i32,
_pix: Handle,
_rotate: i32,
) -> *mut c_char {
if !type_out.is_null() {
unsafe {
*type_out = BarcodeType::None as i32;
}
}
ptr::null_mut()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_validate(barcode_type: i32, value: *const c_char) -> i32 {
if value.is_null() {
return 0;
}
let data = match unsafe { CStr::from_ptr(value) }.to_str() {
Ok(s) => s,
Err(_) => return 0,
};
let bt = BarcodeType::from_i32(barcode_type);
match bt {
BarcodeType::Ean13 => {
let digits: String = data.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() >= 12 { 1 } else { 0 }
}
BarcodeType::Ean8 => {
let digits: String = data.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() >= 7 { 1 } else { 0 }
}
BarcodeType::UpcA => {
let digits: String = data.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() >= 11 { 1 } else { 0 }
}
BarcodeType::UpcE => {
let digits: String = data.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() >= 6 { 1 } else { 0 }
}
BarcodeType::Code39 => {
let valid_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
if data.to_uppercase().chars().all(|c| valid_chars.contains(c)) {
1
} else {
0
}
}
BarcodeType::Code128 => {
if data.chars().all(|c| c as u32 >= 32 && c as u32 <= 126) {
1
} else {
0
}
}
BarcodeType::QrCode => {
if !data.is_empty() && data.len() <= 2953 {
1
} else {
0
}
}
_ => {
if !data.is_empty() {
1
} else {
0
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_barcode_check_digit(barcode_type: i32, value: *const c_char) -> i32 {
if value.is_null() {
return -1;
}
let data = match unsafe { CStr::from_ptr(value) }.to_str() {
Ok(s) => s,
Err(_) => return -1,
};
let bt = BarcodeType::from_i32(barcode_type);
let digits: Vec<u8> = data
.chars()
.filter_map(|c| c.to_digit(10).map(|d| d as u8))
.collect();
match bt {
BarcodeType::Ean13 | BarcodeType::UpcA => {
if digits.len() < 12 {
return -1;
}
let mut sum = 0u32;
for (i, &d) in digits[..12].iter().enumerate() {
sum += d as u32 * if i % 2 == 0 { 1 } else { 3 };
}
((10 - (sum % 10)) % 10) as i32
}
BarcodeType::Ean8 => {
if digits.len() < 7 {
return -1;
}
let mut sum = 0u32;
for (i, &d) in digits[..7].iter().enumerate() {
sum += d as u32 * if i % 2 == 0 { 3 } else { 1 };
}
((10 - (sum % 10)) % 10) as i32
}
_ => -1,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_barcode_type_enum() {
assert_eq!(BarcodeType::from_i32(0), BarcodeType::None);
assert_eq!(BarcodeType::from_i32(14), BarcodeType::QrCode);
assert_eq!(BarcodeType::from_i32(5), BarcodeType::Code128);
assert_eq!(BarcodeType::from_i32(99), BarcodeType::None);
}
#[test]
fn test_barcode_type_string() {
assert_eq!(BarcodeType::QrCode.to_string(), "qrcode");
assert_eq!(BarcodeType::Code128.to_string(), "code128");
assert_eq!(BarcodeType::Ean13.to_string(), "ean13");
}
#[test]
fn test_barcode_type_from_string() {
assert_eq!(BarcodeType::from_string("qrcode"), BarcodeType::QrCode);
assert_eq!(BarcodeType::from_string("QR"), BarcodeType::QrCode);
assert_eq!(BarcodeType::from_string("code-128"), BarcodeType::Code128);
assert_eq!(BarcodeType::from_string("ean-13"), BarcodeType::Ean13);
assert_eq!(BarcodeType::from_string("unknown"), BarcodeType::None);
}
#[test]
fn test_barcode_is_2d() {
assert!(BarcodeType::QrCode.is_2d());
assert!(BarcodeType::DataMatrix.is_2d());
assert!(BarcodeType::Aztec.is_2d());
assert!(!BarcodeType::Code128.is_2d());
assert!(!BarcodeType::Ean13.is_2d());
}
#[test]
fn test_barcode_is_1d() {
assert!(BarcodeType::Code128.is_1d());
assert!(BarcodeType::Ean13.is_1d());
assert!(BarcodeType::UpcA.is_1d());
assert!(!BarcodeType::QrCode.is_1d());
assert!(!BarcodeType::None.is_1d());
}
#[test]
fn test_generate_qr_matrix() {
let matrix = generate_qr_matrix("Hello", 0, EcLevel::M);
assert!(matrix.is_some());
let m = matrix.unwrap();
assert!(m.len() >= 21); }
#[test]
fn test_generate_code128() {
let pattern = generate_code128("Hello");
assert!(pattern.is_some());
let p = pattern.unwrap();
assert!(!p.is_empty());
}
#[test]
fn test_generate_code39() {
let pattern = generate_code39("HELLO");
assert!(pattern.is_some());
let p = pattern.unwrap();
assert!(!p.is_empty());
}
#[test]
fn test_generate_ean13() {
let pattern = generate_ean13("5901234123457");
assert!(pattern.is_some());
let p = pattern.unwrap();
assert!(p.len() > 90);
}
#[test]
fn test_generate_ean8() {
let pattern = generate_ean8("96385074");
assert!(pattern.is_some());
}
#[test]
fn test_ffi_barcode_type_from_string() {
let qr = CString::new("qrcode").unwrap();
assert_eq!(
fz_barcode_type_from_string(qr.as_ptr()),
BarcodeType::QrCode as i32
);
let code128 = CString::new("code128").unwrap();
assert_eq!(
fz_barcode_type_from_string(code128.as_ptr()),
BarcodeType::Code128 as i32
);
}
#[test]
fn test_ffi_barcode_is_2d() {
assert_eq!(fz_barcode_is_2d(BarcodeType::QrCode as i32), 1);
assert_eq!(fz_barcode_is_2d(BarcodeType::Code128 as i32), 0);
}
#[test]
fn test_ffi_barcode_validate() {
let ean13 = CString::new("5901234123457").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::Ean13 as i32, ean13.as_ptr()),
1
);
let short = CString::new("123").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::Ean13 as i32, short.as_ptr()),
0
);
}
#[test]
fn test_ffi_barcode_check_digit() {
let ean = CString::new("590123412345").unwrap();
let check = fz_barcode_check_digit(BarcodeType::Ean13 as i32, ean.as_ptr());
assert!(check >= 0 && check <= 9);
}
#[test]
fn test_ffi_new_barcode_pixmap_qr() {
let ctx = 1;
let data = CString::new("Hello World").unwrap();
let handle =
fz_new_barcode_pixmap(ctx, BarcodeType::QrCode as i32, data.as_ptr(), 4, 1, 1, 0);
assert!(handle > 0);
if let Some(pix) = PIXMAPS.get(handle) {
let guard = pix.lock().unwrap();
assert!(guard.w() > 0);
assert!(guard.h() > 0);
}
PIXMAPS.remove(handle);
}
#[test]
fn test_ffi_new_barcode_pixmap_code128() {
let ctx = 1;
let data = CString::new("ABC123").unwrap();
let handle =
fz_new_barcode_pixmap(ctx, BarcodeType::Code128 as i32, data.as_ptr(), 2, 0, 1, 0);
assert!(handle > 0);
if let Some(pix) = PIXMAPS.get(handle) {
let guard = pix.lock().unwrap();
assert!(guard.w() > 0);
assert!(guard.h() > 0);
}
PIXMAPS.remove(handle);
}
#[test]
fn test_ffi_new_barcode_pixmap_ean13() {
let ctx = 1;
let data = CString::new("5901234123457").unwrap();
let handle =
fz_new_barcode_pixmap(ctx, BarcodeType::Ean13 as i32, data.as_ptr(), 2, 0, 1, 0);
assert!(handle > 0);
PIXMAPS.remove(handle);
}
#[test]
fn test_barcode_type_count() {
assert_eq!(fz_barcode_type_count(), 21);
}
#[test]
fn test_ec_level() {
assert_eq!(EcLevel::from_i32(0), EcLevel::L);
assert_eq!(EcLevel::from_i32(1), EcLevel::M);
assert_eq!(EcLevel::from_i32(2), EcLevel::Q);
assert_eq!(EcLevel::from_i32(3), EcLevel::H);
}
#[test]
fn test_null_handling() {
assert_eq!(fz_barcode_type_from_string(ptr::null()), 0);
assert_eq!(
fz_barcode_validate(BarcodeType::QrCode as i32, ptr::null()),
0
);
assert_eq!(
fz_barcode_check_digit(BarcodeType::Ean13 as i32, ptr::null()),
-1
);
assert_eq!(
fz_new_barcode_pixmap(1, BarcodeType::QrCode as i32, ptr::null(), 4, 1, 1, 0),
0
);
}
#[test]
fn test_string_from_barcode_type() {
let ptr = fz_string_from_barcode_type(14);
assert!(!ptr.is_null());
let s = unsafe { CStr::from_ptr(ptr).to_str().unwrap() };
assert_eq!(s, "qrcode");
}
#[test]
fn test_string_from_barcode_type_oob() {
let ptr = fz_string_from_barcode_type(100);
assert!(!ptr.is_null());
}
#[test]
fn test_decode_barcode_from_pixmap() {
let mut type_out = 0i32;
let result = fz_decode_barcode_from_pixmap(0, &mut type_out, 0, 0);
assert!(result.is_null());
assert_eq!(type_out, BarcodeType::None as i32);
}
#[test]
fn test_decode_barcode_from_pixmap_null_type() {
let result = fz_decode_barcode_from_pixmap(0, ptr::null_mut(), 0, 0);
assert!(result.is_null());
}
#[test]
fn test_ffi_barcode_validate_all_types() {
let code39 = CString::new("HELLO").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::Code39 as i32, code39.as_ptr()),
1
);
let upca = CString::new("012345678901").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::UpcA as i32, upca.as_ptr()),
1
);
let upce = CString::new("012345").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::UpcE as i32, upce.as_ptr()),
1
);
let datamatrix = CString::new("x").unwrap();
assert_eq!(
fz_barcode_validate(BarcodeType::DataMatrix as i32, datamatrix.as_ptr()),
1
);
}
#[test]
fn test_ffi_barcode_check_digit_ean8() {
let ean8 = CString::new("9638507").unwrap();
let check = fz_barcode_check_digit(BarcodeType::Ean8 as i32, ean8.as_ptr());
assert!(check >= 0 && check <= 9);
}
#[test]
fn test_ffi_barcode_check_digit_invalid() {
let short = CString::new("123").unwrap();
assert_eq!(
fz_barcode_check_digit(BarcodeType::Ean13 as i32, short.as_ptr()),
-1
);
assert_eq!(
fz_barcode_check_digit(
BarcodeType::Code128 as i32,
CString::new("x").unwrap().as_ptr()
),
-1
);
}
#[test]
fn test_ffi_barcode_default_size() {
assert_eq!(fz_barcode_default_size(BarcodeType::QrCode as i32), 4);
assert_eq!(fz_barcode_default_size(BarcodeType::Code128 as i32), 2);
}
#[test]
fn test_ffi_new_barcode_image() {
let data = CString::new("img").unwrap();
let h = fz_new_barcode_image(0, BarcodeType::QrCode as i32, data.as_ptr(), 4, 1, 1, 0);
assert!(h > 0);
PIXMAPS.remove(h);
}
}