O echipă de procesare a dosarelor de daune avea treizeci de ani de arhivă pe hârtie care treceau printr-un scaner cu alimentare din coli. Scanerul scuipa câte un JPEG per pagină într-un folder, numite 0001.jpg, 0002.jpg și așa mai departe. Ce îi trebuia de fapt arhivei era câte un PDF per dosar, cu paginile în ordine, ca un evaluator să poată deschide un singur document în loc să dea clic prin o sută de miniaturi. Acest ultim pas, transformarea unui teanc numerotat de scanări într-un singur PDF ordonat, este treaba de aici
PDFium Component se ocupă direct de el. Dincolo de randare și de extragerea textului, componenta poate construi un PDF de la zero: creează un document gol, adaugă o pagină goală de dimensiunea dorită, așază o imagine pe acea pagină în coordonate din spațiul utilizator, apoi salvează. Toată conducta trăiește pe componenta TPdf, așa că un convertor pe loturi înseamnă o buclă peste nume de fișiere plus un pumn de apeluri
Forma conversiei
Trei lucruri trebuie să se întâmple pentru fiecare scanare. Decideți dimensiunea paginii, plasați imaginea în interiorul paginii lăsând o margine și avansați la pagina următoare. PDFium Component vă dă câte o metodă pentru fiecare: AddPage creează o pagină goală de o anumită dimensiune, AddImage (sau AddPicture, dacă aveți deja un TPicture) desenează imaginea bitmap în pagina curentă, iar PageNumber îi spune componentei ce pagină vizează apelurile de desenare ulterioare
Singurul detaliu care încurcă lumea este sistemul de coordonate. Spațiul utilizator PDF pune originea în colțul din stânga-jos al paginii, cu Y crescând în sus, opusul coordonatelor de ecran spre care dezvoltatorii Delphi se întind din reflex. Perechea X, Y pe care o transmiteți lui AddImage este colțul din stânga-jos al dreptunghiului imaginii, iar Width, Height sunt dimensiunea de plasare în puncte, nu dimensiunea în pixeli a fișierului sursă. Dacă le luați invers, scanările ajung în afara paginii sau răsturnate față de unde vă așteptați
Crearea documentului și a câte unei pagini per scanare
Începeți cu un document gol. CreateDocument alocă un PDF proaspăt și lasă componenta activă, așa că nu există un pas separat de deschidere. De acolo parcurgeți lista de fișiere scanate și, pentru fiecare, adăugați o pagină, o faceți curentă și plasați imaginea. Dimensiunile paginii de aici sunt A4 în puncte (595 × 842 portret), formatul standard de coală pentru corespondența arhivată
procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
PageW = 595.0; // lățimea A4 în puncte
PageH = 842.0; // înălțimea A4 în puncte
Margin = 36.0; // chenar de o jumătate de țol în jurul fiecărei scanări
var
I: Integer;
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument; // nou, gol, deja activ
for I := 0 to Files.Count - 1 do
begin
Pdf.AddPage(I + 1, PageW, PageH); // index de pagină de la 1
Pdf.PageNumber := I + 1; // fă curentă pagina nouă
PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
end;
Pdf.SaveAs(OutputPath);
finally
Pdf.Free;
end;
end;
Fiecare iterație creează o pagină și îi setează imediat PageNumber. Al doilea rând contează: AddPage inserează pagina, dar metodele de desenare acționează asupra paginii care este curentă, așa că setarea lui PageNumber este cea care îndreaptă AddImage spre pagina tocmai făcută. Dacă o săriți, imaginile se îngrămădesc pe orice pagină se întâmpla să fie încărcată înainte
În bucla aceea se ascunde o presupunere: ordinea lui Files. Un scaner numește paginile de la 0001.jpg la 0100.jpg, dar o enumerare de director nu le returnează întotdeauna sortate, iar în clipa în care ajungeți la page9.jpg lângă page10.jpg, o sortare simplă de șiruri pune pagina 10 înaintea paginii 9. Sortați lista explicit înainte de buclă și preferați nume completate cu zerouri la momentul scanării, ca ordinea lexicală să corespundă ordinii paginilor. Succesiunea paginilor este singurul lucru pe care un evaluator îl observă imediat și este cea mai ieftină greșeală de prevenit
Plasarea unei scanări și păstrarea raportului de aspect
O scanare are rareori aceeași formă ca pagina. Dacă o întindeți ca să umple coala, distorsionați textul; dacă o plasați la dimensiunea completă în pixeli, dă pe dinafară. Remediul este să scalați cu cel mai mic dintre cele două rapoarte, potrivirea pe lățime sau pe înălțime, și să centrați ce rămâne. Pentru că originea stă în stânga-jos, centrarea înseamnă împărțirea egală a spațiului rămas și adăugarea lui atât la X, cât și la Y
procedure TArchiveForm.PlaceScan(Pdf: TPdf; const FileName: string;
PageW, PageH, Margin: Double);
var
Pic: TPicture;
AvailW, AvailH, Scale, DrawW, DrawH, X, Y: Double;
begin
Pic := TPicture.Create;
try
Pic.LoadFromFile(FileName); // BMP, JPG, PNG etc. prin unitățile grafice VCL
AvailW := PageW - 2 * Margin;
AvailH := PageH - 2 * Margin;
// Încadrează în margini fără să distorsioneze scanarea.
Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
DrawW := Pic.Width * Scale;
DrawH := Pic.Height * Scale;
// Centrare: spațiul rămas se împarte egal. Y se măsoară de la baza paginii.
X := (PageW - DrawW) / 2;
Y := (PageH - DrawH) / 2;
Pdf.AddImage(FileName, X, Y, DrawW, DrawH);
finally
Pic.Free;
end;
end;
Acest cod încarcă fișierul o dată ca să îi citească dimensiunile în pixeli, calculează o singură scalare uniformă și transmite dreptunghiul de plasare lui AddImage. AddImage acceptă direct o cale de fișier și o dirijează prin aceeași conductă de imagine ca AddPicture, așa că orice format recunoscut de unitățile grafice VCL funcționează fără tratare specială. Dacă aveți deja imaginea decodată într-un TPicture dintr-un panou de previzualizare, apelați AddPicture(Pic, X, Y, DrawW, DrawH) cu același dreptunghi și săriți a doua citire de fișier
Evitarea decodării pentru scanările JPEG
Scanerele emit aproape întotdeauna JPEG. Încărcarea unui JPEG într-un TPicture îl decodează într-o imagine bitmap, iar apoi PDFium îl recodează la salvare, două drumuri dus-întors cu pierderi de care nu aveți nevoie. AddJpegImage încorporează octeții comprimați originali direct în pagină, dintr-un flux, ceea ce este și mai rapid, și mai curat vizual pentru un lot de volum mare
var
Stream: TFileStream;
begin
// ... după AddPage + PageNumber pentru pagina curentă ...
Stream := TFileStream.Create(FileName, fmOpenRead);
try
// Încorporează octeții JPEG ca atare; fără ciclu de decodare/recodare.
Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
finally
Stream.Free;
end;
end;
Tot calculați X, Y, DrawW și DrawH în același fel, pentru că aveți nevoie de dimensiunile în pixeli ca să scalați. Citiți-le din fișier sau printr-o parsare rapidă a antetului, apoi dați fluxul brut lui AddJpegImage. Pentru scanări PNG sau TIFF, drumul AddImage este cel potrivit; păstrați scurtătura JPEG pentru formatul căruia i se aplică de fapt
Etichetarea fiecărei pagini
Scanările arhivate sunt mai ușor de auditat când fiecare pagină poartă numele fișierului ei sursă. AddText desenează un șir la o coordonată din spațiul utilizator, așa că o legendă stă chiar sub imagine. Rețineți axa Y inversată: pentru a pune o etichetă sub scanare, scădeți din marginea de jos a imaginii, nu adunați la ea
// Legendă sub scanare: Y scade spre baza paginii.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
X, Y - 14, clGray);
O ultimă observație despre salvare. SaveAs este o funcție care returnează un Boolean, așa că în codul de producție verificați-i rezultatul în loc să presupuneți că scrierea a reușit; altfel un disc plin sau o cale de ieșire blocată eșuează în tăcere. Odată ce bucla se termină și fișierul este scris, aveți exact ce îi trebuia arhivei: câte un PDF ordonat per dosar, cu paginile scalate ca să încapă, gata de citit în orice vizualizator
Aceleași cărămizi acoperă și lucrări înrudite. Schimbați regula de dimensionare per pagină și obțineți un album foto cu o imagine per coală; păstrați bucla, dar citiți dintr-o sursă TIFF cu mai multe pagini și aveți un convertor de arhivă de faxuri. Dacă vreți imaginea mai largă a construirii programatice de PDF-uri, vedeți crearea documentelor PDF de la zero cu PDFium Component; pentru a randa mai târziu rezultatul înapoi pe ecran, vedeți conversia paginilor PDF în imagini JPEG cu PDFium Component
PDFium Component de la loslab.com include API-urile de creare a documentelor, de randare și de text folosite în toată această serie