Un team di elaborazione dei sinistri ha trascorso trent'anni a far passare file cartacei attraverso uno scanner a foglio singolo. Lo scanner sputava un JPEG per pagina in una cartella, denominato 0001.jpg, 0002.jpg e così via. Ciò di cui l'archivio aveva effettivamente bisogno era un PDF per ogni file di caso, con le pagine in ordine, in modo che un revisore potesse aprire un singolo documento invece di fare clic su un centinaio di miniature di immagini. Quell'ultimo passaggio, trasformare una pila numerata di scansioni in un singolo PDF ordinato, è il lavoro qui in esame
PDFium Component lo gestisce direttamente. Oltre al rendering e all'estrazione del testo, il componente può creare un PDF da zero: creare un documento vuoto, aggiungere una pagina vuota delle dimensioni desiderate, rilasciare un'immagine su quella pagina nelle coordinate dello spazio utente, quindi salvare. L'intera pipeline vive sul componente TPdf, quindi un convertitore batch è un ciclo sui nomi dei file più una manciata di chiamate
La forma della conversione
Per ogni scansione devono accadere tre cose. Decidi le dimensioni della pagina, posizioni l'immagine all'interno della pagina lasciando un margine e avanzi alla pagina successiva. PDFium Component ti offre un metodo per ciascuna: AddPage crea una pagina vuota di una determinata dimensione, AddImage (o AddPicture se possiedi già un TPicture) disegna la bitmap nella pagina corrente e PageNumber dice al componente a quale pagina mirano le successive chiamate di disegno
L'unico dettaglio che fa inciampare le persone è il sistema di coordinate. Lo spazio utente del PDF posiziona l'origine nell'angolo in basso a sinistra della pagina, con la Y che aumenta verso l'alto, l'opposto delle coordinate dello schermo a cui gli sviluppatori Delphi ricorrono per riflesso. La X, Y che passi a AddImage è l'angolo in basso a sinistra del rettangolo dell'immagine e Width, Height sono le dimensioni di posizionamento in punti, non la dimensione in pixel del file di origine. Sbaglia e le tue scansioni finiranno fuori dalla pagina o capovolte rispetto a dove ti aspettavi
Creazione del documento e di una pagina per scansione
Inizia con un documento vuoto. CreateDocument alloca un nuovo PDF e lascia il componente attivo, quindi non c'è nessun passaggio di apertura separato. Da lì percorri l'elenco dei file scansionati e per ognuno aggiungi una pagina, la rendi corrente e posizioni l'immagine. Le dimensioni della pagina qui sono A4 in punti (595 × 842 verticale), il formato foglio standard per la corrispondenza archiviata:
procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
PageW = 595.0; // larghezza A4 in punti
PageH = 842.0; // altezza A4 in punti
Margin = 36.0; // bordo di mezzo pollice attorno a ogni scansione
var
I: Integer;
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument; // nuovo, vuoto, già attivo
for I := 0 to Files.Count - 1 do
begin
Pdf.AddPage(I + 1, PageW, PageH); // indice di pagina basato su 1
Pdf.PageNumber := I + 1; // rende corrente la nuova pagina
PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
end;
Pdf.SaveAs(OutputPath);
finally
Pdf.Free;
end;
end;
Ogni iterazione crea una pagina e ne imposta immediatamente il PageNumber. Quella seconda riga è importante: AddPage inserisce la pagina ma i metodi di disegno agiscono su qualsiasi pagina sia corrente, quindi l'impostazione di PageNumber è ciò che indirizza AddImage alla pagina che hai appena creato. Saltalo e le tue immagini si impileranno su qualsiasi pagina fosse caricata prima
In quel ciclo si nasconde un presupposto: l'ordine dei Files. Uno scanner nomina le pagine da 0001.jpg a 0100.jpg, ma l'enumerazione di una directory non sempre le restituisce ordinate, e nel momento in cui incontri page9.jpg accanto a page10.jpg, un semplice ordinamento di stringhe (string sort) posiziona la pagina 10 prima della pagina 9. Ordina l'elenco esplicitamente prima del ciclo e preferisci i nomi con zeri riempitivi (zero-padded) al momento della scansione in modo che l'ordine lessicale corrisponda all'ordine delle pagine. La sequenza delle pagine è l'unica cosa che un revisore nota immediatamente ed è l'errore più economico da prevenire
Posizionamento di una scansione e mantenimento del suo rapporto di aspetto (aspect ratio)
Una scansione raramente ha la stessa forma della pagina. Se la allunghi per riempire il foglio distorci il testo; se la posizioni alla dimensione in pixel completa, straborda. La soluzione è scalare in base al minore dei due rapporti, adattamento della larghezza o adattamento dell'altezza, e centrare ciò che rimane. Poiché l'origine si trova in basso a sinistra, centrare significa dividere equamente lo spazio rimanente e aggiungerlo sia a X che a 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, ecc. tramite le unità grafiche della VCL
AvailW := PageW - 2 * Margin;
AvailH := PageH - 2 * Margin;
// Adatta all'interno dei margini senza distorcere la scansione.
Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
DrawW := Pic.Width * Scale;
DrawH := Pic.Height * Scale;
// Centra: spazio rimanente diviso uniformemente. Y misurata dal fondo della pagina.
X := (PageW - DrawW) / 2;
Y := (PageH - DrawH) / 2;
Pdf.AddImage(FileName, X, Y, DrawW, DrawH);
finally
Pic.Free;
end;
end;
Questo carica il file una volta per leggere le sue dimensioni in pixel, calcola una singola scala uniforme e passa il rettangolo di posizionamento ad AddImage. AddImage accetta direttamente il percorso di un file e lo instrada attraverso la stessa pipeline di immagini di AddPicture, quindi qualsiasi formato che le unità grafiche della VCL riconoscono funziona senza casi speciali. Se possiedi già l'immagine decodificata in una TPicture da un riquadro di anteprima, chiama AddPicture(Pic, X, Y, DrawW, DrawH) con lo stesso rettangolo e salta la seconda lettura del file
Saltare la decodifica per le scansioni JPEG
Gli scanner emettono quasi sempre JPEG. Il caricamento di un JPEG in una TPicture lo decodifica in una bitmap, quindi PDFium lo ricodifica al momento del salvataggio: due round trip lossy (con perdita) di cui non hai bisogno. AddJpegImage incorpora i byte originali compressi direttamente nella pagina da uno stream, il che è sia più veloce che visivamente più pulito per un batch ad alto volume
var
Stream: TFileStream;
begin
// ... dopo AddPage + PageNumber per la pagina corrente ...
Stream := TFileStream.Create(FileName, fmOpenRead);
try
// Incorpora i byte JPEG così come sono; nessun ciclo decodifica/ricodifica.
Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
finally
Stream.Free;
end;
end;
Calcoli ancora X, Y, DrawW e DrawH allo stesso modo, poiché ti servono le dimensioni in pixel per la scalatura. Leggile dal file o con una rapida analisi dell'intestazione, quindi passa lo stream grezzo (raw stream) a AddJpegImage. Per scansioni PNG o TIFF il percorso AddImage è quello corretto; riserva la scorciatoia JPEG per il formato a cui si applica effettivamente
Etichettare ogni pagina
Le scansioni archiviate sono più facili da controllare quando ogni pagina porta il suo nome di file di origine. AddText disegna una stringa a una coordinata nello spazio utente, in modo che una didascalia si trovi appena sotto l'immagine. Ricorda l'asse Y invertito: per mettere un'etichetta sotto la scansione, sottrai dal bordo inferiore dell'immagine piuttosto che aggiungere a esso:
// Didascalia sotto la scansione: Y diminuisce verso il fondo della pagina.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
X, Y - 14, clGray);
Un ultimo punto sul salvataggio. SaveAs è una funzione che restituisce un booleano (Boolean), quindi nel codice di produzione controlla il suo risultato piuttosto che presumere che la scrittura sia riuscita; altrimenti un disco pieno o un percorso di output bloccato fallisce silenziosamente. Una volta terminato il ciclo e scritto il file, hai esattamente ciò di cui l'archivio aveva bisogno: un PDF ordinato per fascicolo, pagine ridimensionate per adattarsi, pronto da leggere in qualsiasi visualizzatore
Gli stessi blocchi costitutivi coprono lavori correlati. Scambia la regola di ridimensionamento per pagina e otterrai un fotolibro con un'immagine per foglio; mantieni il ciclo ma leggi da un'origine multipagina TIFF e otterrai un convertitore di archivio fax. Se vuoi un quadro più ampio della creazione di PDF a livello di codice, vedi la creazione di documenti PDF da zero con PDFium Component; per renderizzare nuovamente il risultato su schermo in seguito, vedi la conversione di pagine PDF in immagini JPEG con PDFium Component
PDFium Component da loslab.com raggruppa le API di creazione di documenti, rendering e testo utilizzate in questa serie