uniapp实现了热敏打印机的图片打印
智力不高的我终于用uniapp实现了热敏打印机的图片打印,也是走了不少弯路,现在来总结一下开发流程,以供他人品鉴这段赤石经历,
热敏打印机这里连接到打印机部分,因为是在一个电子称的安卓系统上的,貌似直接是电子秤已经连好了USB,所以这里用了一个uniapp的打印插件,名字叫:简单打印:https://ext.dcloud.net.cn/plugin?id=12381,而且这个插件是免费的,感谢开源大神,然后这里的插件试用里,作者发送的是HEX也就是16进制的请求命令,这里可能新手看的一脸懵逼,实际上我拆分一下就好理解了,运行一下这个print.wirte,这个esc命令值是:1b401d57d0011b61011d21001d286b0400314132001d286b03003143061d286b03003145301d286b0e0031503068656c6c6f20776f726c641d286b03003151301c26c4e3bac31c2e0a1d683c1d77041d48021d6b430d363933373038323238303838311b6402,而我用这个在电子秤的热敏打印机打印出来是"200hellow world0",我们把这个数据问ai,ai解析数据如下:
1B 40 初始化打印机, 1D 57 D0 01 设置左边界, 1B 61 01 设置文本对齐居中,1D 21 00设置字符大小为正常大小......
大体使用后会得到如下:
一个二维码图像(扫描结果为 "hello world")。
文本 "你好"(在二维码之后)。
一个条码图像(类型 Code 128),条码下方有文本 "6937082280881"。
最后进纸2行,产生多行空白。
虽然不知道为什么他的实际打印比我多那么多(我的只有一行字符串...),但由此我们可知,你传给打印机什么esc数据,就会立即执行什么操作(废话)。但是如果这里依旧想着直接把汉字也转成HEX去执行,我发现没什么用,打出来的要么黑乎乎一片要么还是英文,(这里有人说是要转化成GBK之类的再转回来,但是我试了也没有成功,于是放弃这条路,你的打印机不同的话可以试试),这里我转而想到打印图片。
最后我寻找了不少教程,终于得到一个具体能走通的打印,不过我这里只做了一些笼统的操作,一些关于宽度适应切纸等东西写的并不完善(因为我自己都不完全懂),下面大体操作如下,choose获取图片,图片转化成数据,数据拼接成带有打印头的数组,然后遍历这个数组执行插件的打印操作,demo代码如下。
这是我删除了项目接口和样式后的代码,安装大神的插件后试试插件的能不能运行,如果打印不出来任何东西就换一个方案吧,安装免费插件并依次点击四个按钮后,应该能得到结果,可以先在本机运行,看看有打印的数据对不对,有问题评论区欢迎,随便问问即可
<template>
<div id="app">
<div class="container">
<view class="print-card">
<view class="row-between row-margin">
<view class="col-item">
<canvas canvas-id="secondCanvas"
id="secondCanvas"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
></canvas>
</view>
<button class="action-btn print-btn" @click="chooseImage()">
<i class="fas fa-print"></i> 1
</button>
<button class="action-btn print-btn" @click="chooseImage2()">
<i class="fas fa-print"></i> 2
</button>
<button class="action-btn print-btn" @click="chooseImage3EX()">
<i class="fas fa-print"></i> 3
</button>
<button class="action-btn print-btn" @click="topt()">
<i class="fas fa-print"></i> p
</button>
</view>
</view>
</div>
</div>
</template>
<script>
const print = uni.requireNativePlugin("XXH-USB-PRINT")
export default {
data() {
return {
lastML:null,
orderId:null,
loading: true,
orderdetail: {
},
paperWidth:380,
img:null,
// 这边是进行设置画布高度
canvasHeight:null,
canvasWidth:null,
lastBitmap:null,//得到的位图是也看看效果
fuck:null,
fuck2:null,
};
},
onLoad(options) {
},
onShow(){
},
methods: {
// 新加一个data里的paperWidth,
chooseImage() {
const ctx = uni.createCanvasContext("secondCanvas", this);
uni.chooseImage({
success: (res) => {
console.log("res", res);
// if (res.statusCode === 200) {//fffffffffffffffffffffffffffffffffffffffffff
if(true){
const tempFilePath = res.tempFilePaths[0];
uni.getImageInfo({
src: tempFilePath,
success: (res) => {
// 打印宽度须是8的整数倍,这里处理掉多余的,使得宽度合适,不然有可能乱码
// paperWidth 用于设置打印机的宽度 最大打印宽度 产品说明书上有写 我的是576点
const mw = this.paperWidth % 8;
// 计算出宽度以及高度
const w = mw === 0 ? this.paperWidth : this.paperWidth - mw;
// 等比算出图片的高度
const h = Math.floor((res.height * w) / res.width);
// 设置canvas宽高
// console.log('mw,w和h')
this.img = tempFilePath;
// 这边是进行设置画布高度
this.canvasHeight = h;
this.canvasWidth = w;
console.log("this.canvasHeight", this.canvasHeight);
console.log("this.canvasWidth", this.canvasWidth);
// 使用nextTick 让canvas宽度生效
this.$nextTick(() => {
console.log('我怎么不知道你画了')
// 在canvas 画一张图片
ctx.fillStyle = "rgba(255,255,255,1)";
ctx.clearRect(0, 0, w, h);
ctx.fillRect(0, 0, w, h);
// 进行绘画图片
ctx.drawImage(tempFilePath, 0, 0, w, h);
ctx.draw(false, () => {
console.log('我画完了,你随意')
});
});
},
fail: (res) => {
console.log(res)
},
});
}
},
});
},
chooseImage2(){
console.log('下面是宽高')
console.log( this.canvasWidth)
console.log(this.canvasHeight)
uni.canvasGetImageData({
canvasId: "secondCanvas", // canvas 唯一id
x: 0,
y: 0,
width: this.canvasWidth, // 之前保存的画布宽高
height: this.canvasHeight,
success: (res) => {
console.log('回调走成功')
console.log(res)
const pix = res.data;
// 将图片数据转成位图数据--------------------------------------
let fff=this.overwriteImageData({
threshold: null,
imageData: pix,
width: this.canvasWidth,
height: this.canvasHeight,
})
console.log('我要看看ffff')
console.log(fff)
this.lastBitmap=fff
},
complete: (e) => {},
fali:(e)=>{
console.log(e)
}
});
},
chooseImage3EX(){
console.log('此时的lastBitbam')
console.log(this.lastBitmap)
const opt = { // 获取蓝牙配置文件
deviceId: null, // 设备id
serviceId: null, // 设备服务
characteristicId: null, // 设备特性
printAlign: "left",
lineByLine: true,
onProgress: (percentage) => {
// self.percentage = percentage; // 回调的进度
},
lasterSuccess: () => {
// console.log("laster success");
// self.printing = false;
// self.percentage = 0;
// uni.showModal({
// title: "提示",
// content: "数据已发送完,请检查打印的内容是否正常",
// showCancel: false,
// confirmText: "好的",
// });
},
};
let cr=this.getPrintImageWriteArray(opt,this.lastBitmap)
console.log('最后的东西')
console.log(cr)
// let cr2
const hexArray = cr.map(obj => {
return Object.keys(obj)
.sort((a, b) => parseInt(a) - parseInt(b)) // 按键的数字顺序排序
.map(key => {
const value = obj[key];
// 确保16进制格式为两位数
return value.toString(16).padStart(2, '0');
})
.join(''); // 拼接成连续HEX字符串
});
console.log('最后的最后的HEX')
console.log(hexArray)
this.lastML=hexArray
},
topt(){
console.log('喵')
this.lastML.forEach(e =>console.log(e));
print.initUsbDevice();
// print.write(this.fuck2)
this.lastML.forEach(e => print.write(e));
},
// ****************************************************************************************************
//----------------------------------------------------------------------------------------------------------
getImageCommandArray(opt = {}, imageInfo = {}) {
console.log('秘密资迎奥')
console.log(imageInfo)
// const lineByLine =
// typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine;
if(opt){
const lineByLine=true
// const lineByLine =typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine;
}else{
const lineByLine=true
}
console.log('走完了123')
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 = [];
console.log(opt)
// if (lineByLine) {
if(true){
// 分段逐行的指令
command = command.concat([29, 118, 48, 0, xl, xh, 1, 0]);
} else {
// 非分段逐行的指令
command = command.concat([29, 118, 48, 0, xl, xh, yl, yh]);
}
console.log('我走完了好像没问题')
console.log(command)
return command;
},
// 这个函数是做什么处理成unit8的代码,但是我看不出来有啥用----------------------------------------------------
getPrintImageWriteArray(opt = {}, imageInfo = {}) {
console.log('老的imgifno')
console.log(imageInfo)
const printCommand = {
left: [27, 97, 0], //居左
center: [27, 97, 1], //居中
right: [27, 97, 2], //居右
clear: [27, 64], //初始化
enter: [10],
};
// const lineByLine =
// typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine;
if(opt){
const lineByLine =typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine;
}else{
const lineByLine=true
}
const width = imageInfo.width;
let arr = imageInfo.array;
let writeArray = [];
// const iniTcommand = []
// .concat(printCommand.clear)
// .concat(printCommand[opt.printAlign || "left"]);
const iniTcommand = []
.concat(printCommand.clear)
.concat(printCommand["left"]);
const command = this.getImageCommandArray(opt, imageInfo);
writeArray.push(new Uint8Array(iniTcommand));
console.log('下面是更好的')
// 分段逐行打印的数据
// if (lineByLine) {
if(true){
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;
},
convertToHexString(data) {
// 将对象转换为数组(确保按数字索引顺序)
const array = [];
Object.keys(data)
.filter(key => !isNaN(parseInt(key))) // 过滤数字索引
.sort((a, b) => parseInt(a) - parseInt(b)) // 按索引升序排序
.forEach(key => {
array.push(data[key]);
});
// 转换为16进制并拼接
return array.map(byte => {
// 确保两位数表示(不足补0)
return byte.toString(16).padStart(2, '0');
}).join('');
},
overwriteImageData(data) {
console.log('走到转化里了')
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 = this.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);
}
sendImageData[index++] = temp;
}
console.log('看看转化后的width')
console.log(sendWidth)
console.log(sendHeight)
return {
array: Array.from(sendImageData),
// width: sendWidth / 8,
width: sendWidth / 8,
height: sendHeight,
};
},
grayPixle(pix) {
const rgbResulut = pix[0] * 0.299 + pix[1] * 0.587 + pix[2] * 0.114
if (rgbResulut === 0) {
return 255
} else {
return rgbResulut
}
},
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
};
</script>
参考文章,感谢下面的诸位大神
非常详尽的文
https://juejin.cn/post/7297529039312158730
其他值得一看的,有些可能
https://blog.csdn.net/Linxi_001/article/details/130867268
https://blog.csdn.net/weixin_45615791/article/details/125332309
https://www.jianshu.com/p/dd6ca0054298
https://ask.dcloud.net.cn/article/id-39002__page-5#reply
https://www.jianshu.com/p/4c6cd7563759
https://juejin.cn/post/7362537451170185252
https://blog.csdn.net/qq_44721831/article/details/143208615
转gbk中文转化的
https://www.jianshu.com/p/20419462e381
更多推荐




所有评论(0)