R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。 MUGer, hacker, developer, amateur UI designer, punster, Japanese learner.

由于硬件问题引发的条形码扫描 Bug

听一个希捷的朋友说的。有用户反映每次用笔记本播放李娜的青藏高原时,电脑就会死机。经测试发现,唱到最后的“那就是青藏高……”时,硬盘产生了共振,振幅过大,读写头读不出数据了。为了抓住这个bug,朋友听了一个月的青藏高原……

一开始看到这个段子(出处:有哪些让你目瞪口呆的 Bug ? - wuz的回答 - 知乎)的时候,只是笑一笑。然而万万没想到,我一个前端居然也会遇到硬件问题。

问题现象

隔壁组有一个业务需求:在专用的纸上批量打印一大堆条形码(商品 ID 之类的数据),然后可以方便的撕下来贴在包裹上,以方便后续的操作。由于我负责的面单平台有一定的扩展性,因此他们决定让我们支持一下条形码的功能(这样他们就不用写代码画了,业务改起来也方便)。

我花了两三天的时间支持了一下,本地测试通过之后,本以为高枕无忧,然而他们报了个 Bug 过来:大部分的条形码打出来都没法扫,有一些条形码只有 PDA 设备可以扫,但普通的立式扫码枪扫不了。

需求是不能延期的,现在的瓶颈在我这里。我之前没有遇到过这样的问题,所以一时不知道该如何下手,只好跑到他们那边看一看。

排查问题

首先我要到了他们之前打印出来的一些条形码,发现锯齿状特别严重(下图是条码的一小部分),很多条码因为锯齿加上间距太窄,几乎黑成了一片,但有些条形码就打印的很完美,完全没有锯齿状。隔壁组的同事说,之前前端自己用 JSBarcode 画的时候一点问题都没有,自从切到了面单平台就这样了。然而面单平台生成的 PDF 是绝对不会有这种问题的,不然这两年早就各种线上事故了。

带有锯齿状的条码

一般来说,锯齿的原因是传给打印机的图片分辨率太低(打印需要 300 DPI,而一般的电脑则是 72 DPI)。但是我们所有的单位统一用了厘米,并且后端由 Chrome 来渲染生成 PDF,出来的效果应当跟 Word 差不多,而众所周知 Word 的打印是不会有这种问题的。

失败的试探

为了确定面单究竟是在哪一步开始变锯齿的,我找到了他们的前端、后端和打印插件的负责人,分析了所有的链路,并一点点排查问题:

  • 从面单平台生成的 PDF:正常
  • 隔壁组后端接收到的 PDF 数据:正常
  • 他们往打印插件发送的数据:看起来正常
  • 打印机出来的效果:有锯齿

于是我们把重点放到了打印插件中。他们用了一个开源的打印插件(底层大多是 C 代码)并加以封装,考虑到看这些代码可能会折寿,我决定放弃,想想是否有其它思路。

一个有效的解决方案

在不断消耗脑细胞的过程中,我突然注意到了文字也有锯齿的现象。于是我问他们的 QA 要到了以前测试的时候打过的各种各样的面单,发现都有这样的问题(这个条形码就很完美,但是文字是有锯齿的):

文字有锯齿状

看到这儿,可能有些小伙伴已经能猜到原因了:打印机不支持灰度打印,只能打印纯黑和纯白,对于灰色的区域,打印机会用黑点和白点的密度来显示一个视觉效果。对于 PDF 来说,文字是有反锯齿(次像素渲染)的,因此当次像素渲染由于打印机的原因“消失”之后,就成了这个样子。条形码的图片也是如此,如果条形码图片(PNG)由于缩放而导致每一条的边缘出现了灰色像素,那么也会出现类似的问题。

我瞬间想到了为什么 JSBarcode 画的条形码没问题了。之前我在他们组的时候用过那个库,它会生成一个 SVG 元素,而 Chrome 在渲染的时候,如果一个元素的定位信息的单位是像素,那么所有的数字都会被取整。例如你设置一个 left: 5.8px; width: 10.2px,那么最终你只会得到 left: 6px; width: 10px。只要每个矩形的定位信息都是整数,那么最后一填充,必然不会出现次像素渲染的问题。

我跟我们后端商量了一下,把条形码相关的生成代码换成了 SVG 的版本,隔壁组又打印了几次,条形码已经不会再有锯齿了。

问题结束了吗?

我由于迅速的解决了问题,心情愉悦,决定晚上跟同事去吃萨莉亚。然而就在我们即将下班的时候,他们又报过来一个问题:虽然条形码打出来看着没问题,但只有 PDA 设备可以扫,立式扫码枪还是扫不了。我的内心:________________。

此时恰好隔壁组的老板路过,问我条形码是如何生成的,是否会因为算法的原因让某个长条偏移了一两像素?然而我快速使用公司的高质量打印机打了一下,并且粗略的将打出来的条形码(见下图)与谷歌到的在线生成工具生成的样子对比了一下,凭我的视力好像是看不出什么太大的区别。以及条形码的容错性应当非常好啊,即使偏移了一两像素应当也能识别,否则收银员扫码将是一件极其辛苦的事情。

一个非常简单的条形码

真相原来是这样

When you have eliminated the impossibles, whatever remains, however improbable, must be the truth. - Sherlock Holmes

正如这句话所说的,我在极短的时间内排除了所有可能的问题之后,最后只剩下了一个可能:QA 手中的立式扫码枪有问题。

就是这个扫码枪

由于不想鸽了同事,也不想让同事等太久,我便拍下了扫码枪的背面标签,在萨莉亚等餐的时候慢慢查。这个扫码枪的型号是 Orbit MS7120,搜了一下它的参数,感觉没有什么特别的,于是跟同事开玩笑的说道:“难不成这个破扫码枪只能扫最短三个字符?”顺便随手谷歌了一下 orbit ms7120 3 chars,结果第一条就是这么个链接:MS7120 Orbit - How to configure code 128 to decode a minimum of three characters,我的内心:________________。

文中是这样说的:

默认情况下 MS7120 Orbit 针对 code128 条形码只能扫最短四个字符,如果要改为最短三个字符,点击 这里 下载一个 PDF,然后按顺序扫描里面的每一个条形码。注意,这会让你的设备初始化。

我是谁?我为什么要设置为最短三个字符?我感觉自己就跟那个 计算机二级准考证传错照片的哥们 一样。

我还能说些什么?

玩梗归玩梗。为了验证我的猜想是否正确,吃完萨莉亚,我回到了公司,按照 PDF 中的方法试了一下,还真特喵的能扫三个字符的条形码了……然而我查了半天 MS7120 的手册,也没查到如何让它支持任意字符数量的条形码。

因为这个需求可能有延期风险,所以他们发了邮件并且 @ 了我,为了不被“人在家中坐,锅从天上来”,我赶紧回了邮件,并附上了自己完整的验证步骤,我给的解决方案是“4 位以下的数据就不要用条形码了,如果必须有这个需求,就加一些空白字符使得长度至少为 4”。

后记

  1. 第二天,他们的 QA 找了过来:“你是不是把扫码枪刷坏了?”我:“嗯,昨晚我调试来着,现在帮你恢复一下出厂设置……”
  2. 之后他们让业务方用自己的扫码枪试一下,是可以扫出来的,因此大家都不需要改代码了,这个问题也算是解决了。
版权声明:除文章开头有特殊声明的情况外,所有文章均可在遵从 CC BY 4.0 协议的情况下转载。
上一篇: 面单工具:对 Chrome Headless 渲染的极致优化
下一篇: 编写状态驱动的业务代码(2)

这是我们共同度过的

第 1834 天