use std::io;
#[cfg(feature = "snapshot")]
use serde::{Serialize, Deserialize};
#[allow(unused_imports)]
use log::{error, warn, info, debug, trace};
use super::*;
#[derive(Clone, Default, Debug)]
#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "snapshot", serde(default))]
pub struct EpsonPrinterGfx {
#[cfg_attr(feature = "snapshot", serde(skip))]
state: GrabberState,
#[cfg_attr(feature = "snapshot", serde(skip))]
line_mm: f32,
#[cfg_attr(feature = "snapshot", serde(skip))]
eo_line: usize,
#[cfg_attr(feature = "snapshot", serde(skip))]
buf: Vec<u8>
}
const ESC_CODE: u8 = 0x1B;
const CR_CODE: u8 = 0x0D;
const LF_CODE: u8 = 0x0A;
const DATA_LINE_WIDTH: usize = 3 * 256;
const GRAPHICS_LINE_START: &[u8;6] = &[ESC_CODE, b'1', ESC_CODE, b'L', 0x00, 0x03]; const GRAPHICS_EXP_START: &[u8;6] = &[CR_CODE, LF_CODE, ESC_CODE, b'L', 0x00, 0x03];
const GRAPHICS_SPACING: u8 = b'3';const GRAPHICS_ENDS: u8 = b'2';
const GRAPHICS_RESET: u8 = b'@';
const INCH_MM: f32 = 25.4;
const INCH7_BY_72_LINE_MM: f32 = 2.47;
#[derive(Clone, Copy, Debug, PartialEq)]
enum GrabberState {
NoMatch,
LineStart(usize),
SelectSpacing,
LineData(usize),
}
impl Default for GrabberState {
fn default() -> Self {
GrabberState::NoMatch
}
}
impl EpsonPrinterGfx {
pub fn intercept<'a, 'b: 'a>(&'a mut self, ch: &'b u8) -> Option<&'a [u8]> {
match self.state {
GrabberState::NoMatch if *ch == ESC_CODE => {
self.buf.truncate(self.eo_line);
self.buf.push(*ch);
self.state = GrabberState::LineStart(1);
self.line_mm = INCH7_BY_72_LINE_MM;
None
}
GrabberState::NoMatch => Some(core::slice::from_ref(ch)),
GrabberState::SelectSpacing => {
self.line_mm = *ch as f32 / 216.0 * INCH_MM;
self.buf.truncate(self.eo_line);
self.state = GrabberState::LineStart(0);
None
}
GrabberState::LineStart(mut index) => {
self.buf.push(*ch);
if ch == &GRAPHICS_LINE_START[index] || ch == &GRAPHICS_EXP_START[index] {
index +=1;
if index == GRAPHICS_LINE_START.len() {
debug_assert_eq!(GRAPHICS_LINE_START.len(), GRAPHICS_EXP_START.len());
self.buf.truncate(self.eo_line);
self.state = GrabberState::LineData(0);
}
else {
self.state = GrabberState::LineStart(index);
}
None
}
else if index == 1 && (*ch == GRAPHICS_ENDS ||
*ch == GRAPHICS_RESET ||
*ch == GRAPHICS_SPACING) {
self.buf.truncate(self.eo_line);
self.state = if *ch == GRAPHICS_SPACING {
GrabberState::SelectSpacing
}
else {
GrabberState::NoMatch
};
None
}
else {
self.state = GrabberState::NoMatch;
Some(&self.buf[self.eo_line..])
}
}
GrabberState::LineData(width) => {
if width == DATA_LINE_WIDTH {
self.state = GrabberState::NoMatch;
if *ch == LF_CODE { self.eo_line = self.buf.len();
None
}
else if *ch == CR_CODE || *ch == ESC_CODE { self.eo_line = self.buf.len();
self.state = GrabberState::LineStart(1);
None
}
else { self.buf.push(*ch);
Some(&self.buf[self.eo_line..])
}
}
else {
self.buf.push(*ch);
self.state = GrabberState::LineData(width + 1);
None
}
}
}
}
}
impl DotMatrixGfx for EpsonPrinterGfx {
fn is_spooling(&self) -> bool {
self.state != GrabberState::NoMatch
}
fn lines_buffered(&self) -> usize {
self.eo_line / DATA_LINE_WIDTH
}
fn clear(&mut self) {
self.buf.drain(..self.eo_line);
self.eo_line = 0;
}
fn write_svg_dot_gfx_lines(&self, description: &str, target: &mut dyn io::Write) -> io::Result<bool> {
let lines = self.lines_buffered();
if lines == 0 {
return Ok(false)
}
write!(target, r##"<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="190mm" height="{height_mm:.2}mm" version="1.1"
viewBox="0 0 {pixel_width} {pixel_height}" preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg">
<desc>{description}</desc>
<g stroke-width="0.125" stroke="#333" fill="#000">
"##,
description=description,
height_mm=self.line_mm * lines as f32,
pixel_width=DATA_LINE_WIDTH,
pixel_height = lines * 8)?;
for (row, line) in self.buf[..self.eo_line].chunks(DATA_LINE_WIDTH).enumerate() {
for (x, mut dots) in line.iter().copied().enumerate() {
let y = row * 8;
for i in 0..8 {
dots = dots.rotate_left(1);
if dots & 1 == 1 {
write!(target, r##"<circle cx="{}" cy="{}" r="0.5"/>"##,
x, y + i)?;
}
}
}
}
target.write_all(b"</g></svg>")?;
Ok(true)
}
fn write_gfx_data(&mut self, _target: &mut Vec<u8>) -> Option<(u32, u32)> {
None
}
}