Technical Article

Coduri de bare în PDF cu Delphi: QR, PDF417, DataMatrix

Un cod de bare de pe o etichetă de expediere sau o factură are o singură sarcină, aceea de a fi citit de un scaner la prima încercare. Dacă supraviețuiește acelei încercări se decide cu mult înainte ca pachetul să ajungă la rampă. Acest lucru este determinat de modul în care simbolul a fost plasat pe pagină. Cea mai frecventă greșeală într-o conductă de raportare Delphi este redarea codului de bare ca o hartă de biți (bitmap) în altă parte și plasarea acelei imagini în PDF. Arată bine pe ecran la un anumit nivel de zoom și apoi se degradează în rest.

Alternativa este desenarea simbolului ca și conținut vectorial, direct în pagină. PDFlibPas expune o familie de apeluri de desenare exact pentru aceasta, acoperind simbolurile matriceale 2D QR, PDF417 și DataMatrix, familiile liniare prin Code128 și GS1-128, precum și USPS Intelligent Mail pentru automatizarea poștală. Argumentul pentru vector nu este unul estetic. Este vorba despre stabilirea faptului dacă barele ajung acolo unde scanerul le așteaptă.

De ce grafica vectorială este mai bună decât o imagine bitmap plasată

Un cod de bare este un model de bare și spații, sau, în două dimensiuni, o grilă de module întunecate și luminoase. Decodorul funcționează prin măsurarea raportului acestor lățimi. Orice lucru care distorsionează rapoartele reprezintă zgomot care consumă din bugetul de eroare al simbolului. O imagine de cod de bare rasterizată conține pixeli ficși. Când PDF-ul este redat pe o imprimantă ale cărei puncte nu se împart uniform în grila imaginii, rasterizatorul trebuie să reeșantioneze, iar marginile modulelor care ar trebui să fie clare se extind pe doi pixeli ai dispozitivului. O bară îngustă se poate îngroșa, un spațiu adiacent se poate subția, iar raportul de lățime pe care se bazează decodorul deviază.

Desenat ca și conținut vectorial, același simbol este un set de dreptunghiuri umplute descrise în coordonatele spațiului de utilizator PDF. Nu există o grilă fixă de pixeli cu care să vă luptați. La tipărire, dispozitivul redă fiecare dreptunghi la rezoluția pe care o are de fapt, astfel încât fiecare margine de modul este la fel de clară pe cât permite hardware-ul, la orice scară și orice dimensiune de tipărire. Scalați un simbol vectorial în sus pentru o etichetă de palet sau micșorați-l pentru un colet și geometria rămâne exactă. Acea precizie este cea care menține rata de citire la prima încercare ridicată, ceea ce reprezintă întregul scop al plasării unui cod de bare pe pagină.

Codurile QR și cele patru niveluri de corecție

QR este un simbol matriceal 2D citit pe ambele axe simultan, motiv pentru care împachetează o mulțime de date într-un pătrat mic. Toleranța sa la deteriorare provine din corecția erorilor Reed-Solomon, oferită pe patru niveluri. Nivelul L recuperează aproximativ 7 procente din cuvintele de cod (codewords), M aproximativ 15 procente, Q aproximativ 25 de procente și H aproximativ 30 de procente. Corecția mai mare nu este gratuită. Cuvintele de cod de recuperare ocupă capacitatea modulului, astfel încât, pentru o cantitate fixă de date, un nivel mai ridicat forțează un simbol mai dens sau mai mare fizic.

Compromisul este o întrebare despre mediul în care va exista simbolul. Un document digital curat care va fi scanat doar de pe un ecran poate rămâne la nivelul L și să fie compact. O etichetă care va fi tipărită, manipulată, zgâriată și eventual acoperită parțial de bandă adezivă necesită Q sau H, deoarece redundanța suplimentară este cea care permite unui decodor să reconstruiască sarcina utilă dintr-un simbol care nu mai este impecabil. DrawQRCode preia poziția și un SymbolSize care stabilește lățimea și înălțimea desenate, plus o valoare EncodeOptions care selectează modul de date (0 pentru automat, sau variante numerice, alfanumerice, ISO-8859-1 și UTF-8) și o valoare DrawOptions pentru orientare.

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;

Nivelul de corecție în sine este ales de codificator pentru a încadra datele în simbolul pe care l-ați solicitat. Dacă aveți nevoie de un nivel ridicat garantat pentru un mediu dificil, dimensionați simbolul cu generozitate, astfel încât codificatorul să aibă bugetul de module necesar pentru a-l cheltui pe redundanță, în loc să fie forțat să-l reducă pentru a se încadra.

PDF417 pentru ID-uri și etichete de expediere

PDF417 este un simbol liniar stivuit. Fiecare rând este un cod de bare liniar scurt, iar rândurile se stivuiesc pentru a forma un bloc, motiv pentru care apare pe permisele de conducere, permisele de îmbarcare și etichetele de expediere ale transportatorilor unde o bandă mai lată de date trebuie să se încadreze într-o amprentă dreptunghiulară. Corecția sa de eroare rulează pe o scară de la 0 la 8. Fiecare pas dublează aproximativ numărul de cuvinte de cod de corecție, astfel încât nivelul 5 conține mult mai multă redundanță decât nivelul 1, cu prețul a mai multor cuvinte de cod pe pagină.

Forma unui bloc PDF417 este reglabilă, iar acest lucru contează deoarece eticheta are o zonă fixă de completat. DrawPDF417SymbolEx expune controalele pe care apelul de bază nu le oferă. FixedColumns și FixedRows fixează numărul de coloane de date și numărul de rânduri, valoarea 0 însemnând lăsarea codificatorului să decidă. ErrorLevel preia -1 pentru automat sau o valoare explicită de la 0 la 8. ModuleSize este lățimea celui mai îngust element în unitatea de măsură curentă, iar HeightWidthRatio stabilește cât de înalt este fiecare modul în raport cu lățimea sa, mod prin care faceți blocul scurt și lat sau înalt și îngust pentru a se potrivi cu spațiul pe care îl aveți.

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

Fixarea coloanelor este pârghia obișnuită pe un șablon de etichetă. Un număr constant de coloane oferă blocului o lățime previzibilă, astfel încât aspectul din jur nu se deplasează pe măsură ce sarcina utilă codificată își schimbă lungimea de la un document la altul, în timp ce codificatorul adaugă rânduri în jos pentru a absorbi diferența.

DataMatrix pentru marcaje mici

DataMatrix este simbolul la care să apelați atunci când marcajul trebuie să fie mic. Este o grilă 2D compactă care folosește ECC 200, schema modernă Reed-Solomon, și rămâne lizibilă la dimensiuni la care un simbol QR cu aceleași date ar fi incomod. Acest lucru o face alegerea standard pentru marcarea directă a pieselor, componente electronice mici și etichete logistice dense.

DrawDataMatrixSymbol preia un ModuleSize pentru pasul punctelor (dot pitch), un Encoding cu valoarea 1 pentru ASCII și un SymbolSize care este fie 0 pentru automat, fie una dintre dimensiunile standard pătrate și dreptunghiulare, de la 10x10 până la 132x132. Parametrul Options combină orientarea cu lățimea zonei de liniște (quiet-zone), unde adăugarea de valori de la 100 la 400 setează o margine albă de la unul până la patru module. Zona de liniște nu este o decorațiune. Un decodor are nevoie de acea margine liberă pentru a găsi modelul de detectare al simbolului, iar un simbol înghesuit lângă altă cerneală este un simbol care nu reușește să fie citit.

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

Unde codurile de bare 1D încă domină

Simbolurile bidimensionale atrag atenția, dar codurile de bare liniare dețin încă o mare parte din retail și logistică, iar motivul este baza instalată de scanere laser care citesc o singură trecere. Code128 este calul de bătaie pentru datele alfanumerice, iar eficiența sa provine din trei seturi de caractere. Setul A acoperă caracterele de control și majusculele, setul B acoperă întregul interval ASCII imprimabil, iar setul C este cel care contează pentru numere. Subsetul C codifică o pereche de cifre într-un singur caracter simbolic, astfel încât o serie de date numerice ocupă jumătate din caracterele simbolice pe care le-ar ocupa în setul A sau B. Aceasta este cea mai compactă modalitate de a așeza un cod de bare numeric lung, iar implementarea Code128 din PDFlibPas combină automat seturile B și C pentru a o realiza.

GS1-128, standardul numit anterior EAN-128, se bazează pe Code128 purtând identificatori de aplicație (Application Identifiers), prefixele între paranteze care indică unui sistem receptor dacă cifrele următoare sunt un număr de serie, un cod de lot sau o dată de expirare. Structura este marcată de FNC1, un caracter special care nu reprezintă date, care marchează simbolul ca fiind codificat GS1 și separă câmpurile de lungime variabilă. În PDFlibPas desenați un simbol GS1-128 cu DrawBarcode folosind tipul Code128 și marcatorul literal [FNC1] plasat în șirul de date unde începe fiecare identificator de aplicație.

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;

Pentru corespondență, USPS Intelligent Mail, numit și OneCode, codifică datele de rutare și urmărire într-un singur cod de bare cu înălțime modulată pentru automatizarea poștală. DrawIntelligentMailBarcode preia geometria explicită pentru lățimea barei, înălțimea completă a barei, înălțimea trackerului și lățimea spațiului, datele fiind furnizate ca un șir de doar 20, 25, 29 sau 31 de cifre. Înălțimile explicite ale barei și ale trackerului există deoarece simbolul conține informații prin faptul dacă fiecare bară este o bară completă, un ascendent sau un descendent, iar cititorul poștal depinde de menținerea acelor înălțimi conform specificațiilor.

Desenarea în pagină și măsurarea pentru machetare

Fiecare apel prezentat aici desenează în conținutul paginii selectate curent, aceeași suprafață care primește textul și imaginile dvs., astfel încât un cod de bare este produs ca parte a generării normale a documentului, mai degrabă decât importat ca un element separat. Deoarece simbolurile sunt conținut vectorial, datele pe care le codifică și geometria pe care o ocupă sunt ambele cunoscute la momentul desenării, ceea ce vă permite să le plasați în mod determinist.

Aspectul (layout-ul) pentru familiile liniare beneficiază de măsurarea prealabilă. GetBarcodeWidth returnează lățimea totală desenată a unui cod de bare pentru o lățime dată a barei înguste și un tip de cod de bare, astfel încât să puteți rezerva spațiul orizontal exact înainte de a efectua desenarea, în loc să ghiciți și să descoperiți o suprapunere după ce pagina este construită. Simbolurile 2D sunt mai simplu de plasat deoarece le setați dimensiunea desenată direct prin SymbolSize sau ModuleSize, iar simbolul umple acea amprentă. Oricum, disciplina este aceeași. Decideți dimensiunea fizică pe baza mediului de scanare, confirmați că simbolul se încadrează în slotul pe care îl aveți și lăsați geometria vectorială să mențină fiecare margine clară, de la previzualizarea pe ecran la imprimarea finală.

Pentru fluxul mai larg de construire a paginilor în care se încadrează aceste coduri de bare, tehnicile din articolul nostru despre extragerea de text, imagini și fonturi acoperă citirea conținutului înapoi dintr-un PDF, iar ghidul pentru îmbinarea și divizarea PDF-urilor mari cu acces direct arată cum se asamblează eficient documentele cu volum mare. Ambele se potrivesc natural cu API-ul de desenare descris aici, care este livrat ca parte a Delphi PDF Library pentru Delphi și C++Builder, alături de API-urile de text, grafică, formulare și semnătură acoperite în alte părți ale acestui blog.