PDFium Component Docs

Programmeringsexempel för PDFium VCL

Grundläggande dokumentoperationer

Läsa in och visa ett PDF-dokument

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;

Skapa ett nytt PDF-dokument

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;

Textoperationer

Extrahera text från 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;

Sökning av text

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;

Lägg till text med formatering

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;

Inspektera tecken- och typsnittsmetadata

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;

Projekten Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties och Demo\Lazarus\FontProperties visar samma metadata via både TPdf och TPdfView.

Bygga en rapport för innehållsextraktion med flera kategorier

Projekten Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab och Demo\Lazarus\ContentExtractionLab gör innehållsextraktion till ett enda arbetsflöde. De öppnar eller skapar en PDF, låter användare välja metadata, sidtext, sidobjekt, bilder, bilagor, länkar, annoteringar, bokmärken samt typsnitts- / teckenmått, och genererar sedan en sammanfattning plus en detaljerad TXT- eller JSON-rapport. Labbet inkluderar också val av alla / inga kategorier, kopiering av rapport till urklipp och åtgärder för att spara käll-PDF så att användare kan behålla den genererade exempel-PDF:en bredvid sin extraktionsrapport. De äldre demoerna ExtractText, ExtractImages, Attachment och FontProperties är kvar som fokuserade exempel; ExtractText stöder alternativ för sidintervall och sidseparator för att producera textutdata per sida över Delphi, C++Builder och Lazarus, medan ContentExtractionLab är det tvärgående inventeringsexemplet.

Formulärsoperationer

Arbeta med formulärfält

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;

Platta ifyllda formulärvärden in i sidan

// Utan plattläggning skriver FormField[i] := value endast /V-posten —
// den synliga utseendet kommer från /AP-strömmen och kan komma att inte uppdateras.
// GenerateFormAppearances regenererar /AP för varje widget, sedan
// bakar FlattenAllPages in varje sidas annoteringar och widgetar i
// permanent, icke-redigerbart innehåll.
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;

Bildoperationer

Extrahera bilder

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;

Lägga till bilder

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;

Annoteringsoperationer

Arbeta med annoteringar

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;

Ritoperationer

Rita former

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;

Navigering och bokmärken

Arbeta med bokmärken

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;

Arbeta med namngivna destinationer

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;

Bilageoperationer

Arbeta med bilagor

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;

Sidhantering

Sidoperationer

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

Sparande och komprimering

Spara med komprimering

// Skapa ett komprimerat PDF-dokument
Pdf1.CreateDocument;
Pdf1.Compressed := True; // säkerställ att alla strömmar är FlateDecode-komprimerade (standard)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4-storlek
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Spara med alternativ
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // inkrementellt sparande
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // ta bort säkerhet

// Spara till ett specifikt PDF-versionsmål. PDF-versionsvalideraren avvisar
// alla funktioner som introducerats efter det valda målet (JBIG2Decode, MarkInfo,
// AES-kryptering, Polygon / Caret / Watermark / Redact-annoteringar, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

Skapa PDF/A-1b arkivutdata

// SaveAsPdfA efterbehandlar grundsparandet med en inkrementell uppdatering som
// injicerar en XMP-metadataström, en sRGB ICC OutputIntent och en uppdaterad
// dokumentkatalog så att resultatet är PDF/A-1b-konformt. Den medföljande
// sRGB IEC61966-2.1-profilen används automatiskt när ingen anpassad profil
// tillhandahålls.
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 med en anpassad ICC-profil (t.ex. CMYK-utskriftspipelines)
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-utdata i minnet
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;

Validera PDF/A-konformans

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

  // Billig sond i header-stil — läser konformansen utan att iterera
  // den fullständiga valideraren. Resultat: pacNone, pac1a, pac1b, pac2b, pac3b eller
  // 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;

  // Fullständig validerare med per-problem-diagnostik
  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;

Inspektera flera PDF-standarder i StandardsLab

Projekten Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab och Demo\Lazarus\StandardsLab läser in eller skapar en PDF, kör ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR och ValidatePdfVT, och visar sedan varje detekterad konformansnivå och antal problem i ett enda rutnät. Deras sparknappar anropar SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR och SaveAsPdfVT så att arbetsflödet för markörutdata är synligt utan att skriva ett anpassat ramverk.

Generera preflight-rapporter

Enheten FPdfPreflightReport omsluter ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR och ValidatePdfVT till ett rapportobjekt. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport och Demo\CBuilder\PreflightReport låter användare välja standarder, generera en sammanfattning, inspektera status, prioritet, problemkategorier, kategoriantal, nästa åtgärder, problemkoder och rekommenderade åtgärder, växla förhandsgranskningen mellan text, Markdown, JSON och CSV, samt exportera TXT-, HTML-, Markdown-, JSON- eller CSV-rapporter. Delphi GUI-exemplet lägger också till en Action Plan-förhandsgranskning som kondensar ej godkända standarder, prioriteringar, problemmarkeringar och rekommendationer till en åtgärdslista för korrigering. Demo\Delphi\PreflightReportCli exponerar samma rapportväg som ett konsolarbetsflöde med argument för in- / utdata, valfritt lösenord, val av utdataformat inklusive plan / actionplan, standardfiltrering, valfri enkelfilsrapport-inbäddning attach=output.pdf, utgångskoder för CI-grindning failon=, bearbetning av batch=list.txt, katalogskanning batchdir=folder med valfri rekursion och stabil sökvägssorterad bearbetning, rapportruttning outdir=reports, kollisionssäkra rapportnamn för duplicerade indatafilnamn, TXT- / JSON- / HTML-sammanfattningar med aggregerade och standardstatus-totaler, CSV-radmanifest med körningsinställningar som upprepas per objekt, samt ett argumentlöst exempelläge för skript och CI-jobb. Den genererade rapporten anger att de inbyggda validerarna täcker kontroller på markör- och utvalda filnivåer, så att fullständig preflight på innehållsnivå fortfarande kan delegeras till en dedikerad valideringsmotor.

Strömmande inläsning och stora dokument

Läsa in en PDF on-demand med LoadCustomDocument

// LoadCustomDocument låter PDFium hämta block från valfri sökbar TStream on-
// demand. Strömmen kopieras INTE in i minnet, så multi-GB-PDF:er, fjärr-
// HTTP-bodyströmmar och databasbaserade källor är alla genomförbara utan
// en buffert i förväg.
var
  FileStream: TFileStream;
begin
  // Strömmande från fil (ingen minneskopia)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ ägandeskap överfört — Pdf1 frigör FileStream vid UnloadDocument.
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Externt ägd ström (anroparen frigör den efter 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;

Asynkron avbrytning och progressiv rendering

Kooperativa avbrytningstoken

// FPdfAsync exponerar IPdfCancellationToken /
// IPdfCancellationTokenSource för avbrytning-från-UI-semantik som fungerar
// över de långvariga export- och renderingsvägarna.
uses FPdfAsync;

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

  // ... användaren klickar på Avbryt ...
  CancelSource.Cancel;

  // Workern kontrollerar periodvis CancelToken.IsCancelled, eller renderingen
  // pollar den via PDFiums IFSDK_PAUSE-callback.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

Avbrytbar progressiv sidrendering

// RenderPageProgressive låter en lång rendering med hög DPI avbryta mitt i sidan när
// användaren klickar på Avbryt, istället för att blockera anroparen tills klar.
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;

Köra renderingar på en bakgrundstråd med TPdfFuture

// TPdfFuture<T> startar en worker på en bakgrundstråd och postar tillbaka ett
// TPdfFutureResult<T>-kuvert till huvudtråden.
//
// Viktigt: TPdf serialiserar renderingsanrop per instans, men workern äger fortfarande
// dispatch och dokumentets livstid. Mutera inte en TPdf medan en rendering pågår;
// batch-export håller vanligtvis en TPdf per fil.
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;

Transformationsmatriser

Komponera Translate / Scale / Rotate / Skew

// FPdfMatrix omsluter PDFiums FS_MATRIX så att anropare kan bygga en transformation
// deklarativt och överlämna den till FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // flytta 1in höger, ~2.8in upp
    M.Scale(0.5, 0.5);          // halvstorleksstämpel
    M.Rotate(15);               // 15-grader CCW rotation
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // post-multiplicera med en annan matris

    // Tillämpa på valfritt PDFium-sidobjekt (text / sökväg / bild / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Sidkomposition

Importera en delmängd av sidor efter index

// ImportPagesByIndex kopierar en explicit nollbaserad indexarray från en
// käll-PDF in i detta dokument. InsertAt = 0 infogar före sida 1;
// PageCount lägger till på slutet.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Importera sidor 0, 2, 4 (ettbaserat: sidor 1, 3, 5) från Source
    // och lägg till dem i slutet av Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Tom array importerar alla källsidor
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

N-up kompositionsutdata

// ImportNPagesToOne returnerar en helt ny TPdf vars sidor är NumX*NumY
// kompositioner av DETTA dokument. OutputWidth/Height är PDF-användarenheter
// (1 enhet = 1/72 in).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 liggande: 842 x 595 pt, 2 kolumner x 2 rader
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Omordning av sidor på plats

// MovePages tar de nollbaserade indexen för de sidor som ska flyttas plus
// destinationsindexet för den första flyttade sidan efter att flyttningen är klar.
// Andra sidor förskjuts runt det flyttade blocket så att dokumentlängden förblir
// densamma.
//
// Exempel: i ett 5-sidigt dokument med sidor numrerade [0,1,2,3,4],
// MovePages([2, 3], 0) omordnar till [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

Vattenstämpling via återanvändning av Form XObject-sidor

// CreateXObjectFromPage tillverkar ett återanvändbart Form XObject-handle från
// valfri sida av valfri annan TPdf. Stämpla det handlet på så många sidor av
// DETTA dokument du vill — varje stämpel är ett enda PDFium-sidobjekt
// som kan positioneras, skalas och roteras via matris-API:et.
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;

    // Omslut sida 0 av WatermarkSource som ett återanvändbart 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;

        // Centrera vattenstämpeln på sidan
        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; // stänger FPDF_XOBJECT-handlet
    end;

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

Projekten Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp och Demo\CBuilder\WatermarkStamp gör detta mönster till ett körbart arbetsflöde: de skapar en mål-PDF och en återanvändbar stämpel-PDF, omsluter stämpelsidan med CreateXObjectFromPage, infogar samma Form XObject på varje målsida, tillämpar TPdfMatrix-transformationer för centrerade vattenstämplar och hörnstämplar uppe till höger, och lägger till valfria sidnummeretiketter innan resultatet sparas.

Bildinfogning (direkta AddImage-överbelastningar)

Lägga till en registrerad bildfil eller en TBitmap

// AddImage(FileName) accepterar alla format registrerade med VCL- eller LCL-
// grafikenheter (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) hoppar över TPicture-mellansteget, idealt när
// bitmappen redan kommer från renderings- eller batch-genereringsarbete.
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;

Projekten Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF och Demo\Lazarus\ImageToPDF gör detta till ett fullständigt batch-arbetsflöde: välj flera bildfiler, förhandsgranska det valda objektet, skapa en PDF-sida per bild, skala varje bild till en A4 stående eller liggande sida, spara resultatet och öppna den genererade PDF:en.

Sökmarkering i visaren

Markera varje träff på aktuell sida

var
  MatchCount: Integer;
begin
  // Måla en clYellow-mask över varje träff på vyns aktuella sida.
  // Byte till en annan sida rensar markeringen automatiskt.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // Programmatiskt ta bort överlägget innan sidan ändras
  PdfView1.ClearHighlight;
end;

Visarens layout och sidfärg

Anpassningsläge och sidbakgrund

// FitMode håller långa dokument inramade under Resize eller sidbyten.
// Att sätta Zoom direkt avbryter FitMode (det återgår till pfmNone).
PdfView1.FitMode := pfmFitPage;       // hela sidan i viewporten
// PdfView1.FitMode := pfmFitWidth;   // sidbredden matchar viewportbredden
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor separerar den renderade PDF-sidan från värdkontrollens Color
// — typisk inställning för visare i mörkt läge håller rullningsområdet mörkt medan
// PDF-sidan förblir pappersvit.
PdfView1.Color     := clBlack;        // rullningsområde
PdfView1.PageColor := clWhite;        // PDF-sidans bakgrund

// Valfri skuggeffekt och sidram för att lyfta sidan från en mörk bakgrund
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

Utforska visarinteraktion i ViewerInteractionLab

Projekten Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab och Demo\Lazarus\ViewerInteractionLab håller den moderna interaktionsytan för TPdfView i ett kompakt formulär: öppna eller skapa en exempel-PDF, växla DisplayMode, tillämpa FitMode, ändra PageColor, rotera sidor, markera sökträffar på aktuell sida med HighlightSearchText, aktivera eller inaktivera användartextmarkering, inspektera SelectedText, anropa SelectAll, CopySelectionToClipboard och ClearSelection, och se DeviceToPage-koordinater uppdateras i statusraden medan musen rör sig över sidan.

Söka och markera text i SearchAndSelect

Projekten Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect och Demo\Lazarus\SearchAndSelect gör sök-och-markera-arbetsflödet i visaren till fristående exempel. De öppnar eller skapar en PDF, listar sökresultat över alla sidor med sidnummer, teckenindex och förhandsvisningstext, stödjer Föregående / Nästa-navigering och dubbelklicksresultatshopp, markerar träffar på aktuell sida med HighlightSearchText, exponerar alternativ för match-case och hela ord, växlar AllowUserTextSelection, förhandsvisar SelectedText, driver SelectAll, CopySelectionToClipboard och ClearSelection från knappar, och låter användare växla mellan enkel- och uppslagsvisningslägen.

Läsa PDF-utskriftsinställningar i PrintPreferences

Projekten Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences och Demo\Lazarus\PrintPreferences läser författarens utskriftsinställningar innan utskrift. De visar PrintCopies, PrintPageRanges, PrintScaling och PrintPaperHandling, kan kopiera författarspecificerade kopior och intervall in i utskriftsindata, och visar hur SetPdfPrintPaperHandlingDevMode mappar PDF-duplex-avsikt till en Windows DEVMODE.

Standard PDF-utskrift i PrintPDF

Projekten Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF och Demo\Lazarus\PrintPDF visar standardutskriftsarbetsflödet: öppna en PDF, förhandsgranska sidor, välj sidintervall och kopior i plattformens utskriftsdialog, hedra kollationeringsläge, rendera varje sida via TPdf.RenderPage och exponera utskriftsprogress med avbrytning. Delphi-exemplet accepterar också släppta PDF-filer för snabb inläsning.

Snabb flersidvisning i MultiPageViewer

Projekten Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer och Demo\Lazarus\MultiPageViewer övar kontinuerlig, uppslags- och omslagsmedveten visning med vanliga sidnavigerings- och zoomarbetsflöden. De är också användbara för att testa öppna-till-första-sida-hastighet och renderingsresponsivitet på mycket stora PDF:er: i tester med stora filer kan 2,x GB-dokument som kan få Acrobat eller Foxit att spendera 20-30 sekunder innan första vy bli användbara nästan omedelbart via PDFiumPas-visarvägen.

Sida-vid-sida-granskning i SplitView

Projekten Demo\Delphi\SplitView, Demo\CBuilder\SplitView och Demo\Lazarus\SplitView visar sida-vid-sida-PDF-granskning. C++Builder- och Lazarus-exemplen fokuserar på två- eller trefönsterjämförelse, val av aktiv vy, valfria synkroniserade navigeringskommandon, delade zoomförinställningar och rotation för den valda vyn eller alla inlästa vyer. Använd dessa exempel för att jämföra stora dokument samtidigt som första användbara vy och efterföljande sidrendering förblir responsiv; samma 2,x GB PDF-filer som kan ta Acrobat eller Foxit 20-30 sekunder att öppna kan bli användbara nästan omedelbart i PDFiumPas-visarvägen.

Granska PDF-riskytan i SecurityAudit

Projekten Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit och Demo\Lazarus\SecurityAudit öppnar en PDF skrivskyddat och listar rättighetsstatus, inbäddade bilagor, dokument-JavaScript-åtgärder, URI- och Launch-länkannoteringar, webblänkar, signatursammanfattningar, XFA-status, V8-hjälpartillgänglighet och callbacks för funktioner som inte stöds. Samma sammanfattning och fyndrader kan sparas eller kopieras som en TXT-revisionsrapport eller sparas som strukturerad JSON. De är avsedda som riskpaneler för värdapplikationer, inte som malware-detektering.

Hantering av visarens länkar

Säkert klick-för-att-följa med tillåtelselistor per åtgärd

// LinkOptions kontrollerar automatisk hantering av de fyra PDF-länkåtgärds-
// typerna. Standardvärden aktiverar endast goto och URI; launch (kör ett program) och
// inbäddad GotoR (hoppa till en annan fil) förblir AVSTÄNGDA så att ett oavsiktligt klick inte kan
// köra godtycklig kod.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // säkra standardvärden

// OnAnnotationLinkClick aktiveras för dokumentinterna destinationer (sidhopp,
// namngivna destinationer, åtgärdskedjor). Markera Handled = True för att förhindra
// att vyn auto-följer länken.
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;  // undertryck auto-hantering
  end;
end;

// OnWebLinkClick aktiveras för URI-strängar som upptäckts av PDFiums webblänk-
// skanner (auto-detekterade URL:er i textströmmen, även utan en
// /A <</S/URI...>>-annotering).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

Redigering av formulärwidgets i visaren

Markering och Undo / Redo

// På den fokuserade AcroForm-widgeten driver sex nya metoder PDFiums
// formulärredigeringshistorik. Alla sex kortsluter säkert om ingen widget är fokuserad eller
// dokumentet inte bär något AcroForm.
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;

Låsa ned visaren för kiosker

Inaktivera sidnavigeringsgester

// Alla tre är som standard True så att befintliga appar uppgraderas utan beteende-
// ändring. Vänd dem till False för kiosker, förhandsgranskningsfält eller skrivskyddade
// integrationer utan att underklassa TPdfView.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // hjul upp/ned-hopp
PdfView1.AllowUserTextSelection    := False;  // mus/tangentbord textmarkering

// Ctrl+hjul är reserverat för zoom och hoppas över av navigerings-
// hanteraren oavsett ChangePageOnMouseScrolling.

Sidopanel för miniatyrbilder

Länka en TPdfThumbnailView till en TPdfView

// TPdfThumbnailView (enhet FPdfThumbnail) renderar en sidminiatyrbild per
// rad i en rullbar sidopanel.
uses FPdfThumbnail;

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

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex är nollbaserad
end;

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

Kooperativt avbrytbar batch-export

Flerfils PDF till JPG med progress per fil och per sida

// Mönster som används av Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// och Demo\Lazarus\BatchExport. En TPdf per fil, IPdfCancellationToken
// för avbrytning-från-UI, TPdfStreamAdapter för buffrad filinläsning.
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;

Visarens koordinater och hit-testning

Anpassa aktuell sida och inspektera text under musen

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;

Felhantering

Robusta PDF-operationer

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 och tillgänglighet

Detektera Tagged PDF och gå igenom strukturträdet

// IsTagged är en billig katalogsond — True när /StructTreeRoot existerar.
// StructureElements materialiserar trädet till en plan array av
// TPdfStructureElement-poster med typ, titel, alternativ text, faktisk
// text, expansionstext, språk, nivå, förälderindex och antal barn / märkta
// innehåll / attribut.
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;

Val av utskriftsläge

Ange EMF / PostScript / Image-Mask-utskriftsläge

// SetPdfPrintMode är en global funktion som mappar till PDFiums
// FPDF_SetPrintMode. Använd matchande TPdfPrintMode-värde för
// skrivardrivrutinen som används.
SetPdfPrintMode(pmEmf);                   // GDI EMF (standard)
// SetPdfPrintMode(pmTextOnly);           // endast text-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-typsnitt

Läsa författarens avsedda utskriftsinställningar

// PrintCopies, PrintPageRanges, PrintScaling och PrintPaperHandling är
// poster i visningsinställningsordboken — de kodar vad
// dokumentförfattaren ville att PDF-läsare skulle använda som standard.
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
    Ord(Pdf1.PrintScaling)));

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

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

Dokumentnivå-JavaScript-åtgärder

Räkna upp namnträdets JavaScript

// JavaScript på dokumentnivå lagras i namnträdet /Names /JavaScript.
// JavaScriptAction[i] returnerar en post; JavaScriptActions returnerar
// den fullständiga arrayen.
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;

Prestandatips

Optimera PDF-operationer

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-renderingsväg (standard sedan v1.21.0): RenderPage skriver
// direkt till destinationens TBitmap-DIB-buffert när anropare skickar en
// förallokerad bitmap, vilket undviker en width * height * 4 bytes-kopia.
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;