Work in progress. Not ready for production.
Escpos-rw is a fork of escpos-rs:
- fix for endpoint address
- added a read_raw function to read data (eg. status bytes) from the printer
Escpos-rs builds a bit on top of escpospp
, which aims to bring relatively easy communication to thermal printers that understand the ESC/POS protocol. Here is an example of a simple print with escpos-rs
use escpos_rw::{Printer, PrinterProfile};
fn main() {
// We create the printer details
let mut printer_details = PrinterProfile::usb_builder(0x0001, 0x0001).build();
// We pass it to the printer
let printer = match Printer::new(printer_details) {
Ok(maybe_printer) => match maybe_printer {
Some(printer) => printer,
None => panic!("No printer was found :(")
},
Err(e) => panic!("Error: {}", e)
};
// We print simple text
match printer.println("Hello, world!") {
Ok(_) => (),
Err(e) => println!("Error: {}", e)
}
}
In order to connect to a printer, you need to know both the vendor id, and the product id of your printer. Commonly known printers have these details published online, while for the least common you will have at least the following two options
- Some printers do print their information on test prints (often holding the feed button)
- If you are on linux, the
lsusb
command shows you this information.
With this information, you can start a connection to the printer
// Here goes the vendor id, and the product it (in that order)
let mut printer_details = PrinterProfile::usb_builder(0x0001, 0x0001).build();
// We pass it to the printer
let printer = match Printer::new(printer_details) {
Ok(maybe_printer) => match maybe_printer {
Some(printer) => printer,
None => panic!("No printer was found :(")
},
Err(e) => panic!("Error: {}", e)
};
The printer has the write_raw
method, which allows you to send raw bytes to the printer. Pretty straightforward if you need to operate on the low-level.
use escpos_rw::{
Printer, PrinterModel,
command::Command
};
fn main() {
let printer = match Printer::new(PrinterModel::ZKTeco.usb_profile()) {
Ok(maybe_printer) => match maybe_printer {
Some(printer) => printer,
None => panic!("No printer was found :(")
},
Err(e) => panic!("Error: {}", e)
};
match printer.write_raw(b"Hello, world!\n") {
Ok(_) => (),
Err(e) => println!("Error: {}", e)
}
match printer.write_raw(Command::Cut.as_bytes()) {
Ok(_) => (),
Err(e) => println!("Error: {}", e)
}
}
You can take a look at the Command
enum to see which commands are implemented (the list will grow).
You can also send images to the printer (assuming it is supported) through the EscposImage
structure.
use escpos_rw::{
EscposImage, Printer, PrinterProfile, Justification
};
fn main() {
let printer_profile = PrinterProfile::usb_builder(0x0001, 0x0001).build();
let printer = match Printer::new(printer_profile) {
Ok(maybe_printer) => match maybe_printer {
Some(printer) => printer,
None => panic!("No printer was found :(")
},
Err(e) => panic!("Error: {}", e)
};
let img = image::open("logo.jpg").unwrap();
let escpos_image = EscposImage::new(img, 128, Justification::Center).unwrap();
match printer.image(escpos_image) {
Ok(_) => (), // Image should be printed
Err(e) => println!("Error: {}", e)
};
}
The EscposImage
's constructor takes as first argument a DynamicImage
, as second a width scale (from 0 to 255), and as third, a justification.
To be added soon.
The Instruction structure has as primary goal the construction of a template, which can be used to print multiple documents with dynamic data.
use escpos_rw::{Printer, PrintData, PrinterProfile, Instruction, Justification, command::Font};
fn main() {
// Printer profile...
let printer_profile = PrinterProfile::usb_builder(0x0001, 0x0001)
.with_font_width(Font::FontA, 32)
.build();
// We pass it to the printer
let printer = match Printer::new(printer_profile) {
Ok(maybe_printer) => match maybe_printer {
Some(printer) => printer,
None => panic!("No printer was found :(")
},
Err(e) => panic!("Error: {}", e)
};
// We create a simple instruction with a single substitution
let instruction = Instruction::text(
"Hello, %name%!",
Font::FontA,
Justification::Center,
// Words that will be replaced in this specific instruction
Some(vec!["%name%".into()].into_iter().collect())
);
// We create custom information for the instruction
let print_data_1 = PrintData::builder()
.replacement("%name%", "Carlos")
.build();
// And a second set...
let print_data_2 = PrintData::builder()
.replacement("%name%", "John")
.build();
// We send the instruction to the printer, along with the custom data
// for this particular print
match printer.instruction(&instruction, Some(&print_data_1)) {
Ok(_) => (), // "Hello, Carlos!" should've been printed.
Err(e) => println!("Error: {}", e)
}
// Now we print the second data
match printer.instruction(&instruction, Some(&print_data_2)) {
Ok(_) => (), // "Hello, John!" should've been printed.
Err(e) => println!("Error: {}", e)
}
}
Instructions can be added up to form a complex instruction. Moreover, you can use serde to serialize and deserialize these instructions so you can save your templates.
You can run the examples contained in the examples folder, invoking them through cargo.
cargo run --example basic
Lib usb is needed for the compilation. Go to https://github.com/libusb/libusb/releases and download the compiled binaries, and put them in your include and bin folders for mingw. You will also need a pkg config file.
-
Execute the command
pkg-config.exe --variable pc_path pkg-config
to know wherepkg-config
looks uppc
files -
Add, in any of those routes, the file
libusb-1.0.pc
with the following content
prefix=c:/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include
Name: libusb-1.0
Description: C API for USB device access from Linux, Mac OS X, Windows, OpenBSD/NetBSD and Solaris userspace
Version: 1.0.23
Libs: -L${libdir} -lusb-1.0
Libs.private: -ludev -pthread
Cflags: -I${includedir}/libusb-1.0
Note: The version must match your libusb version, and the prefix must also match your main include and lib folders for MinGW.
The following steps are based on this stackoverflow post.
We assume your mingw installation has its binaries in C:\MinGW\bin
- Go to gnome, and download the
pkg-config_0.26-1_win32.zip
package - Extract the file
bin/pkg-config.exe
toC:\MinGW\bin
- Download the file gettext-runtime_0.18.1.1-2_win32.zip
- Extract the file bin/intl.dll to
C:\MinGW\bin
go to http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28 - Download the file
glib_2.28.8-1_win32.zip
from here (gnome's website, again). - Extract the file
bin/libglib-2.0-0.dll
toC:\MinGW\bin
I've only been able to use this library when WinUSB driver is in use for the chosen printer. You can use a tool like Zadig to change your printer's driver. Just bear in mind that this driver change might make the printer invisible to other tools ;).