目前有一个需求,就是将外卖订单绘制成 html页面,再通过 c-lodop 打印出来,外卖小票大概是如下的样子。其中外卖小票的底部有一个条码,尝试了 3 种方案,前两种方案中,这个条码是基于 JsBarcode 将 订单编号转为的条码。第三种方案,是采用 c-lodop 来生成的条码。 刚开始听不到扫码枪“滴”的声音。经过自己的排查,最终听到了“滴”的声音。
问题描述
目前,在实现这需求的过程中,遇到一个问题 ,打印出来的小票,扫码枪识别不了底部的条码。
场景 1:通过 c-lodop + 电脑自带的打印, 打印出来的pdf. 底部的条码,手机是可以扫描识别的,但是扫码枪识别不了。
场景 2:通过 c-lodop + 真是打印机,打印出来的纸质小票。 底部的条码,手机和扫码枪都识别不了。
JsBarcode 绘制条码的代码如下
function _barCodeFoo(tid){
const $svg = $(`<svg id="ele_${tid}"/>`)
$svg.JsBarcode(tid, {
format: 'CODE128',
width: 3, // 条的宽度,建议 1~3
// height: 120, // 条码高度,建议 >= 50,太低可能影响扫描
displayValue: true, // 显示数字
margin: 0,
});
let src = 'data:image/svg+xml;base64,' + btoa(unescape($svg.prop('outerHTML')))
console.log('条形码 bar ',src)
return src;
}
排查1
可能原因 1:JsBarcode 方法中,width 设置的原因
可能原因 2:图片的宽度、高度导致的
于是做了 一些尝试,但是问题依然存在。扫码枪依然识别不了底部的条码。
排查 2
先看图
图 1
图 2
html页面中,底部条码图片中样式 object-fit: cover; 是生效的。但是实际用 c-lodop 来打印的时候,object-fit: cover;就没有生效。特别是订单编号很短的时候,非常明显。
推测 于是,我有了如下的推测。
因为底部的条码是最终生成的是 [SVG 格式的 Base64 数据 URL],再将这个 URL 通过 img 标签来显示出来。是不是 c-lodop 在打印条码的时候,条码图片会被压缩、失真。于是我问了下豆包。豆包提供的回答如下。
新的尝试
基于上述排查 2 的思路,于是我改变下生成条码图片的方式。问了下豆包,生成条码图片的方式。
将订单编号改为 123456789,用两种方式生成的条码图片如下。base64 生成的条码图片用扫码枪不能识别,基于 canvas生成的条码图片扫码枪是可以识别的。
纠正下: 这里说的 base64 生成的条码图片,准确的说法应该是 [SVG 格式的 Base64 数据 URL] ,也就是下面的左图。
我使用扫码枪是可以识别方式 2 生成的条码图片的。听到一声“滴”,我开心的笑了。哈哈
关键代码如下
// 之前基于 [SVG 格式的 Base64 数据 URL]生成条码图片,c-lodop打印的时候会压缩,导致失真,条码枪识别不了。
// 改为 基于 canvas 生成图片,这种图片不会因压缩导致线条失真
function _barCodeFoo3(tid) {
const canvas = document.createElement('canvas');
JsBarcode(canvas, 123456789, {
format: 'CODE128',
width: 2, // 条码宽度(建议 2~3)
height: 50, // 条码高度(建议 >=50)
displayValue: true, // 是否显示数字
margin: 0
});
// 转换为 Base64 PNG
const pngDataUrl = canvas.toDataURL('image/png');
return pngDataUrl;
}
排查 3
昨天晚上,将代码合并到测试分支后。今天早上就叫测试同学测试下条码不能识别的问题。测试同学反馈说,这个条码的编号是 “123456789”,我一看,昨天粗心将订单编号改为了 “123456789”。 这个 9 位数的条码,用扫码枪确实可以扫码出来。
于是我将订单改为真实的 19 位数,发现扫码枪还是不能识别。昨晚弄到晚上 10 点多,以为搞定了,发现还是没有搞定。还得继续排查原因。
我仔细观察了下这次生成的条码有类似拖影的感觉。如下图。
反思: 为啥打印出来的 9 位数的条码,扫码枪可以识别。但是打印出来的 19 位的条码,扫码枪不能识别呢?
可能原因,9 位数的条码信息不是很密集,所以扫码枪轻轻松松识别。但是 19 位的条码,信息比较密集,从条码的样子就可以看出来,这些竖条纹是很密集的。所以扫码枪识别很困难。
新的尝试
c-lodop 打印条码验证
就在我比较疑惑的时候,旁边的大佬给我了我一个思路。使用 lodop 来打印条码(lodop 直接基于订单号来生成并打印条码)。
于是我先用 lodop 打印了一下快递发货单,发现打印出来的条码,很容易被扫码枪识别。
我肉眼反正是看不出来有啥区别。但是我听到“滴”的声音的时候,我知道 c-lodop 生成的条码是可以的。于是我采用这种方案。
其中,c-lodop 生成条码的 api 如下。这个 Top 的计算很关键。
LODOP.ADD_PRINT_BARCODE(Top, Left, Width, Height, CodeType, CodeContent);
遇到了这个问题: 因为整个外卖小票上部分我是采用 c-lodop 打印 html 的方式来实现的。如果要使用 c-lodop 来生成底部的条码,条码的位置如何计算?于是我做了如下两种方式的探索。
处理方式 1
如果不计算 Top 的位置,c-lodop 打印完 html 后,再接着打印条码。但是如何让 c-lodop 接着打印呢。我问了下 AI 以及 c-lodop 的文档。发现了如下的 api .
LODOP.ADD_PRINT_HTM(0, 0, "100%", "100%", htmlContent);
LODOP.SET_PRINT_STYLEA(0, "LinkedItem", -1); // 关联到前一项
LODOP.ADD_PRINT_BARCODE("5mm", "10mm", "200px", "50px", "128A", "123456"); // 自动接在HTML下方
后面,我按照上面的代码去验证的时候,发现打印的条码到另一页了。
处理方式 2
使用 c-lodop 来生成条码,并计算条码的位置。于是问了下 ai ,采用这种方式。关键代码如下。
1、计算高度的代码如下
function _getHtmlHeight(htmlContent){
const tempDiv = document.createElement("div");
tempDiv.innerHTML = htmlContent;
document.body.appendChild(tempDiv);
const htmlHeightPx = tempDiv.offsetHeight + 40; // 额外留出 40px 的间距
const htmlHeightMm = htmlHeightPx * 0.264583; // px转mm (1px≈0.264583mm)
document.body.removeChild(tempDiv);
return htmlHeightMm +'mm';
}
2、打印条码的关键代码如下
orderHtmlArr.forEach((item, index) => {
const htmlStr = item.theHtml
const htmlTop = item.height
lodopDom.ADD_PRINT_HTM(0, 0, '100%', '100%', htmlStr);
lodopDom.ADD_PRINT_BARCODE(htmlTop, '10mm', '56mm', '16mm', "128Auto", item.tid);
if (index < orderHtmlArr.length - 1) lodopDom.NewPage(); // 非最后一页则分页
});
后面发现生成的条码还是有点问题,后来在其他同事的帮助下,将生成条码的 code 改为 ‘128Auto’,就好了。
总结
测试了这么多,终于弄好了,心里还是蛮开心的。也感谢同事提供的帮助。
本文记录了自己用 c-lodop 打印条码遇到的一些问题。记录下,方便后续回顾。
- 生成条码的时候,最好采用 c-lodop 来生成条码。
- 并且 生成条码的时候,code 设置为 “128Auto” 识别效果会更好些。
- 扫码枪使用的时候,扫码枪和条码成一个大概 30 度的角度(扫码枪和条码不要垂直哦),这样识别的效果会更好些。
看了下时间,晚上 11 点了,先这样了。
附上其他更新
- 25.10.13
SVG格式的Base64数据、PNG格式的Base64数据之间的区别。