自制热敏打印机连接器始末(8)一些基本概念

其实之前参考过这篇文章

https://www.printnode.com/en/docs/what-is-raw-printing

但是他们提供的api没有命令式的,只能打文件,现在回头来看,打印机确实可以这么连接,直接安装为raw的方式就可以用程序打印了,并不需要找驱动。

当然,为了简化cups的操作,对普通用户来说,提供一个tsc的驱动在传统的打印机界面上安装会更简单一些。

在cups中添加的raw打印机只能在cups中可见,打印机与扫描仪管理中是看不见的,当然,node-printer中可以调用到这个打印机。

梳理一下基本概念:

标签打印机:重点当然在标签,每次打印一小截,标签一般是固定大小,指令如TSPL

票据打印机:重点当然是票据,按需打印长度,然后撕掉。指令如EOP。

面单打印机:快递单那种,应该是跟标签打印机差不多,只不过更大。

其实对于打印机厂商来说,很多都已经兼容多种指令了,当然不包括得力这种OEM还不提供开发文档的。

要做一个跨平台的连接器,需要满足几个条件:

(1)能够构建跨平台的发布,所以范围就锁定在electron、xamarin这样的方案上,js和c#虽然都熟悉,但最近用的多的还是js,所以优先选electron,但是其实对node不够熟悉,所以造成了上一篇关于buffer类型的困惑。

(2)搞清楚打印机在不同的操作系统如何连接,目前还没尝试usb直驱这种,在mac上还是通过cups来连接,windows上都有相应的驱动,可以不单独讨论。

(3)指令集兼容性,目前只尝试了TSPL这一种,后续还需要其他的ZPL之类的测试一下,还需要在各种指令之间转换,方便原来用某个指令集编写的程序直接对接。

(4)接入方式的兼容性,至少应该支持socket和http。

(5)统一的api,尤其是友好的RestFul模式的api,便于开发对接。

自制热敏打印机连接器始末(7)指令如何编码,尤其是汉字的编码

接下来就想为什么汉字是乱码呢?是command.setText(50, 100, “TSS24.BF2”, 1, 1, “一二三”);里面的汉字需要转码吗?

对照jpPrinter.addCommand里面把原来的转码去掉了,如果在command.setText(50, 100, “TSS24.BF2”, 1, 1, “一二三”);中把汉字转成unico呢,试了试也不行。

把jpPrinter.addCommand恢复回去,

jpPrinter.addCommand = function(content) {
      // 将指令转成数组装起;
      var code = new encode.TextEncoder("gb18030", {
        NONSTANDARD_allowLegacyEncoding: true,
      }).encode(content);
      for (var i = 0; i < code.length; ++i) {
        command.push(code[i]);
      }
      // command = command + content;
    };

还是会报错,throw new TypeError(‘first argument must be a string or Buffer’);

然后仔细一看,原来data复制不止是string类型,还可以是buffer类型。

那就接着从BluetoothPrinter往这里扒,因为不是蓝牙,不受每次传输的长度限制,所以把分段传输的逻辑去掉。

var data = Array.from(uint8Buf);
  var buffer = new ArrayBuffer(data.length);
  
  var dataView = new DataView(buffer);
  for (var i = 0; i < data.length; ++i) {
    dataView.setUint8(i, data[i]);
  }
  console.log(
    "data type is: " + typeof data + ", is buffer: " + Buffer.isBuffer(data)
  );

但是,还是报同样地错误。

然后发现Buffer.isBuffer(data)是false,为什么类型不对呢?

于是搜到了这里http://nodejs.cn/api/buffer.html#buffer_static_method_buffer_from_arraybuffer_byteoffset_length

是nodejs里面对Buffer有不同于js的类型定义。

于是改成:

var uint8Buf = command.getData();
  var buffer=Buffer.from(uint8Buf);
  console.log(
    "buffer type is: " + typeof buffer + ", is buffer: " + Buffer.isBuffer(buffer)
  );
  var jobid = "";
  printer.printDirect({
    data: buffer, // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

打印成功。

自制热敏打印机连接器始末(6)验证指令可以驱动打印机输出但有乱码

后来我找到了https://github.com/qihang666/BluetoothPrinter这个项目,人家用蓝牙直接连打印机,比如我手头的DL-888AW就有蓝牙,我用usb为什么就不行呢,所以我就着手将这个项目中的代码拿过来。

尽管他里面的打印机指令集叫jprinter,但指令集实际上是TSPL,所以理论上应该可以复用。

拿过来的部分是https://github.com/qihang666/BluetoothPrinter/tree/master/components/gprint,

考虑到https://github.com/tojocky/node-printer/blob/master/examples/print_raw.js这里面提示的是data部分需要string类型,所以,我把tsc.js里面的command改成了字符串类型,把

jpPrinter.addCommand = function(content) {
      // 将指令转成数组装起;
      var code = new encode.TextEncoder("gb18030", {
        NONSTANDARD_allowLegacyEncoding: true,
      }).encode(content);
      for (var i = 0; i < code.length; ++i) {
        command.push(code[i]);
      }
    };

改成了

jpPrinter.addCommand = function(content) {
       command = command + content;
    };

然后打印的时候:

var command = tsc.jpPrinter.createNew();
  console.log(command);
  command.setSize(60, 40);
  command.setGap(2);
  command.setCls();
  command.setText(50, 10, "2", 1, 1, "Hello");
  command.setText(50, 100, "TSS24.BF2", 1, 1, "一二三");
  // command.setQR(50, 50, "L", 5, "A", "977767937@qq.com");
  command.setPagePrint();

  var data=command.getData();
  var jobid = "";
  printer.printDirect({
    data: data, // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

惊喜出现了,打印机工作了,但是汉字是乱码。

自制热敏打印机连接器始末(5)放弃直接驱动usb,尝试找替代的驱动程序

中间走了一段弯路,因为一直无法直接用程序驱动打印机打出东西来,无法验证以前的思路是否可行,所以,想着是否应该给打印机找一找mac版的驱动,然后尝试直接在电脑上打印一个pdf文件,来和程序做下对比。于是,按照打印机的说明书里说的TSPL这个语言,找到了TSC这个厂商,他们家的网站上资源真是全啊https://www.chinatsc.cn/

按照类似的规格找了几个型号的打印机,下载驱动,然后发现其实人家的驱动都在一起。

这个ppd文件就是所谓驱动,用vscode打开,发现就是文本文件,对比一下,发现基本上都是一样的,对于热敏打印机来说,只有几个参数有区别,主要是打印宽度、打印速度、打印方式(热敏/热转印)。

找到了驱动,就可以在系统的打印机管理中添加打印机,然后自选某个驱动文件,比如我试了TA200,Deli_DL_888B_NEW是可以打印文件的。

但是我用node-printer还是无法打印。

  printer.printDirect({
    data:"测试", // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

打印机有时间连动都不动。