Started implementing queues and escposprinter struct

This commit is contained in:
Jurn Wubben 2025-09-24 17:32:07 +02:00
parent 410e203f9e
commit fbf8111ddd

View file

@ -1,46 +1,130 @@
use crate::dithering::atkinson_mono; use crate::dithering::atkinson_mono;
use image::{DynamicImage, imageops::FilterType}; use image::{DynamicImage, imageops::FilterType};
use std::{
collections::{HashMap, VecDeque},
ops::{Add, AddAssign},
};
const MAX_WIDTH: u32 = 384; const MAX_WIDTH: u32 = 384;
pub enum EscPosError {
InvalidBitmapMode,
InvalidQueueId,
}
pub enum ImageOrientation { pub enum ImageOrientation {
Preserve, Preserve,
Largest, Largest,
} }
pub fn escpos_raster( 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, img: &DynamicImage,
orientation: Option<ImageOrientation>, orientation: Option<ImageOrientation>,
mode: Option<u8>, mode: Option<u8>,
) -> Vec<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); let mode = mode.unwrap_or(0);
assert!(mode <= 3 || (48..=51).contains(&mode)); 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 (w, h) = (img.width(), img.height());
let swap = matches!(orientation, Some(ImageOrientation::Largest)) let swap = matches!(orientation, Some(ImageOrientation::Largest))
&& h.min(MAX_WIDTH) * w > w.min(MAX_WIDTH) * h; && h.min(MAX_WIDTH) * w > w.min(MAX_WIDTH) * h;
let (w, h) = if swap { (h, w) } else { (w, h) }; let scale = if swap {
let (w, h) = (MAX_WIDTH, h.saturating_mul(MAX_WIDTH).div_ceil(w)); MAX_WIDTH as f32 / h as f32
} else {
MAX_WIDTH as f32 / w as f32
};
let img = if swap { &img.rotate90() } else { &img }; let (w, h) = if swap {
let img = img.resize(w, h, FilterType::Triangle); ((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 mono = atkinson_mono(&img);
let (width_px, height_px) = (img.width() as u16, img.height() as u16); let width_bytes = ((w + 7) >> 3) as u16;
let width_bytes = ((width_px + 7) >> 3) as u16;
let mut out = Vec::with_capacity(8 + mono.len()); buf.reserve(8 + mono.len());
out.extend_from_slice(&[ buf.extend_from_slice(&[
0x1D, 0x1D,
0x76, 0x76,
0x30, 0x30,
mode, mode,
width_bytes as u8, width_bytes as u8,
(width_bytes >> 8) as u8, (width_bytes >> 8) as u8,
height_px as u8, h as u8,
(height_px >> 8) as u8, (h >> 8) as u8,
]); ]);
out.extend_from_slice(&mono); buf.extend_from_slice(&mono);
out
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(());
}
} }