Технічна стаття

Генерація сканованих штрих-кодів у PDF з HotPDF у Delphi

Штрих-код не є картинкою, якою ви прикрашаєте документ. Це вимірювання, а сканер є інструментом, що його зчитує. Таке переосмислення визначає майже все щодо того, як ви маєте малювати його у PDF. Смуги не несуть жодної інформації у своїй чорноті; інформація закладена у співвідношенні ширини смуг до ширини проміжків, і зчитувач відновлює її, вимірюючи час переходів, коли лазер або сенсор проходить по них. Стисніть цю геометрію, розмийте її або загромадьте її поля, і ви отримаєте щось, що виглядає точнісінько як штрих-код, а сканується як пляма. HotPDF надає вам два способи розмістити його на сторінці, і різниця між ними полягає саме в різниці між контролем цієї геометрії та відмовою від нього

Сторінка PDF, що демонструє сітку лінійних штрих-кодів у різних символіках, намальованих HotPDF у Delphi
Лінійні символіки штрих-кодів, намальовані в одному PDF-файлі за допомогою HotPDF

Що може кодувати HotPDF

HotPDF малює лінійні (одновимірні) символіки, і цей набір ширший, ніж потрібно для більшості проєктів. Перелік THPDFBarcodeType охоплює сімейство Code 2 of 5 у його interleaved, industrial та matrix формах; Code 39 та його розширений варіант; три підмножини Code 128: A, B та C; Code 93 звичайний та розширений; MSI; PostNet; Codabar; роздрібну групу UPC та EAN, а саме EAN-8, EAN-13, UPC-A, стиснуті UPC-E0 та UPC-E1, а також додаткові 2- та 5-значні розширення UPC; і підмножини 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 очікує дванадцять цифр і обчислює тринадцяту як контрольну; EAN-8 очікує сім і обчислює восьму; UPC-A приймає дванадцять. Передайте символіці дані, які вона не може представити, і ви не отримаєте корисного винятку, ви отримаєте штрих-код, який кодує сміття, що ще гірше, тому що він виглядає нормально, поки хтось не відсканує його на касі

Два шляхи малювання, два рівні контролю

Методом, до якого варто звертатися в продакшені, є DrawBarcode в об'єкті сторінки. Він приймає символіку, позицію, висоту і один параметр, який має більше значення за решту: MUnit, ширину модуля. Модуль - це ширина найвужчої смуги, атом, кратно якому виражається кожен інший розмір у коді, і тут він вказується в пунктах. Усе, що визначає, чи буде надрукований результат скануватися, зводиться до цього єдиного цілого числа

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 додає контрольну цифру за модулем 10, яку очікує символіка, і для роздрібних кодів ви майже завжди хочете, щоб це було True; вимикайте це лише тоді, коли ваші дані вже містять попередньо обчислену перевірку, інакше ви отримаєте подвоєну цифру. Кольори смуг і фону - це два останні параметри, і спокуса проявити тут креативність є пасткою, яка обговорюється нижче. Зверніть також увагу на початок координат: як і кожен інший виклик малювання в HotPDF, X та Y вимірюються від лівого нижнього кута сторінки в пунктах, причому Y збільшується вгору, та сама конвенція, яку розглядає приклад Hello World

Другий шлях - це DirectDrawBarcode, який приймає дані та обмежувальний прямокутник, X, Y, Width, Height, і масштабує візерунок смуг, щоб заповнити цю ширину. Це зручно для розміщення кодів у сітці, оскільки ви називаєте площу, а метод пристосовує до неї смуги. Ця зручність водночас є його небезпекою. Коли ви задаєте йому ширину, ви більше не встановлюєте розмір модуля; метод ділить увесь дозволений вами простір на таку кількість смуг, якої потребують дані, і найвужча смуга стає тим, що виходить у результаті цього ділення. Запросіть щільний рядок Code 128 всередині занадто вузького блоку, і модулі зменшаться нижче межі, яку може розпізнати будь-який сканер, причому мовчки. Для всього, що має надійно скануватися, віддавайте перевагу DrawBarcode і встановлюйте MUnit усвідомлено. Залиште DirectDrawBarcode для попереднього перегляду та для компонувань, де ви виміряли, що отримані смуги залишаються розбірливими

Ширина модуля є рішенням щодо роздільної здатності

Ось арифметика, яка визначає, чи працюватиме ваша етикетка. Як лазерний сканер, так і камера мають найменший елемент, який вони можуть розрізнити, і вузька смуга має комфортно перевищувати цю межу після друку. Широко цитований мінімум для лінійних кодів загального призначення становить 13 міл (mil) для вузької смуги, що дорівнює близько 0,33 мм, і багато роздрібних та промислових посібників розглядають це як мінімум, а не як ціль. Переведіть це в одиниці PDF: один пункт становить 1/72 дюйма, приблизно 0,353 мм, тому один пункт ширини модуля лежить якраз на цій межі. Саме тому MUnit := 1 є найменшим значенням, якому варто довіряти для коду, призначеного для справжнього сканера, і чому його подвоєння до 2 створює запас, який майже нічого не коштує на етикетці, де є вільне місце

Тепер пов'яжіть це з роздільною здатністю виводу, оскільки модуль також має пережити принтер. На лазерному принтері з роздільною здатністю 300 DPI одна крапка пристрою становить 1/300 дюйма, тому однопунктовий модуль має ширину близько чотирьох крапок. Чотирьох крапок ледь вистачає, щоб відтворити чистий край; розтікання тонера та невелика похибка суміщення "з'їдають" його, і смуга, яка вимірювалася одним пунктом у вашому PDF, друкується товщою або тоншою, ніж дозволяє специфікація. Збільште модуль до 2 пунктів, і ви матимете вісім крапок для роботи, що поглинає цей шум. Правило, яке варто запам'ятати: ширина модуля, яку ви встановлюєте в пунктах, має відповідати цілій, зручній кількості крапок пристрою за вашої реальної роздільної здатності друку, а не тієї роздільної здатності, яку ви хотіли б мати. Код, який бездоганно сканується з екрана і дає збій з принтера на складі, майже завжди провалював цю перевірку

Вільна зона є частиною символу

Найпоширеніша єдина причина, з якої правильно закодований штрих-код не сканується, - це вільна зона (quiet zone), порожнє поле з обох боків від смуг. Сканери використовують цю порожнечу, щоб знайти, де починається і де закінчується код; без неї зчитувач не зможе відрізнити першу смугу від того, що лежить поруч із нею на сторінці. Стандарти є конкретними. Більшість лінійних символік вимагають вільної зони щонайменше в десять разів більшої за ширину модуля з кожного боку, а роздрібні коди UPC та EAN вимагають дев'ять модулів зліва та сім справа. З модулем в один пункт це приблизно десять пунктів, близько сьомої частини дюйма, гарантованого білого простору, що оточує смуги

HotPDF малює смуги і більше нічого. Він не резервує вільну зону для вас, що означає, що це ваша відповідальність і про це легко забути. Сценарій відмови є тонким: ви розміщуєте штрих-код впритул до межі клітинки таблиці або дозволяєте макету сторінки притиснути до нього логотип, і код, який пройшов усі тести на порожній сторінці, перестає скануватися в той момент, коли він потрапляє до реального документа. Закладайте поля явно. Перш ніж викликати DrawBarcode, залиште щонайменше десять ширин модуля вільного простору з обох боків і ставтеся до будь-якої графіки, лінії або тексту, що вторгаються в цю смугу, як до дефекту, а не косметичного рішення

Колір, контраст і рядок для читання людиною

Кольори смуг і фону існують для того, щоб ви могли відповідати палітрі бренду, і це найшвидший спосіб зламати робочий код. Сканери зчитують контраст, класично - за допомогою червоного світла, і вони очікують темних смуг на світлому полі. Чорний на білому - це єдина комбінація, до якої вам слід звертатися без тестування. Темно-синій або темно-зелений на білому можуть пройти; будь-що з низьким контрастом яскравості, і зокрема червоні смуги, які сканер червоного світла бачить як фон, не пройдуть. Якщо дизайнер просить кольорові штрих-коди, чесна відповідь полягає в тому, що смуги залишаються чорними, а колір йде в інше місце на етикетці

Шлях DrawBarcode також може відтворювати текст, зручний для читання людиною, під смугами - цифри, які клерк вводить вручну, коли сканування зазнає невдачі. Цей текст є резервним варіантом, а не прикрасою, тому, коли ви розміщуєте власний підпис, тримайте його подалі від вільної зони; мітка символіки, втиснута в бічне поле, зводить нанівець той самий порожній простір, від якого залежить сканер. Поля в прикладі тут, включно з TextOut для будь-яких навколишніх міток, є тими самими викликами малювання, які розглядаються в посібнику з виведення звітів, до якого варто звернутися, коли штрих-код є одним із елементів на більшій скомпонованій сторінці

Коротка звичка до перевірки

Векторні смуги є перевагою, яку варто назвати. Оскільки DrawBarcode записує код як оператори малювання PDF, а не растрове зображення, смуги залишаються чіткими за будь-якого масштабу, а файл не має власної роздільної здатності; єдина роздільна здатність, яка має значення - це роздільна здатність принтера. Це не звільняє вас від тестування, це лише означає, що тест має проходити на папері. Згенеруйте зразок, роздрукуйте його на пристрої з найнижчою роздільною здатністю, з якою фактично зустрінуться ваші коди, і відскануйте його за допомогою зчитувача того ж класу, який тримають ваші користувачі, а не висококласного іміджера на вашому столі. Перевірте вільні зони лінійкою на роздруківці, переконайтеся, що ширина модуля пережила подорож від пунктів до крапок, і перевірте, чи відповідає розкодоване значення тому, що ви закодували, включно з контрольною цифрою. П'ять хвилин зі справжнім сканером виявляють кожну описану вище помилку, і це відбувається до того, як її виявить палета неправильно маркованого товару

Методи DrawBarcode та DirectDrawBarcode, показані тут, є частиною HotPDF Component для Delphi та C++Builder