PDFium Delphi Component Docs

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

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

بارگذاری و نمایش سند 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 از گزینه‌های محدودهٔ صفحه و جداکنندهٔ صفحه برای تولید خروجی متن هر صفحه در Delphi، C++Builder و Lazarus پشتیبانی می‌کند، در حالی که ContentExtractionLab نمونهٔ فهرست مقطعی است

آشکارسازی و استخراج جدول‌ها

پروژه‌های Demo\Delphi\TableExtractionLab، Demo\CBuilder\TableExtractionLab و Demo\Lazarus\TableExtractionLab آشکارسازی جدول را به یک جریان کاری واحد تبدیل می‌کنند. آزمایشگاه سندی نمونه می‌سازد که شامل جدولی خط‌کشی‌شده با سلول‌های سرجدول ادغام‌شده، جدولی هم‌ترازشده با whitespace بدون خط‌کشی و جدول بلندی است که از مرز صفحه می‌شکند، سپس برای صفحهٔ جاری ExtractTables یا برای کل سند ExtractDocumentTables را اجرا می‌کند و سطرها، ستون‌ها، مقادیر RowSpan و ColumnSpan، پرچم‌های سرجدول، حالت آشکارسازی، ضریب اطمینان و گروه‌های ادامه را گزارش می‌کند. گزینه‌های DetectRuledTables، DetectWhitespaceTables، DetectContinuations، DetectFilledRulings و MinConfidence در رابط کاربری قابل ویرایش‌اند و جدول انتخاب‌شده از طریق TPdfTable.ToCsv و TPdfTable.ToJson برون‌سپاری می‌شود. آزمایشگاه همچنین یک پوشهٔ کامل را دسته‌ای پردازش می‌کند: Batch folder روی هر PDF اجرای ExtractDocumentTables را انجام می‌دهد، شمار جدول هر سند را گزارش می‌کند، اسکن‌های بدون لایهٔ متن را نشانه‌گذاری می‌کند و اسناد خالی را با پیش‌تنظیم ستون فشرده دوباره می‌آزماید؛ Export all CSV هر جدول آشکارشده را کنار فایل‌های مبدأ می‌نویسد. هر سند در نمونهٔ TPdf خودش باز می‌شود تا یک فایل ناخوانا نتواند بقیهٔ دسته را تحت تأثیر بگذارد

عملیات فرم

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

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;

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

کار با نشانک‌ها

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

unit از نوع FPdfPreflightReport توابع ValidatePdfA، ValidatePdfUa، ValidatePdfE، ValidatePdfX، ValidatePdfR و ValidatePdfVT را در یک شیء گزارش قابل پرس‌وجو با شدت Info/Warning/Error، جمع‌بندی وضعیت و مشکل، جست‌وجو، فیلتر و اثر انگشت پایدار SHA-256 می‌پیچد. گزارش‌ها می‌توانند به‌صورت متن، HTML، Markdown، JSON، CSV، SARIF 2.1.0، JUnit XML، NDJSON یا XML بومی تولید شوند. Demo\Delphi\PreflightReport، Demo\Lazarus\PreflightReport و Demo\CBuilder\PreflightReport گردش‌کارهای انتخاب و پیش‌نمایش تعاملی فراهم می‌کنند، در حالی که Demo\Delphi\PreflightReportCli آرگومان‌های ورودی/خروجی اسکریت‌پذیر، فیلتر استاندارد، جاسازی پیوست گزارش اختیاری، دروازه‌بندی exit-code CI، پردازش فهرست و پوشهٔ دسته‌ای، مسیریابی امن در برابر برخورد، خلاصه‌های جمعی و مانیفست‌های CSV اضافه می‌کند. گزارش تولیدشده بیان می‌کند که اعتبارسنج‌های داخلی بررسی‌های سطح نشانگر و سطح فایل انتخابی را پوشش می‌دهند، بنابراین 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;

لغو ناهمگام و رندر پیشرونده

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

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

رندر صفحهٔ پیشروندهٔ قابل لغو

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

// 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 را برای واترمارک‌های وسط‌چین و مهرهای بالا-راست اعمال می‌کنند و پیش از ذخیرهٔ نتیجه برچسب‌های اختیاری شماره صفحه را می‌افزایند

درج تصویر (سربارگذاری‌های مستقیم 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 مقیاس کنید، نتیجه را ذخیره کنید و 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;

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

حالت تناسب و پس‌زمینهٔ صفحه

// 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 هایلایت می‌کنند، گزینه‌های match-case و whole-word را در معرض دید قرار می‌دهند، AllowUserTextSelection را روشن/خاموش می‌کنند، SelectedText را پیش‌نمایش می‌کنند، SelectAll، CopySelectionToClipboard و ClearSelection را از دکمه‌ها هدایت می‌کنند و به کاربران اجازه تعویض میان حالت‌های نمایش تک و گسترده می‌دهند

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

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

چاپ استاندارد 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 نمایش پیوسته، گسترده و آگاه از جلد را با گردش‌کارهای ناوبری صفحه و بزرگ‌نمایی معمول تمرین می‌کنند. برای آزمایش سرعت باز-به-صفحهٔ-نخست و پاسخگویی رندر روی PDFهای بسیار بزرگ نیز مفیدند: در آزمون‌های فایل بزرگ، اسناد 2.x گیگابایتی که می‌توانند Acrobat یا Foxit را 20-30 ثانیه پیش از نخستین نما معطل کنند از طریق مسیر نمایشگر PDFium Delphi Component تقریباً بلافاصله قابل استفاده می‌شوند

بازبینی کنارهم در SplitView

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

ممیزی سطح ریسک PDF در SecurityAudit

پروژه‌های Demo\Delphi\SecurityAudit، Demo\CBuilder\SecurityAudit و Demo\Lazarus\SecurityAudit PDF را فقط‌خواندنی باز می‌کنند و وضعیت مجوز، پیوست‌های جاسازی‌شده، اکشن‌های JavaScript سند، حاشیه‌نویسی‌های پیوند URI و Launch، پیوندهای وب، خلاصهٔ امضاها، وضعیت XFA، در دسترس‌بودن یاری‌رسان V8 و callbackهای ویژگی پشتیبانی‌نشده را فهرست می‌کنند. همان ردیف‌های خلاصه و یافته می‌توانند به‌صورت گزارش ممیزی TXT ذخیره یا کپی یا به‌صورت 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;

ویرایش ویجت فرم نمایشگر

انتخاب و Undo/Redo

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

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

// 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.PdfView1PageNumberChanged(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 درخت نام

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