技術文章

在 Delphi 中使用 HotPDF 產生可掃描的條碼 PDF

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

顯示由 Delphi 中的 HotPDF 繪製的各種符號系統的一維條碼網格的 PDF 頁面
使用 HotPDF 繪製到單一 PDF 中的一維條碼符號系統

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。它們在具有自身錯誤更正數學的網格中編碼位元組,如果需求指定了其中之一,那麼這就是錯誤的工具,您應該在圍繞它進行建置之前就知道這一點,而不是在之後才知道。對於一維條碼,實際問題要窄得多:哪種符號系統接受您實際擁有的資料,因為編碼是不可互換的

資料限制是真實的,並且會在產生時造成影響。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。它需要符號系統、位置、高度,以及一個比其他參數更重要的參數:MUnit,即模組寬度。模組是最窄條碼的寬度,也是條碼中所有其他測量值的倍數基底原子,在這裡它以點 (points) 表示。列印結果是否能被掃描,一切都可以追溯到這個單一整數

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'label.pdf';
    Pdf.BeginDoc;

    // BCType, X, Y, Height, MUnit (module width in points), angle,
    // data, UseCheckSum, bar color, background color.
    Pdf.CurrentPage.DrawBarcode(
      bcCodeEAN13,           // symbology
      72, 680,               // X, Y in points from the bottom-left
      60,                    // bar height
      1,                     // MUnit: 1pt narrowest bar
      0,                     // no rotation
      '123456789012',        // 12 digits; the 13th is the check
      True,                  // append the modulo-10 check digit
      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 僅繪製條碼,別無其他。它不會為您保留靜區,這意味著責任在您身上,而且很容易忘記。失敗模式很微妙:您將條碼與表格儲存格邊框齊平放置,或者您讓頁面佈局將標誌擠到它旁邊,而通過空白頁面上每個測試的條碼在它進入真實文件的那一刻就會停止掃描。明確地規劃邊緣。在您呼叫 DrawBarcode 之前,請在兩側留下至少十個模組寬度的清晰空間,並將任何侵入該帶狀區域的圖形、線條或文字視為缺陷,而不是外觀選擇

顏色、對比度和人類可讀的行

條碼和背景顏色的存在是為了讓您可以搭配品牌色系,而它們也是破壞正常運作條碼最快的方法。掃描器讀取對比度,傳統上是使用紅光,它們預期是在淺色背景上的深色條碼。黑底白字是您唯一應該在沒有測試的情況下採用的組合。白底深藍或深綠可以通過;任何低亮度對比的顏色,特別是紅光掃描器視為背景的紅色條碼,都將無法通過。如果設計師要求使用彩色條碼,誠實的答案是條碼保持黑色,顏色應該放在標籤上的其他地方

DrawBarcode 路徑還可以呈現條碼下方人類可讀的文字,即掃描失敗時職員輸入的數字。該文字是一種備援,而不是裝飾,因此當您放置自己的標題時,請使其遠離靜區;擠進側面邊緣的符號標籤會破壞掃描器所依賴的同一個空白空間。這裡範例中的欄位,包括周圍任何標籤的 TextOut,與 報表輸出指南 中涵蓋的繪圖呼叫相同,當條碼是更大組合頁面中的一個元素時,這就是您該去的地方

簡短的驗證習慣

向量條碼是一個值得提及的優勢。因為 DrawBarcode 將代碼寫入為 PDF 繪圖運算符而不是光柵化影像,因此條碼在任何縮放比例下都保持清晰,並且檔案本身不帶有解析度;唯一重要的是印表機的解析度。這並不能成為您免除測試的藉口,這只是意味著測試必須在紙上進行。產生一個樣本,在您的條碼實際會遇到的最低解析度裝置上列印它,然後使用與您的使用者所持有同等級的讀取器掃描它,而不是您桌上的高階影像掃描器。在列印品上用尺檢查靜區,確認模組寬度在從點到點的轉換中存活下來,並驗證解碼值是否與您編碼的值相符,包括檢查碼和所有內容。使用真正的掃描器五分鐘就能找出上述的所有故障,並且它能在整棧貼錯標籤的庫存發生之前發現它們

此處顯示的 DrawBarcodeDirectDrawBarcode 方法是適用於 Delphi 和 C++Builder 的 HotPDF 元件 的一部分