use super::Component;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BarcodeFormat {
Code128,
Code39,
Ean13,
Ean8,
UpcA,
UpcE,
QrCode,
DataMatrix,
Pdf417,
Itf,
Codabar,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Barcode {
pub data: String,
pub format: BarcodeFormat,
#[serde(default = "default_barcode_width")]
pub width: String,
#[serde(default = "default_barcode_height")]
pub height: String,
#[serde(default = "default_true")]
pub show_text: bool,
#[serde(default)]
pub error_correction: Option<String>,
}
fn default_barcode_width() -> String {
"150pt".into()
}
fn default_barcode_height() -> String {
"50pt".into()
}
fn default_true() -> bool {
true
}
impl Barcode {
pub fn new(data: impl Into<String>, format: BarcodeFormat) -> Self {
Self {
data: data.into(),
format,
width: "150pt".into(),
height: "50pt".into(),
show_text: true,
error_correction: None,
}
}
pub fn code128(data: impl Into<String>) -> Self {
Self::new(data, BarcodeFormat::Code128)
}
pub fn ean13(data: impl Into<String>) -> Self {
Self::new(data, BarcodeFormat::Ean13)
}
pub fn qr_code(data: impl Into<String>) -> Self {
Self {
width: "100pt".into(),
height: "100pt".into(),
error_correction: Some("M".into()),
..Self::new(data, BarcodeFormat::QrCode)
}
}
pub fn data_matrix(data: impl Into<String>) -> Self {
Self {
width: "80pt".into(),
height: "80pt".into(),
..Self::new(data, BarcodeFormat::DataMatrix)
}
}
pub fn with_size(mut self, width: impl Into<String>, height: impl Into<String>) -> Self {
self.width = width.into();
self.height = height.into();
self
}
pub fn hide_text(mut self) -> Self {
self.show_text = false;
self
}
pub fn with_error_correction(mut self, level: impl Into<String>) -> Self {
self.error_correction = Some(level.into());
self
}
fn encode_1d(&self) -> Option<Vec<u8>> {
match self.format {
BarcodeFormat::Code128 => {
let prefixed = if self.data.starts_with('\u{00C0}')
|| self.data.starts_with('\u{0181}')
|| self.data.starts_with('\u{0106}')
{
self.data.clone()
} else {
format!("\u{0181}{}", self.data)
};
barcoders::sym::code128::Code128::new(&prefixed)
.ok()
.map(|b| b.encode())
}
BarcodeFormat::Code39 => barcoders::sym::code39::Code39::new(&self.data)
.ok()
.map(|b| b.encode()),
BarcodeFormat::Ean13 => barcoders::sym::ean13::EAN13::new(&self.data)
.ok()
.map(|b| b.encode()),
BarcodeFormat::Ean8 => barcoders::sym::ean8::EAN8::new(&self.data)
.ok()
.map(|b| b.encode()),
BarcodeFormat::Codabar => barcoders::sym::codabar::Codabar::new(&self.data)
.ok()
.map(|b| b.encode()),
BarcodeFormat::Itf => barcoders::sym::tf::TF::interleaved(&self.data)
.ok()
.map(|b| b.encode()),
BarcodeFormat::UpcA => {
let ean = if self.data.len() == 12 {
format!("0{}", &self.data[..12])
} else {
format!("0{}", self.data)
};
barcoders::sym::ean13::EAN13::new(&ean)
.ok()
.map(|b| b.encode())
}
BarcodeFormat::UpcE => {
None
}
_ => None,
}
}
fn encode_data_matrix(&self) -> Option<(Vec<Vec<u8>>, usize, usize)> {
datamatrix::DataMatrix::encode_str(
&self.data,
datamatrix::SymbolList::default().enforce_square(),
)
.ok()
.map(|dm| {
let bitmap = dm.bitmap();
let w = bitmap.width();
let h = bitmap.height();
let bits = bitmap.bits();
let matrix: Vec<Vec<u8>> = (0..h)
.map(|row| {
(0..w)
.map(|col| if bits[row * w + col] { 1 } else { 0 })
.collect()
})
.collect();
(matrix, w, h)
})
}
fn encode_qr(&self) -> Option<(Vec<Vec<u8>>, usize)> {
use qrcode::EcLevel;
let ec = match self.error_correction.as_deref() {
Some("L") => EcLevel::L,
Some("Q") => EcLevel::Q,
Some("H") => EcLevel::H,
_ => EcLevel::M,
};
qrcode::QrCode::with_error_correction_level(self.data.as_bytes(), ec)
.ok()
.map(|code| {
let width = code.width();
let colors = code.to_colors();
let matrix: Vec<Vec<u8>> = colors
.chunks(width)
.map(|row| {
row.iter()
.map(|c| if *c == qrcode::Color::Dark { 1 } else { 0 })
.collect()
})
.collect();
(matrix, width)
})
}
}
impl Component for Barcode {
fn component_id(&self) -> &'static str {
"barcode"
}
fn to_data(&self) -> serde_json::Value {
let mut val = serde_json::to_value(self).unwrap_or_default();
let obj = val.as_object_mut().unwrap();
match self.format {
BarcodeFormat::QrCode => {
if let Some((matrix, width)) = self.encode_qr() {
obj.insert("encoding_2d".into(), serde_json::json!(matrix));
obj.insert("qr_width".into(), serde_json::json!(width));
}
}
BarcodeFormat::DataMatrix => {
if let Some((matrix, cols, rows)) = self.encode_data_matrix() {
obj.insert("encoding_2d".into(), serde_json::json!(matrix));
obj.insert("qr_width".into(), serde_json::json!(cols));
obj.insert("qr_height".into(), serde_json::json!(rows));
}
}
BarcodeFormat::Pdf417 => {
obj.insert("unsupported".into(), serde_json::json!(true));
}
_ => {
if let Some(bars) = self.encode_1d() {
let bars_json: Vec<serde_json::Value> =
bars.iter().map(|&b| serde_json::json!(b)).collect();
obj.insert("encoding".into(), serde_json::json!(bars_json));
}
}
}
val
}
}