use krilla::{
color::luma,
geom::{PathBuilder, Point, Transform},
num::NormalizedF32,
paint::Fill,
surface::Surface
};
use rxing::{
aztec::AztecWriter, common::BitMatrix, datamatrix::DataMatrixWriter,
pdf417::PDF417Writer, qrcode::QRCodeWriter, BarcodeFormat, Writer
};
pub type Result<T = (), E = rxing::Exceptions> = std::result::Result<T, E>;
pub struct Barcode {
bit_matrix: BitMatrix,
bit_size: f32,
border: f32
}
impl Barcode {
fn new(bit_matrix: BitMatrix, width: f32, border: f32) -> Self {
Self {
border,
bit_size: width / (bit_matrix.width() as f32 + 2.0 * border),
bit_matrix
}
}
pub fn new_aztec(text: &str, width: f32) -> Result<Self> {
let bit_matrix = AztecWriter.encode(text, &BarcodeFormat::AZTEC, 0, 0)?;
Ok(Self::new(bit_matrix, width, 0.0))
}
pub fn new_data_matrix(text: &str, width: f32) -> Result<Self> {
let bit_matrix =
DataMatrixWriter.encode(text, &BarcodeFormat::DATA_MATRIX, 0, 0)?;
Ok(Self::new(bit_matrix, width, 1.0))
}
pub fn new_pdf417(text: &str, width: f32) -> Result<Self> {
let bit_matrix = PDF417Writer.encode(text, &BarcodeFormat::PDF_417, 0, 0)?;
Ok(Self::new(bit_matrix, width, 0.0))
}
pub fn new_qr_code(text: &str, width: f32) -> Result<Self> {
let bit_matrix = QRCodeWriter.encode(text, &BarcodeFormat::QR_CODE, 0, 0)?;
Ok(Self::new(bit_matrix, width, 0.0))
}
pub fn width(&self) -> f32 {
self.bit_size * (self.bit_matrix.width() as f32 + 2.0 * self.border)
}
pub fn height(&self) -> f32 {
self.bit_size * (self.bit_matrix.height() as f32 + 2.0 * self.border)
}
}
struct RleBits {
x: u32,
y: u32,
w: u32,
h: u32
}
struct Rle {
bits: Vec<RleBits>
}
impl Rle {
fn new_horiz(bit_matrix: &BitMatrix) -> Self {
let mut bits = Vec::new();
let mut y = 0;
for row in 0 .. bit_matrix.height() {
let mut x = 0;
let mut col = 0;
while col < bit_matrix.width() {
if !bit_matrix.get(col, row) {
col += 1;
x += 1;
continue;
}
let col_start = col;
while col < bit_matrix.width() && bit_matrix.get(col, row) {
col += 1;
}
let colspan = col - col_start;
bits.push(RleBits {
x,
y,
w: colspan,
h: 1
});
x += colspan;
}
y += 1;
}
Self { bits }
}
fn new_vert(bit_matrix: &BitMatrix) -> Self {
let mut bits = Vec::new();
let mut x = 0;
for col in 0 .. bit_matrix.width() {
let mut y = 0;
let mut row = 0;
while row < bit_matrix.height() {
if !bit_matrix.get(col, row) {
row += 1;
y += 1;
continue;
}
let row_start = row;
while row < bit_matrix.height() && bit_matrix.get(col, row) {
row += 1;
}
let rowspan = row - row_start;
bits.push(RleBits {
x,
y,
w: 1,
h: rowspan
});
y += rowspan;
}
x += 1;
}
Self { bits }
}
fn len(&self) -> usize {
self.bits.len()
}
}
impl Barcode {
fn draw(&self, surface: &mut Surface<'_>, x: f32, y: f32) {
let width = self.bit_matrix.width() as f32 + 2.0 * self.border;
let height = self.bit_matrix.height() as f32 + 2.0 * self.border;
surface.push_transform(&Transform::from_translate(x, y));
surface.push_transform(&Transform::from_scale(self.bit_size, self.bit_size));
surface.set_stroke(None);
surface.set_fill(Some(Fill {
paint: luma::Color::white().into(),
opacity: NormalizedF32::new(0.9).unwrap(),
..Default::default()
}));
let mut bg = PathBuilder::new();
bg.move_to(0.0, 0.0);
bg.line_to(width, 0.0);
bg.line_to(width, height);
bg.line_to(0.0, height);
bg.close();
let bg = bg.finish().unwrap();
surface.draw_path(&bg);
surface.set_stroke(None);
surface.set_fill(Some(Fill {
paint: luma::Color::black().into(),
..Default::default()
}));
let rle_horiz = Rle::new_horiz(&self.bit_matrix);
let rle_vert = Rle::new_vert(&self.bit_matrix);
let rle = if rle_horiz.len() > rle_vert.len() {
rle_vert
} else {
rle_horiz
};
for bit in rle.bits {
surface.push_transform(&Transform::from_translate(
self.border + bit.x as f32,
self.border + bit.y as f32
));
let mut path = PathBuilder::new();
path.move_to(0.0, 0.0);
path.line_to(bit.w as f32, 0.0);
path.line_to(bit.w as f32, bit.h as f32);
path.line_to(0.0, bit.h as f32);
path.close();
let path = path.finish().unwrap();
surface.draw_path(&path);
surface.pop();
}
surface.pop();
surface.pop();
}
}
pub trait SurfaceExt {
fn draw_aztec(&mut self, text: &str, top_left: Point, width: f32) -> Result {
let barcode = Barcode::new_aztec(text, width)?;
self.draw_barcode(&barcode, top_left);
Ok(())
}
fn draw_data_matrix(&mut self, text: &str, top_left: Point, width: f32) -> Result {
let barcode = Barcode::new_data_matrix(text, width)?;
self.draw_barcode(&barcode, top_left);
Ok(())
}
fn draw_pdf417(&mut self, text: &str, top_left: Point, width: f32) -> Result {
let barcode = Barcode::new_pdf417(text, width)?;
self.draw_barcode(&barcode, top_left);
Ok(())
}
fn draw_qr_code(&mut self, text: &str, top_left: Point, width: f32) -> Result {
let barcode = Barcode::new_qr_code(text, width)?;
self.draw_barcode(&barcode, top_left);
Ok(())
}
fn draw_barcode(&mut self, barcode: &Barcode, top_left: Point);
}
impl SurfaceExt for Surface<'_> {
fn draw_barcode(&mut self, barcode: &Barcode, top_left: Point) {
barcode.draw(self, top_left.x, top_left.y);
}
}