Technical Article

Stregkoder i PDF med Delphi: QR, PDF417, DataMatrix

En stregkode på en fragtlabel eller en faktura har ét job, hvilket er at blive læst af en scanner i første forsøg. Om den overlever dette forsøg besluttes længe før pakken når frem til rampen. Det besluttes af, hvordan symbolet blev placeret på siden. Den mest almindelige fejl i en Delphi-rapporteringspipeline er at rendere stregkoden som en bitmap et andet sted og placere dette billede i PDF'en. Det ser fint ud på skærmen ved ét zoomniveau, og forringes derefter alle andre steder.

Alternativet er at tegne symbolet som vektorindhold direkte på siden. PDFlibPas eksponerer en familie af tegnekald til netop dette, som dækker 2D-matrixsymbolerne QR, PDF417 og DataMatrix, de lineære familier via Code128 og GS1-128, samt USPS Intelligent Mail til postautomation. Argumentet for vektor er ikke æstetisk. Det handler om, hvorvidt stregerne lander der, hvor scanneren forventer dem.

Hvorfor vektor vinder over en placeret bitmap

En stregkode er et mønster af streger og mellemrum, eller i to dimensioner et gitter af mørke og lyse moduler. Afkoderen fungerer ved at måle forholdet mellem disse bredder. Alt, hvad der forvrænger forholdene, er støj, der spiser af symbolets fejlbudget. Et rasteriseret stregkodebillede bærer faste pixels. Når PDF'en renderes to en printer, hvis punkter ikke deler sig jævnt i billedgitteret, skal rasteriseringen gen-sample, og modulkanter, der burde være skarpe, spredes over to enhedspixels. En smal streg kan blive tykkere, et tilstødende mellemrum kan blive tyndere, og det breddeforhold, som afkoderen stoler på, skrider.

Tegnet som vektorindhold er det samme symbol et sæt udfyldte rektangler beskrevet i PDF-brugerrumskoordinater. Der er intet fast pixelgitter at kæmpe imod. Ved udskrivning renderes hvert rektangel i den opløsning, enheden faktisk har, så hver modulkant bliver så skarp, som hardwaren tillader, i enhver skala og udskriftsstørrelse. Skaler et vektorsymbol op til en pallelabel eller krymp det til en pakke, og geometrien forbliver nøjagtig. Denne præcision er det, der holder læsehastigheden i første forsøg høj, hvilket er hele formålet med at placere en stregkode på siden.

QR-koder og de fire korrektionsniveauer

QR er et 2D-matrixsymbol, der læses i begge akser på én gang, hvilket er grunden til, at det pakker meget data ind i en lille firkant. Dets skadetolerance kommer fra Reed-Solomon-fejlkorrektion, som tilbydes på fire niveauer. Niveau L gendanner cirka 7 procent af kodeordene, M cirka 15 procent, Q cirka 25 procent og H cirka 30 procent. Højere korrektion er ikke gratis. Gendannelseskodeordene optager modulkapacitet, så for en fast mængde data tvinger et højere niveau et tættere eller fysisk større symbol frem.

Afvejningen er et spørgsmål om det miljø, symbolet skal leve i. Et rent digitalt dokument, der kun vil blive scannet fra en skærm, kan ligge på L og forblive kompakt. En label, der skal udskrives, håndteres, slides og måske delvist dækkes af tape, kræver Q eller H, fordi den ekstra redundans er det, der lader en afkoder rekonstruere indholdet fra et symbol, der ikke længere er fejlfrit. DrawQRCode tager positionen og en SymbolSize, der fastsætter den tegnede bredde og højde, plus en EncodeOptions-værdi, der vælger datatilstand (0 for automatisk, eller numeriske, alfanumeriske, ISO-8859-1- og UTF-8-varianter) og en DrawOptions-værdi for orientering.

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;

Selve korrektionsniveauet vælges af koderen for at tilpasse dataene til det symbol, du bad om. Hvis du har brug for et garanteret højt niveau til et barsk miljø, skal du dimensionere symbolet generøst, så koderen har modulbudgettet til at bruge på redundans i stedet for at blive tvunget ned i størrelse for at passe.

PDF417 til id- og forsendelseslabels

PDF417 er et stablet lineært symbol. Hver række er en kort lineær stregkode, og rækkerne stables for at danne en blok, hvilket er grunden til, at den ses på kørekort, boardingpas og transportørers forsendelseslabels, hvor en bredere datastrimmel skal sidde i et rektangulært område. Dens fejlkorrektion kører på en skala fra 0 til 8. Hvert trin fordobler omtrent antallet af korrektionskodeord, så niveau 5 bærer langt mere redundans end niveau 1, på bekostning af flere kodeord på siden.

Formen på en PDF417-blok kan justeres, og det betyder noget, fordi labelen har et fast område, der skal udfyldes. DrawPDF417SymbolEx eksponerer de kontroller, som det grundlæggende kald ikke gør. FixedColumns og FixedRows fastlåser datakolonneantallet og rækkeantallet, hvor 0 betyder, at koderen bestemmer. ErrorLevel tager -1 for automatisk eller en eksplicit 0 til 8. ModuleSize er bredden af det smalleste element i den aktuelle måleenhed, og HeightWidthRatio bestemmer, hvor højt hvert modul er i forhold til dets bredde, hvilket er måden, hvorpå du gør blokken kort og bred eller høj og smal for at matche den plads, du har.

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

Fastlåsning af kolonner er det sædvanlige greb på en skabelon. Et konstant kolonneantal giver blokken en forudsigelig bredde, så det omgivende layout ikke flytter sig, når det kodede indhold ændrer længde fra ét dokument til det næste, mens koderen tilføjer rækker nedad for at optage forskellen.

DataMatrix til små mærker

DataMatrix is symbolet, man skal række ud efter, når mærket skal være lille. Det er et kompakt 2D-gitter, der bruger ECC 200, det moderne Reed-Solomon-skema, og det forbliver læsbart ved størrelser, hvor et QR-symbol med samme data ville være besværligt. Det gør det til standardvalget til direkte delmærkning, små elektroniske komponenter og tætte logistiklabels.

DrawDataMatrixSymbol tager en ModuleSize for punktstørrelsen, en Encoding på 1 for ASCII og en SymbolSize, der enten er 0 for automatisk eller en af de standardiserede kvadratiske og rektangulære dimensioner, fra 10x10 op til 132x132. Parameteren Options kombinerer orientering med bredden af den tomme zone (quiet-zone), hvor tilføjelse af 100 til 400 indstiller en hvid kant på et til fire moduler. Den tomme zone er ikke dekoration. En afkoder har brug for denne klare margen for at finde symbolets søgemønster, og et symbol, der er klemt op mod andet blæk, er et symbol, der ikke kan registreres.

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

Hvor 1D-stregkoder stadig regerer

To-dimensionelle symboler får opmærksomheden, men lineære stregkoder ejer stadig store dele af detailhandel og logistik, og årsagen er den installerede base af laserscannere, der læser et enkelt sweep. Code128 er arbejdshesten til alfanumeriske data, og dens effektivitet kommer fra tre tegnsæt. Sæt A dækker kontroltegn og store bogsaver, sæt B dækker hele det udskrivelige ASCII-område, og sæt C er det, der betyder noget for tal. Undersæt C koder et par cifre i et enkelt symboltegn, så en kørsel af numeriske data tager halvdelen af de symboltegn, den ville i sæt A eller B. Dette er den mest kompakte måde at oprette en lang numerisk stregkode på, og PDFlibPas Code128-implementeringen kombinerer automatisk B- og C-sættene for at opnå det.

GS1-128, standarden der tidligere hed EAN-128, bygger på Code128 ved at bære Application Identifiers, de præfikser i firkantede parenteser, der fortæller et modtagende system, om de efterfølgende cifre er et serienummer, en batchkode eller en udløbsdato. Strukturen er markeret med FNC1, et specielt ikke-datategn, der flager symbolet som GS1-kodet og adskiller felter med variabel længde. I PDFlibPas tegner du et GS1-128-symbol med DrawBarcode ved hjælp af Code128-typen og den bogstavelige [FNC1]-markør placeret i datastrengen, hvor hver applikationsidentifikator begynder.

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;

Til post koder USPS Intelligent Mail, også kaldet OneCode, rute- og sporingsdata i en enkelt højdemoduleret stregkode til postautomation. DrawIntelligentMailBarcode tager eksplicit geometri for stregbredden, den fulde streghøjde, trackerhøjden og mellemrumsbredden, med dataene leveret som en streng på kun 20, 25, 29 eller 31 cifre. De eksplicitte streg- og trackerhøjder findes, fordi symbolet bærer information i, om hver streg er en fuld streg, en ascender eller en descender, og postlæseren afhænger af, at disse højder holdes i henhold til specifikationen.

Tegning på siden og måling til layout

Hvert kald vist her tegner ind i indholdet af den aktuelt valgte side, den samme overflade, der modtager din tekst og dine billeder, så en stregkode produceres som en del af normal dokumentgenerering frem for at blive importeret som et separat aktiv. Fordi symbolerne er vektorindhold, er de data, de koder, og den geometri, de indtager, begge kendt på tegnetidspunktet, hvilket er det, der lader dig placere dem deterministisk.

Layout for de lineære familier har gavn af at måle først. GetBarcodeWidth returnerer den samlede tegnede bredde af en stregkode for en given smal-stregsbredde og stregkodetype, så du kan reservere den nøjagtige horisontale plads, før du udfører tegningen, i stedet for at gætte og opdage et overlap, efter siden er bygget. De todimensionelle symboler er enklere at placere, fordi du indstiller deres tegnede størrelse direkte via SymbolSize eller ModuleSize, og symbolet udfylder dette område. Eten vej er disciplinen den samme. Beslut den fysiske størrelse ud fra scanningsmiljøet, bekræft, at symbolet passer til den plads, du har, og lad vektorgeometrien holde hver kant skarp fra skærmvisning til endelig udskrift.

For den bredere sideopbygningsarbejdsgang, som disse stregkoder falder ind under, dækker teknikkerne i vores artikel om tekst-, billed- og skrifttypeudtrækning læsning af indhold ud af en PDF, og vejledningen til stor PDF-fletning og -opdeling med direkte adgang viser, hvordan man samler store dokumenter effektivt. Begge parres naturligt med det her beskrevne tegne-API, som leveres som en del af Delphi PDF Library til Delphi og C++Builder sammen med API'erne til tekst, grafik, formularer og signaturer, der er dækket andre steder på denne blog.