聽一個希捷的朋友說的。有使用者反映每次用筆記本播放李娜的青藏高原時,電腦就會宕機。經測試發現,唱到最後的“那就是青藏高……”時,硬碟產生了共振,振幅過大,讀寫頭讀不出資料了。為了抓住這個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 裝置可以掃,立式掃碼槍還是掃不了。我的內心:________________。
此時恰好隔壁組的老闆路過,問我條形碼是如何生成的,是否會因為演算法的原因讓某個長條偏移了一兩畫素?然而我快速使用公司的高質量印表機打了一下,並且粗略的將打出來的條形碼(見下圖)與谷歌到的線上生成工具生成的樣子對比了一下,憑我的視力好像是看不出什麼太大的區別。以及條形碼的容錯性應當非常好啊,即使偏移了一兩畫素應當也能識別,否則收銀員掃碼將是一件極其辛苦的事情。

真相原來是這樣
當你排除了所有不可能的,無論多麼不可能,剩下的,無論多麼不可能,就是真相。——夏洛克·福爾摩斯
正如這句話所說的,我在極短的時間內排除了所有可能的問題之後,最後只剩下了一個可能: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”。
後記
- 第二天,他們的 QA 找了過來:“你是不是把掃碼槍刷壞了?”我:“嗯,昨晚我除錯來著,現在幫你恢復一下出廠設定……”
- 之後他們讓業務方用自己的掃碼槍試一下,是可以掃出來的,因此大家都不需要改程式碼了,這個問題也算是解決了。