PDFiumPas Dokümantasyon

PDFium VCL Programlama Örnekleri

Temel Belge İşlemleri

Bir PDF Belgesi Yükleme ve Görüntüleme

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;

Yeni Bir PDF Belgesi Oluşturma

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;

Metin İşlemleri

PDF'den Metin Çıkarma

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;

Metin Arama

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;

Biçimlendirme ile Metin Ekleme

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 ve Yazı Tipi Metaverisini İnceleme

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;

Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties ve Demo\Lazarus\FontProperties projeleri aynı metaveriyi hem TPdf hem de TPdfView aracılığıyla gösterir.

Çok Kategorili İçerik Çıkarma Raporu Oluşturma

Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab ve Demo\Lazarus\ContentExtractionLab projeleri içerik çıkarmayı tek bir iş akışına dönüştürür. Bir PDF açar veya oluşturur, kullanıcıların metaveri, sayfa metni, sayfa nesneleri, görüntüler, ekler, bağlantılar, ek açıklamalar, yer imleri ve yazı tipi / karakter metriklerini seçmesine izin verir, ardından bir özet artı ayrıntılı bir TXT veya JSON raporu oluşturur. Lab ayrıca tüm / hiçbiri kategori seçimi, raporu panoya kopyalama ve kullanıcıların oluşturulan örnek PDF'yi çıkarma raporunun yanında tutabilmesi için kaynak PDF kayıt eylemleri içerir. Daha eski ExtractText, ExtractImages, Attachment ve FontProperties demoları odaklanmış örnekler olmaya devam etmektedir; ExtractText, Delphi, C++Builder ve Lazarus genelinde sayfa başına metin çıktısı üretmek için sayfa aralığı ve sayfa ayırıcı seçeneklerini desteklerken, ContentExtractionLab kesişen bir envanter örneğidir.

Form İşlemleri

Form Alanlarıyla Çalışma

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;

Doldurulmuş Form Değerlerini Sayfada Düzleştirme

// Without flattening, FormField[i] := value writes only the /V entry —
// the visible appearance comes from the /AP stream and may not refresh.
// GenerateFormAppearances regenerates /AP for every widget, then
// FlattenAllPages bakes every page's annotations and widgets into
// permanent, non-editable content.
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;

Görüntü İşlemleri

Görüntü Çıkarma

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;

Görüntü Ekleme

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;

Ek Açıklama İşlemleri

Ek Açıklamalarla Çalışma

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;

Çizim İşlemleri

Şekiller Çizme

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;

Navigasyon ve Yer İmleri

Bookmarks ile Çalışma

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;

Adlandırılmış Hedeflerle Çalışma

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;

Ek İşlemleri

Eklerle Çalışma

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;

Sayfa Yönetimi

Sayfa İşlemleri

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

Kaydetme ve Sıkıştırma

Sıkıştırma ile Kaydetme

// Create a compressed PDF document
Pdf1.CreateDocument;
Pdf1.Compressed := True; // ensure all streams are FlateDecode compressed (default)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4 size
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Save with options
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // incremental save
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // remove security

// Save to a specific PDF version target. The PDF version validator rejects
// any feature introduced after the chosen target (JBIG2Decode, MarkInfo,
// AES encryption, Polygon / Caret / Watermark / Redact annotations, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

PDF/A-1b Arşiv Çıktısı Oluşturma

// SaveAsPdfA post-processes the base save with an incremental update that
// injects an XMP metadata stream, an sRGB ICC OutputIntent, and an updated
// document catalog so the result is PDF/A-1b conformant. The bundled
// sRGB IEC61966-2.1 profile is used automatically when no custom profile
// is supplied.
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 with a custom ICC profile (CMYK printing pipelines for example)
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;

// In-memory PDF/A output
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 Uyumluluğunu Doğrulama

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

  // Cheap header-style probe — reads the conformance without iterating
  // the full validator. Result: pacNone, pac1a, pac1b, pac2b, pac3b, or
  // 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;

  // Full validator with per-issue diagnostics
  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;

StandardsLab'da Birden Çok PDF Standardını İnceleme

Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab ve Demo\Lazarus\StandardsLab projeleri bir PDF yükler veya oluşturur, ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR ve ValidatePdfVT çalıştırır, ardından algılanan her uyumluluk seviyesini ve sorun sayısını tek bir ızgarada gösterir. Kaydet düğmeleri SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR ve SaveAsPdfVT çağırır, böylece işaretleyici çıktı iş akışı özel bir koşum yazmadan görünür hale gelir.

Preflight Raporları Oluşturma

FPdfPreflightReport birimi, ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR ve ValidatePdfVT'yi tek bir rapor nesnesine sarmalar. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport ve Demo\CBuilder\PreflightReport kullanıcıların standartları seçmesine, bir özet oluşturmasına, durumu, önceliği, sorun kategorilerini, kategori sayılarını, sonraki eylemleri, sorun kodlarını ve önerilen eylemleri incelemesine, önizlemeyi metin, Markdown, JSON ve CSV arasında değiştirmesine ve TXT, HTML, Markdown, JSON veya CSV raporları dışa aktarmasına izin verir. Delphi GUI örneği ayrıca, geçmeyen standartları, öncelikler, sorun kodları ve önerileri bir düzeltme kontrol listesinde toparlayan bir Action Plan önizlemesi de ekler. Demo\Delphi\PreflightReportCli, aynı rapor yolunu giriş / çıkış argümanları, isteğe bağlı parola, çıktı formatı seçimi içinde plan / actionplan, standart filtreleme, isteğe bağlı tek dosya attach=output.pdf rapor gömme, failon= CI geçit çıkış kodları, batch=list.txt işleme, isteğe bağlı özyineleme ve kararlı yol sıralı işleme ile batchdir=folder dizin tarama, outdir=reports rapor yönlendirme, yinelenen giriş dosya adları için çarpışma güvenli rapor adları, toplam ve standart durum toplamları içeren TXT / JSON / HTML özetler, öğe başına tekrarlanan çalıştırma ayarlarıyla CSV satır manifestoları ve betikler ve CI işleri için argümansız bir örnek mod ile konsol iş akışı olarak ortaya çıkarır. Oluşturulan rapor, yerleşik doğrulayıcıların işaretleyici düzeyinde ve seçilmiş dosya düzeyinde kontrolleri kapsadığını belirtir, böylece tam içerik düzeyindeki preflight, özel bir doğrulama motoruna devredilebilir.

Akış Yükleme ve Büyük Belgeler

LoadCustomDocument ile İstek Üzerine PDF Yükleme

// LoadCustomDocument lets PDFium pull blocks from any seekable TStream on
// demand. The stream is NOT copied into memory, so multi-GB PDFs, remote
// HTTP body streams, and database-backed sources are all viable without
// an up-front buffer.
var
  FileStream: TFileStream;
begin
  // Read-from-file streaming (no in-memory copy)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ ownership transferred — Pdf1 frees FileStream on UnloadDocument.
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Externally-owned stream (caller frees it after 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;

Asenkron İptal ve Aşamalı Render

İşbirlikçi İptal Belirteçleri

// FPdfAsync exposes IPdfCancellationToken /
// IPdfCancellationTokenSource for cancel-from-UI semantics that work
// across the long-running export and render paths.
uses FPdfAsync;

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

  // ... user clicks Cancel ...
  CancelSource.Cancel;

  // The worker periodically checks CancelToken.IsCancelled, or the render
  // path polls it through PDFium's IFSDK_PAUSE callback.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

İptal Edilebilir Aşamalı Sayfa Render

// RenderPageProgressive lets a long high-DPI render abort mid-page when
// the user clicks Cancel, instead of blocking the caller until done.
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;

TPdfFuture ile Arka Plan Thread'inde Render Çalıştırma

// TPdfFuture<T> spins up a worker on a background thread and posts a
// TPdfFutureResult<T> envelope back to the main thread.
//
// Important: TPdf serializes render calls per instance, but the worker still
// owns dispatch and document lifetime. Do not mutate a TPdf while a render is
// in flight; batch export usually keeps one 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('Cancelled');
        pfsFailed:    Log.Add('Worker failed: ' + AResult.ErrorMessage);
      end;
    end);
end;

Dönüşüm Matrisleri

Translate / Scale / Rotate / Skew Kompozisyonu

// FPdfMatrix wraps PDFium's FS_MATRIX so callers can build a transform
// declaratively and hand it to FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // move 1in right, ~2.8in up
    M.Scale(0.5, 0.5);          // half-size stamp
    M.Rotate(15);               // 15-degree CCW rotation
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // post-multiply with another matrix

    // Apply to any PDFium page object (text / path / image / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Sayfa Kompozisyonu

Dizine Göre Sayfa Alt Kümesi İçeri Aktarma

// ImportPagesByIndex copies an explicit zero-based index array from a
// source PDF into this document. InsertAt = 0 inserts before page 1;
// PageCount appends.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Import pages 0, 2, 4 (one-based: pages 1, 3, 5) from Source
    // and append them at the end of Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Empty array imports every source page
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

N-up Bileşik Çıktı

// ImportNPagesToOne returns a brand-new TPdf whose pages are NumX*NumY
// composites of THIS document. OutputWidth/Height are PDF user units
// (1 unit = 1/72 in).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 landscape: 842 x 595 pt, 2 columns x 2 rows
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Sayfaları Yerinde Yeniden Sıralama

// MovePages takes the zero-based indices of the pages to move plus the
// destination index for the first moved page after the move completes.
// Other pages shift around the moved block so the document length stays
// the same.
//
// Example: in a 5-page document with pages numbered [0,1,2,3,4],
// MovePages([2, 3], 0) reorders to [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

Form XObject Sayfa Yeniden Kullanımı ile Filigran

// CreateXObjectFromPage manufactures a reusable Form XObject handle from
// any page of any other TPdf. Stamp that handle onto as many pages of
// THIS document as you want — each stamp is a single PDFium page object
// that can be positioned, scaled, and rotated through the 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;

    // Wrap page 0 of WatermarkSource as a reusable 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;

        // Centre the watermark on the page
        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; // closes the FPDF_XOBJECT handle
    end;

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

Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp ve Demo\CBuilder\WatermarkStamp projeleri bu deseni çalışır bir iş akışına dönüştürür: bir hedef PDF ve yeniden kullanılabilir bir damga PDF oluşturur, damga sayfasını CreateXObjectFromPage ile sarmalar, aynı Form XObject'i her hedef sayfaya ekler, ortalanmış filigranlar ve sağ üst damgalar için TPdfMatrix dönüşümleri uygular ve sonucu kaydetmeden önce isteğe bağlı sayfa numarası etiketleri ekler.

Görüntü Ekleme (Doğrudan AddImage Aşırı Yüklemeleri)

Kayıtlı Görüntü Dosyası veya TBitmap Ekleme

// AddImage(FileName) accepts any format registered with the VCL or LCL
// graphics units (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) skips the TPicture intermediary, ideal when the
// bitmap already comes from rendering or batch generation work.
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;

Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF ve Demo\Lazarus\ImageToPDF projeleri bunu tam toplu iş akışına dönüştürür: birden çok görüntü dosyası seçin, seçili öğeyi önizleyin, görüntü başına bir PDF sayfası oluşturun, her görüntüyü bir A4 dikey veya yatay sayfaya ölçeklendirin, sonucu kaydedin ve oluşturulan PDF'yi açın.

Görüntüleyici Arama Vurgulama

Mevcut Sayfadaki Her Eşleşmeyi Vurgulayın

var
  MatchCount: Integer;
begin
  // Paint a clYellow mask over every match on the view's current page.
  // Switching to a different page clears the highlight automatically.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // Programmatically remove the overlay before the page changes
  PdfView1.ClearHighlight;
end;

Görüntüleyici Düzeni ve Sayfa Rengi

Sığdırma Modu ve Sayfa Arka Planı

// FitMode keeps long documents framed during Resize or page changes.
// Setting Zoom directly cancels FitMode (it reverts to pfmNone).
PdfView1.FitMode := pfmFitPage;       // whole page in viewport
// PdfView1.FitMode := pfmFitWidth;   // page width matches viewport width
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor decouples the rendered PDF page from the host control's Color
// — typical dark-mode viewer setup keeps the scroll area dark while the
// PDF page stays paper-white.
PdfView1.Color     := clBlack;        // scroll area
PdfView1.PageColor := clWhite;        // PDF page background

// Optional drop shadow and page border to lift the page off a dark bg
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

ViewerInteractionLab'da Görüntüleyici Etkileşimini Keşfetme

Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab ve Demo\Lazarus\ViewerInteractionLab projeleri modern TPdfView etkileşim yüzeyini tek bir kompakt formda tutar: bir örnek PDF açın veya oluşturun, DisplayMode değiştirin, FitMode uygulayın, PageColor değiştirin, sayfaları döndürün, mevcut sayfa arama isabetlerini HighlightSearchText ile vurgulayın, kullanıcı metin seçimini etkinleştirin veya devre dışı bırakın, SelectedText'i inceleyin, SelectAll, CopySelectionToClipboard ve ClearSelection çağırın ve fare sayfa üzerinde hareket ederken durum çubuğunda DeviceToPage koordinatlarının güncellenmesini izleyin.

SearchAndSelect'te Metin Arama ve Seçme

Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect ve Demo\Lazarus\SearchAndSelect projeleri arama ve seçim görüntüleyici iş akışını bağımsız örneklere dönüştürür. Bir PDF açarlar veya oluştururlar, sayfa numarası, karakter dizini ve önizleme metniyle tüm sayfa arama sonuçlarını listelerler, Önceki / Sonraki navigasyonu ve çift tıklama sonucu atlamayı desteklerler, mevcut sayfa eşleşmelerini HighlightSearchText ile vurgularlar, büyük/küçük harf ve tam sözcük seçeneklerini ortaya çıkarırlar, AllowUserTextSelection'ı açıp kapatırlar, SelectedText'i önizlerler, düğmelerden SelectAll, CopySelectionToClipboard ve ClearSelection'ı sürerler ve kullanıcıların tek ve yayılım görüntüleme modları arasında geçiş yapmasına izin verirler.

PrintPreferences'ta PDF Yazdırma Tercihlerini Okuma

Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences ve Demo\Lazarus\PrintPreferences projeleri yazdırma öncesi yazar yazdırma tercihlerini okur. PrintCopies, PrintPageRanges, PrintScaling ve PrintPaperHandling gösterirler, yazar tarafından sağlanan kopyalar ve aralıkları yazdırma girişlerine kopyalayabilirler ve SetPdfPrintPaperHandlingDevMode'un PDF dupleks amacını bir Windows DEVMODE'a nasıl eşlediğini gösterirler.

PrintPDF'de Standart PDF Yazdırma

Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF ve Demo\Lazarus\PrintPDF projeleri standart yazdırma iş akışını gösterir: bir PDF açın, sayfaları önizleyin, platform yazdırma iletişim kutusunda sayfa aralıkları ve kopyalar seçin, harmanlama modunu onurlandırın, her sayfayı TPdf.RenderPage aracılığıyla render edin ve iptalle birlikte yazdırma ilerlemesini ortaya çıkarın. Delphi örneği ayrıca hızlı yükleme için bırakılan PDF dosyalarını kabul eder.

MultiPageViewer'da Hızlı Çok Sayfalı Görüntüleme

Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer ve Demo\Lazarus\MultiPageViewer projeleri düzenli sayfa navigasyonu ve yakınlaştırma iş akışlarıyla sürekli, yayılım ve kapak farkındalıklı görüntülemeyi çalıştırır. Çok büyük PDF'lerde ilk sayfaya açılma hızını ve render duyarlılığını test etmek için de yararlıdırlar: büyük dosya testlerinde, Acrobat veya Foxit'in ilk görünüm öncesi 20-30 saniye harcayabildiği 2.x GB belgeler PDFiumPas görüntüleyici yolu aracılığıyla neredeyse anında kullanılabilir hale gelebilir.

SplitView'da Yan Yana İnceleme

Demo\Delphi\SplitView, Demo\CBuilder\SplitView ve Demo\Lazarus\SplitView projeleri yan yana PDF incelemesi gösterir. C++Builder ve Lazarus örnekleri iki veya üç bölmeli karşılaştırma, aktif görünüm seçimi, isteğe bağlı senkronize navigasyon komutları, paylaşılan yakınlaştırma ön ayarları ve seçili görünüm veya tüm yüklü görünümler için döndürmeye odaklanır. Bu örnekleri, ilk kullanılabilir görünümü ve sonraki sayfa render'ı duyarlı tutarken büyük belgeleri karşılaştırmak için kullanın; Acrobat veya Foxit'in açması 20-30 saniye sürebilen aynı 2.x GB PDF dosyaları PDFiumPas görüntüleyici yolunda neredeyse anında kullanılabilir hale gelebilir.

SecurityAudit'te PDF Risk Yüzeyi Denetimi

Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit ve Demo\Lazarus\SecurityAudit projeleri bir PDF'yi salt okunur açar ve izin durumu, gömülü ekler, belge JavaScript eylemleri, URI ve Launch bağlantı ek açıklamaları, web bağlantıları, imza özetleri, XFA durumu, V8 yardımcı kullanılabilirliği ve desteklenmeyen özellik geri çağrılarını listeler. Aynı özet ve bulgu satırları, bir TXT denetim raporu olarak kaydedilebilir veya kopyalanabilir veya yapılandırılmış JSON olarak kaydedilebilir. Bunlar, kötü amaçlı yazılım algılaması değil, host uygulama risk panelleri olarak amaçlanmıştır.

Görüntüleyici Bağlantı İşleme

Eylem Başına İzin Listeleri ile Güvenli Tıkla-Takip Et

// LinkOptions controls automatic handling of the four PDF link action
// types. Defaults enable goto and URI only; launch (run a program) and
// embedded GotoR (jump to another file) stay OFF so a stray click cannot
// run arbitrary code.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // safe defaults

// OnAnnotationLinkClick fires for in-document destinations (page jumps,
// named destinations, action chains). Mark Handled = True to prevent the
// view from auto-following the link.
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;  // suppress auto-handling
  end;
end;

// OnWebLinkClick fires for URI strings discovered by PDFium's web-link
// scanner (auto-detected URLs in the text stream, even without an
// /A <</S/URI...>> annotation).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

Görüntüleyici Form Widget Düzenleme

Seçim ve Geri Al / Yinele

// On the focused AcroForm widget, six new methods drive PDFium's form
// edit history. All six short-circuit safely if no widget is focused or
// the document carries no 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;

Kiosk'lar için Görüntüleyiciyi Kilitleme

Sayfa Navigasyon Hareketlerini Devre Dışı Bırakın

// All three default to True so existing apps upgrade with no behaviour
// change. Flip them False for kiosks, preview panes, or read-only
// embeddings without subclassing TPdfView.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // wheel up/down jumps
PdfView1.AllowUserTextSelection    := False;  // mouse/keyboard text selection

// Ctrl+wheel is reserved for zoom and is skipped by the navigation
// handler regardless of ChangePageOnMouseScrolling.

Küçük Resim Yan Paneli

Bir TPdfThumbnailView'i bir TPdfView'e Bağlama

// TPdfThumbnailView (unit FPdfThumbnail) renders one page thumbnail per
// row in a scrollable side panel.
uses FPdfThumbnail;

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

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

procedure TForm1.PdfView1PageNumberDeğiştirildi (Changed)(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

İşbirlikçi İptal Edilebilir Toplu Dışa Aktarma

Dosya Başına ve Sayfa Başına İlerlemeyle Çoklu Dosya PDF'den JPG'ye

// Pattern used by Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// and Demo\Lazarus\BatchExport. One TPdf per file, IPdfCancellationToken
// for cancel-from-UI, TPdfStreamAdapter for buffered 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;

Görüntüleyici Koordinatları ve İsabet Testi

Mevcut Sayfayı Sığdırın ve Fare Altındaki Metni İnceleyin

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;

Hata İşleme

Sağlam PDF İşlemleri

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;

Etiketli PDF ve Erişilebilirlik

Etiketli PDF Algılama ve Yapı Ağacını Dolaşma

// IsTagged is a cheap catalog probe — True when /StructTreeRoot exists.
// StructureElements materialises the tree into a flat array of
// TPdfStructureElement records with type, title, alternate text, actual
// text, expansion text, language, level, parent index, and 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;

Yazdırma Modu Seçimi

EMF / PostScript / Görüntü-Maskesi Yazdırma Modu Ayarlama

// SetPdfPrintMode is a global function that maps to PDFium's
// FPDF_SetPrintMode. Use the matching TPdfPrintMode value for the
// printer driver in use.
SetPdfPrintMode(pmEmf);                   // GDI EMF (default)
// SetPdfPrintMode(pmTextOnly);           // text-only 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

Yazar-Amaçlı Yazdırma Ayarlarını Okuma

// PrintCopies, PrintPageRanges, PrintScaling, and PrintPaperHandling are
// the viewer-preference dictionary entries — they encode what the
// document author wanted PDF readers to default to.
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 ranges
  for Range in Ranges do
    Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));

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

Belge JavaScript Eylemleri

Ad-Ağacı JavaScript'ini Listeleme

// Document-level JavaScript is stored in the /Names /JavaScript name
// tree. JavaScriptAction[i] returns one entry; JavaScriptActions returns
// the full 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;

Performans İpuçları

PDF İşlemlerini Optimize Etme

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 render path (default since v1.21.0): RenderPage writes
// directly into the destination TBitmap's DIB buffer when callers pass a
// pre-allocated bitmap, avoiding one width * height * 4 bytes copy.
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;