Documentazione PDFiumPas

Esempi di programmazione PDFium VCL

Operazioni di base sui documenti

Caricamento e visualizzazione di un documento PDF

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;

Creazione di un nuovo documento PDF

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;

Operazioni sul testo

Estrazione del testo da un PDF

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;

Ricerca di testo

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;

Aggiunta di testo con formattazione

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;

Ispezione dei metadati di caratteri e font

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.

Costruzione di un report di estrazione contenuti multi-categoria

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.

Operazioni sui moduli

Lavorare con i campi dei moduli

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;

Appiattimento dei valori dei moduli compilati nella pagina

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

Operazioni sulle immagini

Estrazione di immagini

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;

Aggiunta di immagini

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;

Operazioni sulle annotazioni

Lavorare con le annotazioni

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;

Operazioni di disegno

Disegno di forme

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;

Navigazione e segnalibri

Lavorare con i segnalibri

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;

Lavorare con le destinazioni con nome

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;

Operazioni sugli allegati

Lavorare con gli allegati

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;

Gestione delle pagine

Operazioni sulle pagine

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);

Salvataggio e compressione

Salvataggio con compressione

// 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');

Creazione di output di archiviazione PDF/A-1b

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

Validazione della conformità PDF/A

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;

Ispezione di più standard PDF in StandardsLab

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.

Generazione di report di preflight

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.

Caricamento in streaming e documenti di grandi dimensioni

Caricamento di un PDF su richiesta con LoadCustomDocument

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

Annullamento asincrono e rendering progressivo

Token di annullamento cooperativi

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

Rendering progressivo di pagina annullabile

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

Esecuzione di rendering su un thread di sfondo con TPdfFuture

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

Matrici di trasformazione

Composizione di Translate / Scale / Rotate / Skew

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

Composizione di pagine

Importazione di un sottoinsieme di pagine per indice

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

Output composito N-up

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

Riordino delle pagine in posto

// 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');

Filigranatura tramite riutilizzo di pagine con Form XObject

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

Inserimento immagini (overload diretti di AddImage)

Aggiunta di un file immagine registrato o di un TBitmap

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

Evidenziazione di ricerca nel visualizzatore

Evidenziazione di ogni corrispondenza nella pagina corrente

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;

Layout del visualizzatore e colore della pagina

Modalità di adattamento e sfondo della pagina

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

Esplorazione dell'interazione del visualizzatore in ViewerInteractionLab

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.

Ricerca e selezione del testo in SearchAndSelect

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.

Lettura delle preferenze di stampa PDF in PrintPreferences

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.

Stampa PDF standard in PrintPDF

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.

Visualizzazione multi-pagina veloce in MultiPageViewer

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.

Revisione side-by-side in SplitView

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.

Audit della superficie di rischio PDF in SecurityAudit

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.

Gestione dei link nel visualizzatore

Clic sicuro per seguire i link con liste consenti per azione

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

Modifica dei widget di modulo nel visualizzatore

Selezione e Undo / Redo

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

Blocco del visualizzatore per chioschi

Disabilitare i gesti di navigazione delle pagine

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

Pannello laterale miniature

Collegamento di un TPdfThumbnailView a un TPdfView

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

Esportazione batch cooperativamente annullabile

Conversione multi-file PDF in JPG con progresso per file e per pagina

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

Coordinate del visualizzatore e hit testing

Adattare la pagina corrente e ispezionare il testo sotto il mouse

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;

Gestione degli errori

Operazioni PDF robuste

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;

PDF taggato e accessibilità

Rilevamento di PDF taggato e attraversamento dell'albero della struttura

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

Selezione della modalità di stampa

Impostazione della modalità di stampa EMF / PostScript / Image-Mask

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

Lettura delle impostazioni di stampa volute dall'autore

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

Azioni JavaScript del documento

Enumerazione del JavaScript del name tree

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

Suggerimenti sulle prestazioni

Ottimizzazione delle operazioni PDF

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;