Technical Article

Codici a barre in PDF con Delphi: QR, PDF417, DataMatrix

Un codice a barre su un'etichetta di spedizione o una fattura ha un solo scopo: essere letto da uno scanner al primo tentativo. Se supererà questa prova viene stabilito molto prima che il pacco raggiunga il punto di carico. Dipende da come il simbolo è stato inserito nella pagina. L'errore più comune in una pipeline di reportistica Delphi consiste nel generare il codice a barre come bitmap esterna e inserire tale immagine nel PDF. Appare corretto sullo schermo a un determinato livello di zoom e perde di definizione in tutte le altre situazioni.

L'alternativa consiste nel disegnare il simbolo come contenuto vettoriale, direttamente sulla pagina. PDFlibPas offre una serie di chiamate di disegno dedicate, che coprono i simboli a matrice 2D QR, PDF417 e DataMatrix, le famiglie lineari Code128 e GS1-128, e USPS Intelligent Mail per l'automazione postale. La scelta del vettoriale non è estetica: garantisce che le barre siano posizionate esattamente dove lo scanner le attende.

Perché il vettoriale è superiore a una bitmap inserita

Un codice a barre è una sequenza di barre e spazi o, in due dimensioni, una griglia di moduli chiari e scuri. Il decodificatore funziona misurando il rapporto tra queste larghezze. Qualsiasi elemento che alteri questi rapporti rappresenta del rumore che riduce i margini di tolleranza all'errore del simbolo. Un'immagine rasterizzata di un codice a barre contiene pixel fissi. Quando PDF viene inviato a una stampante i cui punti non si dividono equamente nella griglia dell'immagine, il rasterizzatore deve rieseguire il campionamento, e i bordi dei moduli che dovrebbero essere netti vengono distribuiti su due pixel del dispositivo. Una barra sottile può allargarsi, uno spazio adiacente può restringersi, e il rapporto di larghezza su cui si basa il decodificatore ne risulterà alterato.

Disegnato come contenuto vettoriale, lo stesso simbolo è un insieme di rettangoli pieni descritti in coordinate dello spazio utente PDF. Non vi è alcuna griglia di pixel fissa con cui confrontarsi. Al momento della stampa il dispositivo traccia ogni rettangolo alla risoluzione effettiva, mantenendo i bordi dei moduli definiti in base alle capacità dell'hardware, a qualsiasi scala e dimensione di stampa. Ridimensionare un simbolo vettoriale per un'etichetta o ridurlo per un pacco mantiene la geometria esatta. Questa precisione garantisce un'elevata percentuale di lettura al primo tentativo, che è lo scopo principale dell'inserimento del codice a barre sulla pagina.

I codici QR e i quattro livelli di correzione

Il QR è un simbolo a matrice 2D letto su entrambi gli assi contemporaneamente, motivo per cui racchiude molti dati in un piccolo quadrato. La sua tolleranza ai danni deriva dalla correzione d'errore Reed-Solomon, disponibile in quattro livelli. Il livello L recupera circa il 7 percento dei codici, il livello M circa il 15 percento, il livello Q circa il 25 percento e il livello H circa il 30 percento. Una maggiore correzione ha un costo: i codici di recupero occupano spazio nei moduli, quindi a parità di dati un livello superiore richiede un simbolo più denso o fisicamente più grande.

La scelta dipende dall'ambiente in cui il simbolo verrà utilizzato. Un documento digitale pulito che verrà letto solo da uno schermo può utilizzare il livello L rimanendo compatto. Un'etichetta che verrà stampata, maneggiata, usurata o parzialmente coperta da nastro adesivo richiede i livelli Q o H, poiché la ridondanza aggiuntiva consente al decodificatore di ricostruire le informazioni da un simbolo non più perfetto. DrawQRCode accetta la posizione e un parametro SymbolSize che definisce la larghezza e l'altezza del disegno, oltre a un valore EncodeOptions che seleziona la modalità dei dati (0 per automatico, o varianti numeriche, alfanumeriche, ISO-8859-1 e UTF-8) e un valore DrawOptions per l'orientamento.

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;

Il livello di correzione stesso viene scelto dall'encoder per adattare i dati al simbolo richiesto. Se si necessita di un livello elevato garantito per ambienti difficili, dimensiona il simbolo in modo generoso in modo da concedere all'encoder lo spazio necessario per la ridondanza anziché limitarlo per adattarlo a uno spazio ridotto.

PDF417 per documenti di identità ed etichette di spedizione

Il PDF417 è un simbolo lineare sovrapposto. Ogni riga rappresenta un breve codice a barre lineare, e le righe si sovrappongono per formare un blocco, motivo per cui è utilizzato su patenti di guida, carte d'imbarco ed etichette di spedizione in cui una quantità significativa di dati deve risiedere in un ingombro rettangolare. La sua correzione dell'errore è definita su una scala da 0 a 8. Ogni incremento raddoppia il numero di codici di correzione, quindi il livello 5 offre molta più ridondanza rispetto al livello 1, a scapito di un maggior numero di elementi sulla pagina.

La forma di un blocco PDF417 è regolabile, fattore importante poiché l'etichetta ha un'area definita da occupare. La funzione DrawPDF417SymbolEx offre controlli assenti nella chiamata base. FixedColumns e FixedRows definiscono il numero di colonne e righe di dati, con 0 per lasciare la scelta all'encoder. ErrorLevel accetta -1 per la gestione automatica o un valore specifico da 0 a 8. ModuleSize rappresenta la larghezza dell'elemento più sottile nell'unità di misura corrente, e HeightWidthRatio definisce l'altezza di ogni modulo rispetto alla sua larghezza, consentendo di creare un blocco largo e basso o stretto e alto per adattarsi allo spazio disponibile.

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

Definire le colonne è il metodo comune sui modelli di etichette. Un numero costante di colonne garantisce al blocco una larghezza prevedibile, impedendo che il layout circostante si sposti quando la lunghezza del payload cambia da un documento all'altro, mentre l'encoder aggiunge righe verso il basso per gestire le variazioni.

DataMatrix per marcature di piccole dimensioni

Il DataMatrix è il simbolo ideale quando lo spazio di marcatura è ridotto. È una griglia 2D compatta che utilizza lo schema ECC 200 basato su Reed-Solomon, e rimane leggibile a dimensioni in cui un simbolo QR con gli stessi dati risulterebbe inadatto. Questo lo rende la scelta standard per la marcatura diretta dei componenti, piccoli moduli elettronici ed etichette logistiche dense.

La funzione DrawDataMatrixSymbol richiede un parametro ModuleSize per la distanza tra i punti, un parametro Encoding impostato a 1 per ASCII e un parametro SymbolSize che può essere 0 per la gestione automatica o una delle dimensioni standard quadrate e rettangolari, da 10x10 fino a 132x132. Il parametro Options combina l'orientamento con la larghezza della zona di rispetto (quiet zone), in cui l'aggiunta di valori da 100 a 400 definisce un bordo bianco da uno a quattro moduli. La zona di rispetto non è un elemento decorativo: il decodificatore necessita di questo margine chiaro per individuare il tracciato di riferimento del simbolo, e un codice posizionato a ridosso di altri elementi grafici risulterà illeggibile.

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

Dove i codici a barre 1D mantengono la loro importanza

I simboli bidimensionali attirano l'attenzione, ma i codici a barre lineari dominano ancora ampie aree della vendita al dettaglio e della logistica, a causa dei lettori laser installati basati su una singola scansione. Il Code128 è la scelta standard per i dati alfanumerici, e la sua efficienza deriva da tre set di caratteri. Il set A comprende i caratteri di controllo e le lettere maiuscole, il set B copre l'intervallo ASCII stampabile e il set C è dedicato ai numeri. Il sottoinsieme C codifica una coppia di cifre in un singolo carattere del simbolo, quindi una sequenza numerica occupa la metà dello spazio rispetto ai set A o B. Questo rappresenta il modo più compatto per definire un codice a barre numerico lungo, e l'implementazione Code128 di PDFlibPas combina automaticamente i set B e C per ottenerlo.

Lo standard GS1-128, in precedenza denominato EAN-128, si basa su Code128 includendo gli Identificatori dell'Applicazione (Application Identifiers), i prefissi tra parentesi che indicano se le cifre successive rappresentino un numero di serie, un codice di lotto o una data di scadenza. La struttura è contrassegnata da FNC1, un carattere speciale non legato ai dati che indica che il simbolo è codificato GS1 e separa i campi a lunghezza variabile. In PDFlibPas, si disegna un simbolo GS1-128 tramite DrawBarcode utilizzando il tipo Code128 e inserendo l'indicatore letterale [FNC1] nella stringa dei dati all'inizio di ogni identificatore.

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;

Per il settore postale, USPS Intelligent Mail, chiamato anche OneCode, codifica i dati di instradamento e tracciamento in un singolo codice a barre modulato in altezza per l'automazione. La funzione DrawIntelligentMailBarcode richiede la geometria specifica per la larghezza delle barre, l'altezza complessiva, l'altezza del tracker e la larghezza degli spazi, con i dati forniti come stringa composta da sole cifre di lunghezza pari a 20, 25, 29 o 31 caratteri. Le altezze specifiche della barra e del tracker sono necessarie poiché il simbolo contiene informazioni basate sul fatto che ciascuna barra sia completa, ascendente o discendente, e i lettori postali richiedono che tali altezze rispettino le specifiche.

Disegnare sulla pagina e misurare per il layout

Ogni chiamata illustrata disegna direttamente nel contenuto della pagina selezionata, la medesima superficie che accoglie testi e immagini; in questo modo il codice a barre viene generato durante la creazione del documento anziché essere importato come elemento esterno. Poiché si tratta di elementi vettoriali, i dati codificati e la geometria occupata sono noti al momento del disegno, consentendo un posizionamento preciso.

Il layout delle famiglie lineari beneficia di una misurazione preliminare. La funzione GetBarcodeWidth restituisce la larghezza totale occupata dal codice a barre per una data larghezza della barra più stretta e un tipo di codice, consentendo di riservare lo spazio orizzontale esatto prima di procedere con il disegno, evitando approssimazioni e sovrapposizioni a pagina creata. I simboli 2D sono più semplici da posizionare poiché la dimensione del disegno viene configurata direttamente tramite SymbolSize o ModuleSize, e il simbolo occupa tale spazio. In ogni caso, la regola è la stessa: stabilisci la dimensione fisica in base all'ambiente di scansione, verifica che il simbolo rientri nello spazio assegnato e lascia che la grafica vettoriale mantenga nitido ogni bordo dall'anteprima alla stampa.

Per il flusso di lavoro di creazione delle pagine in cui si inseriscono questi codici a barre, le tecniche descritte nel nostro articolo sull'estrazione di testo, immagini e font illustrano come rileggere i contenuti da un PDF, e la guida all'unione e divisione di PDF di grandi dimensioni con accesso diretto mostra come assemblare documenti ad alto volume in modo efficiente. Entrambi si integrano con le API di disegno descritte in questa pagina, incluse all'interno di Delphi PDF Library per Delphi e C++Builder insieme alle API per testo, grafica, moduli e firme descritte in altre sezioni del blog.