기술 문서

델파이에서 HotPDF를 사용하여 PDF에 스캔 가능한 바코드 생성하기

바코드는 문서를 장식하는 그림이 아닙니다. 바코드는 측정값이며, 스캐너는 이를 읽는 도구입니다. 이러한 재구성은 PDF에 바코드를 그리는 방법에 대한 거의 모든 것을 결정합니다. 막대는 검은색 자체로 정보를 전달하지 않습니다; 정보는 막대 너비 대 공백 너비의 비율에 존재하며, 리더기(reader)는 레이저나 센서가 스캔할 때의 전환을 타이밍하여 이를 복구합니다. 해당 기하학적 구조를 압축하거나, 흐리게 하거나, 여백을 좁히면 겉보기에는 바코드와 똑같아 보이지만 얼룩처럼 스캔되는 결과물이 생성됩니다. HotPDF는 페이지에 바코드를 배치하는 두 가지 방법을 제공하며, 그 둘의 차이는 기하학적 구조를 통제하는 것과 포기하는 것의 차이와 정확히 같습니다

델파이에서 HotPDF가 그린 다양한 기호의 선형 바코드 그리드를 보여주는 PDF 페이지
HotPDF를 사용하여 단일 PDF에 그려진 선형 바코드 기호

HotPDF가 인코딩할 수 있는 것

HotPDF는 선형(1차원) 기호를 그리며, 그 세트는 대부분의 프로젝트에서 필요로 하는 것보다 광범위합니다. THPDFBarcodeType 열거형은 교차(interleaved), 산업용 및 매트릭스 형태의 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자리 부가 기호(add-on); 그리고 GS1-128 (EAN-128) 하위 집합을 다룹니다. 이는 공급망 라벨, 소매점 POS(Point of Sale) 및 창고에 여전히 남아 있는 오래된 산업용 코드를 처리하기에 충분합니다

이 제품군은 설계부터 선형(1차원) 전용입니다. QR, Data Matrix, PDF417 같은 2차원 심볼로지는 자체 오류 정정 수학으로 그리드에 바이트를 인코딩하며 이 API의 범위 밖에 있으므로, 그 위에 구축하기 전에 요구 사항이 어느 쪽 제품군을 지칭하는지부터 확정해야 합니다. 1차원 코드에 대한 실용적인 질문은 더 좁습니다. 인코딩이 서로 호환되지 않는 이상, 실제 가진 데이터를 받아들이는 심볼로지가 무엇인가입니다

데이터 제약은 실제로 존재하며 생성 시점에 발을 듭니다. Code 2 of 5 변형과 MSI는 숫자만 받습니다. Code 39는 대문자, 숫자, 몇 안 되는 구두점을 다룹니다. 소문자나 전체 ASCII 범위가 필요하다면 그것은 Code 39 Extended나 Code 128 하위 집합의 영역입니다. Code 128C는 밀도를 위해 기호 하나에 숫자 두 개를 담으므로 짝수 길이의 숫자 문자열만 받습니다. EAN-13은 열두 자리를 받고 열세 번째 자리를 체크 디지트로 계산하고, EAN-8은 일곱 자리를 받고 여덟 번째를 계산하며, UPC-A는 열두 자리를 받습니다. DrawBarcode를 호출하기 전에 이 규칙들을 기준으로 데이터를 검증하십시오. 심볼로지가 표현할 수 없는 데이터로 만든 바코드는 화면에서는 멀쩡해 보이지만 누군가 계산대에서 스캔하는 순간에야 실패가 드러납니다

두 가지 그리기 경로, 두 가지 수준의 제어

프로덕션 환경에서 사용해야 할 메서드는 페이지 객체의 DrawBarcode입니다. 이는 기호, 위치, 높이, 그리고 다른 모든 것보다 중요한 매개변수 하나, 즉 모듈 너비인 MUnit을 취합니다. 모듈은 가장 좁은 막대의 너비로, 코드의 다른 모든 측정값이 이에 대한 배수가 되는 원자 단위이며 여기서는 포인트 단위로 표현됩니다. 인쇄된 결과물이 스캔되는지 여부에 대한 모든 것은 이 단일 정수로 귀결됩니다

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

    // BCType, X, Y, Height, MUnit(point 단위 module width), angle
    // data, UseCheckSum, bar color, background color.
    Pdf.CurrentPage.DrawBarcode(
      bcCodeEAN13,           // symbology
      72, 72,                // 왼쪽 위 모서리의 X, Y, point 단위
      60,                    // bar height
      1,                     // MUnit: 1pt narrowest bar
      0,                     // no rotation
      '123456789012',        // 12 digits; the 13th is the check
      True,                  // 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는 미리 보기용으로, 또는 결과 막대가 여전히 읽을 수 있음을 측정한 레이아웃용으로 남겨 두세요

모듈 너비는 해상도 결정입니다

레이블이 작동하는지 여부를 결정하는 산술 연산은 다음과 같습니다. 레이저 스캐너와 카메라 모두 구별할 수 있는 가장 작은 특징이 있으며, 좁은 막대는 인쇄 후 그 크기보다 충분히 커야 합니다. 범용 선형 코드에 대해 널리 인용되는 하한선은 약 0.33mm인 13 mil의 좁은 막대이며, 많은 소매 및 산업 가이드에서는 이를 목표가 아닌 최소값으로 취급합니다. 이를 PDF 단위로 변환해 보세요: 1포인트는 1/72인치(약 0.353mm)이므로 1포인트의 모듈 너비는 바로 그 하한선에 위치합니다. 그렇기 때문에 실제 스캐너용 코드에서 신뢰할 수 있는 가장 작은 값은 MUnit := 1이며, 이를 2로 두 배 늘리는 것은 여유 공간이 있는 레이블에서 거의 비용이 들지 않는 여백을 확보하는 이유입니다

이제 이를 출력 해상도와 연결하세요. 모듈 또한 프린터에서 견뎌내야 하기 때문입니다. 300 DPI 레이저 프린터에서 하나의 장치 도트는 1/300인치이므로 1포인트 모듈은 약 4도트 너비입니다. 4도트는 깔끔한 가장자리를 렌더링하기에 겨우 충분합니다; 토너 번짐과 약간의 정렬 오류로 인해 너비가 줄어들고, PDF에서 1포인트로 측정한 막대는 사양이 허용하는 것보다 더 두껍거나 얇게 인쇄됩니다. 모듈을 2포인트로 늘리면 8도트를 사용할 수 있게 되어 그러한 노이즈를 흡수합니다. 내재화할 가치가 있는 규칙은 다음과 같습니다: 포인트 단위로 설정한 모듈 너비는 여러분이 원하는 해상도가 아니라 실제 인쇄 해상도에서 편안한 정수 개의 장치 도트에 매핑되어야 합니다. 화면에서는 완벽하게 스캔되지만 창고 프린터에서는 실패하는 코드는 거의 항상 이 확인에 실패한 것입니다

여백(Quiet Zone)은 기호의 일부입니다

올바르게 인코딩된 바코드가 스캔되지 않는 가장 흔한 단일 이유는 막대 양쪽의 공백인 여백(quiet zone) 때문입니다. 스캐너는 코드의 시작과 끝을 찾기 위해 이 여백을 사용합니다; 여백이 없으면 리더기는 첫 번째 막대와 페이지에서 그 옆에 있는 것을 구별할 수 없습니다. 표준은 구체적입니다. 대부분의 선형 기호는 양쪽에 모듈 너비의 최소 10배에 해당하는 여백을 필요로 하며, UPC 및 EAN 소매 코드는 왼쪽에 9개 모듈, 오른쪽에 7개 모듈을 요구합니다. 1포인트 모듈의 경우 막대 양쪽에 약 1/7인치인 약 10포인트의 보장된 공백이 있게 됩니다

HotPDF는 막대를 지정한 위치에 정확히 그리고, 막대 주위의 여백(quiet zone)은 페이지 레이아웃의 몫이므로 잊기 쉽습니다. 실패 양상은 미묘합니다. 바코드를 표 셀 테두리에 바짝 붙이거나 페이지 레이아웃이 로고를 바코드 바로 옆으로 끌어다 놓으면, 빈 페이지에서 모든 테스트를 통과했던 코드가 실제 문서에 실려 나가는 순간 스캔을 거부합니다. 여백은 명시적으로 확보하십시오. DrawBarcode를 호출하기 전에 양쪽에 최소 모듈 너비 열 배의 빈 공간을 남기고, 그 띠를 침범하는 그래픽이나 선, 텍스트는 취향의 선택이 아니라 결함으로 취급하십시오

색상, 대비 및 사람이 읽을 수 있는 선

브랜드 팔레트와 일치시킬 수 있도록 막대 및 배경색 설정이 존재하지만, 이는 작동하는 코드를 깨뜨리는 가장 빠른 방법입니다. 스캐너는 대조를 읽으며 고전적으로 빨간색 빛을 사용하여 밝은 바탕에 어두운 막대를 기대합니다. 흰색 바탕에 검은색은 테스트 없이 선택할 수 있는 유일한 조합입니다. 흰색 바탕에 진한 파란색이나 진한 녹색은 통과할 수 있습니다; 광도(luminance) 대비가 낮은 색상, 특히 빨간색 빛을 내는 스캐너가 배경으로 인식하는 빨간색 막대는 스캔되지 않습니다. 디자이너가 유색 바코드를 요구한다면 솔직한 답변은 막대는 검은색으로 유지하고 색상은 레이블의 다른 곳에 적용하라는 것입니다

DrawBarcode 경로는 스캔이 실패할 때 점원이 직접 키보드로 입력하는 숫자인 막대 아래의 사람이 읽을 수 있는 텍스트를 렌더링할 수도 있습니다. 해당 텍스트는 대체(fallback) 수단이지 장식이 아니므로 자체 캡션을 배치할 때 여백에 닿지 않게 하세요; 여백에 욱여넣은 기호 캡션은 스캐너가 의존하는 바로 그 공백을 무산시킵니다. 여백 레이블을 위한 TextOut을 포함하여 여기에 나온 예제의 필드들은 보고서 출력 가이드에서 다루는 것과 동일한 그리기 호출이며, 이 가이드는 바코드가 더 큰 구성된 페이지의 한 요소일 때 참고할 곳입니다

짧은 검증 습관

벡터 막대는 언급할 가치가 있는 장점입니다. DrawBarcode는 래스터화된 이미지가 아니라 PDF 그리기 연산자로 코드를 작성하기 때문에 막대는 어떤 줌에서도 선명하게 유지되며 파일 자체 해상도를 가지지 않습니다; 유일하게 중요한 해상도는 프린터의 해상도뿐입니다. 그렇다고 해서 테스트가 면제되는 것은 아니며, 단지 종이 위에서 테스트해야 함을 의미할 뿐입니다. 샘플을 생성하여 코드가 실제로 만날 수 있는 가장 낮은 해상도의 장치에서 인쇄하고, 책상 위의 고급 이미저가 아닌 사용자가 쥐고 있는 것과 동일한 등급의 리더기로 스캔해 보세요. 인쇄물에서 자로 여백을 확인하고, 모듈 너비가 포인트에서 도트로 잘 변환되었는지 확인하고, 디코딩된 값이 체크 숫자를 포함하여 인코딩한 것과 일치하는지 확인하세요. 실제 스캐너로 5분만 확인하면 위에서 설명한 모든 오류를 잡아낼 수 있으며, 잘못된 레이블이 붙은 재고 팔레트가 배송되기 전에 잡아냅니다

여기에 표시된 DrawBarcode 및 DirectDrawBarcode 메서드는 델파이 및 C++Builder용 HotPDF 컴포넌트의 일부입니다