emis-app/components/tpx-bluetooth-print/print/printJob.js
2024-06-23 10:54:24 +08:00

308 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//微信小程序向蓝牙打印机发送数据进行打印的坑:
//小程序api向蓝牙打印机发送数据打印,发送的任何内容都应该要转成二进制数据,而且蓝牙打印的文本编码是GBK的,发送中文需转成GBK编码再转成二进制数据发送
//发送打印机指令也要转成二进制数据发送
//蓝牙打印机一次接收的二级制数据有限制,不同的系统不同的蓝牙设备限制可能不同,微信建议一次20个字节,需做递归分包发送
//发送完要打印的内容后,一定要发送一个打印的指令才能顺利打印 (有些指令就不需要)
/**
*
*
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
value [ArrayBuffer],
lasterSuccess,
}
*/
export function sendDataToDevice(options) {
let byteLength = options.value.byteLength;
// 这里默认一次20个字发送
const speed = options.onceByleLength || 20;
console.log('byteLength====', byteLength)
if (byteLength > 0) {
console.log('speed====', speed)
console.log('value====', options.value)
uni.writeBLECharacteristicValue({
...options,
// writeType:"writeNoResponse",
value: options.value.slice(0, byteLength > speed ? speed : byteLength),
success: function (res) {
// 【writeBLECharacteristicValue】安卓存在频繁调用失败的情况,下次调用前必须加上延迟
setTimeout(()=> {
if (byteLength > speed) {
sendDataToDevice({
...options,
value: options.value.slice(speed, byteLength),
});
} else {
options.lasterSuccess && options.lasterSuccess();
}
},10)
},
fail: function (res) {
console.log('writeBLECharacteristicValue:fail====', res)
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;
console.log("sendDataToPrint:allLenth=======", allLenth)
console.log("sendDataToPrint:allUint8Array-len=======", allUint8Array.length)
const writeArrayCopyer = allUint8Array.slice(0);
const print = (options, writeArray) => {
console.log("===writeArray.length===", writeArray.length)
if (writeArray.length) {
let curBuffer = writeArray.shift().buffer
sendDataToDevice({
...options,
value: curBuffer,
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,
),
);
}