12 个版本

0.4.3 2024年2月5日
0.4.2 2023年7月19日
0.3.1 2022年3月10日
0.3.0 2021年8月23日
0.1.0 2021年8月7日

#157 in 硬件支持


rawprinter 中使用

MIT 许可证

110KB
1.5K SLoC

escpos-rs: 用于热敏打印机的 Rust crate

正在进行中。尚未准备好用于生产。

Escpos-rs 在 escpospp 的基础上构建,旨在为理解 ESC/POS 协议的热敏打印机提供相对简单的通信。下面是使用 escpos-rs 的简单打印示例

use escpos_rs::{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)
    }
}

连接打印机

为了连接打印机,您需要知道打印机的供应商 ID 和产品 ID。常见的打印机在网上公布了这些详细信息,而对于不常见的打印机,您至少有以下两种选择

  • 某些打印机会在测试打印中打印其信息(通常按住进纸按钮)
  • 如果您使用 Linux,则 lsusb 命令会显示这些信息。

有了这些信息,您就可以开始连接打印机了

// 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)
};

发送原始信息

打印机有 raw 方法,允许您向打印机发送原始字节。如果您需要操作底层,这非常直接。

use escpos_rs::{
    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.raw(b"Hello, world!\n") {
        Ok(_) => (),
        Err(e) => println!("Error: {}", e)
    }

    match printer.raw(Command::Cut.as_bytes()) {
        Ok(_) => (),
        Err(e) => println!("Error: {}", e)
    }
}

您可以通过查看 Command 枚举来了解实现了哪些命令(列表将增长)。

打印图像

您还可以通过 EscposImage 结构向打印机发送图像(假设它支持)。

use escpos_rs::{
    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)
    };
}

EscposImage 的构造函数接受第一个参数为 DynamicImage,第二个参数为宽度缩放(从 0 到 255),第三个参数为对齐方式。

网络功能

即将添加。

指令结构

指令结构的主要目标是构建一个 模板,该模板可以用于打印包含动态数据的多个文档。

use escpos_rs::{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)
    }
}

可以将指令添加起来形成一个复杂的指令。此外,您可以使用 serde 对这些指令进行序列化和反序列化,以便您可以保存模板。

运行示例

您可以在 examples 文件夹中运行示例,通过 cargo 运行它们。

cargo run --example basic

关于在 Windows 上构建此库

编译需要libusb。请访问https://github.com/libusb/libusb/releases下载编译好的二进制文件,并将它们放入mingw的include和bin文件夹中。您还需要一个pkg config文件。

  • 执行以下命令pkg-config.exe --variable pc_path pkg-config以了解pkg-config在哪里查找pkg-config文件。

  • 在这些路径中的任何一个中添加文件libusb-1.0.pc,内容如下:

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

注意:版本必须与您的libusb版本匹配,并且前缀也必须与MinGW的主要include和lib文件夹匹配。

以下步骤基于这篇Stackoverflow帖子

假设您的mingw安装的二进制文件在C:\MinGW\bin

在Windows上使用库

我只能在WinUSB驱动程序用于所选打印机时使用此库。您可以使用像Zadig这样的工具更改打印机的驱动程序。只需记住,此驱动程序更改可能会使打印机对其他工具不可见;)。

依赖项

~8MB
~122K SLoC