PDFium Component Docs

Programmeervoorbeelden voor PDFium VCL

Basis documentbewerkingen

Een PDF-document laden en weergeven

Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Title: ' + Pdf1.Title);
ShowMessage('Author: ' + Pdf1.Author);

var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Stream.LoadFromFile('C:\MyDocument.pdf');
    Pdf1.LoadDocument(Stream, True);
    Pdf1.Active := True;
    ShowMessage('PDF loaded from stream');
  finally
    Stream.Free;
  end;
end;

Een nieuw PDF-document maken

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;

Tekstbewerkingen

Tekst extraheren uit een PDF

var
  PageText: WString;
  RectText: WString;
begin
  Pdf1.PageNumber := 1;
  PageText := Pdf1.Text;
  Memo1.Text := PageText;
  RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
  ShowMessage('Text in rectangle: ' + RectText);
end;

Tekst zoeken

var
  Position: Integer;
begin
  Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
  while Position >= 0 do
  begin
    ShowMessage('Found at character: ' + IntToStr(Position));
    Position := Pdf1.FindNext;
  end;
end;

Tekst met opmaak toevoegen

Pdf1.PageNumber := 1;
Pdf1.AddText('Document Title', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('This is the document content.', 'Arial', 12, 100, 700, clBlack);
Pdf1.UpdatePage;

Karakter- en font-metadata inspecteren

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('Object ' + IntToStr(I));
      Memo1.Lines.Add('Base name: ' + Pdf1.FontBaseName[I]);
      Memo1.Lines.Add('Family: ' + Pdf1.FontFamilyName[I]);
      Memo1.Lines.Add('Embedded: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
      Memo1.Lines.Add('Weight: ' + IntToStr(Pdf1.FontWeight[I]));
      Memo1.Lines.Add('Italic angle: ' + IntToStr(Pdf1.FontItalicAngle[I]));
      Memo1.Lines.Add('Ascent: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
      Memo1.Lines.Add('Descent: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
      Memo1.Lines.Add('Font data bytes: ' + IntToStr(Length(FontBytes)));
    end;

  if Pdf1.CharacterCount > 0 then
  begin
    Memo1.Lines.Add('First character size: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
    Memo1.Lines.Add('First character weight: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
    Memo1.Lines.Add('First character angle: ' + FloatToStr(Pdf1.CharacterAngle[0]));
  end;
end;

De projecten Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties en Demo\Lazarus\FontProperties tonen dezelfde metadata via zowel TPdf als TPdfView.

Een rapport voor inhoudsextractie met meerdere categorieën bouwen

De projecten Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab en Demo\Lazarus\ContentExtractionLab maken van inhoudsextractie één workflow. Ze openen of maken een PDF, laten gebruikers metadata, paginatekst, pagina-objecten, afbeeldingen, bijlagen, links, annotaties, bladwijzers en font-/karaktermetrics kiezen, en genereren vervolgens een samenvatting plus een gedetailleerd TXT- of JSON-rapport. De lab bevat ook alle / geen categorie-selectie, rapport-kopiëren-naar-klembord en bron-PDF-opslag-acties, zodat gebruikers de gegenereerde voorbeeld-PDF naast zijn extractierapport kunnen bewaren. De oudere demo's ExtractText, ExtractImages, Attachment en FontProperties blijven gerichte voorbeelden; ExtractText ondersteunt opties voor paginabereik en paginascheidingstekens om tekstuitvoer per pagina te produceren in Delphi, C++Builder en Lazarus, terwijl ContentExtractionLab het overkoepelende inventarisvoorbeeld is.

Formulierbewerkingen

Met formuliervelden werken

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;

Ingevulde formulierwaarden afvlakken in de pagina

// Zonder afvlakken schrijft FormField[i] := value alleen de /V-entry —
// de zichtbare appearance komt uit de /AP-stream en kan mogelijk niet
// vernieuwen. GenerateFormAppearances regenereert /AP voor elk widget,
// daarna bakt FlattenAllPages alle annotaties en widgets van elke pagina
// in als permanente, niet-bewerkbare inhoud.
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;

Afbeeldingsbewerkingen

Afbeeldingen extraheren

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;

Afbeeldingen toevoegen

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;

Annotatiebewerkingen

Met annotaties werken

var
  I: Integer;
  Annotation: TPdfAnnotation;
begin
  for I := 0 to Pdf1.AnnotationCount - 1 do
  begin
    Annotation := Pdf1.Annotation[I];
    ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
  end;
end;

Tekenbewerkingen

Vormen tekenen

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;

Navigatie en bladwijzers

Met bladwijzers werken

var
  I: Integer;
  Bookmarks: TBookmarks;
begin
  Bookmarks := Pdf1.Bookmarks;
  for I := 0 to Length(Bookmarks) - 1 do
    ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;

Met benoemde bestemmingen werken

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;

Bijlagebewerkingen

Met bijlagen werken

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;

Paginabeheer

Paginabewerkingen

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

Opslaan en compressie

Opslaan met compressie

// Maak een gecomprimeerd PDF-document
Pdf1.CreateDocument;
Pdf1.Compressed := True; // zorg dat alle streams FlateDecode-gecomprimeerd zijn (standaard)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4-formaat
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Opslaan met opties
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // incrementeel opslaan
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // beveiliging verwijderen

// Opslaan met specifieke PDF-versie als doel. De PDF-versie-validator
// weigert elke functie die na de gekozen doelversie is geïntroduceerd
// (JBIG2Decode, MarkInfo, AES-versleuteling, Polygon / Caret / Watermark /
// Redact-annotaties, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

PDF/A-1b archiveringsuitvoer maken

// SaveAsPdfA nabewerkt de basisopslag met een incrementele update die een
// XMP-metadatastream, een sRGB ICC OutputIntent en een bijgewerkte
// documentcatalogus injecteert zodat het resultaat PDF/A-1b-conform is.
// Het meegeleverde sRGB IEC61966-2.1-profiel wordt automatisch gebruikt
// wanneer geen aangepast profiel wordt geleverd.
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
  ShowMessage('Saved PDF/A-1b archive');

// PDF/A met een aangepast ICC-profiel (bijvoorbeeld voor CMYK-print-pijplijnen)
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;

// PDF/A-uitvoer in het geheugen
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;

PDF/A-conformiteit valideren

var
  Validation: TPdfAValidationResult;
  Issue: TPdfAValidationIssue;
begin
  Pdf1.FileName := 'C:\Incoming\report.pdf';
  Pdf1.Active   := True;

  // Goedkope header-stijl probe — leest de conformiteit zonder de
  // volledige validator te itereren. Resultaat: pacNone, pac1a, pac1b,
  // pac2b, pac3b of pacUnknown.
  case Pdf1.PdfAConformance of
    pac1b: Status.Caption := 'PDF/A-1b';
    pac2b: Status.Caption := 'PDF/A-2b';
    pacNone: Status.Caption := 'Not PDF/A';
  else
    Status.Caption := 'Unknown conformance';
  end;

  // Volledige validator met diagnostiek per probleem
  Validation := Pdf1.ValidatePdfA;
  Memo1.Lines.Add('Conformance: ' + GetEnumName(TypeInfo(TPdfAConformance),
    Ord(Validation.Conformance)));
  for Issue in Validation.Issues do
    Memo1.Lines.Add('- ' + Issue.Description);
end;

Meerdere PDF-standaarden inspecteren in StandardsLab

De projecten Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab en Demo\Lazarus\StandardsLab laden of maken een PDF, voeren ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR en ValidatePdfVT uit en tonen vervolgens elk gedetecteerd conformiteitsniveau en aantal problemen in één raster. Hun save-knoppen roepen SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR en SaveAsPdfVT aan, zodat de marker-output-workflow zichtbaar is zonder een aangepast harnas te schrijven.

Preflight-rapporten genereren

De unit FPdfPreflightReport wikkelt ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR en ValidatePdfVT in één rapportobject. Met Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport en Demo\CBuilder\PreflightReport kunnen gebruikers standaarden kiezen, een samenvatting genereren, status, prioriteit, probleem-categorieën, categorie-tellingen, volgende acties, probleem-codes en aanbevolen acties inspecteren, de voorvertoning wisselen tussen tekst, Markdown, JSON en CSV en TXT-, HTML-, Markdown-, JSON- of CSV-rapporten exporteren. De Delphi-GUI-voorbeeldapplicatie bevat ook een Action Plan-voorbeeldweergave die niet-voltooide standaarden, prioriteiten, probleemcodes en aanbevelingen samenvoegt tot een reparatielijst. Demo\Delphi\PreflightReportCli stelt hetzelfde rapport-pad beschikbaar als console-workflow met argumenten voor invoer/uitvoer, optioneel wachtwoord, selectie van uitvoerformaat met plan / actionplan, filtering op standaard, optionele rapport-inbedding van één bestand via attach=output.pdf, CI-gating-exitcodes via failon=, verwerking via batch=list.txt, mapscanning via batchdir=folder met optionele recursie en stabiele verwerking op pad-volgorde, rapportroutering via outdir=reports, collision-veilige rapportnamen voor dubbele invoerbestandsnamen, TXT-/JSON-/HTML-samenvattingen met aggregaat- en standaard-status-totalen, CSV-rij-manifesten met run-instellingen die per item worden herhaald, en een sample-modus zonder argumenten voor scripts en CI-jobs. Het gegenereerde rapport vermeldt dat de ingebouwde validators marker-level- en geselecteerde file-level-checks dekken, zodat full content-level preflight nog steeds kan worden gedelegeerd aan een gespecialiseerde validatie-engine.

Streaming-load en grote documenten

Een PDF op aanvraag laden met LoadCustomDocument

// LoadCustomDocument laat PDFium blokken ophalen uit elke seekable TStream
// op aanvraag. De stream wordt NIET naar het geheugen gekopieerd, zodat
// PDF's van meerdere GB, remote HTTP body streams en databasegebaseerde
// bronnen allemaal haalbaar zijn zonder een vooraf gemaakte buffer.
var
  FileStream: TFileStream;
begin
  // Streamen vanuit bestand (geen kopie in geheugen)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ eigendom overgedragen — Pdf1 vrijgeeft FileStream bij UnloadDocument.
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Stream in extern beheer (aanroeper geeft hem vrij na 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;

Async-annulering en progressieve rendering

Coöperatieve annuleringstokens

// FPdfAsync biedt IPdfCancellationToken / IPdfCancellationTokenSource voor
// cancel-from-UI-semantiek die werkt over de langlopende export- en
// renderpaden.
uses FPdfAsync;

var
  CancelSource: IPdfCancellationTokenSource;
  CancelToken:  IPdfCancellationToken;
begin
  CancelSource := PdfCancellationTokenSource;
  CancelToken  := CancelSource.Token;

  // ... gebruiker klikt op Annuleren ...
  CancelSource.Cancel;

  // De worker controleert periodiek CancelToken.IsCancelled, of het
  // renderpad peilt het via PDFium's IFSDK_PAUSE-callback.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

Annuleerbare progressieve pagina-render

// RenderPageProgressive laat een lange high-DPI-render halverwege de
// pagina afbreken wanneer de gebruiker op Annuleren klikt, in plaats van
// de aanroeper te blokkeren tot het klaar is.
var
  Status: TPdfProgressiveStatus;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(2480, 3508); // A4 @ 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('Render cancelled — partial result available');
      prsFailed:    Memo1.Lines.Add('PDFium reported FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

Renders draaien op een achtergrondthread met TPdfFuture

// TPdfFuture<T> start een worker op een achtergrondthread en stuurt een
// TPdfFutureResult<T>-envelop terug naar de hoofdthread.
//
// Belangrijk: TPdf serialiseert render-aanroepen per instance, maar de
// worker beheert nog steeds dispatch en levensduur van het document. Muteer
// geen TPdf terwijl een render onderweg is; batch-export houdt meestal één
// TPdf per bestand aan.
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('Cancelled');
        pfsFailed:    Log.Add('Worker failed: ' + AResult.ErrorMessage);
      end;
    end);
end;

Transformatie-matrices

Translate / Scale / Rotate / Skew componeren

// FPdfMatrix wikkelt PDFium's FS_MATRIX zodat aanroepers een transform
// declaratief kunnen bouwen en aan FPDFPageObj_SetMatrix kunnen overdragen.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // verplaats 1in naar rechts, ~2.8in omhoog
    M.Scale(0.5, 0.5);          // half-formaat stempel
    M.Rotate(15);               // 15 graden CCW-rotatie
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // post-multipliceer met een andere matrix

    // Toepassen op elk PDFium-pagina-object (text / path / image / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Paginacompositie

Een subset pagina's importeren op basis van index

// ImportPagesByIndex kopieert een expliciete zero-based index-array van
// een bron-PDF naar dit document. InsertAt = 0 voegt in vóór pagina 1;
// PageCount voegt toe aan het einde.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Importeer pagina's 0, 2, 4 (one-based: pagina's 1, 3, 5) uit Source
    // en voeg ze toe aan het einde van Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Een lege array importeert elke bronpagina
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

N-up samengestelde uitvoer

// ImportNPagesToOne retourneert een gloednieuwe TPdf waarvan de pagina's
// NumX*NumY composieten zijn van DIT document. OutputWidth/Height zijn
// PDF-user-units (1 unit = 1/72 in).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 landschap: 842 x 595 pt, 2 kolommen x 2 rijen
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Pagina's in-place herordenen

// MovePages neemt de zero-based indices van de te verplaatsen pagina's
// plus de doelindex voor de eerste verplaatste pagina nadat de verplaatsing
// is voltooid. Andere pagina's schuiven rond het verplaatste blok zodat de
// documentlengte hetzelfde blijft.
//
// Voorbeeld: in een document van 5 pagina's met pagina's [0,1,2,3,4],
// herordent MovePages([2, 3], 0) naar [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

Watermerken via hergebruik van pagina's met Form XObject

// CreateXObjectFromPage maakt een herbruikbare Form XObject-handle van
// elke pagina van elke andere TPdf. Stempel die handle op zoveel pagina's
// van DIT document als u wilt — elke stempel is één PDFium-pagina-object
// dat kan worden gepositioneerd, geschaald en geroteerd via de matrix-API.
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;

    // Wikkel pagina 0 van WatermarkSource in als herbruikbaar XObject
    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;

        // Centreer het watermerk op de 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; // sluit de FPDF_XOBJECT-handle
    end;

    Pdf1.SaveAs('C:\Watermarked.pdf');
  finally
    WatermarkSource.Free;
  end;
end;

De projecten Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp en Demo\CBuilder\WatermarkStamp maken van dit patroon een uitvoerbare workflow: ze maken een doel-PDF en een herbruikbare stempel-PDF, wikkelen de stempelpagina in met CreateXObjectFromPage, voegen hetzelfde Form XObject in op elke doelpagina, passen TPdfMatrix-transformaties toe voor gecentreerde watermerken en stempels rechtsboven en voegen optionele paginanummerlabels toe vóór het opslaan van het resultaat.

Afbeeldingsinvoeging (directe AddImage-overloads)

Een geregistreerd afbeeldingsbestand of een TBitmap toevoegen

// AddImage(FileName) accepteert elk formaat dat is geregistreerd bij de
// grafische units van VCL of LCL (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) slaat de tussenstap via TPicture over, ideaal wanneer
// de bitmap al uit rendering- of batch-generatiewerk komt.
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;

De projecten Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF en Demo\Lazarus\ImageToPDF maken hiervan een volledige batchworkflow: selecteer meerdere afbeeldingsbestanden, bekijk het geselecteerde item, maak één PDF-pagina per afbeelding, schaal elke afbeelding in een A4-staand- of liggende pagina, sla het resultaat op en open de gegenereerde PDF.

Zoek-markering in de viewer

Markeer elke match op de huidige pagina

var
  MatchCount: Integer;
begin
  // Schilder een clYellow-masker over elke match op de huidige pagina van
  // de viewer. Wisselen naar een andere pagina wist de markering automatisch.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // Verwijder de overlay programmatisch voordat de pagina wisselt
  PdfView1.ClearHighlight;
end;

Viewer-lay-out en paginakleur

Fit-modus en pagina-achtergrond

// FitMode houdt lange documenten ingekaderd tijdens Resize of paginawisselingen.
// Het rechtstreeks instellen van Zoom annuleert FitMode (wordt teruggezet naar pfmNone).
PdfView1.FitMode := pfmFitPage;       // hele pagina in viewport
// PdfView1.FitMode := pfmFitWidth;   // paginabreedte komt overeen met viewport-breedte
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor ontkoppelt de gerenderde PDF-pagina van de Color van de host-control
// — een typische donker-thema viewer-opzet houdt het scrollgebied donker terwijl
// de PDF-pagina papierwit blijft.
PdfView1.Color     := clBlack;        // scrollgebied
PdfView1.PageColor := clWhite;        // PDF-pagina-achtergrond

// Optionele slagschaduw en paginarand om de pagina van een donkere achtergrond te tillen
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

Viewer-interactie verkennen in ViewerInteractionLab

De projecten Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab en Demo\Lazarus\ViewerInteractionLab houden de moderne TPdfView-interactie-oppervlakte in één compact formulier: open of maak een voorbeeld-PDF, wissel DisplayMode, pas FitMode toe, wijzig PageColor, roteer pagina's, markeer huidige-pagina-zoekresultaten met HighlightSearchText, schakel gebruikerstekstselectie in of uit, inspecteer SelectedText, roep SelectAll, CopySelectionToClipboard en ClearSelection aan, en bekijk hoe DeviceToPage-coördinaten in de statusbalk bijwerken terwijl de muis over de pagina beweegt.

Tekst zoeken en selecteren in SearchAndSelect

De projecten Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect en Demo\Lazarus\SearchAndSelect maken van de viewer-workflow voor zoeken en selecteren standalone voorbeelden. Ze openen of maken een PDF, vermelden zoekresultaten over alle pagina's met paginanummer, karakterindex en voorvertoningstekst, ondersteunen Vorige/Volgende-navigatie en dubbelklik-resultaat-springen, markeren huidige-pagina-matches met HighlightSearchText, stellen hoofdletter-/heel-woord-opties beschikbaar, schakelen AllowUserTextSelection aan/uit, bekijken SelectedText, sturen vanuit knoppen SelectAll, CopySelectionToClipboard en ClearSelection aan, en laten gebruikers wisselen tussen enkele en spread-weergavemodi.

PDF-printvoorkeuren lezen in PrintPreferences

De projecten Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences en Demo\Lazarus\PrintPreferences lezen auteur-printvoorkeuren vóór het afdrukken. Ze tonen PrintCopies, PrintPageRanges, PrintScaling en PrintPaperHandling, kunnen door de auteur opgegeven kopieën en bereiken kopiëren naar de print-invoeren, en laten zien hoe SetPdfPrintPaperHandlingDevMode de PDF-duplex-intentie vertaalt naar een Windows-DEVMODE.

Standaard PDF-afdrukken in PrintPDF

De projecten Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF en Demo\Lazarus\PrintPDF tonen de standaard print-workflow: open een PDF, bekijk pagina's vooraf, kies paginabereiken en kopieën in het platform-printdialoog, respecteer collate-modus, render elke pagina via TPdf.RenderPage en stel printvoortgang beschikbaar met annulering. Het Delphi-voorbeeld accepteert ook gesleepte PDF-bestanden voor snel laden.

Snel pagina-doorbladeren in MultiPageViewer

De projecten Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer en Demo\Lazarus\MultiPageViewer oefenen continue, spread- en omslag-bewuste weergave met reguliere pagina-navigatie- en zoom-workflows. Ze zijn ook nuttig voor het testen van open-to-first-page-snelheid en render-responsiviteit op zeer grote PDF's: in tests met grote bestanden kunnen documenten van 2,x GB die Acrobat of Foxit 20-30 seconden kunnen kosten vóór de eerste weergave, bijna onmiddellijk bruikbaar worden via het PDFiumPas-viewer-pad.

Naast elkaar bekijken in SplitView

De projecten Demo\Delphi\SplitView, Demo\CBuilder\SplitView en Demo\Lazarus\SplitView tonen PDF-beoordeling naast elkaar. De C++Builder- en Lazarus-voorbeelden richten zich op vergelijking in twee of drie panelen, selectie van actieve weergave, optionele gesynchroniseerde navigatie-commando's, gedeelde zoom-presets en rotatie voor de geselecteerde weergave of alle geladen weergaven. Gebruik deze voorbeelden om grote documenten te vergelijken terwijl de eerste bruikbare weergave en daaropvolgende pagina-rendering responsief blijven; dezelfde 2,x GB PDF-bestanden die Acrobat of Foxit 20-30 seconden nodig hebben om te openen, kunnen bijna onmiddellijk bruikbaar worden in het PDFiumPas-viewer-pad.

PDF-risico-oppervlak auditen in SecurityAudit

De projecten Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit en Demo\Lazarus\SecurityAudit openen een PDF alleen-lezen en vermelden de status van rechten, ingebedde bijlagen, document-JavaScript-acties, URI- en Launch-linkannotaties, weblinks, samenvattingen van handtekeningen, XFA-status, beschikbaarheid van V8-helpers en niet-ondersteunde-functie-callbacks. Dezelfde samenvattings- en bevindingsrijen kunnen worden opgeslagen of gekopieerd als TXT-auditrapport of als gestructureerd JSON-rapport worden opgeslagen. Ze zijn bedoeld als risico-panelen voor de host-toepassing, niet als malware-detectie.

Linkafhandeling in de viewer

Veilig klik-om-te-volgen met per-actie allow lists

// LinkOptions bestuurt de automatische afhandeling van de vier PDF-link-
// actietypen. Standaardwaarden schakelen alleen goto en URI in; launch
// (een programma uitvoeren) en embedded GotoR (naar een ander bestand
// springen) blijven UIT, zodat een per ongeluk klik geen willekeurige code
// kan uitvoeren.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // veilige standaardwaarden

// OnAnnotationLinkClick vuurt voor bestemmingen binnen het document
// (paginasprongen, benoemde bestemmingen, actie-ketens). Markeer Handled = True
// om te voorkomen dat de viewer de link automatisch volgt.
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
  LinkIndex: Integer; const Action: TPdfAction;
  var Handled: Boolean);
begin
  if Action.Kind = paUri then
  begin
    if MessageDlg('Open ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
      <> mrYes then
      Handled := True;  // onderdruk automatische afhandeling
  end;
end;

// OnWebLinkClick vuurt voor URI-strings ontdekt door PDFium's weblink-
// scanner (automatisch gedetecteerde URL's in de tekststream, zelfs zonder
// /A <</S/URI...>>-annotatie).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

Formulier-widget bewerken in de viewer

Selectie en ongedaan maken / opnieuw uitvoeren

// Op het gefocuste AcroForm-widget sturen zes nieuwe methoden de
// formulier-edit-geschiedenis van PDFium aan. Alle zes vallen veilig
// terug wanneer geen widget gefocust is of het document geen AcroForm
// bevat.
procedure TForm1.btnSelectAllClick(Sender: TObject);
begin
  PdfView1.SelectAllFormText;
  EditMenuCopy.Caption := 'Copy: ' + 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;

De viewer vergrendelen voor kiosken

Pagina-navigatie-gebaren uitschakelen

// Alle drie staan standaard op True, zodat bestaande apps zonder
// gedragswijziging upgraden. Zet ze op False voor kiosken, preview-panes
// of alleen-lezen embeds zonder TPdfView te subclassen.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // wiel-omhoog/omlaag-sprongen
PdfView1.AllowUserTextSelection    := False;  // muis-/toetsenbord-tekstselectie

// Ctrl+wheel is gereserveerd voor zoom en wordt overgeslagen door de
// navigatie-handler ongeacht ChangePageOnMouseScrolling.

Miniatuur-zijpaneel

Een TPdfThumbnailView koppelen aan een TPdfView

// TPdfThumbnailView (unit FPdfThumbnail) rendert één paginaminiatuur per
// rij in een scrollbaar zijpaneel.
uses FPdfThumbnail;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThumbView.Pdf              := Pdf1;
  ThumbView.ThumbnailWidth   := 120;
  ThumbView.ThumbnailHeight  := 160;
  ThumbView.SelectionColor   := $00CFA85F;  // warme accentkleur
  ThumbView.OnPageClick      := ThumbViewPageClick;
end;

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex is zero-based
end;

procedure TForm1.PdfView1PageNumberGewijzigd(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

Coöperatief annuleerbare batchexport

Multi-file PDF naar JPG met progress per bestand en per pagina

// Patroon gebruikt door Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// en Demo\Lazarus\BatchExport. Één TPdf per bestand, IPdfCancellationToken
// voor cancel-from-UI, TPdfStreamAdapter voor gebufferde file load.
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;

Viewer-coördinaten en hit-testing

De huidige pagina passend maken en tekst onder de muis inspecteren

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('Page %d, X %.2f, Y %.2f, character %s',
      [PageNo, PageX, PageY, string(Ch)]));
  end;
end;

Foutafhandeling

Robuuste PDF-bewerkingen

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;

Tagged PDF en toegankelijkheid

Tagged PDF detecteren en de structuurboom doorlopen

// IsTagged is een goedkope catalog-probe — True wanneer /StructTreeRoot
// bestaat. StructureElements materialiseert de boom in een flat array van
// TPdfStructureElement-records met type, title, alternate text, actual
// text, expansion text, language, level, parent index en child / marked
// content / attribute counts.
var
  Elements: TPdfStructureElements;
  I: Integer;
begin
  if not Pdf1.IsTagged then
  begin
    Log.Add('Document is NOT tagged — accessibility tools may struggle.');
    Exit;
  end;
  if Pdf1.Language = '' then
    Log.Add('Tagged PDF without /Lang — fails 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;

Selectie van afdrukmodus

EMF- / PostScript- / image-mask-print-modus instellen

// SetPdfPrintMode is een globale functie die overeenkomt met PDFium's
// FPDF_SetPrintMode. Gebruik de bijpassende TPdfPrintMode-waarde voor het
// gebruikte printer-stuurprogramma.
SetPdfPrintMode(pmEmf);                   // GDI EMF (standaard)
// SetPdfPrintMode(pmTextOnly);           // alleen-tekst-rendering
// SetPdfPrintMode(pmPostScript2);        // PostScript Level 2
// SetPdfPrintMode(pmPostScript3);        // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks);      // EMF + image masks
// SetPdfPrintMode(pmPostScript3Type42);  // PostScript 3 + Type 42-fonts

Door de auteur bedoelde printinstellingen lezen

// PrintCopies, PrintPageRanges, PrintScaling en PrintPaperHandling zijn
// de viewer-preference-dictionary-entries — ze coderen wat de
// documentauteur wilde dat PDF-readers standaard zouden gebruiken.
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
    Ord(Pdf1.PrintScaling)));

  Ranges := Pdf1.PrintPageRanges; // one-based bereiken
  for Range in Ranges do
    Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));

  if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
    ApplyPrinterDevMode(PrinterDevMode);
end;

JavaScript-acties op documentniveau

Name-tree-JavaScript opsommen

// JavaScript op documentniveau wordt opgeslagen in de /Names /JavaScript
// name-tree. JavaScriptAction[i] retourneert één entry; JavaScriptActions
// retourneert de volledige array.
var
  I: Integer;
  Action: TPdfJavaScriptAction;
begin
  for I := 0 to Pdf1.JavaScriptActionCount - 1 do
  begin
    Action := Pdf1.JavaScriptAction[I];
    Memo1.Lines.Add('Name:   ' + string(Action.Name));
    Memo1.Lines.Add('Script: ' + string(Action.Script));
    Memo1.Lines.Add('---');
  end;
end;

Prestatietips

PDF-bewerkingen optimaliseren

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;

// Zero-copy renderpad (standaard sinds v1.21.0): RenderPage schrijft
// rechtstreeks in de DIB-buffer van de bestemmings-TBitmap wanneer
// aanroepers een vooraf gealloceerde bitmap doorgeven, waarmee één kopie
// van width * height * 4 bytes wordt vermeden.
procedure RenderIntoCallerBitmap(Bitmap: TBitmap);
begin
  Bitmap.PixelFormat := pf32bit;
  Bitmap.SetSize(2480, 3508); // A4 @ 300 DPI
  Pdf1.RenderPage(Bitmap, 0, 0, Bitmap.Width, Bitmap.Height);
end;