Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pagine: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Titolo: ' + Pdf1.Title);
ShowMessage('Autore: ' + Pdf1.Author);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile('C:\MyDocument.pdf');
Pdf1.LoadDocument(Stream, True);
Pdf1.Active := True;
ShowMessage('PDF caricato dallo stream');
finally
Stream.Free;
end;
end;
Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;
var
PageText: WString;
RectText: WString;
begin
Pdf1.PageNumber := 1;
PageText := Pdf1.Text;
Memo1.Text := PageText;
RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
ShowMessage('Testo nel rettangolo: ' + RectText);
end;
var
Position: Integer;
begin
Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
while Position >= 0 do
begin
ShowMessage('Trovato al carattere: ' + IntToStr(Position));
Position := Pdf1.FindNext;
end;
end;
Pdf1.PageNumber := 1;
Pdf1.AddText('Titolo del documento', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('Questo è il contenuto del documento.', 'Arial', 12, 100, 700, clBlack);
Pdf1.UpdatePage;
var
I: Integer;
MetricSize: Single;
FontBytes: TBytes;
begin
Pdf1.PageNumber := 1;
for I := 0 to Pdf1.ObjectCount - 1 do
if Pdf1.ObjectType[I] = otText then
begin
MetricSize := 12.0;
FontBytes := Pdf1.FontData[I];
Memo1.Lines.Add('Oggetto ' + IntToStr(I));
Memo1.Lines.Add('Nome base: ' + Pdf1.FontBaseName[I]);
Memo1.Lines.Add('Famiglia: ' + Pdf1.FontFamilyName[I]);
Memo1.Lines.Add('Incorporato: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
Memo1.Lines.Add('Peso: ' + IntToStr(Pdf1.FontWeight[I]));
Memo1.Lines.Add('Angolo italico: ' + IntToStr(Pdf1.FontItalicAngle[I]));
Memo1.Lines.Add('Ascendente: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
Memo1.Lines.Add('Discendente: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
Memo1.Lines.Add('Byte dati del font: ' + IntToStr(Length(FontBytes)));
end;
if Pdf1.CharacterCount > 0 then
begin
Memo1.Lines.Add('Dimensione del primo carattere: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
Memo1.Lines.Add('Peso del primo carattere: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
Memo1.Lines.Add('Angolo del primo carattere: ' + FloatToStr(Pdf1.CharacterAngle[0]));
end;
end;
I progetti Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties e Demo\Lazarus\FontProperties mostrano gli stessi metadati tramite TPdf e TPdfView.
I progetti Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab e Demo\Lazarus\ContentExtractionLab trasformano l'estrazione di contenuti in un unico workflow. Aprono o creano un PDF, permettono agli utenti di scegliere metadati, testo di pagina, oggetti di pagina, immagini, allegati, link, annotazioni, segnalibri e metriche di font / caratteri, quindi generano un riepilogo più un report TXT o JSON dettagliato. Il laboratorio include anche selezione di categoria tutto / niente, copia del report negli appunti e azioni di salvataggio del PDF sorgente affinché gli utenti possano mantenere il PDF di esempio generato accanto al suo report di estrazione. Le demo più vecchie ExtractText, ExtractImages, Attachment e FontProperties restano esempi mirati; ExtractText supporta le opzioni di intervallo di pagine e separatore di pagine per produrre l'output di testo per pagina in Delphi, C++Builder e Lazarus, mentre ContentExtractionLab è l'esempio trasversale di inventario.
var
I: Integer;
FieldInfo: TPdfFormFieldInfo;
begin
Pdf1.FormField[0] := 'John';
for I := 0 to Pdf1.FormFieldCount - 1 do
begin
FieldInfo := Pdf1.FormFieldInfo[I];
ShowMessage(FieldInfo.Name + ': ' + Pdf1.FormField[I]);
end;
end;
// Senza appiattimento, FormField[i] := value scrive solo la voce /V —
// l'aspetto visibile proviene dallo stream /AP e potrebbe non aggiornarsi.
// GenerateFormAppearances rigenera /AP per ogni widget, quindi
// FlattenAllPages consolida annotazioni e widget di ogni pagina in
// contenuti permanenti e non modificabili.
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := 'john@example.com';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;
var
I: Integer;
Bitmap: TBitmap;
begin
for I := 0 to Pdf1.BitmapCount - 1 do
begin
Bitmap := Pdf1.Bitmap[I];
try
Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
finally
Bitmap.Free;
end;
end;
end;
var
JpegStream: TFileStream;
begin
JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
try
Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
finally
JpegStream.Free;
end;
Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
Pdf1.UpdatePage;
end;
var
I: Integer;
Annotation: TPdfAnnotation;
begin
for I := 0 to Pdf1.AnnotationCount - 1 do
begin
Annotation := Pdf1.Annotation[I];
ShowMessage('Annotazione ' + IntToStr(I) + ': ' + Annotation.ContentsText);
end;
end;
Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;
Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;
var
I: Integer;
Bookmarks: TBookmarks;
begin
Bookmarks := Pdf1.Bookmarks;
for I := 0 to Length(Bookmarks) - 1 do
ShowMessage(Bookmarks[I].Title + ' -> Pagina ' + IntToStr(Bookmarks[I].PageNumber));
end;
var
I: Integer;
Destination: TDestination;
begin
for I := 0 to Pdf1.DestinationCount - 1 do
begin
Destination := Pdf1.Destination[I];
if Destination.Name = 'Chapter1' then
begin
Pdf1.PageNumber := Destination.PageNumber;
Break;
end;
end;
end;
var
I: Integer;
AttachmentData: TBytes;
AttachmentName: WString;
FileStream: TFileStream;
begin
for I := 0 to Pdf1.AttachmentCount - 1 do
begin
AttachmentName := Pdf1.AttachmentName[I];
AttachmentData := Pdf1.Attachment[I];
FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
try
if Length(AttachmentData) > 0 then
FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
finally
FileStream.Free;
end;
end;
if Pdf1.CreateAttachment('MyFile.txt') then
Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;
Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);
var
SourcePdf: TPdf;
begin
SourcePdf := TPdf.Create(nil);
try
SourcePdf.FileName := 'C:\SourceDocument.pdf';
SourcePdf.Active := True;
Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
finally
SourcePdf.Free;
end;
end;
if Pdf1.PageCount > 1 then
Pdf1.DeletePage(Pdf1.PageCount);
// Crea un documento PDF compresso
Pdf1.CreateDocument;
Pdf1.Compressed := True; // garantisce che tutti gli stream siano compressi con FlateDecode (default)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // formato A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Salvataggio con opzioni
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // salvataggio incrementale
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // rimozione della sicurezza
// Salvataggio con versione PDF di destinazione specifica. Il validatore di versione PDF rifiuta
// qualunque funzionalità introdotta dopo il target scelto (JBIG2Decode, MarkInfo,
// cifratura AES, annotazioni Polygon / Caret / Watermark / Redact, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Salvataggio rifiutato: il documento contiene funzionalità più recenti di PDF 1.5');
// SaveAsPdfA post-elabora il salvataggio di base con un aggiornamento incrementale che
// inietta uno stream di metadati XMP, un OutputIntent ICC sRGB e un catalogo del
// documento aggiornato, in modo che il risultato sia conforme a PDF/A-1b. Il profilo
// sRGB IEC61966-2.1 incluso viene utilizzato automaticamente se non viene fornito un
// profilo personalizzato.
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
ShowMessage('Archivio PDF/A-1b salvato');
// PDF/A con un profilo ICC personalizzato (ad esempio pipeline di stampa CMYK)
var
Opts: TPdfASaveOptions;
begin
Opts := Default(TPdfASaveOptions);
Opts.Conformance := pac1b;
Opts.IccProfileData := TFile.ReadAllBytes('C:\Profiles\Coated_GRACoL_2006.icc');
Pdf1.SaveAsPdfA('C:\Report.cmyk.pdfa.pdf', Opts);
end;
// Output PDF/A in memoria
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Opts := Default(TPdfASaveOptions);
Opts.Conformance := pac1b;
if Pdf1.SaveAsPdfAToStream(Stream, Opts) then
UploadToArchive(Stream);
finally
Stream.Free;
end;
end;
var
Validation: TPdfAValidationResult;
Issue: TPdfAValidationIssue;
begin
Pdf1.FileName := 'C:\Incoming\report.pdf';
Pdf1.Active := True;
// Sonda economica in stile header — legge la conformità senza iterare
// il validatore completo. Risultato: pacNone, pac1a, pac1b, pac2b, pac3b o
// pacUnknown.
case Pdf1.PdfAConformance of
pac1b: Status.Caption := 'PDF/A-1b';
pac2b: Status.Caption := 'PDF/A-2b';
pacNone: Status.Caption := 'Non PDF/A';
else
Status.Caption := 'Conformità sconosciuta';
end;
// Validatore completo con diagnostica per problema
Validation := Pdf1.ValidatePdfA;
Memo1.Lines.Add('Conformità: ' + GetEnumName(TypeInfo(TPdfAConformance),
Ord(Validation.Conformance)));
for Issue in Validation.Issues do
Memo1.Lines.Add('- ' + Issue.Description);
end;
I progetti Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab e Demo\Lazarus\StandardsLab caricano o creano un PDF, eseguono ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR e ValidatePdfVT, quindi mostrano in un'unica griglia il livello di conformità rilevato e il conteggio dei problemi. I loro pulsanti di salvataggio chiamano SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR e SaveAsPdfVT in modo che il workflow di output con marker sia visibile senza scrivere un harness personalizzato.
L'unità FPdfPreflightReport avvolge ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR e ValidatePdfVT in un unico oggetto report. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport e Demo\CBuilder\PreflightReport permettono agli utenti di scegliere gli standard, generare un riepilogo, ispezionare stato, priorità, categorie di problemi, conteggi per categoria, prossime azioni, codici dei problemi e azioni raccomandate, alternare l'anteprima tra testo, Markdown, JSON e CSV, ed esportare report TXT, HTML, Markdown, JSON o CSV. Il campione GUI Delphi aggiunge anche un'anteprima Action Plan che condensa gli standard non superati, le priorità, i codici dei problemi e le raccomandazioni in una checklist di rimedio. Demo\Delphi\PreflightReportCli espone lo stesso percorso di report come workflow da console con argomenti di input / output, password opzionale, selezione del formato di output, inclusi plan / actionplan, filtro per standard, incorporamento opzionale del report come singolo file attach=output.pdf, codici di uscita per il gating CI tramite failon=, elaborazione batch=list.txt, scansione di directory batchdir=folder con ricorsione opzionale ed elaborazione stabile ordinata per percorso, routing dei report tramite outdir=reports, nomi di report a prova di collisione per nomi file di input duplicati, riepiloghi TXT / JSON / HTML con totali aggregati e per stato dello standard, manifest di riga CSV con impostazioni di esecuzione ripetute per voce e una modalità di esempio senza argomenti per script e job CI. Il report generato indica che i validatori incorporati coprono controlli a livello di marker e selezionati a livello di file, quindi il preflight completo a livello di contenuto può comunque essere delegato a un motore di validazione dedicato.
// LoadCustomDocument consente a PDFium di estrarre blocchi da qualsiasi TStream
// posizionabile su richiesta. Lo stream NON viene copiato in memoria, quindi
// PDF di più gigabyte, stream di body HTTP remoti e sorgenti su database sono
// tutti praticabili senza un buffer iniziale.
var
FileStream: TFileStream;
begin
// Streaming di lettura da file (nessuna copia in memoria)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ proprietà trasferita — Pdf1 libera FileStream su UnloadDocument.
Pdf1.Active := True;
ShowMessage('Pagine: ' + IntToStr(Pdf1.PageCount));
end;
// Stream di proprietà esterna (il chiamante lo libera dopo UnloadDocument)
var
Stream: TFileStream;
begin
Stream := TFileStream.Create('C:\Live.pdf',
fmOpenRead or fmShareDenyNone);
try
Pdf1.LoadCustomDocument(Stream, False);
Pdf1.Active := True;
DoWork;
Pdf1.Active := False;
finally
Stream.Free;
end;
end;
// FPdfAsync espone IPdfCancellationToken /
// IPdfCancellationTokenSource per una semantica di annullamento dall'interfaccia utente
// che funziona sui percorsi di esportazione e rendering di lunga durata.
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... l'utente clicca su Annulla ...
CancelSource.Cancel;
// Il worker controlla periodicamente CancelToken.IsCancelled, oppure il percorso
// di rendering lo interroga tramite la callback IFSDK_PAUSE di PDFium.
if CancelToken.IsCancelled then
Memo1.Lines.Add('Annullato dall''utente');
end;
// RenderPageProgressive consente a un rendering lungo ad alta DPI di interrompersi
// a metà pagina quando l'utente clicca su Annulla, invece di bloccare il chiamante
// fino al completamento.
var
Status: TPdfProgressiveStatus;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.PixelFormat := pf32bit;
Bitmap.SetSize(2480, 3508); // A4 a 300 DPI
Status := Pdf1.RenderPageProgressive(Bitmap, 0, 0, Bitmap.Width,
Bitmap.Height, CancelToken, ro0, [reAnnotations], clWhite);
case Status of
prsDone: SaveBitmap(Bitmap);
prsCancelled: Memo1.Lines.Add('Rendering annullato — risultato parziale disponibile');
prsFailed: Memo1.Lines.Add('PDFium ha riportato FPDF_RENDER_FAILED');
end;
finally
Bitmap.Free;
end;
end;
// TPdfFuture<T> avvia un worker su un thread di sfondo e restituisce un
// envelope TPdfFutureResult<T> al thread principale.
//
// Importante: TPdf serializza le chiamate di rendering per istanza, ma il worker
// possiede comunque il dispatch e il ciclo di vita del documento. Non mutare un TPdf
// mentre un rendering è in corso; l'esportazione batch in genere mantiene un TPdf per file.
type
TRenderResult = record
Bitmap : TBitmap;
PageNo : Integer;
end;
var
Future: TPdfFuture<TRenderResult>;
begin
Future := TPdfFuture<TRenderResult>.Create(
function (const ACancelToken: IPdfCancellationToken): TRenderResult
var
LocalPdf: TPdf;
begin
LocalPdf := TPdf.Create(nil);
try
LocalPdf.FileName := 'C:\Huge.pdf';
LocalPdf.Active := True;
LocalPdf.PageNumber := 1;
Result.Bitmap := LocalPdf.RenderPage(0, 0, 2480, 3508);
Result.PageNo := 1;
finally
LocalPdf.Free;
end;
end,
procedure (const AResult: TPdfFutureResult<TRenderResult>)
begin
case AResult.State of
pfsSuccess: PaintBitmapToView(AResult.Value.Bitmap);
pfsCancelled: Log.Add('Annullato');
pfsFailed: Log.Add('Worker fallito: ' + AResult.ErrorMessage);
end;
end);
end;
// FPdfMatrix avvolge la FS_MATRIX di PDFium in modo che i chiamanti possano costruire
// una trasformazione in modo dichiarativo e passarla a FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // sposta 1 pollice a destra, ~2,8 pollici in alto
M.Scale(0.5, 0.5); // timbro a metà dimensione
M.Rotate(15); // rotazione di 15 gradi in senso antiorario
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // moltiplicazione post con un'altra matrice
// Applica a qualsiasi oggetto di pagina PDFium (testo / tracciato / immagine / form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex copia un array di indici espliciti a base zero da un
// PDF sorgente in questo documento. InsertAt = 0 inserisce prima della pagina 1;
// PageCount accoda.
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Importa le pagine 0, 2, 4 (a base uno: pagine 1, 3, 5) da Source
// e le accoda alla fine di Pdf1.
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Un array vuoto importa tutte le pagine sorgenti
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne restituisce un nuovo TPdf le cui pagine sono composti NumX*NumY
// di QUESTO documento. OutputWidth/Height sono unità utente PDF
// (1 unità = 1/72 di pollice).
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 orizzontale: 842 x 595 pt, 2 colonne x 2 righe
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages accetta gli indici a base zero delle pagine da spostare più
// l'indice di destinazione per la prima pagina spostata al termine dello spostamento.
// Le altre pagine vengono spostate attorno al blocco mosso in modo che la lunghezza
// del documento rimanga la stessa.
//
// Esempio: in un documento di 5 pagine con pagine numerate [0,1,2,3,4],
// MovePages([2, 3], 0) riordina in [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Indici non validi passati a MovePages');
// CreateXObjectFromPage produce un handle Form XObject riutilizzabile da
// qualsiasi pagina di qualsiasi altro TPdf. Applicare quell'handle su quante
// pagine di QUESTO documento si desidera — ogni timbro è un singolo oggetto di
// pagina PDFium che può essere posizionato, scalato e ruotato tramite l'API delle matrici.
uses FPdfMatrix, FPdfView;
var
WatermarkSource: TPdf;
XObj: TPdfXObject;
PageObj: FPDF_PAGEOBJECT;
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
I: Integer;
begin
WatermarkSource := TPdf.Create(nil);
try
WatermarkSource.FileName := 'C:\Watermarks\Confidential.pdf';
WatermarkSource.Active := True;
// Avvolge la pagina 0 di WatermarkSource come XObject riutilizzabile
XObj := Pdf1.CreateXObjectFromPage(WatermarkSource, 0);
try
for I := 1 to Pdf1.PageCount do
begin
Pdf1.PageNumber := I;
PageObj := Pdf1.InsertFormObjectFromXObject(XObj);
if PageObj = nil then Continue;
// Centra la filigrana sulla pagina
M := TPdfMatrix.Create;
try
M.Scale(0.5, 0.5);
M.Translate(0.25 * Pdf1.PageWidth, 0.5 * Pdf1.PageHeight);
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
Pdf1.UpdatePage;
end;
finally
XObj.Free; // chiude l'handle FPDF_XOBJECT
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
I progetti Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp e Demo\CBuilder\WatermarkStamp trasformano questo pattern in un workflow eseguibile: creano un PDF di destinazione e un PDF timbro riutilizzabile, avvolgono la pagina del timbro con CreateXObjectFromPage, inseriscono lo stesso Form XObject su ogni pagina di destinazione, applicano trasformazioni TPdfMatrix per filigrane centrate e timbri in alto a destra, e aggiungono etichette di numero di pagina opzionali prima di salvare il risultato.
// AddImage(FileName) accetta qualsiasi formato registrato con le unità
// grafiche VCL o LCL (BMP, PNG tramite PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) salta l'intermediario TPicture, ideale quando la
// bitmap proviene già da rendering o generazione batch.
var
Bitmap: TBitmap;
begin
Bitmap := Pdf1.RenderPage(0, 0, 595, 842);
try
Pdf1.PageNumber := 2;
Pdf1.AddImage(Bitmap, 50, 50, 250, 350);
Pdf1.UpdatePage;
finally
Bitmap.Free;
end;
end;
I progetti Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF e Demo\Lazarus\ImageToPDF trasformano ciò in un workflow batch completo: selezione di più file immagine, anteprima dell'elemento selezionato, creazione di una pagina PDF per immagine, ridimensionamento di ciascuna immagine in una pagina A4 verticale o orizzontale, salvataggio del risultato e apertura del PDF generato.
var
MatchCount: Integer;
begin
// Dipinge una maschera clYellow su ogni corrispondenza nella pagina corrente del visualizzatore.
// Passare a una pagina diversa cancella automaticamente l'evidenziazione.
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('fattura', False{Case}, True{Word});
Status.Caption := Format('%d corrispondenze in questa pagina', [MatchCount]);
// Rimuove la sovrapposizione a livello programmatico prima del cambio di pagina
PdfView1.ClearHighlight;
end;
// FitMode mantiene i documenti lunghi inquadrati durante Resize o cambi di pagina.
// Impostare Zoom direttamente annulla FitMode (torna a pfmNone).
PdfView1.FitMode := pfmFitPage; // pagina intera nel viewport
// PdfView1.FitMode := pfmFitWidth; // larghezza pagina corrispondente alla larghezza del viewport
// PdfView1.FitMode := pfmActualSize; // 100%
// PageColor disaccoppia la pagina PDF renderizzata dal Color del controllo host
// — configurazione tipica del visualizzatore in modalità scura: mantiene l'area di
// scorrimento scura mentre la pagina PDF resta bianco-carta.
PdfView1.Color := clBlack; // area di scorrimento
PdfView1.PageColor := clWhite; // sfondo della pagina PDF
// Ombra opzionale e bordo della pagina per staccare la pagina dallo sfondo scuro
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
I progetti Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab e Demo\Lazarus\ViewerInteractionLab mantengono la moderna superficie di interazione TPdfView in un unico form compatto: aprire o creare un PDF di esempio, alternare DisplayMode, applicare FitMode, cambiare PageColor, ruotare le pagine, evidenziare le occorrenze di ricerca della pagina corrente con HighlightSearchText, abilitare o disabilitare la selezione del testo da parte dell'utente, ispezionare SelectedText, chiamare SelectAll, CopySelectionToClipboard e ClearSelection, e osservare l'aggiornamento delle coordinate DeviceToPage nella barra di stato mentre il mouse si muove sulla pagina.
I progetti Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect e Demo\Lazarus\SearchAndSelect trasformano il workflow di ricerca-e-selezione del visualizzatore in esempi autonomi. Aprono o creano un PDF, elencano i risultati di ricerca su tutte le pagine con numero di pagina, indice di carattere e testo di anteprima, supportano la navigazione Precedente / Successivo e il salto al risultato con doppio clic, evidenziano le corrispondenze nella pagina corrente con HighlightSearchText, espongono le opzioni case sensitive e parola intera, alternano AllowUserTextSelection, mostrano l'anteprima di SelectedText, pilotano SelectAll, CopySelectionToClipboard e ClearSelection dai pulsanti, e permettono agli utenti di passare tra modalità di visualizzazione singola e doppia pagina.
I progetti Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences e Demo\Lazarus\PrintPreferences leggono le preferenze di stampa dell'autore prima di stampare. Visualizzano PrintCopies, PrintPageRanges, PrintScaling e PrintPaperHandling, possono copiare le copie e gli intervalli forniti dall'autore negli input di stampa, e mostrano come SetPdfPrintPaperHandlingDevMode mappa l'intento duplex PDF in un DEVMODE di Windows.
I progetti Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF e Demo\Lazarus\PrintPDF mostrano il workflow di stampa standard: apertura di un PDF, anteprima delle pagine, scelta degli intervalli di pagine e copie nella finestra di dialogo di stampa della piattaforma, rispetto della modalità di fascicolazione, rendering di ciascuna pagina tramite TPdf.RenderPage ed esposizione del progresso di stampa con annullamento. L'esempio Delphi accetta anche file PDF trascinati per un caricamento rapido.
I progetti Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer e Demo\Lazarus\MultiPageViewer esercitano la visualizzazione continua, a doppia pagina e con copertina con workflow regolari di navigazione e zoom delle pagine. Sono utili anche per testare la velocità di apertura alla prima pagina e la reattività del rendering su PDF molto grandi: nei test con file di grandi dimensioni, documenti da 2,x GB che possono richiedere ad Acrobat o Foxit 20-30 secondi prima della prima vista possono diventare usabili quasi immediatamente attraverso il percorso del visualizzatore PDFiumPas.
I progetti Demo\Delphi\SplitView, Demo\CBuilder\SplitView e Demo\Lazarus\SplitView mostrano la revisione PDF side-by-side. Gli esempi C++Builder e Lazarus si concentrano sul confronto a due o tre riquadri, sulla selezione della view attiva, su comandi di navigazione sincronizzati opzionali, preset di zoom condivisi e rotazione per la view selezionata o per tutte le view caricate. Utilizzare questi esempi per confrontare documenti di grandi dimensioni mantenendo reattiva la prima vista utilizzabile e il rendering successivo delle pagine; gli stessi file PDF da 2,x GB che potrebbero richiedere ad Acrobat o Foxit 20-30 secondi per essere aperti possono diventare usabili quasi immediatamente nel percorso del visualizzatore PDFiumPas.
I progetti Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit e Demo\Lazarus\SecurityAudit aprono un PDF in sola lettura ed elencano lo stato dei permessi, gli allegati incorporati, le azioni JavaScript del documento, le annotazioni di link URI e Launch, i link web, i riepiloghi di firma, lo stato XFA, la disponibilità degli helper V8 e le callback di funzionalità non supportate. Lo stesso riepilogo e le righe dei risultati possono essere salvati o copiati come report di audit TXT o salvati come JSON strutturato. Sono pensati come pannelli di rischio per applicazioni host, non come rilevamento di malware.
// LinkOptions controlla la gestione automatica dei quattro tipi di azione
// dei link PDF. I valori predefiniti abilitano solo goto e URI; launch (esecuzione
// di un programma) ed embedded GotoR (salto a un altro file) restano DISATTIVATI
// in modo che un clic accidentale non possa eseguire codice arbitrario.
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // valori predefiniti sicuri
// OnAnnotationLinkClick si attiva per destinazioni interne al documento (salti di
// pagina, destinazioni con nome, catene di azioni). Impostare Handled = True per
// impedire alla view di seguire automaticamente il link.
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
LinkIndex: Integer; const Action: TPdfAction;
var Handled: Boolean);
begin
if Action.Kind = paUri then
begin
if MessageDlg('Aprire ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
<> mrYes then
Handled := True; // sopprime la gestione automatica
end;
end;
// OnWebLinkClick si attiva per le stringhe URI rilevate dallo scanner di link web
// di PDFium (URL rilevati automaticamente nel flusso di testo, anche senza
// un'annotazione /A <</S/URI...>>).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Link web cliccato: ' + string(Url));
end;
// Sul widget AcroForm focalizzato, sei nuovi metodi pilotano la cronologia
// di modifica dei moduli di PDFium. Tutti e sei effettuano un'uscita sicura
// in cortocircuito se nessun widget è focalizzato o se il documento non
// presenta un AcroForm.
procedure TForm1.btnSelectAllClick(Sender: TObject);
begin
PdfView1.SelectAllFormText;
EditMenuCopy.Caption := 'Copia: ' + string(PdfView1.GetSelectedFormText);
end;
procedure TForm1.btnUndoClick(Sender: TObject);
begin
btnUndo.Enabled := PdfView1.FormCanUndo;
btnRedo.Enabled := PdfView1.FormCanRedo;
if PdfView1.FormCanUndo then
PdfView1.FormUndo;
end;
procedure TForm1.btnRedoClick(Sender: TObject);
begin
if PdfView1.FormCanRedo then
PdfView1.FormRedo;
end;
// Tutti e tre per default sono True in modo che le app esistenti si
// aggiornino senza cambiamenti di comportamento. Impostarli a False per chioschi,
// riquadri di anteprima o integrazioni di sola lettura senza sottoclassare TPdfView.
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // salti con rotella su/giù
PdfView1.AllowUserTextSelection := False; // selezione testo da mouse/tastiera
// Ctrl+rotella è riservato per lo zoom e viene saltato dal gestore di navigazione
// indipendentemente da ChangePageOnMouseScrolling.
// TPdfThumbnailView (unità FPdfThumbnail) renderizza una miniatura di pagina per
// riga in un pannello laterale scorrevole.
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // evidenziazione calda
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex è a base zero
end;
procedure TForm1.PdfView1PageNumberModificato(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Pattern utilizzato da Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport e
// Demo\Lazarus\BatchExport. Un TPdf per file, IPdfCancellationToken
// per l'annullamento dall'interfaccia utente, TPdfStreamAdapter per il caricamento
// file con buffer.
uses FPdfAsync;
procedure ExportBatch(const Files: TArray<string>;
const OutDir: string; DPI: Integer; Quality: Integer;
const ACancelToken: IPdfCancellationToken);
var
I, J: Integer;
Job: TPdf;
Bmp: TBitmap;
Jpeg: TJPEGImage;
begin
for I := 0 to High(Files) do
begin
if ACancelToken.IsCancelled then Break;
OverallProgress.Position := I;
Job := TPdf.Create(nil);
try
Job.FileName := Files[I];
Job.Active := True;
PageProgress.Max := Job.PageCount;
for J := 1 to Job.PageCount do
begin
if ACancelToken.IsCancelled then Break;
PageProgress.Position := J;
Job.PageNumber := J;
Bmp := Job.RenderPage(
0, 0,
Round(Job.PageWidth * DPI / 72),
Round(Job.PageHeight * DPI / 72));
try
Jpeg := TJPEGImage.Create;
try
Jpeg.CompressionQuality := Quality;
Jpeg.Assign(Bmp);
Jpeg.SaveToFile(Format('%s\%s.p%.3d.jpg',
[OutDir, ExtractFileName(Files[I]), J]));
finally
Jpeg.Free;
end;
finally
Bmp.Free;
end;
end;
finally
Job.Free;
end;
end;
end;
procedure FitCurrentPage;
begin
PdfView1.Zoom := PdfView1.PageZoom[PdfView1.PageNumber];
end;
procedure InspectViewerPoint(X, Y: Integer);
var
PageNo: Integer;
PageX, PageY: Double;
CharIndex: Integer;
Ch: WString;
begin
PageNo := PdfView1.PageNumber;
if not PdfView1.DeviceToPage(X, Y, PageNo, PageX, PageY) then
Exit;
CharIndex := PdfView1.CharacterIndexAtPos(X, Y, 8, 8);
if CharIndex >= 0 then
begin
Ch := PdfView1.Text(CharIndex, 1);
ShowMessage(Format('Pagina %d, X %.2f, Y %.2f, carattere %s',
[PageNo, PageX, PageY, string(Ch)]));
end;
end;
function LoadPdfSafely(const FileName: string): Boolean;
begin
Result := False;
if not FileExists(FileName) then
Exit;
try
Pdf1.FileName := FileName;
Pdf1.Active := True;
Result := Pdf1.Active;
except
Result := False;
end;
end;
function ExtractTextSafely(PageNo: Integer): WString;
begin
Result := '';
if not Pdf1.Active then
Exit;
if (PageNo < 1) or (PageNo > Pdf1.PageCount) then
Exit;
Pdf1.PageNumber := PageNo;
Result := Pdf1.Text;
end;
// IsTagged è una sonda economica del catalogo — True quando /StructTreeRoot esiste.
// StructureElements materializza l'albero in un array piatto di record
// TPdfStructureElement con tipo, titolo, testo alternativo, testo effettivo,
// testo di espansione, lingua, livello, indice del genitore e conteggi di figli /
// contenuto marcato / attributi.
var
Elements: TPdfStructureElements;
I: Integer;
begin
if not Pdf1.IsTagged then
begin
Log.Add('Il documento NON è taggato — gli strumenti di accessibilità potrebbero avere difficoltà.');
Exit;
end;
if Pdf1.Language = '' then
Log.Add('PDF taggato senza /Lang — non supera PDF/UA.');
Elements := Pdf1.StructureElements;
for I := 0 to High(Elements) do
Log.Add(StringOfChar(' ', Elements[I].Level * 2)
+ Elements[I].StructType + ' "' + Elements[I].Title + '"');
end;
// SetPdfPrintMode è una funzione globale che si mappa su FPDF_SetPrintMode
// di PDFium. Usare il valore TPdfPrintMode corrispondente al driver di
// stampante in uso.
SetPdfPrintMode(pmEmf); // EMF GDI (default)
// SetPdfPrintMode(pmTextOnly); // rendering solo testo
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + maschere immagine
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + font Type 42
// PrintCopies, PrintPageRanges, PrintScaling e PrintPaperHandling sono
// le voci del dizionario delle preferenze del visualizzatore — codificano ciò
// che l'autore del documento voleva che i lettori PDF impostassero per default.
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Copie suggerite: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Scaling di stampa: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // intervalli a base uno
for Range in Ranges do
Log.Add(Format('Pagine %d..%d', [Range.First, Range.Last]));
if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
ApplyPrinterDevMode(PrinterDevMode);
end;
// Il JavaScript di livello documento è memorizzato nel name tree
// /Names /JavaScript. JavaScriptAction[i] restituisce una voce; JavaScriptActions
// restituisce l'array completo.
var
I: Integer;
Action: TPdfJavaScriptAction;
begin
for I := 0 to Pdf1.JavaScriptActionCount - 1 do
begin
Action := Pdf1.JavaScriptAction[I];
Memo1.Lines.Add('Nome: ' + string(Action.Name));
Memo1.Lines.Add('Script: ' + string(Action.Script));
Memo1.Lines.Add('---');
end;
end;
procedure ProcessMultiplePages;
var
I: Integer;
PageText: WString;
begin
for I := 1 to Pdf1.PageCount do
begin
Pdf1.PageNumber := I;
PageText := Pdf1.Text;
ProcessPageText(PageText);
end;
end;
procedure SetOptimalRenderOptions;
begin
PdfView1.Options := [reAnnotations, reLcd];
end;
// Percorso di rendering a zero copia (default dalla v1.21.0): RenderPage scrive
// direttamente nel buffer DIB del TBitmap di destinazione quando i chiamanti passano
// una bitmap preallocata, evitando una copia di width * height * 4 byte.
procedure RenderIntoCallerBitmap(Bitmap: TBitmap);
begin
Bitmap.PixelFormat := pf32bit;
Bitmap.SetSize(2480, 3508); // A4 a 300 DPI
Pdf1.RenderPage(Bitmap, 0, 0, Bitmap.Width, Bitmap.Height);
end;