友情帮助朋友做一个小程序打印自定义内容模块,本以为只是简单的文本、条形码、二维码等内容,实际拿到需求后发现是打印图片,瞬间傻眼。研究一周,最终才在小程序上直接实现,特此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"; writeArray.push(charToArrayBuffer(command)); command = "REFERENCE 0,0"; writeArray.push(charToArrayBuffer(command)); setConfigInfo(config ? config : defaultConfig, writeArray); command = "CLS"; 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) + ''; writeArray.push(charToArrayBuffer(command)); return writeArray;}//常用参数指令处理function setConfigInfo(config, writeArray) { var command = "SPEED " + config.printSpeed + ""; writeArray.push(charToArrayBuffer(command)); command = "DENSITY " + config.printConcentration + ""; writeArray.push(charToArrayBuffer(command)); //反转打印 // command = "DIRECTION " + config.反转打印 ? "1" : "0"; command = "DIRECTION " + config.direction + ""; writeArray.push(charToArrayBuffer(command)); //纸张类型无设置指令 //送纸偏移 command = "GAP " + config.paperOffset + " mm"; 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(arrayB













