Technical Article

Čárové kódy v PDF s Delphi: QR, PDF417, DataMatrix

Čárový kód na přepravním štítku nebo na faktuře má jediný úkol, a to být přečten skenerem hned napoprvé. O tom, zda se to podaří, se rozhoduje dlouho předtím, než balíček dorazí do expedice. Rozhoduje o tom způsob, jakým byl symbol na stránku umístěn. Nejčastější chybou v reportovací pipeline v Delphi je vykreslit čárový kód jako bitmapu na jiném místě a tento obrázek pak vložit do PDF. Při jednom přiblížení to na obrazovce vypadá v pořádku a na všech ostatních úrovních kvalita degraduje.

Alternativou je vykreslit symbol jako vektorový obsah přímo na stránku. PDFlibPas vystavuje sadu kreslicích volání přesně pro tento účel, pokrývající 2D maticové symboly QR, PDF417 a DataMatrix, lineární rodiny jako Code128 a GS1-128 a USPS Intelligent Mail pro poštovní automatizaci. Důvod pro použití vektoru není estetický. Jde o to, zda pruhy skončí přesně tam, kde je skener očekává.

Proč vektor poráží vloženou bitmapu

Čárový kód je vzor pruhů a mezer, nebo ve dvou rozměrech mřížka tmavých a světlých modulů. Dekodér pracuje tak, že měří poměr těchto šířek. Cokoli, co tyto poměry zkresluje, představuje šum, který spotřebovává toleranci chyb symbolu. Rastrovaný obrázek čárového kódu obsahuje pevně dané pixely. Pokud se PDF vykresluje na tiskárně, jejíž body se nedělí rovnoměrně do mřížky obrázku, rastrovací proces musí provést převzorkování (resample) a hrany modulů, které by měly být ostré, se rozptýlí do dvou pixelů zařízení. Úzký pruh se může rozšířit, sousední mezera zúžit a poměr šířky, na který se dekodér spoléhá, se odchýlí. Symbol vyjádřený jako vektorový obsah je sadou vyplněných obdélníků popsaných v uživatelských souřadnicích PDF. Neexistuje žádná pevná mřížka pixelů, se kterou by bylo nutné bojovat. Při tisku zařízení vykreslí každý obdélník ve skutečném rozlišení, které má, takže každá hrana modulu je tak ostrá, jak hardware dovoluje, a to při jakémkoli měřítku a velikosti tisku. Zvětšete vektorový symbol pro paletový štítek nebo jej zmenšete pro balík a geometrie zůstane naprosto přesná. Tato přesnost udržuje vysokou úspěšnost čtení na první pokus, což je celý smysl umístění čárového kódu na stránku.

QR kódy a čtyři úrovně opravy chyb

QR je 2D maticový symbol čtený v obou osách najednou, což je důvod, proč vměstná velké množství dat do malého čtverce. Jeho tolerance vůči poškození pochází z Reed-Solomonovy opravy chyb, která je nabízena ve čtyřech úrovních. Úroveň L obnoví přibližně 7 procent kódových slov, M zhruba 15 procent, Q asi 25 procent a H přibližně 30 procent. Vyšší úroveň opravy není zdarma. Opravná kódová slova zabírají kapacitu modulů, takže při pevném objemu dat si vyšší úroveň vynutí hustší nebo fyzicky větší symbol. Volba je otázkou prostředí, ve kterém bude symbol žít. Čistý digitální dokument, který se bude skenovat pouze z obrazovky, může zůstat na úrovni L a být kompaktní. Štítek, který se bude tisknout, manipulovat, odírat a možná i částečně přelepovat páskou, vyžaduje Q nebo H, protože dodatečná redundance umožňuje dekodéru rekonstruovat data ze symbolu, který již není nedotčený. DrawQRCode přijímá pozici a SymbolSize definující šířku a výšku vykreslení, dále hodnotu EncodeOptions určující režim dat (0 pro automatický, nebo číselné, alfanumerické, ISO-8859-1 a UTF-8 varianty) a hodnotu DrawOptions pro orientaci.

var
  Pdf: TPDFlib;
begin
  Pdf := TPDFlib.Create(nil);
  try
    Pdf.NewDocument;
    Pdf.SetPageSize('A4');
    Pdf.SetMeasurementUnits(1);   // 1 = millimetres
    Pdf.NewPage;

    // 30 mm square QR, automatic encoding, normal orientation
    Pdf.DrawQRCode(20, 20, 30, 'https://www.loslab.com/', 0, 0);

    Pdf.SaveToFile('Label_QR.pdf');
  finally
    Pdf.Free;
  end;
end;

Samotná úroveň opravy je zvolena kodérem tak, aby se data vešla do požadovaného symbolu. Pokud potřebujete garantovanou vysokou úroveň pro náročné prostředí, dimenzujte velikost symbolu velkoryse, aby měl kodér dostatek modulů pro redundanci a nemusel úroveň snižovat, aby se data vešla.

PDF417 pro průkazy totožnosti a přepravní štítky

PDF417 je skládaný lineární symbol. Každý řádek je krátký lineární čárový kód a tyto řádky se skládají nad sebe do bloku. Proto se objevuje na řidičských průkazech, palubních vstupenkách a přepravních štítcích dopravců, kde musí široký pás dat odpovídat obdélníkovému půdorysu. Jeho oprava chyb pracuje na škále od 0 do 8. Každý krok zhruba zdvojnásobí počet opravných kódových slov, takže úroveň 5 nese mnohem více redundance než úroveň 1, ovšem za cenu většího počtu kódových slov na stránce. Tvar bloku PDF417 je nastavitelný, což je důležité, protože štítek má pevně danou plochu k vyplnění. DrawPDF417SymbolEx zpřístupňuje ovládací prvky, které základní volání neobsahuje. FixedColumns a FixedRows pevně určují počet datových sloupců a řádků, přičemž 0 znamená nechat rozhodnutí na kodéru. ErrorLevel přijímá -1 pro automatickou volbu nebo explicitní hodnotu 0 až 8. ModuleSize je šířka nejužšího prvku v aktuální jednotce měření a HeightWidthRatio nastavuje výšku každého modulu vzhledem k jeho šířce. Tímto způsobem můžete blok učinit krátkým a širokým nebo vysokým a úzkým, aby odpovídal prostoru, který máte k dispozici.

// Fixed 10 data columns, automatic rows, error level 5,
// module 0.30 mm wide, rows three times the module width tall
Pdf.DrawPDF417SymbolEx(20, 60, 'PDF417 PAYLOAD 0123456789',
  0,        // Options: 0 = normal orientation
  10,       // FixedColumns
  0,        // FixedRows: 0 = automatic
  5,        // ErrorLevel: 0 to 8
  0.30,     // ModuleSize, in the current measurement unit
  3.0);     // HeightWidthRatio

Pevné určení sloupců je obvyklým nástrojem u šablon štítků. Konstantní počet sloupců dává bloku předvídatelnou šířku, takže okolní rozvržení se neposouvá, když kódovaná data mění délku od jednoho dokumentu k druhému. Kodér pouze přidává řádky směrem dolů, aby rozdíl kompenzoval.

DataMatrix pro malé značky

DataMatrix je symbolem, po kterém byste měli sáhnout, pokud musí být značka malá. Jedná se o kompaktní 2D mřížku využívající ECC 200, moderní Reed-Solomonovo schéma, a zůstává čitelná i při velikostech, kde by symbol QR se stejnými daty byl nepraktický. Díky tomu je standardní volbou pro přímé značení dílů, malé elektronické součástky a husté logistické štítky. DrawDataMatrixSymbol přijímá ModuleSize pro rozteč bodů, Encoding s hodnotou 1 pro ASCII a SymbolSize, která je buď 0 pro automatickou velikost, nebo představuje jeden ze standardních čtvercových či obdélníkových rozměrů od 10x10 do 132x132. Parametr Options kombinuje orientaci s šířkou tiché zóny (quiet zone), kde přičtení 100 až 400 nastavuje bílý okraj o šířce jednoho až čtyř modulů. Tichá zóna není jen na ozdobu. Dekodér potřebuje tento čistý okraj k nalezení vyhledávacího vzoru symbolu. Symbol natlačený na jiný tisk je symbolem, který se nepodaří načíst.

// Auto-sized ASCII DataMatrix, 0.5 mm module, normal orientation
// with a one-module quiet zone (Options 0 + 100)
Pdf.DrawDataMatrixSymbol(20, 110, 0.5, 'DMX-SN-4408812',
  1,        // Encoding: 1 = ASCII
  0,        // SymbolSize: 0 = automatic
  100);     // Options: normal + one-module quiet zone

Kde stále vládnou 1D čárové kódy

Dvourozměrné symboly přitahují pozornost, ale lineární čárové kódy stále ovládají velké oblasti maloobchodu a logistiky. Důvodem je stávající základna laserových skenerů, které čtou jediný paprsek. Code128 je tahounem pro alfanumerická data a jeho efektivita pramení ze tří znakových sad. Sada A pokrývá řídicí znaky a velká písmena, sada B pokrývá plný rozsah tisknutelných znaků ASCII a sada C je tou, na které záleží u čísel. Podmnožina C kóduje dvojici číslic do jediného znaku symbolu, takže řetězec číselných dat zabírá polovinu znaků oproti sadě A nebo B. Jedná se o nejkompaktnější způsob zápisu dlouhého číselného čárového kódu a implementace Code128 v PDFlibPas automaticky kombinuje sady B a C k jeho dosažení. Standard GS1-128, dříve označovaný jako EAN-128, staví na Code128 a přenáší aplikační identifikátory (Application Identifiers) – prefixy v hranatých závorkách, které přijímacímu systému říkají, zda jsou následující číslice sériovým číslem, kódem šarže nebo datem expirace. Struktura je označena FNC1, což je speciální ne-datový znak, který označuje symbol jako kódovaný dle GS1 a odděluje pole s proměnnou délkou. V PDFlibPas vykreslujete symbol GS1-128 pomocí metody DrawBarcode s typem Code128 a doslovnou značkou [FNC1] umístěnou v datovém řetězci na začátku každého aplikačního identifikátoru.

var
  W: Double;
begin
  // Code128, with FNC1 markers this becomes a GS1-128 symbol.
  // AI 21 (serial) = ABC123, AI 20 (variant) = 13
  Pdf.DrawBarcode(20, 150, 60, 18, '[FNC1]21ABC123[FNC1]2013',
    3,        // Barcode: 3 = Code128
    0);       // Options: 0 = default drawing

  // Measure the rendered width for a 0.30 mm narrow bar before laying out
  W := Pdf.GetBarcodeWidth(0.30, '[FNC1]21ABC123[FNC1]2013', 3);
end;

Pro poštu kóduje USPS Intelligent Mail (také nazývaný OneCode) směrovací a sledovací data do jediného výškově modulovaného čárového kódu pro poštovní automatizaci. Metoda DrawIntelligentMailBarcode přijímá explicitní geometrii pro šířku pruhu, plnou výšku pruhu, výšku trasovacího prvku (tracker) a šířku mezery. Data se předávají jako řetězec složený výhradně z 20, 25, 29 nebo 31 číslic. Explicitní výšky pruhů a trasovacích prvků existují proto, že symbol nese informace v tom, zda je každý pruh plným pruhem, horním dotahem (ascender) nebo dolním dotahem (descender). Poštovní čtečka závisí na přesném dodržení těchto výšek podle specifikace.

Kreslení na stránku a měření rozvržení

Každé zde ukázané volání kreslí do obsahu aktuálně vybrané stránky, tedy na stejný povrch, který přijímá váš text a obrázky. Čárový kód se tak vytváří jako součást běžného generování dokumentu a neimportuje se jako samostatný prvek. Vzhledem k tomu, že symboly jsou vektorovým obsahem, data, která kódují, i geometrie, kterou zabírají, jsou v době vykreslování známy. To vám umožňuje umístit je deterministicky. Rozvržení u lineárních rodin těží z předchozího měření. Metoda GetBarcodeWidth vrací celkovou šířku vykreslení čárového kódu pro danou šířku úzkého pruhu a typ kódu. Můžete si tak rezervovat přesný horizontální prostor před zahájením vykreslování, namísto hádání a následného zjišťování překryvu po vytvoření stránky. 2D symboly se umisťují snadněji, protože jejich velikost nastavujete přímo přes SymbolSize nebo ModuleSize a symbol tuto plochu vyplní. V obou případech je disciplína stejná. Rozhodněte o fyzické velikosti na základě prostředí skenování, ověřte, zda se symbol vejde do vyhrazeného prostoru, a nechte vektorovou geometrii udržet každou hranu ostrou od náhledu na obrazovce až po finální tisk. Pro širší proces vytváření stránek, do kterého tyto čárové kódy spadají, popisují techniky v našem článku o extrakci textu, obrázků a písem načítání obsahu zpět z PDF a průvodce slučováním a rozdělováním velkých PDF s přímým přístupem ukazuje, jak efektivně sestavovat velkoobjemové dokumenty. Oba postupy doplňují zde popsané rozhraní API pro kreslení, které je dodáváno jako součást Delphi PDF Library pro Delphi a C++Builder spolu s rozhraními API pro text, grafiku, formuláře a podpisy popsanými jinde na tomto blogu.