Technical Article

Vonalkódok PDF-ben Delphi segítségével: QR, PDF417, DataMatrix

A szállítási címkén vagy számlán lévő vonalkódnak egyetlen feladata van: az első próbálkozásra leolvassa a szkenner. Az, hogy túléli-e ezt a próbát, jóval azelőtt eldől, hogy a csomag elérné a rakodópartot. Az dönti el, hogyan helyezték el a szimbólumot az oldalon. A leggyakoribb hiba a Delphi jelentéskészítő láncokban az, hogy a vonalkódot máshol bitképként (bitmap) renderelik le, és ezt a képet helyezik el a PDF-ben. Képernyőn egy bizonyos nagyítási szinten jól néz ki, de mindenhol máshol leromlik.

Az alternatíva az, hogy a szimbólumot vektoros tartalomként közvetlenül az oldalra rajzoljuk. A PDFlibPas rajzolási hívások családját teszi közzé pontosan erre, lefedve a 2D mátrix szimbólumokat (QR, PDF417 és DataMatrix), a lineáris családokat (Code128 és GS1-128), valamint a postai automatizáláshoz használt USPS Intelligent Mail-t. A vektor melletti érvelés nem esztétikai kérdés. Arról szól, hogy a sávok oda kerülnek-e, ahová a szkenner várja őket.

Miért veri a vektor a beillesztett bitképet

A vonalkód sötét sávok és világosközök mintázata, két dimenzióban pedig sötét és világos modulok rácsa. A dekóder ezen szélességek arányának mérésével működik. Bármi, ami torzítja az arányokat, olyan zaj, amely felemészti a szimbólum hibakereső keretét. A raszterizált vonalkódkép rögzített pixeleket hordoz. Amikor a PDF-et olyan nyomtatóra renderelik, amelynek pontjai nem oszlanak el egyenletesen a képrácson, a raszterezőnek újra kell mintavételeznie, és a modulok szélei, amelyeknek élesnek kellene lenniük, szétterjednek két eszközpixel között. A vékony sáv megvastagodhat, a szomszédos köz elvékonyodhat, és a dekóder által elvárt szélességi arány eltolódik.

Vektoros tartalomként rajzolva ugyanez a szimbólum a PDF felhasználói koordináta-rendszerében leírt kitöltött téglalapok halmaza. Nincs rögzített képpontrács, amellyel küzdeni kellene. Nyomtatáskor a készülék minden egyes téglalapot a tényleges felbontásában renderel le, így a modulok szélei olyan élesek lesznek, amilyet a hardver csak megenged, bármilyen méretben és nyomtatási méretben. Ha felnagyít egy vektoros szimbólumot egy raklapcímkéhez, vagy lekicsinyíti egy csomaghoz, a geometria pontos marad. Ez a precizitás az, ami magasan tartja az első leolvasás sikerességét, ami az egész értelme a vonalkód elhelyezésének az oldalon.

QR-kódok és a négy hibajavítási szint

A QR egy 2D mátrix szimbólum, amelyet mindkét tengelyen egyszerre olvasnak, ezért csomagol sok adatot egy kis négyzetbe. A sérüléstűrő képessége a Reed-Solomon hibajavításból származik, amely négy szinten érhető el. Az L szint a kódszavak nagyjából 7 százalékát, az M szint mintegy 15 százalékát, a Q szint körülbelül 25 százalékát, a H szint pedig megközelítőleg 30 százalékát képes helyreállítani. A magasabb hibajavítás nincs ingyen. A helyreállító kódszavak elfoglalják a modul kapacitását, így rögzített adatmennyiség esetén a magasabb szint sűrűbb vagy fizikailag nagyobb szimbólumot kényszerít ki.

A kompromisszum a környezetről szól, amelyben a szimbólum élni fog. Egy tiszta digitális dokumentum, amelyet csak képernyőről fognak leolvasni, maradhat L szinten és kompakt lehet. Egy címke, amelyet kinyomtatnak, kezelnek, megkarcolnak és esetleg részben letakarnak ragasztószalaggal, Q vagy H szintet igényel, mert a plusz redundancia teszi lehetővé a dekóder számára a hasznos teher rekonstruálását a már nem makulátlan szimbólumból. DrawQRCode átveszi a pozíciót és egy SymbolSize-t, amely rögzíti a rajzolt szélességet és magasságot, plusz egy EncodeOptions értéket, amely kiválasztja az adat módot (0 az automatikushoz, vagy a numerikus, alfanumerikus, ISO-8859-1 és UTF-8 változatokhoz) és egy DrawOptions értéket a tájoláshoz.

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;

A hibajavítási szintet maga a kódoló választja ki, hogy az adatok beleférjenek a kért szimbólumba. Ha garantáltan magas szintre van szüksége zord környezethez, méretezze nagyvonalúan a szimbólumot, hogy a kódolónak legyen elegendő modul-kerete a redundanciára ahelyett, hogy a méret csökkentésére kényszerülne.

PDF417 igazolványokhoz és szállítási címkékhez

A PDF417 egy egymásra rakott (stacked) lineáris szimbólum. Minden sor egy rövid lineáris vonalkód, és a sorok egymásra rakódnak egy blokkot alkotva, ezért jelenik meg a vezetői engedélyeken, beszállókártyákon és a szállítók szállítási címkéin, ahol az adatok szélesebb sávjának kell elhelyezkednie egy téglalap alakú területen. A hibajavítása 0-tól 8-ig terjedő skálán fut. Minden lépés nagyjából megduplázza a javító kódszavak számát, így az 5-ös szint sokkal több redundanciát hordoz, mint az 1-es szint, az oldalon lévő több kódszó árán.

A PDF417 blokk alakja állítható, és ez azért számít, mert a címkének fix kitöltendő területe van. A DrawPDF417SymbolEx közzéteszi azokat a vezérlőket, amelyeket az alaphívás nem. A FixedColumns és FixedRows rögzíti az adatoszlopok és sorok számát, a 0 azt jelenti, hogy a kódoló dönthet. Az ErrorLevel -1-et kap az automatikushoz, vagy egy kifejezett értéket 0-tól 8-ig. A ModuleSize a legkeskenyebb elem szélessége az aktuális mértékegységben, a HeightWidthRatio pedig azt állítja be, hogy a modul milyen magas a szélességéhez képest, így teheti a blokkot alacsonnyá és szélessé, vagy magassá és keskennyé, hogy illeszkedjen a rendelkezésre álló helyhez.

// 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

Az oszlopok rögzítése a szokásos megoldás egy címkesablonon. A konstans oszlopszám kiszámítható szélességet ad a blokknak, így a környező elrendezés nem csúszik el, amikor a kódolt tartalom hossza változik egyik dokumentumról a másikra, miközben a kódoló lefelé adja hozzá a sorokat az eltérés elnyelésére.

DataMatrix a kis jelölésekhez

A DataMatrix az a szimbólum, amelyhez akkor érdemes nyúlni, ha a jelölésnek kicsinek kell lennie. Ez egy kompakt 2D rács, amely az ECC 200-at, a modern Reed-Solomon sémát használja, és olvasható marad olyan méretben is, ahol az azonos adatot tartalmazó QR szimbólum már kényelmetlen lenne. Ez teszi a szabványos választássá közvetlen alkatrész-jelöléshez, kis elektronikai alkatrészekhez és sűrű logisztikai címkékhez.

A DrawDataMatrixSymbol egy ModuleSize-t fogad a ponttávolsághoz (dot pitch), egy Encoding-ot, amely 1 az ASCII-hoz, valamint egy SymbolSize-t, amely vagy 0 az automatikushoz, vagy a szabványos négyzetes és téglalap alakú méretek egyike 10x10-től 132x132-ig. Az Options paraméter a tájolást kombinálja a csendes zóna (quiet zone) szélességével, ahol a 100 és 400 közötti érték hozzáadása egytől négy modulig terjedő fehér szegélyt állít be. A csendes zóna nem díszítés. A dekódernek szüksége van erre a tiszta margóra, hogy megtalálja a szimbólum keresőmintáját, a más tintákhoz nyomott szimbólum pedig olyan szimbólum, amelyet a rendszer nem tud beolvasni.

// 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

Ahol az 1D vonalkódok még mindig uralkodnak

A kétdimenziós szimbólumok kapják a figyelmet, de a lineáris vonalkódok továbbra is uralják a kiskereskedelem és a logisztika nagy részét, az ok pedig a lézerszkennerek telepített bázisa, amelyek egyetlen söpréssel olvasnak. A Code128 az alfanumerikus adatok igáslova, hatékonysága pedig három karakterkészletből származik. Az A készlet a vezérlőkaraktereket és a nagybetűket fedi le, a B készlet a teljes nyomtatható ASCII tartományt, a C készlet pedig a számoknál számít. A C részhalmaz egy számjegypárt kódol egyetlen szimbólumkarakterben, így a numerikus adatok sora feleannyi szimbólumkaraktert vesz igénybe, mint az A vagy B készletben. Ez a legkompaktabb módja egy hosszú numerikus vonalkód elhelyezésének, és a PDFlibPas Code128 megvalósítása automatikusan kombinálja a B és C készleteket ennek eléréséhez.

A GS1-128, a korábban EAN-128 néven ismert szabvány a Code128-ra épül azáltal, hogy alkalmazás-azonosítókat (Application Identifiers) hordoz, a zárójeles előtagokat, amelyek megmondják a fogadó rendszernek, hogy a következő számjegyek sorozatszámot, tétel kódot vagy lejárati dátumot jelölnek-e. A struktúrát az FNC1 jelöli, egy speciális nem-adat karakter, amely GS1-kódolásúnak jelöli a szimbólumot, és elválasztja a változó hosszúságú mezőket. A PDFlibPas-ban a GS1-128 szimbólumot a DrawBarcode segítségével rajzolja meg a Code128 típus és az adatsorban az egyes alkalmazás-azonosítók kezdeténél elhelyezett literális [FNC1] jelölő használatával.

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;

A postánál a USPS Intelligent Mail – OneCode néven is ismert – az útvonal- és követési adatokat egyetlen magasság-modulált vonalkódba kódolja a postai automatizáláshoz. A DrawIntelligentMailBarcode kifejezett geometriát vár a sávszélességre, a teljes sávmagasságra, a követő (tracker) magasságra és a közközti szélességre, az adatokat pedig 20, 25, 29 vagy 31 jegyű, csak számjegyeket tartalmazó karakterláncként kell megadni. A kifejezett sáv- és követőmagasságok azért léteznek, mert a szimbólum információt hordoz abban, hogy az egyes sávok teljes sávok, felnyúlóak (ascender) vagy lenyúlóak (descender), és a postai olvasó függ attól, hogy ezek a magasságok megfeleljenek a specifikációnak.

Rajzolás az oldalra és mérés az elrendezéshez

Az itt bemutatott minden hívás az aktuálisan kiválasztott oldal tartalmába rajzol, ugyanarra a felületre, amely a szöveget és a képeket kapja, így a vonalkód a normál dokumentumgenerálás részeként jön létre ahelyett, hogy külön erőforrásként importálnánk. Mivel a szimbólumok vektoros tartalmak, az általuk kódolt adatok és az általuk elfoglalt geometria egyaránt ismert a rajzoláskor, ami lehetővé teszi a determinisztikus elhelyezésüket.

A lineáris családok elrendezésénél előnyös az előzetes mérés. A GetBarcodeWidth visszaadja a vonalkód teljes rajzolt szélességét egy adott vékony sávszélesség és vonalkód típus esetén, így lefoglalhatja a pontos vízszintes helyet a rajzolás előtt, ahelyett, hogy találgatna, és a lap felépítése után fedezne fel átfedést. A 2D szimbólumokat egyszerűbb elhelyezni, mert a rajzolt méretüket közvetlenül a SymbolSize vagy ModuleSize segítségével állítja be, és a szimbólum kitölti ezt a területet. A fegyelem mindkét esetben ugyanaz. Döntse el a fizikai méretet a beolvasási környezet alapján, ellenőrizze, hogy a szimbólum belefér-e a rendelkezésre álló helyre, és hagyja, hogy a vektoros geometria éles tartsa a széleket a képernyős előnézettől a végső nyomtatásig.

For the broader page-building workflow these barcodes drop into, the techniques in our article on text, image, and font extraction cover reading content back out of a PDF, and the guide to large PDF merge and split with direct access shows how to assemble high-volume documents efficiently. Both pair naturally with the drawing API described here, which ships as part of the Delphi PDF Library for Delphi and C++Builder alongside the text, graphics, form, and signature APIs covered elsewhere on this blog.