一、解碼頁的能力面比文案窄得多
拿一張自家列印的 EAN-13 位圖去條碼解碼頁,版面上出現的不是碼文,而是「目前瀏覽器不支援條碼辨識」那一行小字。
模板一共 195 行,外部腳本 0 個,這條鏈上沒有第二套引擎兜底。第 101 行的 'BarcodeDetector' in window 是唯一判定,本機 headless Chrome 實測 false,第 102–122 行整段解碼因此不可達,只剩第 172–175 行那塊不支援的面板。
為排除圖像本身的問題,我用站內條碼產生器現畫一張合法的 EAN-13(5901234123457,實測圖 95×40 像素)餵進同一個入口,輸出 0 個字元。這張圖檢查碼合法、掃碼槍認,瀏覽器一側卻一個字都給不出。
「掃描」也不存在:模板裡 getUserMedia、video、capture 都是 0 次,第 15 行只有一個 type="file" 的靜態上傳框。
二、檢查碼是從 12 位算出來的
EAN-13 的第 13 位是模 10 加權:產生器第 188 行按索引偶數位 ×1、奇數位 ×3 求和,第 190 行取 (10 - sum % 10) % 10。實測 '590123412345' sum=83 → 7、'999999999999' sum=216 → 4。
圖 1:檢查碼如何從 12 位算出,以及一個被放行的錯碼
三、給滿 13 位,一位都不重算
「明明算了檢查碼卻對不上」的困惑,根子多半在產生端。生成器第 184–194 行只在 length === 12 的分支裡算檢查碼,給滿 13 位直接落進空分支:實測 6/6 組錯檢查碼原樣成圖,'5901234123456' 應為 7、'4006381333930' 應為 1、'9999999999999' 應為 4,一個錯都不報。超過 13 位時生成器第 192–193 行直接截斷,而截斷在補位之後,超長輸入同樣不會觸發重算。圖能出來只證明數字被畫了出來,不證明它自己前後一致。
四、UPC-A 要補一個 0 才等價
兩種碼制的權重方向差一位,換算動作只有一個:11 位 UPC-A 資料前面補一個 0,再走 encodeEAN13。實測 10/10 組與 UPC-A 自身口徑同檢查碼,'04210000526'→4、'01234567890'→5 都落在這一條上。
坑是已經帶末位的 12 位 UPC-A:原樣當作 EAN-13 的 12 位資料餵進去,末位被重算成另一位,'012345678905' 變成 '0123456789050'。同一串數字在兩種口徑下合法與非法互竄,前面那種「對不上」多半就是這個。
五、CODE128 是模 103
換成模 103 就對不上前面那套:第 116–117 行取的起始碼 104 同時是檢查和初值,第 123 行按位置權重 (i+1) 累加,第 126 行做 checksum % 103。實測 'HELLO123' sum=1141 → 8、'abc' sum=502 → 90。
這張模式表本身還是壞的:patterns 陣列實測長度 184,而 CODE128 只需要 107 項;0..102 裡只有 79 種不同圖形,24 組被兩個碼值共用;終止碼 106 落在與碼值 7 完全相同的 11 模組圖形上,標準終止符應是 13 模組。後果是同圖不同文——'CS' 與 'DS' 的 bit 串逐位相同、都是 55 模組,窮舉 1296 個兩字元輸入只得到 1292 種圖形,這一族上產生到解碼的閉環並不成立。
六、CODE39 沒有檢查碼,還吞字元
生成器第 229–257 行沒有一次求和、也沒有取模,只在首尾各加一個 '*' 定界符。生成器第 230 行先 toUpperCase,'abc' 與 'ABC' 的 bit 串完全相同;表外字元更安靜,第 249 行直接丟棄,encodeCode39('A_B') 與 encodeCode39('AB') 同為 51 模組,'UU工具箱' 只剩 51 模組的 UU。
圖 2:三種碼制的檢查口徑對照——模 10、模 103、無檢查
七、卡片全量渲染,複製只取第一條
第 133 行的 forEach 全量渲染,實測傳 4 個碼得到 4 張卡片、margin-top:10px 出現 3 次;但第 159 行只把 codes[0].rawValue 寫進 dataset.result,第 2、3、4 個碼看得見、複製不到。第 165 行「未辨識」與第 174 行「不支援」又都不清 dataset.result,面板換成「不支援」後舊值仍在,第 177–182 行 copyResult 照舊把它送進剪貼簿。
八、三種碼制的口徑並排
| 碼制 | 檢查口徑 | 實測與後果 |
|---|---|---|
| EAN-13 | 模 10 加權 | sum=83 → 7;給滿 13 位不重算 |
| UPC-A | 補一個前導 0 後同 EAN-13 | '01234567890' → 5;12 位原樣餵會重算末位 |
| CODE128 | 模 103,權重 (i+1) | 'HELLO123' sum=1141 → 8;模式表只有 79 種圖形 |
| CODE39 | 沒有檢查碼 | 'A_B' 與 'AB' 同為 51 模組 |
邊界很清楚:條碼能自證的只有「這串數字自己前後一致」。模板裡 checksum、verify、檢查、商品、真偽五個詞都是 0 次,第 134 行只把 rawValue 原樣取值,沒重算檢查碼,也就無從判定背後那件商品是不是真的。
先把碼制、位數和該不該帶檢查碼對上——條碼選型那篇管的是這一層;口徑定了再落一張圖,交給條碼產生器;回到解碼頁要核對的,是螢幕上那串與剪貼簿裡那串是不是同一個東西。
九、常被問反的三件事
Q:產生 13 位沒報錯,檢查碼就是對的?
不是。生成器第 184–194 行只在 length === 12 的分支裡算,13 位直接落空。要自證,把末位去掉、按 12 位重跑一遍再比。
Q:UPC-A 前面加個 0 就行了?
加 0 只在 11 位資料上成立;已經帶末位的 12 位不能再當作 EAN-13 的 12 位資料用,末位會被重算。
Q:面板寫著不支援,複製卻吐出一串碼?
那是上一次的舊值,兩個失敗出口都不清 dataset.result。看到失敗面板時,卡片內容不可信,剪貼簿裡那串更不可信。