Started implementing queues and escposprinter struct
This commit is contained in:
parent
410e203f9e
commit
fbf8111ddd
1 changed files with 119 additions and 35 deletions
154
src/escpos.rs
154
src/escpos.rs
|
|
@ -1,46 +1,130 @@
|
|||
use crate::dithering::atkinson_mono;
|
||||
use image::{DynamicImage, imageops::FilterType};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
const MAX_WIDTH: u32 = 384;
|
||||
|
||||
pub enum EscPosError {
|
||||
InvalidBitmapMode,
|
||||
InvalidQueueId,
|
||||
}
|
||||
pub enum ImageOrientation {
|
||||
Preserve,
|
||||
Largest,
|
||||
}
|
||||
|
||||
pub fn escpos_raster(
|
||||
img: &DynamicImage,
|
||||
orientation: Option<ImageOrientation>,
|
||||
mode: Option<u8>,
|
||||
) -> Vec<u8> {
|
||||
let mode = mode.unwrap_or(0);
|
||||
assert!(mode <= 3 || (48..=51).contains(&mode));
|
||||
|
||||
let (w, h) = (img.width(), img.height());
|
||||
let swap = matches!(orientation, Some(ImageOrientation::Largest))
|
||||
&& h.min(MAX_WIDTH) * w > w.min(MAX_WIDTH) * h;
|
||||
|
||||
let (w, h) = if swap { (h, w) } else { (w, h) };
|
||||
let (w, h) = (MAX_WIDTH, h.saturating_mul(MAX_WIDTH).div_ceil(w));
|
||||
|
||||
let img = if swap { &img.rotate90() } else { &img };
|
||||
let img = img.resize(w, h, FilterType::Triangle);
|
||||
|
||||
let mono = atkinson_mono(&img);
|
||||
let (width_px, height_px) = (img.width() as u16, img.height() as u16);
|
||||
let width_bytes = ((width_px + 7) >> 3) as u16;
|
||||
|
||||
let mut out = Vec::with_capacity(8 + mono.len());
|
||||
out.extend_from_slice(&[
|
||||
0x1D,
|
||||
0x76,
|
||||
0x30,
|
||||
mode,
|
||||
width_bytes as u8,
|
||||
(width_bytes >> 8) as u8,
|
||||
height_px as u8,
|
||||
(height_px >> 8) as u8,
|
||||
]);
|
||||
out.extend_from_slice(&mono);
|
||||
out
|
||||
pub struct EscPosPrinter {
|
||||
increment: u16,
|
||||
queue: HashMap<u16, Vec<u8>>,
|
||||
}
|
||||
pub trait EscPosPrinterTrait {
|
||||
fn new() -> EscPosPrinter;
|
||||
fn new_job(&mut self) -> u16;
|
||||
|
||||
fn bitmap(
|
||||
&mut self,
|
||||
queue_id: u16,
|
||||
img: &DynamicImage,
|
||||
orientation: Option<ImageOrientation>,
|
||||
mode: Option<u8>,
|
||||
) -> Result<(), EscPosError>;
|
||||
fn qr(&mut self, queue_id: u16, text: String) -> Result<(), EscPosError>;
|
||||
}
|
||||
|
||||
impl EscPosPrinterTrait for EscPosPrinter {
|
||||
fn new() -> EscPosPrinter {
|
||||
EscPosPrinter {
|
||||
increment: 0,
|
||||
queue: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_job(&mut self) -> u16 {
|
||||
let job: Vec<u8> = Vec::new();
|
||||
|
||||
self.increment.add_assign(1);
|
||||
self.queue.insert(self.increment, job);
|
||||
self.increment
|
||||
}
|
||||
fn print_job(&mut self, queue_id: u16) -> Result<(), EscPosError> {
|
||||
let page_feed: u8 = 0x0A;
|
||||
|
||||
io::stdout().write(&page_feed);
|
||||
io::stdout().write(&escpos).unwrap();
|
||||
io::stdout().write(&page_feed).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bitmap(
|
||||
&mut self,
|
||||
queue_id: u16,
|
||||
img: &DynamicImage,
|
||||
orientation: Option<ImageOrientation>,
|
||||
mode: Option<u8>,
|
||||
) -> Result<(), EscPosError> {
|
||||
let mode = mode.unwrap_or(0);
|
||||
if !(mode <= 3 || (48..=51).contains(&mode)) {
|
||||
return Err(EscPosError::InvalidBitmapMode);
|
||||
}
|
||||
|
||||
let buf = self
|
||||
.queue
|
||||
.get_mut(&queue_id)
|
||||
.ok_or(EscPosError::InvalidQueueId)?;
|
||||
|
||||
let (w, h) = (img.width(), img.height());
|
||||
let swap = matches!(orientation, Some(ImageOrientation::Largest))
|
||||
&& h.min(MAX_WIDTH) * w > w.min(MAX_WIDTH) * h;
|
||||
|
||||
let scale = if swap {
|
||||
MAX_WIDTH as f32 / h as f32
|
||||
} else {
|
||||
MAX_WIDTH as f32 / w as f32
|
||||
};
|
||||
|
||||
let (w, h) = if swap {
|
||||
((h as f32 * scale) as u32, MAX_WIDTH)
|
||||
} else {
|
||||
(MAX_WIDTH, (h as f32 * scale) as u32)
|
||||
};
|
||||
|
||||
let img =
|
||||
DynamicImage::ImageRgba8(image::imageops::resize(img, w, h, FilterType::Triangle));
|
||||
|
||||
let img = if swap { img.rotate90() } else { img };
|
||||
let mono = atkinson_mono(&img);
|
||||
let width_bytes = ((w + 7) >> 3) as u16;
|
||||
|
||||
buf.reserve(8 + mono.len());
|
||||
buf.extend_from_slice(&[
|
||||
0x1D,
|
||||
0x76,
|
||||
0x30,
|
||||
mode,
|
||||
width_bytes as u8,
|
||||
(width_bytes >> 8) as u8,
|
||||
h as u8,
|
||||
(h >> 8) as u8,
|
||||
]);
|
||||
buf.extend_from_slice(&mono);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn qr(&mut self, queue_id: u16, text: String) -> Result<(), EscPosError> {
|
||||
let buf = self
|
||||
.queue
|
||||
.get_mut(&queue_id)
|
||||
.ok_or(EscPosError::InvalidQueueId)?;
|
||||
|
||||
let text = text.as_bytes();
|
||||
buf.extend_from_slice(&[0x1C, 0x7D, 0x25, text.len() as u8]);
|
||||
buf.extend(&text[..255]);
|
||||
buf.extend(&[0x0A]);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue