條碼不是您用來裝飾文件的圖片。它是一種測量,而掃描器是讀取它的儀器。這種觀念的重塑幾乎決定了您應該如何將條碼繪製到 PDF 中的所有細節。條碼的黑色本身不帶有資訊;資訊存在於條寬與間距寬度的比例中,讀取器透過測量雷射或感測器掃過時的轉變時間來還原它。擠壓該幾何形狀、模糊它,或擁擠其邊緣,您就會產生一個看起來完全像條碼但掃描起來像污漬的東西。HotPDF 為您提供兩種將其放置在頁面上的方法,而它們之間的區別正是控制該幾何形狀與放棄控制之間的區別

HotPDF 可以編碼什麼
HotPDF 繪製一維(線性)符號系統,其涵蓋範圍比大多數專案所需的更廣。THPDFBarcodeType 列舉涵蓋了 Code 2 of 5 家族的交錯、工業和矩陣形式;Code 39 及其延伸變體;三個 Code 128 子集 A、B 和 C;Code 93 及其延伸版本;MSI;PostNet;Codabar;零售的 UPC 和 EAN 群組,即 EAN-8、EAN-13、UPC-A、壓縮的 UPC-E0 和 UPC-E1,以及 UPC 補充的 2 位數和 5 位數附加碼;還有 GS1-128 (EAN-128) 子集。這足以涵蓋供應鏈標籤、零售銷售點以及仍在倉庫中使用的舊工業條碼
這個家族天生就是一維的。QR、Data Matrix、PDF417 這類二維符號系統把位元組編碼進網格裡,各有自己的一套錯誤更正數學,而且都在這個 API 的範圍之外,所以在圍繞它建置之前,先確認需求指定的是哪一個家族。對一維條碼而言,實際問題要窄得多:哪種符號系統接受您實際持有的資料,因為各種編碼彼此並不可互換
資料限制是真實存在的,而且在產生當下就會造成影響。Code 2 of 5 各變體與 MSI 只接受數字。Code 39 可承載大寫字母、數字和少數標點符號;若需要小寫或完整 ASCII 範圍,那就是 Code 39 Extended 或某個 Code 128 子集。Code 128C 為了密度把兩個數字打包進每個符號,因此它只接受偶數長度的數字字串。EAN-13 預期 12 位數字,並把第 13 位計算成檢查碼;EAN-8 預期 7 位並計算第 8 位;UPC-A 接受 12 位。呼叫 DrawBarcode 之前,先依這些規則驗證資料,因為由符號系統無法表示的資料所構成的條碼,在螢幕上看起來毫無異狀,直到有人在收銀台掃描它的那一刻才會失敗
兩條繪圖路徑,兩個控制層級
在生產環境中應該使用的方法是頁面物件上的 DrawBarcode。它需要符號系統、位置、高度,以及一個比其他參數更重要的參數:MUnit,即模組寬度。模組是最窄條碼的寬度,也是條碼中所有其他測量值的倍數基底原子,在這裡它以點 (points) 表示。列印結果是否能被掃描,一切都可以追溯到這個單一整數
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'label.pdf';
Pdf.BeginDoc;
// BCType、X、Y、Height、MUnit(模組寬度,單位為 points)、angle
// data, UseCheckSum, bar color, background color.
Pdf.CurrentPage.DrawBarcode(
bcCodeEAN13, // symbology
72, 72, // X、Y 為左上角座標,單位為 points
60, // bar height
1, // MUnit: 1pt narrowest bar
0, // no rotation
'123456789012', // 12 digits; the 13th is the check
True, // 追加 modulo-10 檢查碼
clBlack, clWhite); // bars black, background white
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
有兩個引數值得仔細觀察。UseCheckSum 附加符號系統所預期的 modulo-10 檢查碼,對於零售條碼,您幾乎總是希望它為 True;僅當您的資料已包含預先計算的檢查碼時才將其關閉,否則您會得到重複的數字。條碼和背景顏色是最後兩個參數,想要在這些地方發揮創意的誘惑是一個稍後會討論的陷阱。另請注意座標原點:就像 HotPDF 中其他每個頁面繪圖呼叫一樣,X 和 Y 以點為單位從頁面的左上角進行測量,且 Y 向下增加,定位的是條碼的左上角,這與 Hello World 範例 中說明的慣例相同
第二條路徑是 DirectDrawBarcode,它接受資料和邊界方塊 X, Y, Width, Height,並縮放條碼圖案以填滿該寬度。它對於在網格上佈局條碼很方便,因為您指定了佔用空間,然後該方法將條碼調整以適應它。這種便利性也是它的危險所在。當您為其提供寬度時,您就不再設定模組大小;該方法會將您允許的任何空間除以資料所需的條碼數量,而最窄的條碼就會變成該除法得出的任何結果。如果您在太窄的方塊內要求密集的 Code 128 字串,模組會縮小到低於任何掃描器可解析的程度,而且不會有任何提示。對於任何必須可靠掃描的內容,請優先使用 DrawBarcode 並刻意設定 MUnit。將 DirectDrawBarcode 保留用於預覽以及您已測量過所得條碼保持可讀性的佈局安排中
模組寬度是一個解析度決定
以下是決定標籤是否有效的算術。雷射掃描器和相機都具有可以辨識的最小特徵,而窄條碼列印後必須舒適地落於其上。通用一維條碼被廣泛引用的底限是 13 mil 的窄條碼,大約 0.33 毫米,許多零售和工業指南將其視為最小值而不是目標。將其轉換為 PDF 單位:一點 (point) 為 1/72 英吋,約為 0.353 毫米,因此單一點的模組寬度正好位於該底限。這就是為什麼 MUnit := 1 是您應該為要在真實掃描器中使用的條碼所信任的最小值,以及為什麼將其加倍到 2 可以購買幾乎不用花費成本且在標籤上有剩餘空間的餘裕
現在將其與輸出解析度連結,因為模組也必須在印表機中存活下來。在 300 DPI 雷射印表機上,一個裝置點是 1/300 英吋,因此一點的模組大約有四個點寬。四個點勉強足以渲染出乾淨的邊緣;碳粉擴散和輕微的套印誤差會侵蝕它,而您的 PDF 中測量為一點的條碼列印出來會比規格允許的更粗或更細。將模組提升到 2 點,您就有 8 個點可以使用,這能吸收這些雜訊。值得內化的規則是:您以點設定的模組寬度必須對應到您實際列印解析度下的整數、舒適的裝置點數,而不是您期望的解析度。如果在螢幕上掃描完美但從倉庫印表機列印出來卻失敗的條碼,幾乎總是沒有通過這項檢查
靜區是符號的一部分
編碼正確的條碼無法掃描的最常見原因是靜區 (quiet zone),即條碼兩側的空白邊緣。掃描器使用該空白來尋找條碼開始和結束的位置;沒有它,讀取器無法區分第一條條碼與頁面上相鄰的任何內容。標準非常明確。大多數一維符號系統需要每側至少模組寬度十倍的靜區,而 UPC 和 EAN 零售代碼則需要左側九個模組和右側七個模組。對於一點的模組,這大約是十點,大約七分之一英吋,保證位於條碼兩側的空白空間
HotPDF 把條碼精確畫在您指定的位置,而條碼周圍的靜區屬於您頁面版面的一部分,這正是它容易被遺忘的原因。失敗模式很隱晦:您把條碼緊貼著表格儲存格邊框放置,或者任由版面把 Logo 擠到它旁邊,於是那個在空白頁面上通過所有測試的條碼,進到真實文件裡的那一刻就掃不出來了。請明確為邊距預留空間。呼叫 DrawBarcode 之前,兩側至少保留十個模組寬度的淨空,並把任何侵入這條帶狀區域的圖形、線條或文字視為缺陷,而不是外觀上的選擇
顏色、對比度和人類可讀的行
條碼和背景顏色的存在是為了讓您可以搭配品牌色系,而它們也是破壞正常運作條碼最快的方法。掃描器讀取對比度,傳統上是使用紅光,它們預期是在淺色背景上的深色條碼。黑底白字是您唯一應該在沒有測試的情況下採用的組合。白底深藍或深綠可以通過;任何低亮度對比的顏色,特別是紅光掃描器視為背景的紅色條碼,都將無法通過。如果設計師要求使用彩色條碼,誠實的答案是條碼保持黑色,顏色應該放在標籤上的其他地方
DrawBarcode 路徑還可以呈現條碼下方人類可讀的文字,即掃描失敗時職員輸入的數字。該文字是一種備援,而不是裝飾,因此當您放置自己的標題時,請使其遠離靜區;擠進側面邊緣的符號標籤會破壞掃描器所依賴的同一個空白空間。這裡範例中的欄位,包括周圍任何標籤的 TextOut,與 報表輸出指南 中涵蓋的繪圖呼叫相同,當條碼是更大組合頁面中的一個元素時,這就是您該去的地方
簡短的驗證習慣
向量條碼是一個值得提及的優勢。因為 DrawBarcode 將代碼寫入為 PDF 繪圖運算符而不是光柵化影像,因此條碼在任何縮放比例下都保持清晰,並且檔案本身不帶有解析度;唯一重要的是印表機的解析度。這並不能成為您免除測試的藉口,這只是意味著測試必須在紙上進行。產生一個樣本,在您的條碼實際會遇到的最低解析度裝置上列印它,然後使用與您的使用者所持有同等級的讀取器掃描它,而不是您桌上的高階影像掃描器。在列印品上用尺檢查靜區,確認模組寬度在從點到點的轉換中存活下來,並驗證解碼值是否與您編碼的值相符,包括檢查碼和所有內容。使用真正的掃描器五分鐘就能找出上述的所有故障,並且它能在整棧貼錯標籤的庫存發生之前發現它們
此處顯示的 DrawBarcode 和 DirectDrawBarcode 方法是適用於 Delphi 和 C++Builder 的 HotPDF 元件 的一部分