PDFium Component Docs

نمونه‌های برنامه‌نویسی PDFium VCL

عملیات پایه سند

بارگذاری و نمایش یک سند PDF

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;

ایجاد یک سند PDF جدید

Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;

عملیات متن

استخراج متن از 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;

جستجوی متن

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;

افزودن متن با قالب‌بندی

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;

بررسی فراداده نویسه و فونت

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 و Demo\Lazarus\FontProperties همین فراداده را هم از طریق TPdf و هم از طریق TPdfView نمایش می‌دهند

ساخت گزارش استخراج محتوای چنددسته‌ای

پروژه‌های Demo\Delphi\ContentExtractionLab، Demo\CBuilder\ContentExtractionLab و Demo\Lazarus\ContentExtractionLab استخراج محتوا را به یک گردش‌کار واحد تبدیل می‌کنند. آن‌ها یک PDF را باز می‌کنند یا می‌سازند، به کاربر اجازه می‌دهند فراداده، متن صفحه، اشیای صفحه، تصویرها، پیوست‌ها، پیوندها، حاشیه‌نویسی‌ها، نشانک‌ها و شاخص‌های فونت / نویسه را انتخاب کند، سپس یک خلاصه همراه با گزارش تفصیلی TXT یا JSON تولید می‌کنند. این آزمایشگاه همچنین انتخاب همه / هیچ دسته‌ها، کپی گزارش به کلیپ‌بورد و ذخیره PDF مبدأ را فراهم می‌کند تا کاربر بتواند PDF نمونه تولیدشده را کنار گزارش استخراج آن نگه دارد. نمونه‌های قدیمی‌تر ExtractText، ExtractImages، Attachment و FontProperties همچنان مثال‌های متمرکز باقی می‌مانند؛ ExtractText از گزینه‌های بازه صفحه و جداکننده صفحه پشتیبانی می‌کند تا خروجی متنی per-page را در Delphi، C++Builder و Lazarus تولید کند، در حالی که ContentExtractionLab نمونه فهرست‌برداری میان‌برشی است

عملیات فرم

کار با فیلدهای فرم

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;

تخت‌کردن مقادیر فرم پرشده در صفحه

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

عملیات تصویر

استخراج تصویرها

var
  I: Integer;
  Bitmap: TBitmap;
begin
  for I := 0 to Pdf1.BitmapCount - 1 do
  begin
    Bitmap := Pdf1.Bitmap[I];
    try
      Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
    finally
      Bitmap.Free;
    end;
  end;
end;

افزودن تصویرها

var
  JpegStream: TFileStream;
begin
  JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
  try
    Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
  finally
    JpegStream.Free;
  end;
  Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
  Pdf1.UpdatePage;
end;

عملیات حاشیه‌نویسی

کار با حاشیه‌نویسی‌ها

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

عملیات ترسیم

رسم شکل‌ها

Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;

Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;

پیمایش و Bookmarkها

کار با Bookmarkها

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;

کار با مقصدهای نام‌دار

var
  I: Integer;
  Destination: TDestination;
begin
  for I := 0 to Pdf1.DestinationCount - 1 do
  begin
    Destination := Pdf1.Destination[I];
    if Destination.Name = 'Chapter1' then
    begin
      Pdf1.PageNumber := Destination.PageNumber;
      Break;
    end;
  end;
end;

عملیات پیوست

کار با پیوست‌ها

var
  I: Integer;
  AttachmentData: TBytes;
  AttachmentName: WString;
  FileStream: TFileStream;
begin
  for I := 0 to Pdf1.AttachmentCount - 1 do
  begin
    AttachmentName := Pdf1.AttachmentName[I];
    AttachmentData := Pdf1.Attachment[I];
    FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
    try
      if Length(AttachmentData) > 0 then
        FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
    finally
      FileStream.Free;
    end;
  end;
  if Pdf1.CreateAttachment('MyFile.txt') then
    Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;

مدیریت صفحه

عملیات صفحه

Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);

var
  SourcePdf: TPdf;
begin
  SourcePdf := TPdf.Create(nil);
  try
    SourcePdf.FileName := 'C:\SourceDocument.pdf';
    SourcePdf.Active := True;
    Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
  finally
    SourcePdf.Free;
  end;
end;

if Pdf1.PageCount > 1 then
  Pdf1.DeletePage(Pdf1.PageCount);

ذخیره و فشرده‌سازی

ذخیره با فشرده‌سازی

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

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

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;

بررسی چند استاندارد PDF در StandardsLab

پروژه‌های Demo\Delphi\StandardsLab، Demo\CBuilder\StandardsLab و Demo\Lazarus\StandardsLab یک PDF را بارگذاری یا ایجاد می‌کنند، ValidatePdfA، ValidatePdfUa، ValidatePdfE، ValidatePdfX، ValidatePdfR و ValidatePdfVT را اجرا می‌کنند و سپس هر سطح انطباق و شمار خطاهای تشخیص‌داده‌شده را در یک جدول نشان می‌دهند. دکمه‌های ذخیره آن‌ها SaveAsPdfA، SaveAsPdfUa، SaveAsPdfE، SaveAsPdfX، SaveAsPdfR و SaveAsPdfVT را فراخوانی می‌کنند تا این گردش‌کار خروجی نمایش داده شود، بدون آن که به یک چارچوب سفارشی نیاز باشد

تولید گزارش‌های Preflight

واحد FPdfPreflightReport ValidatePdfA، ValidatePdfUa، ValidatePdfE، ValidatePdfX، ValidatePdfR و ValidatePdfVT را در یک شیء گزارش واحد بسته‌بندی می‌کند. Demo\Delphi\PreflightReport، Demo\Lazarus\PreflightReport و Demo\CBuilder\PreflightReport به کاربر اجازه می‌دهند استانداردها را انتخاب کند، یک خلاصه تولید کند، وضعیت، اولویت، دسته‌های خطا، شمار دسته‌ها، اقدام‌های بعدی، کدهای خطا و اقدام‌های پیشنهادی را بررسی کند، پیش‌نمایش را میان متن، Markdown، JSON و CSV جابه‌جا کند و گزارش‌های TXT، HTML، Markdown، JSON یا CSV صادر کند. نمونه رابط گرافیکی Delphi نیز یک پیش‌نمایش طرح اقدام اضافه می‌کند که استانداردهای مردود، اولویت‌ها، کدهای خطا و توصیه‌ها را به یک فهرست بررسی اصلاحی فشرده می‌کند. Demo\Delphi\PreflightReportCli همان مسیر گزارش را به صورت یک گردش‌کار کنسولی با آرگومان‌های ورودی / خروجی، گذرواژه اختیاری، انتخاب قالب خروجی شامل plan / actionplan، فیلترکردن استانداردها، جاسازی اختیاری گزارش تک‌فایلی attach=output.pdf، کدهای خروجی CI با failon=، پردازش batch=list.txt، اسکن دایرکتوری batchdir=folder با بازگشت اختیاری و پردازش پایدار بر اساس مسیر مرتب‌شده، مسیریابی گزارش با outdir=reports، نام‌گذاری ایمن در برابر تداخل برای نام فایل‌های ورودی تکراری، خلاصه‌های TXT / JSON / HTML با جمع کل و وضعیت استانداردها، مانیفست ردیف‌های CSV با تکرار تنظیمات اجرا برای هر مورد، و حالت نمونه بدون آرگومان برای اسکریپت‌ها و کارهای CI در دسترس می‌گذارد. گزارش تولیدشده بیان می‌کند که اعتبارسنج‌های داخلی بررسی‌های سطح marker و برخی بررسی‌های سطح فایل را پوشش می‌دهند، بنابراین preflight کامل در سطح محتوا همچنان می‌تواند به یک موتور اعتبارسنجی اختصاصی واگذار شود

بارگذاری جریانی و اسناد بزرگ

بارگذاری PDF برحسب نیاز با LoadCustomDocument

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

لغو هم‌زمان و رندر Progressive

توکن‌های لغو مشارکتی

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

رندر Progressive قابل لغو صفحه

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

اجرای رندرها در thread پس‌زمینه با TPdfFuture

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

ماتریس‌های تبدیل

ترکیب Translate / Scale / Rotate / Skew

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

ترکیب صفحه

وارد کردن زیرمجموعه‌ای از صفحه‌ها بر اساس اندیس

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

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

بازچینی صفحه‌ها درجا

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

// 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 و Demo\CBuilder\WatermarkStamp این الگو را به یک گردش‌کار قابل اجرا تبدیل می‌کنند: آن‌ها یک PDF هدف و یک PDF مهر قابل استفاده مجدد می‌سازند، صفحه مهر را با CreateXObjectFromPage بسته‌بندی می‌کنند، همان Form XObject را روی هر صفحه هدف درج می‌کنند، تبدیل‌های TPdfMatrix را برای واترمارک‌های مرکزی و مهرهای گوشه بالا-راست اعمال می‌کنند و پیش از ذخیره نتیجه، برچسب‌های اختیاری شماره صفحه را اضافه می‌کنند

درج تصویر (overloadهای مستقیم AddImage)

افزودن فایل تصویر ثبت‌شده یا TBitmap

// 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 و Demo\Lazarus\ImageToPDF این را به یک گردش‌کار دسته‌ای کامل تبدیل می‌کنند: چند فایل تصویری را انتخاب کنید، آیتم انتخاب‌شده را پیش‌نمایش بگیرید، برای هر تصویر یک صفحه PDF بسازید، هر تصویر را در یک صفحه A4 portrait یا landscape مقیاس دهید، نتیجه را ذخیره کنید و PDF تولیدشده را باز کنید

برجسته‌سازی جستجوی نما

برجسته‌کردن هر تطابق روی صفحه جاری

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;

چیدمان نما و رنگ صفحه

Fit Mode و پس‌زمینه صفحه

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

پروژه‌های Demo\Delphi\ViewerInteractionLab، Demo\CBuilder\ViewerInteractionLab و Demo\Lazarus\ViewerInteractionLab سطح تعامل مدرن TPdfView را در یک فرم فشرده نگه می‌دارند: یک PDF نمونه را باز یا ایجاد کنید، DisplayMode را عوض کنید، FitMode را اعمال کنید، PageColor را تغییر دهید، صفحات را بچرخانید، با HighlightSearchText تطابق‌های جستجوی صفحه جاری را برجسته کنید، انتخاب متن کاربر را فعال یا غیرفعال کنید، SelectedText را بررسی کنید، SelectAll، CopySelectionToClipboard و ClearSelection را فراخوانی کنید و هنگام حرکت ماوس روی صفحه ببینید که مختصات DeviceToPage در نوار وضعیت به‌روزرسانی می‌شود

جست‌وجو و انتخاب متن در SearchAndSelect

پروژه‌های Demo\Delphi\SearchAndSelect، Demo\CBuilder\SearchAndSelect و Demo\Lazarus\SearchAndSelect گردش‌کار جست‌وجو و انتخاب در نما را به نمونه‌هایی مستقل تبدیل می‌کنند. آن‌ها یک PDF را باز یا ایجاد می‌کنند، نتایج جست‌وجوی همه‌صفحه را همراه با شماره صفحه، شاخص نویسه و متن پیش‌نمایش فهرست می‌کنند، پیمایش قبلی / بعدی و پرش با دوبارکلیک روی نتیجه را پشتیبانی می‌کنند، تطابق‌های صفحه جاری را با HighlightSearchText برجسته می‌کنند، گزینه‌های تطبیق حروف و کل واژه را در دسترس می‌گذارند، AllowUserTextSelection را تغییر می‌دهند، SelectedText را پیش‌نمایش می‌کنند، SelectAll، CopySelectionToClipboard و ClearSelection را از دکمه‌ها اجرا می‌کنند و اجازه می‌دهند کاربر میان حالت نمایش تکی و گسترده جابه‌جا شود

خواندن ترجیحات چاپ PDF در PrintPreferences

پروژه‌های Demo\Delphi\PrintPreferences، Demo\CBuilder\PrintPreferences و Demo\Lazarus\PrintPreferences پیش از چاپ، ترجیحات چاپ نویسنده را می‌خوانند. آن‌ها PrintCopies، PrintPageRanges، PrintScaling و PrintPaperHandling را نمایش می‌دهند، می‌توانند تعداد نسخه‌ها و بازه‌های ارائه‌شده توسط نویسنده را به ورودی‌های چاپ کپی کنند و نشان می‌دهند که SetPdfPrintPaperHandlingDevMode چگونه نیت چاپ دوطرفه PDF را به یک Windows DEVMODE نگاشت می‌کند

چاپ استاندارد PDF در PrintPDF

پروژه‌های Demo\Delphi\PrintPDF، Demo\CBuilder\PrintPDF و Demo\Lazarus\PrintPDF گردش‌کار چاپ استاندارد را نشان می‌دهند: PDF را باز کنید، صفحه‌ها را پیش‌نمایش بگیرید، بازه‌های صفحه و تعداد نسخه‌ها را در گفت‌وگوی چاپ پلتفرم انتخاب کنید، حالت collate را رعایت کنید، هر صفحه را از طریق TPdf.RenderPage رندر کنید و پیشرفت چاپ را همراه با امکان لغو نمایش دهید. نمونه Delphi همچنین فایل‌های PDF رهاشده را برای بارگذاری سریع می‌پذیرد

نمایش سریع چندصفحه‌ای در MultiPageViewer

پروژه‌های Demo\Delphi\MultiPageViewer، Demo\CBuilder\MultiPageViewer و Demo\Lazarus\MultiPageViewer نمایش پیوسته، گسترده و cover-aware را همراه با ناوبری معمول صفحه و گردش‌کارهای بزرگ‌نمایی تمرین می‌کنند. آن‌ها همچنین برای آزمودن سرعت بازشدن تا صفحه اول و پاسخ‌گویی رندر روی PDFهای بسیار بزرگ مفیدند: در آزمون‌های فایل بزرگ، اسناد 2.x GB که می‌توانند Acrobat یا Foxit را پیش از نخستین نمایش 20-30 ثانیه درگیر کنند، از مسیر نمای PDFiumPas تقریباً بلافاصله قابل استفاده می‌شوند

بررسی کناربه‌کنار در SplitView

پروژه‌های Demo\Delphi\SplitView، Demo\CBuilder\SplitView و Demo\Lazarus\SplitView بازبینی PDF را به صورت کناربه‌کنار نشان می‌دهند. نمونه‌های C++Builder و Lazarus بر مقایسه دو یا سه‌پنلی، انتخاب نمای فعال، فرمان‌های ناوبری همگام اختیاری، پیش‌تنظیم‌های بزرگ‌نمایی مشترک و چرخش برای نمای انتخاب‌شده یا همه نماهای بارگذاری‌شده تمرکز دارند. از این نمونه‌ها برای مقایسه اسناد بزرگ استفاده کنید و در عین حال، پاسخ‌گویی نخستین نمای قابل استفاده و رندر صفحه‌های بعدی را حفظ کنید؛ همان فایل‌های PDF 2.x GB که ممکن است Acrobat یا Foxit را برای بازشدن 20-30 ثانیه درگیر کنند، در مسیر نمای PDFiumPas تقریباً بلافاصله قابل استفاده می‌شوند

بررسی سطح ریسک PDF در SecurityAudit

پروژه‌های Demo\Delphi\SecurityAudit، Demo\CBuilder\SecurityAudit و Demo\Lazarus\SecurityAudit یک PDF را فقط‌خواندنی باز می‌کنند و وضعیت مجوزها، پیوست‌های تعبیه‌شده، عملیات JavaScript سند، annotationهای پیوند URI و Launch، web linkها، خلاصهٔ signatureها، وضعیت XFA، در دسترس‌بودن helperهای V8 و callbackهای قابلیت پشتیبانی‌نشده را فهرست می‌کنند. همان ردیف‌های خلاصه و یافته‌ها را می‌توان به‌صورت گزارش TXT برای audit ذخیره یا کپی کرد یا به‌صورت JSON ساختاریافته ذخیره کرد. هدف آن‌ها پنل‌های ریسک در سطح برنامه میزبان است، نه تشخیص بدافزار

مدیریت پیوندهای نما

کلیک‌تا‌دنبال‌کردن امن با فهرست مجاز برای هر عمل

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

ویرایش ابزارک‌های فرم در نما

انتخاب و واگرد / بازگرد

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

محدودسازی نما برای کیوسک‌ها

غیرفعال‌کردن ژست‌های ناوبری صفحه

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

پنل جانبی بندانگشتی

پیوند دادن TPdfThumbnailView به TPdfView

// 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.PdfView1PageNumberتغییر یافته(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

صدور دسته‌ای همکارانه و قابل لغو

PDF چندفایلی به JPG با پیشرفت برای هر فایل و هر صفحه

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

مختصات نما و آزمون برخورد

هم‌تراز کردن صفحه جاری و بررسی متن زیر نشانگر

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;

مدیریت خطا

عملیات PDF مقاوم

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 و دسترس‌پذیری

تشخیص Tagged PDF و پیمایش درخت ساختار

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

انتخاب حالت چاپ

تنظیم حالت چاپ EMF / PostScript / Image-Mask

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

خواندن تنظیمات چاپ در نظر گرفته‌شده توسط نویسنده

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

عملیات JavaScript سند

فهرست‌کردن JavaScript در Name Tree

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

نکات کارایی

بهینه‌سازی عملیات PDF

procedure ProcessMultiplePages;
var
  I: Integer;
  PageText: WString;
begin
  for I := 1 to Pdf1.PageCount do
  begin
    Pdf1.PageNumber := I;
    PageText := Pdf1.Text;
    ProcessPageText(PageText);
  end;
end;

procedure SetOptimalRenderOptions;
begin
  PdfView1.Options := [reAnnotations, reLcd];
end;

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