1pub use self::printer_profile::{PrinterProfile, PrinterConnectionData, PrinterProfileBuilder};
2pub use self::printer_model::PrinterModel;
3use encoding::all::GB18030;
4use encoding::{EncoderTrap, Encoding};
5
6mod printer_profile;
7mod printer_model;
8
9use crate::{
10 Instruction,
11 PrintData,
12 EscposImage,
13 Error,
14 command::{Command, Font},
15 Formatter,
16 utils
17};
18
19extern crate codepage_437;
20extern crate log;
21
22use log::{warn};
23use rusb::{UsbContext, Context, DeviceHandle, TransferType, Direction};
24use codepage_437::{IntoCp437, CP437_CONTROL};
25
26enum PrinterConnection {
28 Usb {
29 endpoint: u8,
31 dh: DeviceHandle<Context>,
33 timeout: std::time::Duration
35 },
36 #[allow(dead_code)]
37 Network,
38 Terminal
39}
40
41pub enum BarcodePostion {
43 None,
44 Top,
45 Bottom,
46 Both
47}
48
49pub struct Printer {
65 printer_profile: PrinterProfile,
66 printer_connection: PrinterConnection,
68 font_and_width: (Font, u8),
70 formatter: Formatter,
72 space_split: bool
74}
75
76impl Printer {
77 pub fn new(printer_profile: PrinterProfile) -> Result<Option<Printer>, Error> {
81 let font_and_width = if let Some(width) = printer_profile.columns_per_font.get(&Font::FontA) {
83 (Font::FontA, *width)
84 } else {
85 return Err(Error::NoFontFound);
86 };
87 let formatter = Formatter::new(font_and_width.1);
88 match printer_profile.printer_connection_data {
90 PrinterConnectionData::Usb{vendor_id, product_id, endpoint, timeout} => {
91 let context = Context::new().map_err(Error::RusbError)?;
92
93 let devices = context.devices().map_err(Error::RusbError)?;
94 for device in devices.iter() {
95 let s = device.device_descriptor().map_err(Error::RusbError)?;
96 if s.vendor_id() == vendor_id && s.product_id() == product_id {
97 let config_descriptor = device.active_config_descriptor().map_err(Error::RusbError)?;
99 let actual_endpoint = if let Some(endpoint) = endpoint {
100 endpoint
101 } else {
102 let mut detected_endpoint: Option<u8> = None;
103 for interface in config_descriptor.interfaces() {
105 for descriptor in interface.descriptors() {
106 for endpoint in descriptor.endpoint_descriptors() {
107 if let (TransferType::Bulk, Direction::Out) = (endpoint.transfer_type(), endpoint.direction()) {
108 detected_endpoint = Some(endpoint.number());
109 }
110 }
111 }
112 }
113
114 if let Some(detected_endpoint) = detected_endpoint {
115 detected_endpoint
116 } else {
117 return Err(Error::NoBulkEndpoint);
118 }
119 };
120
121 match device.open() {
124 Ok(mut dh) => {
125 if let Ok(active) = dh.kernel_driver_active(0) {
126 if active {
127 match dh.detach_kernel_driver(0) {
129 Ok(_) => (),
130 Err(e) => return Err(Error::RusbError(e))
131 };
132 }
133 } else {
134 warn!("Could not find out if kernel driver is active, might encounter a problem soon.");
135 };
136 match dh.claim_interface(0) {
138 Ok(_) => (),
139 Err(e) => return Err(Error::RusbError(e))
140 }
141 return Ok(Some(Printer {
142 printer_connection: PrinterConnection::Usb {
143 endpoint: actual_endpoint,
144 dh,
145 timeout
146 },
147 printer_profile,
148 font_and_width,
149 formatter,
150 space_split: false
151 }));
152 },
153 Err(e) => return Err(Error::RusbError(e))
154 };
155 }
156 }
157 Ok(None)
159 },
160 PrinterConnectionData::Network{..} => panic!("Unsupported!"),
161 PrinterConnectionData::Terminal => Ok(Some(Printer{
162 printer_connection: PrinterConnection::Terminal,
163 printer_profile,
164 font_and_width,
165 formatter,
166 space_split: false
167 }))
168 }
169 }
170
171 pub fn with_context_feeling_lucky() -> Result<Option<Printer>, Error> {
175 match PrinterModel::TMT20 {
181 PrinterModel::TMT20 => (),
182 PrinterModel::ZKTeco => ()
183 }
184 for printer_model in vec![PrinterModel::TMT20, PrinterModel::ZKTeco] {
186 let printer_profile = printer_model.usb_profile();
187 let candidate = Printer::new(printer_profile)?;
188 if candidate.is_some() {
189 return Ok(candidate)
190 }
191 }
192 Ok(None)
194 }
195
196 pub fn instruction(&self, instruction: &Instruction, print_data: Option<&PrintData>) -> Result<(), Error> {
200 let content = instruction.to_vec(&self.printer_profile, print_data)?;
201 self.raw(&content)
202 }
203
204 pub fn print<T: Into<String>>(&self, content: T) -> Result<(), Error> {
208 let content = if self.space_split {
209 self.formatter.space_split(content.into())
210 } else {
211 content.into()
212 };
213 match self.printer_connection {
214 PrinterConnection::Usb{..} => {
215 let feed = content.into_cp437(&CP437_CONTROL).map_err(|e| Error::CP437Error(e.into_string()))?;
216 self.raw(&feed)
217 },
218 PrinterConnection::Network => panic!("Unimplemented!"),
219 PrinterConnection::Terminal => {
220 print!("{}", content);
221 Ok(())
222 }
223 }
224 }
225
226 pub fn print_gb18030(&self, content: String) -> Result<(), Error> {
230 let encoded = GB18030.encode(&content, EncoderTrap::Strict).unwrap();
231 self.raw(encoded)
232 }
233
234 pub fn println<T: Into<String>>(&self, content: T) -> Result<(), Error> {
238 let feed = content.into() + "\n";
239 self.print(&feed)
240 }
241
242 pub fn print_barcode(&self, content: String, position: BarcodePostion) -> Result<(), Error> {
246 match self.set_barcode_position(position) {
247 Ok(_) => Ok({
248 let input = content.as_str();
249 let result = utils::Utils::separate_numbers_and_non_numbers(input);
250 let str_line = result[0].as_bytes().to_vec();
251 let number_line = result[1].as_bytes().to_vec();
252 let mut brcode_arr: Vec<u8> = vec![0x1d, 0x6b, 0x49, 0x0a, 0x7b, 0x42];
253 brcode_arr.extend(&str_line);
254 brcode_arr.push(0x7b);
255 brcode_arr.push(0x43);
256 brcode_arr.extend(&number_line);
257 match self.raw(brcode_arr) {
258 Ok(_) => {},
259 Err(_) => {}
260 };
261 }),
262 Err(_e) => return Err(Error::BarcodeError)
263 }
264 }
265
266 pub fn set_barcode_position(&self, position: BarcodePostion) -> Result<(), Error> {
267 match self.raw(&Command::Reset.as_bytes()) {
268 Ok(_) => {
269 match position.into() {
270 BarcodePostion::None => {
271 self.raw(&[0x1d, 0x48, 0x00])
272 },
273 BarcodePostion::Top => {
274 self.raw(&[0x1d, 0x48, 0x01])
275 },
276 BarcodePostion::Bottom => {
277 self.raw(&[0x1d, 0x48, 0x02])
278 },
279 BarcodePostion::Both => {
280 self.raw(&[0x1d, 0x48, 0x03])
281 }
282 }
283 },
284 Err(_e) => return Err(Error::BarcodeError)
285 }
286 }
287
288 pub fn set_font(&mut self, font: Font) -> Result<(), Error> {
292 if let Some(width) = self.printer_profile.columns_per_font.get(&font) {
293 self.font_and_width = (font, *width);
294 Ok(())
295 } else {
296 Err(Error::UnsupportedFont)
297 }
298 }
299
300 pub fn set_space_split(&mut self, state: bool) {
304 self.space_split = state;
305 }
306
307 pub fn jump(&self, n: u8) -> Result<(), Error> {
309 let feed = vec![b'\n', n];
310 self.raw(&feed)
311 }
312
313 pub fn cut(&self) -> Result<(), Error> {
315 self.raw(&Command::Cut.as_bytes())
316 }
317
318 pub fn duo_table<A: Into<String>, B: Into<String>, C: IntoIterator<Item = (D, E)>, D: Into<String>, E: Into<String>>(&self, headers: (A, B), rows: C) -> Result<(), Error> {
322 let content = self.formatter.duo_table(headers, rows);
323 match &self.printer_connection {
324 PrinterConnection::Terminal => {
325 println!("{}", content);
326 Ok(())
327 },
328 _other => {
329 self.raw(&content)
330 }
331 }
332 }
333
334 pub fn trio_table<A: Into<String>, B: Into<String>, C: Into<String>, D: IntoIterator<Item = (E, F, G)>, E: Into<String>, F: Into<String>, G: Into<String>>(&self, headers: (A, B, C), rows: D) -> Result<(), Error> {
338 let content = self.formatter.trio_table(headers, rows);
339 match &self.printer_connection {
340 PrinterConnection::Terminal => {
341 println!("{}", content);
342 Ok(())
343 },
344 _other => {
345 self.raw(&content)
346 }
347 }
348 }
349
350 pub fn image(&self, escpos_image: EscposImage) -> Result<(), Error> {
351 self.raw(&escpos_image.feed(self.printer_profile.width))
352 }
353
354 pub fn raw<A: AsRef<[u8]>>(&self, bytes: A) -> Result<(), Error> {
365 match &self.printer_connection {
366 PrinterConnection::Usb{endpoint, dh, timeout} => {
367 dh.write_bulk(
368 *endpoint,
369 bytes.as_ref(),
370 *timeout
371 ).map_err(Error::RusbError)?;
372 Ok(())
373 },
374 _other => panic!("Unimplemented")
375 }
376 }
377}