spectrusty_utils/printer/
epson_gfx.rs1use std::io;
9
10#[cfg(feature = "snapshot")]
11use serde::{Serialize, Deserialize};
12#[allow(unused_imports)]
13use log::{error, warn, info, debug, trace};
14
15use super::*;
16
17#[derive(Clone, Default, Debug)]
25#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "snapshot", serde(default))]
27pub struct EpsonPrinterGfx {
28 #[cfg_attr(feature = "snapshot", serde(skip))]
29 state: GrabberState,
30 #[cfg_attr(feature = "snapshot", serde(skip))]
31 line_mm: f32,
32 #[cfg_attr(feature = "snapshot", serde(skip))]
33 eo_line: usize,
34 #[cfg_attr(feature = "snapshot", serde(skip))]
35 buf: Vec<u8>
36}
37
38const ESC_CODE: u8 = 0x1B;
40const CR_CODE: u8 = 0x0D;
41const LF_CODE: u8 = 0x0A;
42const DATA_LINE_WIDTH: usize = 3 * 256;
43const 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];
45const GRAPHICS_SPACING: u8 = b'3';const GRAPHICS_ENDS: u8 = b'2';
47const GRAPHICS_RESET: u8 = b'@';
48
49const INCH_MM: f32 = 25.4;
50const INCH7_BY_72_LINE_MM: f32 = 2.47;
51
52#[derive(Clone, Copy, Debug, PartialEq)]
53enum GrabberState {
54 NoMatch,
55 LineStart(usize),
56 SelectSpacing,
57 LineData(usize),
58}
59
60impl Default for GrabberState {
61 fn default() -> Self {
62 GrabberState::NoMatch
63 }
64}
65
66impl EpsonPrinterGfx {
67 pub fn intercept<'a, 'b: 'a>(&'a mut self, ch: &'b u8) -> Option<&'a [u8]> {
76 match self.state {
77 GrabberState::NoMatch if *ch == ESC_CODE => {
78 self.buf.truncate(self.eo_line);
79 self.buf.push(*ch);
80 self.state = GrabberState::LineStart(1);
81 self.line_mm = INCH7_BY_72_LINE_MM;
82 None
83 }
84 GrabberState::NoMatch => Some(core::slice::from_ref(ch)),
85 GrabberState::SelectSpacing => {
86 self.line_mm = *ch as f32 / 216.0 * INCH_MM;
87 self.buf.truncate(self.eo_line);
88 self.state = GrabberState::LineStart(0);
89 None
90 }
91 GrabberState::LineStart(mut index) => {
92 self.buf.push(*ch);
93 if ch == &GRAPHICS_LINE_START[index] || ch == &GRAPHICS_EXP_START[index] {
94 index +=1;
95 if index == GRAPHICS_LINE_START.len() {
96 debug_assert_eq!(GRAPHICS_LINE_START.len(), GRAPHICS_EXP_START.len());
97 self.buf.truncate(self.eo_line);
98 self.state = GrabberState::LineData(0);
99 }
100 else {
101 self.state = GrabberState::LineStart(index);
102 }
103 None
104 }
105 else if index == 1 && (*ch == GRAPHICS_ENDS ||
106 *ch == GRAPHICS_RESET ||
107 *ch == GRAPHICS_SPACING) {
108 self.buf.truncate(self.eo_line);
109 self.state = if *ch == GRAPHICS_SPACING {
110 GrabberState::SelectSpacing
111 }
112 else {
113 GrabberState::NoMatch
114 };
115 None
116 }
117 else {
118 self.state = GrabberState::NoMatch;
119 Some(&self.buf[self.eo_line..])
120 }
121 }
122 GrabberState::LineData(width) => {
123 if width == DATA_LINE_WIDTH {
124 self.state = GrabberState::NoMatch;
125 if *ch == LF_CODE { self.eo_line = self.buf.len();
127 None
128 }
129 else if *ch == CR_CODE || *ch == ESC_CODE { self.eo_line = self.buf.len();
131 self.state = GrabberState::LineStart(1);
132 None
133 }
134 else { self.buf.push(*ch);
138 Some(&self.buf[self.eo_line..])
139 }
140 }
141 else {
142 self.buf.push(*ch);
143 self.state = GrabberState::LineData(width + 1);
144 None
145 }
146 }
147 }
148 }
149}
150
151impl DotMatrixGfx for EpsonPrinterGfx {
152 fn is_spooling(&self) -> bool {
153 self.state != GrabberState::NoMatch
154 }
155
156 fn lines_buffered(&self) -> usize {
157 self.eo_line / DATA_LINE_WIDTH
158 }
159
160 fn clear(&mut self) {
161 self.buf.drain(..self.eo_line);
162 self.eo_line = 0;
163 }
164
165 fn write_svg_dot_gfx_lines(&self, description: &str, target: &mut dyn io::Write) -> io::Result<bool> {
166 let lines = self.lines_buffered();
167 if lines == 0 {
168 return Ok(false)
169 }
170 write!(target, r##"<?xml version="1.0" standalone="no"?>
171<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
172 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
173<svg width="190mm" height="{height_mm:.2}mm" version="1.1"
174 viewBox="0 0 {pixel_width} {pixel_height}" preserveAspectRatio="none"
175 xmlns="http://www.w3.org/2000/svg">
176 <desc>{description}</desc>
177 <g stroke-width="0.125" stroke="#333" fill="#000">
178"##,
179 description=description,
180 height_mm=self.line_mm * lines as f32,
181 pixel_width=DATA_LINE_WIDTH,
182 pixel_height = lines * 8)?;
183 for (row, line) in self.buf[..self.eo_line].chunks(DATA_LINE_WIDTH).enumerate() {
184 for (x, mut dots) in line.iter().copied().enumerate() {
185 let y = row * 8;
186 for i in 0..8 {
187 dots = dots.rotate_left(1);
188 if dots & 1 == 1 {
189 write!(target, r##"<circle cx="{}" cy="{}" r="0.5"/>"##,
190 x, y + i)?;
191 }
192 }
193 }
194 }
195 target.write_all(b"</g></svg>")?;
196 Ok(true)
197 }
198
199 fn write_gfx_data(&mut self, _target: &mut Vec<u8>) -> Option<(u32, u32)> {
200 None
202 }
203}