#[cfg(feature = "pdf")]
pub mod pdf_native;
#[cfg(feature = "png")]
pub mod png;
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) mod symbology;
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) fn barcode_1d_format(kind: crate::engine::Barcode1DKind) -> rxing::BarcodeFormat {
match kind {
crate::engine::Barcode1DKind::Ean13 => rxing::BarcodeFormat::EAN_13,
crate::engine::Barcode1DKind::Ean8 => rxing::BarcodeFormat::EAN_8,
crate::engine::Barcode1DKind::UpcA => rxing::BarcodeFormat::UPC_A,
crate::engine::Barcode1DKind::UpcE => rxing::BarcodeFormat::UPC_E,
crate::engine::Barcode1DKind::Interleaved2of5 => rxing::BarcodeFormat::ITF,
crate::engine::Barcode1DKind::Code93 => rxing::BarcodeFormat::CODE_93,
crate::engine::Barcode1DKind::Codabar => rxing::BarcodeFormat::CODABAR,
_ => rxing::BarcodeFormat::CODE_128,
}
}
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) fn code128_code_set(data: &str) -> (&str, &'static str) {
if let Some(stripped) = data.strip_prefix(">9") {
(stripped, "A")
} else if let Some(stripped) = data.strip_prefix(">:") {
(stripped, "B")
} else if let Some(stripped) = data.strip_prefix(">;") {
(stripped, "C")
} else {
(data, "B")
}
}
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) fn qr_field_data(data: &str, default_ec: char) -> (char, &str) {
let bytes = data.as_bytes();
if bytes.len() >= 3
&& matches!(bytes[0], b'H' | b'Q' | b'M' | b'L')
&& matches!(bytes[1], b'A' | b'M')
&& bytes[2] == b','
{
return (bytes[0] as char, &data[3..]);
}
if bytes.len() >= 2 && matches!(bytes[0], b'H' | b'Q' | b'M' | b'L') && bytes[1] == b',' {
return (bytes[0] as char, &data[2..]);
}
(default_ec, data)
}
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) const QR_ORIGIN_Y_OFFSET: u32 = 10;
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) fn pdf417_dimensions(
columns: Option<u32>,
rows: Option<u32>,
) -> rxing::pdf417::encoder::Dimensions {
let (min_c, max_c) = match columns.filter(|c| (1..=30).contains(c)) {
Some(c) => (c as usize, c as usize),
None => (1, 1),
};
let (min_r, max_r) = match rows.filter(|r| (3..=90).contains(r)) {
Some(r) => (r as usize, r as usize),
None => (3, 90),
};
rxing::pdf417::encoder::Dimensions::new(min_c, max_c, min_r, max_r)
}
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) const PDF417_ROW_SCALE: u32 = 4;
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) fn pdf417_descale(
matrix: &rxing::common::BitMatrix,
) -> crate::ZplResult<rxing::common::BitMatrix> {
let (w, h) = (matrix.getWidth(), matrix.getHeight());
let rows = h / PDF417_ROW_SCALE;
if rows == 0 {
return Ok(matrix.clone());
}
let mut out = rxing::common::BitMatrix::new(w, rows)
.map_err(|e| crate::ZplError::BackendError(format!("PDF417 rescale failed: {e}")))?;
for r in 0..rows {
let src_y = r * PDF417_ROW_SCALE;
for x in 0..w {
if matrix.get(x, src_y) {
out.set(x, r);
}
}
}
Ok(out)
}
#[cfg(any(feature = "png", feature = "pdf"))]
pub(crate) mod barcode_cache {
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use rxing::common::BitMatrix;
use rxing::{BarcodeFormat, EncodeHints, MultiFormatWriter, Writer};
use crate::{ZplError, ZplResult};
const MAX_ENTRIES: usize = 512;
type Key = (&'static str, String, String);
fn cache() -> &'static Mutex<HashMap<Key, Arc<BitMatrix>>> {
static CACHE: OnceLock<Mutex<HashMap<Key, Arc<BitMatrix>>>> = OnceLock::new();
CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
fn format_key(format: &BarcodeFormat) -> &'static str {
match format {
BarcodeFormat::CODE_128 => "c128",
BarcodeFormat::CODE_39 => "c39",
BarcodeFormat::CODE_93 => "c93",
BarcodeFormat::QR_CODE => "qr",
BarcodeFormat::DATA_MATRIX => "dm",
BarcodeFormat::PDF_417 => "p417",
BarcodeFormat::EAN_13 => "e13",
BarcodeFormat::EAN_8 => "e8",
BarcodeFormat::UPC_A => "upca",
BarcodeFormat::UPC_E => "upce",
BarcodeFormat::ITF => "itf",
BarcodeFormat::CODABAR => "codabar",
BarcodeFormat::AZTEC => "aztec",
_ => "other",
}
}
#[allow(clippy::collapsible_if)]
pub fn encode_cached(
format: BarcodeFormat,
data: &str,
hints_key: &str,
hints: Option<&EncodeHints>,
) -> ZplResult<Arc<BitMatrix>> {
let key: Key = (format_key(&format), data.to_string(), hints_key.to_string());
if let Ok(guard) = cache().lock() {
if let Some(hit) = guard.get(&key) {
return Ok(hit.clone());
}
}
let mut effective = hints.cloned().unwrap_or_default();
if effective.Margin.is_none() {
effective.Margin = Some("0".to_string());
}
let writer = MultiFormatWriter;
let matrix = writer
.encode_with_hints(data, &format, 0, 0, &effective)
.or_else(|e| {
if effective.ForceCodeSet.is_some() {
let mut relaxed = effective.clone();
relaxed.ForceCodeSet = None;
writer.encode_with_hints(data, &format, 0, 0, &relaxed)
} else {
Err(e)
}
})
.map_err(|e| ZplError::BackendError(format!("Barcode Generation Error: {}", e)))?;
let matrix = Arc::new(matrix);
if let Ok(mut guard) = cache().lock() {
if guard.len() >= MAX_ENTRIES {
guard.clear();
}
guard.insert(key, matrix.clone());
}
Ok(matrix)
}
}