微信小程序蓝牙连接TSPL打印机打印图片思路

友情帮助朋友做一个小程序打印自定义内容模块,本以为只是简单的文本、条形码、二维码等内容,实际拿到需求后发现是打印图片,瞬间傻眼。研究一周,最终才在小程序上直接实现,特此Mark。

打印机:GP-2120T
流程:小程序canvas生成画布,可自定义控件,以所见即所得方式通过蓝牙打印
难点:TSPL的BITMAP指令需要十六进制图片数据、位图宽高,所以需要根据图片尺寸、像素,重构以获取新的图片数据

BITMAP x,y,width,height,mode,bitmap data
width、height、data 三者对应不上打印时会出现多种异常

数据处理

// 默认打印参数
var defaultConfig = {
  printSpeed: 4,
  printConcentration: 1,
  direction: 0,
  paperOffset: 2
};

//填充打印数据
function fillPrintData(data, config) {
//以Array存储
  var writeArray = new Array();

//获取XML中打印页数,因为入口限制了数据为XML,需要手动提取数据
  var pageNums = data.pageNums;
  var paper = paperXmlConvertToMap(data.xml);

  var command = "SIZE " + paper.w + " mm," + paper.h + " mm\n";
  writeArray.push(charToArrayBuffer(command));
  
  command = "REFERENCE 0,0\n";
  writeArray.push(charToArrayBuffer(command));

  setConfigInfo(config ? config : defaultConfig, writeArray);

  command = "CLS\n";
  writeArray.push(charToArrayBuffer(command));

//图片处理,采取逐行打印方式,速度感人……
//可以根据图片大小自行处理height,要考虑小程序蓝牙传输的字节限制
  var sendImageInfo = overwriteImageData(data);
  let arr = sendImageInfo.array, width = sendImageInfo.width, height = sendImageInfo.height, tempArr = [];
  for (var i = 0; i < arr.length / data.width; i++) {
    command = 'BITMAP 104,' + i + ',' + width + ',1,0,';
    var bitmapHeader = charToArray(command);
    var subArr = arr.slice(i * width, i * width + width);
    tempArr = tempArr.concat(bitmapHeader).concat(subArr);
    if (tempArr.length > 150){
      writeArray.push(new Uint8Array(tempArr));
      tempArr = [];
    }else{
      if (i == arr.length / w - 1){
        writeArray.push(new Uint8Array(tempArr));
      }
    }
  }

  command = 'PRINT 1,' + parseInt(pageNums) + '\n';
  writeArray.push(charToArrayBuffer(command));

  return writeArray;
}

//常用参数指令处理
function setConfigInfo(config, writeArray) {
  var command = "SPEED " + config.printSpeed + "\n";
  writeArray.push(charToArrayBuffer(command));

  command = "DENSITY " + config.printConcentration + "\n";
  writeArray.push(charToArrayBuffer(command));

  //反转打印
  // command = "DIRECTION " + config.反转打印 ? "1" : "0";
  command = "DIRECTION " + config.direction + "\n";
  writeArray.push(charToArrayBuffer(command));

  //纸张类型无设置指令

  //送纸偏移
  command = "GAP " + config.paperOffset + " mm\n";
  writeArray.push(charToArrayBuffer(command));

  //剥离模式
  // command = "SET CUTTER " + config.剥离模式 ? "1" : "OFF";
  // writeArray.push(charToArrayBuffer(command));

  //蜂鸣--打印前
  // if (config.蜂鸣)
  //   command = "BEEP";
  // writeArray.push(charToArrayBuffer(command));
}

XML提取,使用了github上xmldom/dom-parser.js处理

function paperXmlConvertToMap(xml) {
  var xmlParse = new xmlParser.DOMParser();
  var doc = xmlParse.parseFromString(xml);
  var paper = doc.getElementsByTagName("paper");
  var map = {};
  for (var i = 0; i < paper[0].attributes.length; i++) {
    map[paper[0].attributes[i].name] = getNumberValue(paper[0].attributes[i].value);
  }
  return map;
}

数据类型转换

function getNumberValue(value) {
  if (!isNaN(value)) return parseInt(value); else return value;
}

function charToArrayBuffer(str) {
  var out = new ArrayBuffer(str.length)
  var uint8 = new Uint8Array(out)
  var strs = str.split("")
  for (var i = 0; i < strs.length; i++) {
    uint8[i] = strs[i].charCodeAt()
  }
  return uint8
}

function charToArray(str) {
  var arr = [];
  var strs = str.split("")
  for (var i = 0; i < strs.length; i++) {
    arr[i] = strs[i].charCodeAt()
  }
  return arr
}

图片处理:一开始走了很多弯路,研究了各种格式的图片构成,以Android-SDK取样失败,可能指令异常,打印时连打印机名字都被改了……
参考Windows-SDK图片处理功能平移至小程序,data:wx.canvasGetImageData。

function overwriteImageData(data){
  let sendWidth = data.width, sendHeight = data.height;

  if (data.height % 8 != 0)
    sendHeight = sendHeight + 8 - sendHeight % 8;
  if (data.width % 8 != 0)
    sendWidth = sendWidth + 8 - sendWidth % 8;

  let sendImageData = new ArrayBuffer(sendWidth * sendHeight / 8);
  sendImageData = memset(sendImageData);

  let clsLBit = [127, 191,  223, 239, 247, 251, 253, 254];
  let sumRed = 0, sumGreen = 0, sumBlue = 0;
  let total = sendWidth * sendHeight;
  let pix = data.imageData;
  for (var i = 0; i < pix.length; i += 4) {
    sumRed += pix[i]
    sumGreen += pix[i + 1]
    sumBlue += pix[i + 2]
  }

  let avgRed = parseInt(sumRed / total);
  let avgGreen = parseInt(sumGreen / total);
  let avgBlue = parseInt(sumBlue / total);

  let m = 4;
  for (var j = 0; j < image.height; j++) {
    for (var i = 0; i < image.width; i++) {
      if ((pix[m * i] * 0.299 + pix[m * i + 1] * 0.587 + [m * i + 2] * 0.114) < (avgRed * 0.299 + avgGreen * 0.587 + avgBlue * 0.114))
        sendImageData[parseInt(j * sendWidth / 8 + i / 8)] &= clsLBit[i % 8];
    }
  }

  return {array: Array.from(sendImageData), width: sendWidth / 8, height: sendHeight};
}

function memset(arrayBuffer) {
  let view = new Uint8Array(arrayBuffer);
  for (let i = 0; i < view.length; i++) {
    view[i] = 255;
  }
  return view
}

发送指令

function sendData() {
  var arr = bleDataUtil.fillPrintData(printDataArr, config);
  if (arr.length > 0) {
    sendDataTimer = setInterval(function () {
      if (arr.length > 0) {
        printData(arr[0]["buffer"]);
        arr.splice(0, 1);
      } else {
        clearSendDataTimer();
      }
    }, 100);
  }
  // else closeConnection();
}

此种处理适用于PNG、JPG、BMP,其他的没有做测试。
因为项目本身限制太多,要求小程序处理canvas数据和图片,所以只能如此实现,完全不使用接口。
如果可以,最好还是选择接口处理。

后记

没有最终测试,不排除是否还有其他问题

感谢

尝试过各种方式处理,明白原理后最终才得以实现,感谢以下网站和作者,还有很多参考资料没有保留,非常感谢。

[1]: http://www.vgot.net/test/image2base64.php img转base64
[2]: http://www.ab126.com/goju/1711.html ASCII转换工具
[3]: https://blog.csdn.net/happyq/article/details/39670795 常见图片宽高解析
[4]: https://www.cnblogs.com/go2bed/p/4097758.html 常见图片文件格式简析
[5]:https://www.jianshu.com/p/8953953253b9 JavaScript PNG 图片编码和解码
[6]:https://www.cnblogs.com/suckmyballs/archive/2010/09/22/1833075.html 用Canvas实现图像的灰度效果

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
以下是使用tspl指令让tsc打印机ttp-244pro打印图片的实例: 1. 首先,我们需要将图片转换为二进制格式,并将其保存到文件中。可以使用以下代码完成这一步骤: ``` import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class ImageToBinary { public static void main(String[] args) { try { BufferedImage image = ImageIO.read(new File("path/to/image.png")); int width = image.getWidth(); int height = image.getHeight(); StringBuilder binary = new StringBuilder(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int color = image.getRGB(x, y); int alpha = (color >> 24) & 0xff; int red = (color >> 16) & 0xff; int green = (color >> 8) & 0xff; int blue = color & 0xff; if (red == 255 && green == 255 && blue == 255) { binary.append("0"); // white pixel } else { binary.append("1"); // black pixel } } binary.append("\n"); // end of row } Files.write(Paths.get("path/to/binary.txt"), binary.toString().getBytes()); } catch (IOException e) { e.printStackTrace(); } } } ``` 2. 接下来,我们需要使用tspl指令将二进制数据发送到打印机。以下代码演示如何使用Java将tspl指令发送到打印机: ``` import java.io.IOException; import java.io.OutputStream; import java.net.Socket; public class TscPrinter { private static final String HOST = "192.168.1.100"; // IP address of the printer private static final int PORT = 9100; // default port for TSC printers public static void printImage(String binaryPath) { try (Socket socket = new Socket(HOST, PORT)) { OutputStream out = socket.getOutputStream(); out.write("SIZE 100 mm,100 mm\n".getBytes()); out.write("GAP 2 mm,0 mm\n".getBytes()); out.write("CLS\n".getBytes()); out.write("BITMAP 0,0,200,200,1,".getBytes()); out.write(Files.readAllBytes(Paths.get(binaryPath))); out.write("\nPRINT 1\n".getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } } ``` 3. 最后,我们可以调用printImage方法将打印机打印二进制图像的代码: ``` TscPrinter.printImage("path/to/binary.txt"); ``` 这将使用tspl指令将图像发送到打印机,并在纸张上打印出来。请注意,您需要将上述代码中的IP地址和文件路径更改为匹配您的打印机和文件路径。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值