Technical Article

Barcodes in PDF mit Delphi: QR, PDF417, DataMatrix

Ein Barcode auf einem Versandetikett oder einer Rechnung hat eine einzige Aufgabe: Er muss beim ersten Durchlauf von einem Scanner gelesen werden. Ob er diesen Durchlauf überlebt, entscheidet sich lange bevor das Paket die Laderampe erreicht. Es entscheidet sich daran, wie das Symbol auf die Seite gebracht wurde. Der häufigste Fehler in einer Delphi-Berichtspipeline besteht darin, den Barcode an anderer Stelle als Bitmap zu rendern und dieses Bild in das PDF einzufügen. Es sieht auf dem Bildschirm bei einer Zoomstufe gut aus und verschlechtert sich an allen anderen Stellen

Die Alternative besteht darin, das Symbol direkt als Vektorinhalt auf die Seite zu zeichnen. PDFlibPas stellt hierfür eine Reihe von Zeichenaufrufen bereit, die die 2D-Matrix-Symbole QR, PDF417 und DataMatrix, die linearen Familien über Code128 und GS1-128 sowie USPS Intelligent Mail für die Postautomatisierung abdecken. Das Argument für Vektoren ist kein ästhetisches. Es geht darum, ob die Striche dort landen, wo der Scanner sie erwartet

Warum Vektoren eine platzierte Bitmap schlagen

Ein Barcode ist ein Muster aus Strichen und Lücken oder in zwei Dimensionen ein Raster aus dunklen und hellen Modulen. Der Decoder arbeitet durch Messung des Verhältnisses dieser Breiten. Alles, was die Verhältnisse verzerrt, ist Rauschen, das das Fehlerbudget des Symbols belastet. Ein gerastertes Barcode-Bild enthält feste Pixel. Wenn das PDF auf einem Drucker gerendert wird, dessen Punkte sich nicht gleichmäßig in das Bildraster aufteilen lassen, muss der Rasterisierer ein Resampling durchführen, und Modulkanten, die scharf sein sollten, werden über zwei Gerätepixel verteilt. Ein schmaler Strich kann sich verbreitern, eine benachbarte Lücke kann sich verengen, und das Breitenverhältnis, auf das sich der Decoder verlässt, driftet ab

Als Vektorinhalt gezeichnet ist dasselbe Symbol eine Gruppe gefüllter Rechtecke, die in PDF-Benutzerkoordinaten beschrieben sind. Es gibt kein festes Pixelraster, gegen das man kämpfen muss. Beim Drucken rendert das Gerät jedes Rechteck in der tatsächlich vorhandenen Auflösung, sodass jede Modulkante so scharf ist, wie es die Hardware zulässt - bei jedem Maßstab und jeder Druckgröße. Skalieren Sie ein Vektorsymbol für ein Palettenetikett nach oben oder verkleinern Sie es für ein Paket, die Geometrie bleibt exakt. Diese Präzision hält die Leserate beim ersten Versuch hoch, was der eigentliche Zweck eines Barcodes auf der Seite ist

QR-Codes und die vier Korrekturstufen

QR ist ein 2D-Matrix-Symbol, das in beiden Achsen gleichzeitig gelesen wird, weshalb es viele Daten in einem kleinen Quadrat komprimiert. Seine Toleranz gegenüber Beschädigungen beruht auf der Reed-Solomon-Fehlerkorrektur, die in vier Stufen angeboten wird. Stufe L stellt etwa 7 Prozent der Codewörter wieder her, M etwa 15 Prozent, Q etwa 25 Prozent und H etwa 30 Prozent. Eine höhere Korrektur ist nicht kostenlos. Die Wiederherstellungscodewörter belegen Modulkapazität, sodass eine höhere Stufe bei einer festen Datenmenge ein dichteres oder physisch größeres Symbol erzwingt

Der Kompromiss ist eine Frage der Umgebung, in der das Symbol existiert. Ein sauberes digitales Dokument, das jemals nur von einem Bildschirm gescannt wird, kann auf L verbleiben und kompakt sein. Ein Etikett, das gedruckt, gehandhabt, abgenutzt und möglicherweise teilweise mit Klebeband abgedeckt wird, erfordert Q oder H, da die zusätzliche Redundanz es einem Decoder ermöglicht, die Nutzdaten aus einem nicht mehr makellosen Symbol zu rekonstruieren. DrawQRCode benötigt die Position und eine SymbolSize, die die gezeichnete Breite und Höhe festlegt, plus einen EncodeOptions-Wert, der den Datenmodus auswählt (0 für automatisch, oder numerische, alphanumerische, ISO-8859-1 und UTF-8-Varianten), sowie einen DrawOptions-Wert für die Ausrichtung

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;

Die Korrekturstufe selbst wird vom Encoder gewählt, um die Daten in das von Ihnen angeforderte Symbol einzupassen. Wenn Sie eine garantierte hohe Stufe für eine raue Umgebung benötigen, dimensionieren Sie das Symbol großzügig, damit der Encoder das Modulbudget für Redundanz ausgeben kann, anstatt gezwungen zu sein, es anzupassen

PDF417 für Ausweise und Versandetiketten

PDF417 ist ein gestapeltes lineares Symbol. Jede Zeile ist ein kurzer linearer Barcode, und die Zeilen stapeln sich zu einem Block. Deshalb erscheint es auf Führerscheinen, Bordkarten und Versandetiketten von Transportunternehmen, wo ein breiterer Datenstreifen in einer rechteckigen Grundfläche liegen muss. Seine Fehlerkorrektur reicht auf einer Skala von 0 bis 8. Jeder Schritt verdoppelt in etwa die Anzahl der Korrekturcodewörter, sodass Stufe 5 weitaus mehr Redundanz trägt als Stufe 1, auf Kosten von mehr Codewörtern auf der Seite

Die Form eines PDF417-Blocks ist anpassbar, und das ist wichtig, da das Etikett eine feste Fläche zum Ausfüllen hat. DrawPDF417SymbolEx macht Steuerungsoptionen zugänglich, die der einfache Aufruf nicht bietet. FixedColumns und FixedRows legen die Datenspaltenanzahl und Zeilenanzahl fest, wobei 0 bedeutet, dass der Encoder entscheidet. ErrorLevel nimmt -1 für automatisch oder einen expliziten Wert von 0 bis 8 entgegen. ModuleSize ist die Breite des schmalsten Elements in der aktuellen Maßeinheit, und HeightWidthRatio legt fest, wie hoch jedes Modul im Verhältnis zu seiner Breite ist. So machen Sie den Block kurz und breit oder hoch und schmal, passend zum verfügbaren Platz

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

Das Festlegen von Spalten ist der übliche Hebel bei einer Etikettenvorlage. Eine konstante Spaltenanzahl verleiht dem Block eine vorhersagbare Breite, sodass sich das umgebende Layout nicht verschiebt, wenn die codierte Nutzlast von einem Dokument zum nächsten ihre Länge ändert, während der Encoder Zeilen nach unten hinzugibt, um die Differenz auszugeben

DataMatrix für kleine Markierungen

DataMatrix ist das Symbol, nach dem man greift, wenn die Markierung klein sein muss. Es ist ein kompaktes 2D-Raster, das ECC 200 (das moderne Reed-Solomon-Schema) verwendet, und es bleibt bei Größen lesbar, bei denen ein QR-Symbol für dieselben Daten unhandlich wäre. Dies macht es zur Standardwahl für die direkte Teilekennzeichnung, kleine elektronische Bauteile und dichte Logistiketiketten

DrawDataMatrixSymbol benötigt eine ModuleSize für den Punktabstand (Pitch), ein Encoding von 1 für ASCII und eine SymbolSize, die entweder 0 für automatisch oder eine der Standard-Quadrat- und Rechteckabmessungen von 10x10 bis 132x132 ist. Der Parameter Options kombiniert die Ausrichtung mit der Breite der Ruhezone, wobei das Hinzufügen von 100 bis 400 einen weißen Rand von einem bis vier Modulen festlegt. Die Ruhezone ist keine Dekoration. Ein Decoder benötigt diesen freien Rand, um das Suchermuster des Symbols zu finden, und ein an andere Tinte gedrängtes Symbol ist ein Symbol, dessen Erfassung fehlschlägt

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

Wo 1D-Barcodes immer noch regieren

Zweidimensionale Symbole erhalten die meiste Aufmerksamkeit, aber lineare Barcodes beherrschen immer noch große Teile des Einzelhandels und der Logistik, und der Grund dafür ist die installierte Basis von Laserscannern, die einen einzigen Durchlauf lesen. Code128 ist das Arbeitstier für alphanumerische Daten, und seine Effizienz ergibt sich aus drei Zeichensätzen. Satz A deckt Steuerzeichen und Großbuchstaben ab, Satz B deckt den gesamten druckbaren ASCII-Bereich ab, und Satz C ist derjenige, der für Zahlen wichtig ist. Untersatz C codiert ein Ziffernpaar in einem einzigen Symbolzeichen, sodass eine Reihe numerischer Daten die Hälfte der Symbolzeichen beansprucht, die sie in Satz A oder B benötigen würde. Dies ist der kompakteste Weg, einen langen numerischen Barcode darzustellen, und die PDFlibPas-Code128-Implementierung kombiniert die Sätze B und C automatisch, um dies zu erreichen

GS1-128, der ehemals als EAN-128 bezeichnete Standard, baut auf Code128 auf, indem er Datenbezeichner (Application Identifiers) trägt - die in Klammern gesetzten Präfixe, die einem empfangenden System mitteilen, ob die folgenden Ziffern eine Seriennummer, eine Chargennummer oder ein Ablaufdatum sind. Die Struktur ist durch FNC1 gekennzeichnet, ein spezielles Steuerzeichen (Non-Data Character), das das Symbol als GS1-codiert kennzeichnet und Felder variabler Länge trennt. In PDFlibPas zeichnen Sie ein GS1-128-Symbol mit DrawBarcode unter Verwendung des Typs Code128 und des literalen [FNC1]-Markers, der im Datenstring an der Stelle platziert wird, an der jeder Datenbezeichner beginnt

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;

Für den Postversand codiert USPS Intelligent Mail, auch OneCode genannt, Routing- und Trackingdaten in einem einzigen höhenmodulierten Barcode für die Postautomatisierung. DrawIntelligentMailBarcode benötigt eine explizite Geometrie für die Strichbreite, die volle Strichhöhe, die Tracker-Höhe und die Lückenbreite, wobei die Daten als 20-, 25-, 29- oder 31-stelliger String, der nur Ziffern enthält, bereitgestellt werden. Die expliziten Strich- und Tracker-Höhen existieren, weil das Symbol Informationen darüber trägt, ob jeder Strich ein voller Strich, eine Oberlänge oder eine Unterlänge ist, und das Postlesegerät darauf angewiesen ist, dass diese Höhen den Spezifikationen entsprechen

Zeichnen auf der Seite und Messen für das Layout

Jeder hier gezeigte Aufruf zeichnet in den Inhalt der aktuell ausgewählten Seite, dieselbe Oberfläche, die auch Ihren Text und Ihre Bilder aufnimmt. So wird ein Barcode als Teil der normalen Dokumentengenerierung erzeugt und nicht als separates Asset importiert. Da die Symbole Vektorinhalte sind, sind sowohl die von ihnen codierten Daten als auch die von ihnen eingenommene Geometrie zum Zeitpunkt des Zeichnens bekannt, wodurch Sie sie deterministisch platzieren können

Das Layout für die linearen Familien profitiert von einer vorherigen Messung. GetBarcodeWidth gibt die gesamte gezeichnete Breite eines Barcodes für eine gegebene schmale Strichbreite und einen Barcodetyp zurück, sodass Sie den genauen horizontalen Platz reservieren können, bevor Sie das Zeichnen ausführen, anstatt zu raten und nach dem Erstellen der Seite eine Überschneidung festzustellen. Die 2D-Symbole sind einfacher zu platzieren, da Sie ihre gezeichnete Größe direkt über SymbolSize oder ModuleSize festlegen und das Symbol diesen Platz ausfüllt. In jedem Fall ist die Disziplin dieselbe. Bestimmen Sie die physische Größe anhand der Scan-Umgebung, vergewissern Sie sich, dass das Symbol in den vorhandenen Platz passt, und lassen Sie die Vektorgeometrie jede Kante von der Bildschirmvorschau bis zum endgültigen Druck scharf halten

Für den breiteren Workflow der Seitenerstellung, in den sich diese Barcodes einfügen, decken die Techniken in our article on text, image, and font extraction das Auslesen von Inhalten aus einer PDF-Datei ab, und the guide to large PDF merge and split with direct access zeigt, wie hochvolumige Dokumente effizient zusammengestellt werden. Beide lassen sich hervorragend mit der hier beschriebenen Zeichen-API kombinieren, die als Teil der Delphi PDF Library für Delphi und C++Builder zusammen mit den an anderer Stelle auf diesem Blog behandelten Text-, Grafik-, Formular- und Signatur-APIs ausgeliefert wird