emis-app/components/tpx-bluetooth-print/print/printJob.js

308 lines
9.6 KiB
JavaScript
Raw Permalink Normal View History

2024-05-11 16:46:09 +00:00
//微信小程序向蓝牙打印机发送数据进行打印的坑:
//小程序api向蓝牙打印机发送数据打印,发送的任何内容都应该要转成二进制数据,而且蓝牙打印的文本编码是GBK的,发送中文需转成GBK编码再转成二进制数据发送
//发送打印机指令也要转成二进制数据发送
//蓝牙打印机一次接收的二级制数据有限制,不同的系统不同的蓝牙设备限制可能不同,微信建议一次20个字节,需做递归分包发送
//发送完要打印的内容后,一定要发送一个打印的指令才能顺利打印 (有些指令就不需要)
/**
*
*
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
value [ArrayBuffer],
lasterSuccess,
}
*/
2024-06-05 23:51:19 +00:00
2024-05-11 16:46:09 +00:00
export function sendDataToDevice(options) {
let byteLength = options.value.byteLength;
2024-05-11 18:21:02 +00:00
// 这里默认一次20个字发送
2024-05-11 16:46:09 +00:00
const speed = options.onceByleLength || 20;
2024-05-11 18:21:02 +00:00
console.log('byteLength====', byteLength)
2024-05-11 16:46:09 +00:00
if (byteLength > 0) {
2024-05-11 18:21:02 +00:00
console.log('speed====', speed)
console.log('value====', options.value)
2024-05-11 16:46:09 +00:00
uni.writeBLECharacteristicValue({
...options,
// writeType:"writeNoResponse",
value: options.value.slice(0, byteLength > speed ? speed : byteLength),
success: function (res) {
2024-05-11 18:21:02 +00:00
// 【writeBLECharacteristicValue】安卓存在频繁调用失败的情况,下次调用前必须加上延迟
setTimeout(()=> {
if (byteLength > speed) {
sendDataToDevice({
...options,
value: options.value.slice(speed, byteLength),
});
} else {
options.lasterSuccess && options.lasterSuccess();
}
},10)
2024-05-11 16:46:09 +00:00
},
fail: function (res) {
2024-05-11 18:21:02 +00:00
console.log('writeBLECharacteristicValue:fail====', res)
2024-05-11 16:46:09 +00:00
options.onError && options.onError(res);
},
});
}
}
export 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;
}
export function charToArray(str) {
var arr = [];
var strs = str.split('');
for (var i = 0; i < strs.length; i++) {
arr[i] = strs[i].charCodeAt();
}
return arr;
}
// 使用的 ESC/POS指令, 十进制方式
// 更多指令请查看 ./PrintCommandDocs/ESC-POS指令文档(凯盛诺打印机代表).pdf
export const printCommand = {
left: [27, 97, 0], //居左
center: [27, 97, 1], //居中
right: [27, 97, 2], //居右
clear: [27, 64], //初始化
enter: [10],
};
//打印二维码
/**
*
*
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
value,//ArrayBuffer:二维码的数据
}
*/
export function printQR(options) {
//打印二维码的十进制指令data:
let data = [...printCommand.clear, 29, 107, 97, 7, 4, options.value.byteLength, 0];
sendDataToDevice({
...options,
value: new Uint8Array(data).buffer,
lasterSuccess: () => {
//指令发送成功后,发送二维码的数据
sendDataToDevice(options);
},
});
}
function grayPixle(pix) {
return pix[0] * 0.299 + pix[1] * 0.587 + pix[2] * 0.114;
}
/**
* overwriteImageData 图片数据转 位图数据
* @param {object} data
* {
width,//图片宽度
height,//图片高度
imageData,//Uint8ClampedArray
threshold,//阈值, 越大,打印点数越多,图形越黑
}
*/
export function overwriteImageData(data) {
let sendWidth = data.width,
sendHeight = data.height;
const threshold = data.threshold || 180;
let sendImageData = new ArrayBuffer((sendWidth * sendHeight) / 8);
sendImageData = new Uint8Array(sendImageData);
let pix = data.imageData;
const part = [];
let index = 0;
for (let i = 0; i < pix.length; i += 32) {
//横向每8个像素点组成一个字节(8位二进制数)。
for (let k = 0; k < 8; k++) {
const grayPixle1 = grayPixle(pix.slice(i + k * 4, i + k * 4 + (4 - 1)));
//阈值调整
if (grayPixle1 > threshold) {
//灰度值大于128位 白色 为第k位0不打印
part[k] = 0;
} else {
part[k] = 1;
}
}
let temp = 0;
for (let a = 0; a < part.length; a++) {
temp += part[a] * Math.pow(2, part.length - 1 - a);
}
//开始不明白以下算法什么意思,了解了字节才知道,一个字节是8位的二进制数,part这个数组存的0和1就是二进制的0和1,传输到打印的位图数据的一个字节是0-255之间的十进制数,以下是用相权相加法转十进制数,理解了这个就用上面的for循环替代了
// const temp =
// part[0] * 128 +
// part[1] * 64 +
// part[2] * 32 +
// part[3] * 16 +
// part[4] * 8 +
// part[5] * 4 +
// part[6] * 2 +
// part[7] * 1;
sendImageData[index++] = temp;
}
return {
array: Array.from(sendImageData),
width: sendWidth / 8,
height: sendHeight,
};
}
/**
* 分段发送二进制数据,按allUint8Array的length产生发送的进度
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
lasterSuccess,
onProgress,
}
* * @param {array} allUint8Array 所有的Uint8Array
*/
export function sendDataToPrint(options, allUint8Array) {
const allLenth = allUint8Array.length;
2024-05-11 18:21:02 +00:00
console.log("sendDataToPrint:allLenth=======", allLenth)
console.log("sendDataToPrint:allUint8Array-len=======", allUint8Array.length)
2024-05-11 16:46:09 +00:00
const writeArrayCopyer = allUint8Array.slice(0);
const print = (options, writeArray) => {
2024-05-11 18:21:02 +00:00
console.log("===writeArray.length===", writeArray.length)
2024-05-11 16:46:09 +00:00
if (writeArray.length) {
2024-05-11 18:21:02 +00:00
let curBuffer = writeArray.shift().buffer
2024-05-11 16:46:09 +00:00
sendDataToDevice({
...options,
2024-05-11 18:21:02 +00:00
value: curBuffer,
2024-05-11 16:46:09 +00:00
lasterSuccess: () => {
options.onProgress &&
options.onProgress(Math.floor(((allLenth - writeArray.length) / allLenth) * 100));
if (writeArray.length) {
print(options, writeArray);
} else {
options.lasterSuccess && options.lasterSuccess();
}
},
});
}
};
print(options, writeArrayCopyer);
}
/**
* 获取打印图片的指令
*
* @export
* @param {object} options
* {
lineByLine, // 是否逐行打印,默认true
}
* @param {object} imageInfo overwriteImageData 得到的位图数据数组和宽高信息
{
array,
width,
height
}
*/
export function getImageCommandArray(opt = {}, imageInfo = {}) {
const lineByLine = typeof opt.lineByLine !== 'boolean' ? true : opt.lineByLine;
const width = imageInfo.width;
const h = imageInfo.height;
const xl = width % 256;
const xh = (width - xl) / 256;
const yl = h % 256;
const yh = (h - yl) / 256;
//打印图片的十进制指令数组
let command = [];
if (lineByLine) {
// 分段逐行的指令
command = command.concat([29, 118, 48, 0, xl, xh, 1, 0]);
} else {
// 非分段逐行的指令
command = command.concat([29, 118, 48, 0, xl, xh, yl, yh]);
}
return command;
}
/**
* 获取一张图片数据与指令整合的writeArray,结合 sendDataToPrint 发送二进制数据到蓝牙打印机
* @param {object} options
* {
lineByLine, // 是否逐行打印,默认true
printAlign, // 打印的位置
}
* @param {object} imageInfo overwriteImageData 得到的位图数据数组和宽高信息
{
array,
width,
height
}
*/
export function getPrintImageWriteArray(opt = {}, imageInfo = {}) {
const lineByLine = typeof opt.lineByLine !== 'boolean' ? true : opt.lineByLine;
const width = imageInfo.width;
let arr = imageInfo.array;
let writeArray = [];
const iniTcommand = [].concat(printCommand.clear).concat(printCommand[opt.printAlign || 'left']);
const command = getImageCommandArray(opt, imageInfo);
writeArray.push(new Uint8Array(iniTcommand));
// 分段逐行打印的数据
if (lineByLine) {
for (let i = 0; i < arr.length / width; i++) {
const subArr = arr.slice(i * width, i * width + width);
const tempArr = command.concat(subArr);
writeArray.push(new Uint8Array(tempArr));
}
} else {
// 非逐行打印
writeArray.push(new Uint8Array(command.concat(arr)));
}
// writeArray.push(new Uint8Array([27, 74, 3]));
return writeArray;
}
/**
* 只打印一张图片数据的方法
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
lasterSuccess,
onProgress,
printAlign, "left"|"right"|"center"
}
* @param {object} imageInfo overwriteImageData 得到的位图数据数组和宽高信息
{
array,
width,
height
}
*/
export function printImage(opt, imageInfo) {
sendDataToPrint(
opt,
getPrintImageWriteArray(
{
printAlign: opt.printAlign,
lineByLine: opt.lineByLine,
},
imageInfo,
),
);
}