示例完整的代码,请点击查看github
小程序连接蓝牙打印机打印文本与二维码等示例在 github 上都能找到一些,唯独打印图片这个案例几乎没有。希望能帮助到有打印图片需求的小伙伴。
/**
* 以Promise方式调用 微信api
* @param {string} name 微信api的名称 ,如 wxAsyncPromise("getSystemInfo",options)
* @param {object} options 除了success 和 fail 的其他参数
* @returns
*/
export function wxAsyncPromise(name, options) {
return new Promise((resolve, reject) => {
wx[name]({
...(options || {
}),
success: function(res) {
resolve(res);
},
fail: function(res) {
reject(res);
},
});
});
}
//在多个服务services中递归查找能用的特征值
//deviceId : 已连接的蓝牙设备id
//services : wx.getBLEDeviceServices()取得的服务
export function getDeviceCharacteristics(deviceId, services = [], success, fail) {
services = services.slice(0);
if (services.length) {
const serviceId = services.shift().uuid;
wxAsyncPromise('getBLEDeviceCharacteristics', {
deviceId,
serviceId,
})
.then((res) => {
console.log('getBLEDeviceCharacteristics', deviceId, serviceId, res);
let finished = false;
let write = false;
let notify = false;
let indicate = false;
//有斑马品牌的一款打印机中res.characteristics的所有uuid都是相同的,
//找所有的properties存在(notify || indicate) && write(如果只写入数据只要write=true也可以)这种情况就说明这个uuid是可用的
//(不确保所有的打印机都能用这种方式取得uuid,
//在凯盛诺打印机的res.characteristic只有一个uuid,所以也能用这个方式)
for (var i = 0; i < res.characteristics.length; i++) {
if (!notify) {
notify = res.characteristics[i].properties.notify;
}
if (!indicate) {
indicate = res.characteristics[i].properties.indicate;
}
if (!write) {
write = res.characteristics[i].properties.write;
}
if ((notify || indicate) && write) {
/* 获取蓝牙特征值uuid */
success &&
success({
serviceId,
characteristicId: res.characteristics[i].uuid,
});
finished = true;
break;
}
}
if (!finished) {
getDeviceCharacteristics(deviceId, services, success, fail);
}
})
.catch((res) => {
getDeviceCharacteristics(deviceId, services, success, fail);
});
} else {
fail && fail();
}
}
在 Demo 中手机选任意一张图片用 canvas 绘制,wx.canvasGetImageData()取得 Uint8ClampedArray 类型的图像像素点数据。
需要把 Uint8ClampedArray 类型的数据转成打印机识别的点阵位图数据(也可以让后台实现图片转位图数据,参考热敏打印机编程 ESC/POS 指令)。
不同打印机厂家的指令集可能不同,但打印图片的位图数据是一样的。
//灰度化处理
function grayPixle(pix) {
return pix[0] * 0.299 + pix[1] * 0.587 + pix[2] * 0.114;
}
/**
* overwriteImageData
* @param {object} data
* {
width,//图片宽度
height,//图片高度
imageData,//Uint8ClampedArray
}
*/
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) {
//灰度值大于threshold位 白色 为第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,
};
}
/**
* sendDataToDevice
* @param {object} options
* {
deviceId,//蓝牙设备id
serviceId,//服务id
characteristicId,//可用特征值uuid
value [ArrayBuffer],//二进制数据
lasterSuccess,//最后完成的回调
}
*/
export function sendDataToDevice(options) {
let byteLength = options.value.byteLength;
//这里默认一次20个字节发送
const speed = options.onceByleLength || 20;
if (byteLength > 0) {
wxAsyncPromise('writeBLECharacteristicValue', {
...options,
value: options.value.slice(0, byteLength > speed ? speed : byteLength),
})
.then((res) => {
if (byteLength > speed) {
sendDataToDevice({
...options,
value: options.value.slice(speed, byteLength),
});
} else {
options.lasterSuccess && options.lasterSuccess();
}
})
.catch((res) => {
console.log(res);
});
}
}
/**
* printImage
* @param {object} opt
* {
deviceId,//蓝牙设备id
serviceId,//服务id
characteristicId,//可用特征值uuid
lasterSuccess , //最后完成的回调
}
* @param {object} imageInfo // 由overwriteImageData返回的对象
*/
export function printImage(opt = {
}, imageInfo = {
}) {
let arr = imageInfo.array,
width = imageInfo.width;
const writeArray = [];
const xl = width % 256;
const xh = width / 256;
//分行发送图片数据,用的十进制指令
const command = [29, 118, 48, 0, xl, xh, 1, 0];
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));
}
const print = (options, writeArray) => {
if (writeArray.length) {
sendDataToDevice({
...options,
value: writeArray.shift().buffer,
lasterSuccess: () => {
if (writeArray.length) {
print(options, writeArray);
} else {
options.lasterSuccess && options.lasterSuccess();
}
},
});
}
};
print(opt, writeArray);
}