use super::errors::Result;
#[cfg(feature = "ui")]
use crate::domain::ui::line::Line;
use crate::printer_options::PrinterOptions;
use crate::{domain::*, driver::Driver, utils::Protocol};
use log::debug;
#[derive(Clone)]
pub struct Printer<D: Driver> {
driver: D,
protocol: Protocol,
options: PrinterOptions,
instructions: Vec<Instruction>,
style_state: PrinterStyleState,
}
impl<D: Driver> Printer<D> {
pub fn new(driver: D, protocol: Protocol, options: Option<PrinterOptions>) -> Self {
Self {
driver,
protocol,
options: options.unwrap_or_default(),
instructions: vec![],
style_state: PrinterStyleState::default(),
}
}
pub fn protocol(&self) -> &Protocol {
&self.protocol
}
pub fn options(&self) -> &PrinterOptions {
&self.options
}
pub fn style_state(&self) -> PrinterStyleState {
self.style_state.clone()
}
pub fn reset_style_state(&mut self) -> &mut Self {
self.style_state = PrinterStyleState::default();
self
}
fn flush(&mut self) -> Result<&mut Self> {
for instruction in self.instructions.iter() {
self.driver.write(&instruction.flatten_commands())?
}
self.driver.flush()?;
self.instructions = vec![];
self.reset_style_state();
Ok(self)
}
pub fn debug_mode(&mut self, mode: Option<DebugMode>) -> &mut Self {
self.options.debug_mode(mode);
self
}
pub fn print(&mut self) -> Result<&mut Self> {
self.flush()?;
if self.options.get_debug_mode().is_some() {
debug!("[print]");
}
Ok(self)
}
fn command(&mut self, label: &str, cmd: &[Command]) -> Result<&mut Self> {
let instruction = Instruction::new(label, cmd, self.options.get_debug_mode());
if !label.is_empty() && self.options.get_debug_mode().is_some() {
debug!("{:?}", instruction.clone());
}
self.instructions.push(instruction);
Ok(self)
}
pub fn init(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.init();
self.command("initialization", &[cmd])?;
if let Some(page_code) = self.options.get_page_code() {
let cmd = self.protocol.page_code(page_code);
self.command("character page code", &[cmd])?;
}
Ok(self)
}
pub fn reset(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.reset();
self.command("reset", &[cmd])
}
pub fn cut(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.cut(false);
self.command("full paper cut", &[cmd])
}
pub fn partial_cut(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.cut(true);
self.command("partial paper cut", &[cmd])
}
pub fn print_cut(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.cut(false);
self.command("full paper cut", &[cmd])?.print()
}
pub fn page_code(&mut self, code: PageCode) -> Result<&mut Self> {
self.options.page_code(Some(code));
let cmd = self.protocol.page_code(code);
self.command("character page code", &[cmd])
}
pub fn character_set(&mut self, code: CharacterSet) -> Result<&mut Self> {
let cmd = self.protocol.character_set(code);
self.command("international character set", &[cmd])
}
pub fn bold(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.bold(enabled);
self.style_state.bold = enabled;
self.command("text bold", &[cmd])
}
pub fn underline(&mut self, mode: UnderlineMode) -> Result<&mut Self> {
let cmd = self.protocol.underline(mode);
self.style_state.underline_mode = mode;
self.command("text underline", &[cmd])
}
pub fn double_strike(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.double_strike(enabled);
self.style_state.double_strike = enabled;
self.command("text double strike", &[cmd])
}
pub fn font(&mut self, font: Font) -> Result<&mut Self> {
let cmd = self.protocol.font(font);
self.style_state.font = font;
self.command("text font", &[cmd])
}
pub fn flip(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.flip(enabled);
self.style_state.flip = enabled;
self.command("text flip", &[cmd])
}
pub fn justify(&mut self, mode: JustifyMode) -> Result<&mut Self> {
let cmd = self.protocol.justify(mode);
self.style_state.justify_mode = mode;
self.command("text justify", &[cmd])
}
pub fn reverse(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.reverse_colours(enabled);
self.style_state.reverse = enabled;
self.command("text reverse colour", &[cmd])
}
pub fn size(&mut self, width: u8, height: u8) -> Result<&mut Self> {
let cmd = self.protocol.text_size(width, height)?;
self.style_state.text_size = (width, height);
self.command("text size", &[cmd])
}
pub fn reset_size(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.text_size(1, 1)?;
self.style_state.text_size = (1, 1);
self.command("text size", &[cmd])
}
pub fn smoothing(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.smoothing(enabled);
self.command("smoothing mode", &[cmd])
}
pub fn feed(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.feed(1);
self.command("line feed", &[cmd])
}
pub fn feeds(&mut self, lines: u8) -> Result<&mut Self> {
let cmd = self.protocol.feed(lines);
self.command("line feeds", &[cmd])
}
pub fn line_spacing(&mut self, value: u8) -> Result<&mut Self> {
let cmd = self.protocol.line_spacing(value);
self.command("line spacing", &[cmd])
}
pub fn reset_line_spacing(&mut self) -> Result<&mut Self> {
let cmd = self.protocol.reset_line_spacing();
self.command("reset line spacing", &[cmd])
}
pub fn upside_down(&mut self, enabled: bool) -> Result<&mut Self> {
let cmd = self.protocol.upside_down(enabled);
self.command("upside-down mode", &[cmd])
}
pub fn cash_drawer(&mut self, pin: CashDrawer) -> Result<&mut Self> {
let cmd = self.protocol.cash_drawer(pin);
self.command("cash drawer", &[cmd])
}
pub fn write(&mut self, text: &str) -> Result<&mut Self> {
let cmd = self.protocol.text(text, self.options.get_page_code(), None)?;
self.command("text", &[cmd])
}
pub fn writeln(&mut self, text: &str) -> Result<&mut Self> {
self.write(text)?.feed()
}
pub fn custom(&mut self, cmd: &[u8]) -> Result<&mut Self> {
self.command("custom command", &[cmd.to_vec()])
}
pub fn custom_with_page_code(&mut self, cmd: &[u8], page_code: PageCode) -> Result<&mut Self> {
self.page_code(page_code)?;
self.command(&format!("custom command width page code {page_code}"), &[cmd.to_vec()])
}
pub fn motion_units(&mut self, x: u8, y: u8) -> Result<&mut Self> {
let cmd = self.protocol.motion_units(x, y);
self.command("set motion units", &[cmd])
}
pub fn real_time_status(&mut self, status: RealTimeStatusRequest) -> Result<&mut Self> {
let cmd = self.protocol.real_time_status(status);
self.command("real-time status", &[cmd])
}
pub fn send_status(&mut self) -> Result<&mut Self> {
self.flush()?;
if self.options.get_debug_mode().is_some() {
debug!("[send printer status]");
}
Ok(self)
}
#[cfg(feature = "barcodes")]
fn barcode(&mut self, barcode: Barcode) -> Result<&mut Self> {
let commands = self.protocol.barcode(&barcode.data, barcode.system, barcode.option)?;
self.command(&format!("print {} barcode", barcode.system), commands.as_slice())
}
#[cfg(feature = "barcodes")]
pub fn ean13(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::EAN13, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn ean13_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::EAN13, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn ean8(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::EAN8, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn ean8_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::EAN8, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn upca(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::UPCA, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn upca_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::UPCA, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn upce(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::UPCE, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn upce_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::UPCE, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn code39(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::CODE39, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn code39_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::CODE39, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn codabar(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::CODABAR, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn codabar_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::CODABAR, data, option)?)
}
#[cfg(feature = "barcodes")]
pub fn itf(&mut self, data: &str) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::ITF, data, BarcodeOption::default())?)
}
#[cfg(feature = "barcodes")]
pub fn itf_option(&mut self, data: &str, option: BarcodeOption) -> Result<&mut Self> {
self.barcode(Barcode::new(BarcodeSystem::ITF, data, option)?)
}
#[cfg(feature = "codes_2d")]
fn qrcode_builder(&mut self, data: &str, option: Option<QRCodeOption>) -> Result<&mut Self> {
let qrcode = QRCode::new(data, option)?;
let commands = self.protocol.qrcode(&qrcode.data, qrcode.option)?;
self.command("print qrcode", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn qrcode(&mut self, data: &str) -> Result<&mut Self> {
self.qrcode_builder(data, None)
}
#[cfg(feature = "codes_2d")]
pub fn qrcode_option(&mut self, data: &str, option: QRCodeOption) -> Result<&mut Self> {
self.qrcode_builder(data, Some(option))
}
#[cfg(feature = "codes_2d")]
pub fn gs1_databar_2d_option(&mut self, data: &str, option: GS1DataBar2DOption) -> Result<&mut Self> {
let code = GS1DataBar2D::new(data, option)?;
let commands = self.protocol.gs1_databar_2d(&code.data, code.option)?;
self.command("print 2D GS1 DataBar", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn gs1_databar_2d(&mut self, data: &str) -> Result<&mut Self> {
self.gs1_databar_2d_option(data, GS1DataBar2DOption::default())
}
#[cfg(feature = "codes_2d")]
pub fn pdf417_option(&mut self, data: &str, option: Pdf417Option) -> Result<&mut Self> {
let code = Pdf417::new(data, option);
let commands = self.protocol.pdf417(&code.data, code.option)?;
self.command("print PDF417", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn pdf417(&mut self, data: &str) -> Result<&mut Self> {
let code = Pdf417::new(data, Pdf417Option::default());
self.pdf417_option(data, code.option)
}
#[cfg(feature = "codes_2d")]
pub fn maxi_code_option(&mut self, data: &str, mode: MaxiCodeMode) -> Result<&mut Self> {
let code = MaxiCode::new(data, mode);
let commands = self.protocol.maxi_code(&code.data, code.mode)?;
self.command("print MaxiCode", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn maxi_code(&mut self, data: &str) -> Result<&mut Self> {
let code = MaxiCode::new(data, MaxiCodeMode::default());
self.maxi_code_option(data, code.mode)
}
#[cfg(feature = "codes_2d")]
pub fn data_matrix_option(&mut self, data: &str, option: DataMatrixOption) -> Result<&mut Self> {
let code = DataMatrix::new(data, option);
let commands = self.protocol.data_matrix(&code.data, code.option)?;
self.command("print DataMatrix", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn data_matrix(&mut self, data: &str) -> Result<&mut Self> {
let code = DataMatrix::new(data, DataMatrixOption::default());
self.data_matrix_option(data, code.option)
}
#[cfg(feature = "codes_2d")]
pub fn aztec_option(&mut self, data: &str, option: AztecOption) -> Result<&mut Self> {
let code = Aztec::new(data, option);
let commands = self.protocol.aztec(&code.data, code.option)?;
self.command("print Aztec", commands.as_slice())
}
#[cfg(feature = "codes_2d")]
pub fn aztec(&mut self, data: &str) -> Result<&mut Self> {
let code = Aztec::new(data, AztecOption::default());
self.aztec_option(data, code.option)
}
#[cfg(feature = "graphics")]
pub fn bit_image_option(&mut self, path: &str, option: BitImageOption) -> Result<&mut Self> {
let cmd = self.protocol.cancel();
self.command("cancel data", &[cmd])?;
let cmd = self.protocol.bit_image(path, option)?;
self.command("print bit image", &[cmd])
}
#[cfg(feature = "graphics")]
pub fn bit_image(&mut self, path: &str) -> Result<&mut Self> {
self.bit_image_option(path, BitImageOption::default())
}
#[cfg(feature = "graphics")]
pub fn bit_image_from_bytes_option(&mut self, bytes: &[u8], option: BitImageOption) -> Result<&mut Self> {
let cmd = self.protocol.cancel();
self.command("cancel data", &[cmd])?;
let cmd = self.protocol.bit_image_from_bytes(bytes, option)?;
self.command("print bit image from bytes", &[cmd])
}
#[cfg(feature = "graphics")]
pub fn bit_image_from_bytes(&mut self, bytes: &[u8]) -> Result<&mut Self> {
self.bit_image_from_bytes_option(bytes, BitImageOption::default())
}
#[cfg(feature = "ui")]
pub fn draw_line(&mut self, line: Line) -> Result<&mut Self> {
let commands = self
.protocol
.draw_line(line, self.options.clone(), self.style_state.clone())?;
self.command("draw line", commands.as_slice())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PrinterStyleState {
pub text_size: (u8, u8),
pub justify_mode: JustifyMode,
pub font: Font,
pub underline_mode: UnderlineMode,
pub bold: bool,
pub double_strike: bool,
pub reverse: bool,
pub flip: bool,
}
impl Default for PrinterStyleState {
fn default() -> Self {
Self {
text_size: (1, 1),
justify_mode: JustifyMode::default(),
font: Font::default(),
underline_mode: UnderlineMode::default(),
bold: false,
double_strike: false,
reverse: false,
flip: false,
}
}
}
impl PrinterStyleState {}
#[cfg(test)]
mod tests {
use super::*;
use crate::driver::ConsoleDriver;
#[test]
fn test_command() {
let driver = ConsoleDriver::open(false);
let debug_mode = None;
let mut printer = Printer::new(driver, Protocol::default(), None);
printer.debug_mode(debug_mode).init().unwrap();
let cmd = printer.protocol.cut(false);
let printer = printer.command("test paper cut", &[cmd]).unwrap();
let expected = vec![
Instruction::new("initialization", &[vec![27, 64]], debug_mode),
Instruction::new("test paper cut", &[vec![29, 86, 65, 0]], debug_mode),
];
assert_eq!(printer.instructions, expected);
}
}