#打印 #esc-pos #热敏 #控制 #命令 #指令 #更新

escpos-rust

使用 rust 更新 escpos-rs 控制 esc/pos 打印机

2 个版本

0.0.2 2023 年 5 月 4 日
0.0.1 2023 年 4 月 25 日

#960硬件支持

26 每月下载量

MIT 许可证

105KB
1.5K SLoC

escpos-rust: 基于 escpos-rs 优化扩展

新增内容

  1. 打印一维条形码
  2. 打印 GB18030 文本

未来新增功能

  • 四行表格打印

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 查找 pc 文件的位置

  • 在这些路径中的任何一个添加文件 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 这样的工具更改打印机的驱动程序。但请记住,这种驱动程序更改可能会使打印机对其他工具不可见;)。

依赖项

~10MB
~152K SLoC