PDFium Delphi Component Docs

Ví dụ lập trình PDFium VCL

Thao tác tài liệu cơ bản

Nạp và hiển thị một tài liệu 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;

Tạo một tài liệu PDF mới

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;

Thao tác văn bản

Trích xuất văn bản từ 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;

Tìm kiếm văn bản

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;

Thêm văn bản với định dạng

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;

Khảo sát siêu dữ liệu ký tự và phông chữ

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;

Các dự án Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties, và Demo\Lazarus\FontProperties hiển thị cùng siêu dữ liệu đó qua cả TPdf lẫn TPdfView

Dựng báo cáo trích xuất nội dung đa nhóm

Các dự án Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab, và Demo\Lazarus\ContentExtractionLab biến việc trích xuất nội dung thành một quy trình duy nhất. Chúng mở hoặc tạo một PDF, cho người dùng chọn siêu dữ liệu, văn bản trang, page object, hình ảnh, tệp đính kèm, liên kết, chú thích, bookmark và các số đo phông / ký tự, rồi tạo bản tóm tắt kèm báo cáo TXT hoặc JSON chi tiết. Lab còn có chọn nhóm tất cả / không nhóm, sao chép báo cáo vào clipboard, và các hành động lưu PDF nguồn để người dùng giữ mẫu PDF đã tạo cạnh báo cáo trích xuất của nó. Các demo cũ hơn là ExtractText, ExtractImages, Attachment, và FontProperties vẫn giữ vai trò ví dụ chuyên sâu; ExtractText hỗ trợ tùy chọn dải trang và dấu phân cách trang để tạo đầu ra văn bản theo từng trang trên Delphi, C++Builder, và Lazarus, còn ContentExtractionLab là mẫu khảo kê xuyên suốt

Phát hiện và trích xuất bảng

Các dự án Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab, và Demo\Lazarus\TableExtractionLab biến việc phát hiện bảng thành một quy trình duy nhất. Lab dựng một tài liệu mẫu chứa một bảng có kẻ ô với các ô header gộp, một bảng căn theo khoảng trắng không kẻ ô, và một bảng dài tràn qua ranh giới trang, rồi chạy ExtractTables cho trang hiện tại hoặc ExtractDocumentTables cho toàn tài liệu và báo cáo hàng, cột, giá trị RowSpanColumnSpan, cờ header, chế độ phát hiện, confidence, và các nhóm nối tiếp. Các tùy chọn DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings, và MinConfidence có thể chỉnh trong UI, và bảng đã chọn xuất qua TPdfTable.ToCsvTPdfTable.ToJson. Lab còn xử lý hàng loạt cả một thư mục: Batch folder chạy ExtractDocumentTables trên mọi PDF, báo số bảng theo từng tài liệu, đánh dấu các bản scan không có lớp văn bản, và thử lại các tài liệu rỗng với preset cột thu gọn; Export all CSV ghi mọi bảng phát hiện được cạnh các tệp nguồn. Mỗi tài liệu mở trong một thể hiện TPdf riêng nên một tệp không đọc được không ảnh hưởng phần còn lại của lô

Thao tác biểu mẫu

Làm việc với trường biểu mẫu

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;

Làm phẳng giá trị biểu mẫu đã điền vào trang

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

Thao tác hình ảnh

Trích xuất hình ảnh

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;

Thêm hình ảnh

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;

Thao tác chú thích

Làm việc với chú thích

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;

Thao tác vẽ

Vẽ hình

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;

Dẫn hướng và bookmark

Làm việc với 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;

Làm việc với đích đến có tên

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;

Thao tác tệp đính kèm

Làm việc với tệp đính kèm

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;

Quản lý trang

Thao tác trang

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

Lưu và nén

Lưu với nén

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

Tạo đầu ra lưu trữ 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;

Kiểm tra tuân thủ 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;

Khảo sát nhiều chuẩn PDF trong StandardsLab

Các dự án Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab, và Demo\Lazarus\StandardsLab nạp hoặc tạo một PDF, chạy ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, và ValidatePdfVT, rồi hiển thị từng mức tuân thủ phát hiện được cùng số vấn đề trong một lưới. Các nút lưu của chúng gọi SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR, và SaveAsPdfVT để quy trình đầu ra marker hiển thị ngay mà không cần viết harness riêng

Tạo báo cáo preflight

Unit FPdfPreflightReport gói ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, và ValidatePdfVT vào một đối tượng báo cáo có thể truy vấn với mức nghiêm trọng Info / Warning / Error, tổng hợp trạng thái và vấn đề, tra cứu, tìm kiếm, lọc, cùng vân tay SHA-256 ổn định. Báo cáo có thể phát ở dạng text, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON, hoặc XML gốc. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport, và Demo\CBuilder\PreflightReport cung cấp quy trình chọn và xem trước tương tác, trong khi Demo\Delphi\PreflightReportCli bổ sung tham số input / output kịch bản hóa, lọc tiêu chuẩn, nhúng tệp đính kèm báo cáo tùy chọn, chặn mã thoát CI, xử lý danh sách và thư mục hàng loạt, định tuyến tránh trùng, tổng hợp tổng kết, và manifest CSV. Báo cáo tạo ra nêu rõ các bộ xác thực tích hợp sẵn bao phủ kiểm tra mức marker và một số kiểm tra mức tệp được chọn, nên preflight toàn phần ở mức nội dung vẫn có thể giao cho một engine xác thực chuyên dụng

Nạp dạng stream và tài liệu lớn

Nạp PDF theo nhu cầu với 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;

Hủy bất đồng bộ và kết xuất tăng dần

Cancellation token hợp tác

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

Kết xuất trang tăng dần có thể hủy

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

Chạy kết xuất trên luồng nền với 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;

Ma trận biến đổi

Kết hợp 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;

Ghép trang

Nhập một tập con trang theo chỉ số

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

Đầu ra ghép 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;

Sắp xếp lại thứ tự trang tại chỗ

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

Đóng watermark qua tái sử dụng trang bằng 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;

Các dự án Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp, và Demo\CBuilder\WatermarkStamp biến mẫu này thành quy trình chạy được: chúng tạo một PDF đích và một PDF dấu đóng tái sử dụng được, bọc trang dấu bằng CreateXObjectFromPage, chèn cùng Form XObject vào mọi trang đích, áp các biến đổi TPdfMatrix cho watermark căn giữa và dấu đóng góc trên phải, rồi thêm nhãn số trang tùy chọn trước khi lưu kết quả

Chèn hình ảnh (các overload AddImage trực tiếp)

Thêm một tệp ảnh đã đăng ký hoặc một 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;

Các dự án Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF, và Demo\Lazarus\ImageToPDF biến việc này thành quy trình hàng loạt hoàn chỉnh: chọn nhiều tệp ảnh, xem trước mục đã chọn, tạo một trang PDF cho mỗi ảnh, co giãn từng ảnh vào trang A4 dọc hoặc ngang, lưu kết quả, và mở PDF đã tạo

Tô sáng tìm kiếm trên trình xem

Tô sáng mọi kết quả khớp trên trang hiện tại

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;

Bố cục trình xem và màu trang

Chế độ fit và nền trang

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

Khám phá tương tác trình xem trong ViewerInteractionLab

Các dự án Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab, và Demo\Lazarus\ViewerInteractionLab giữ toàn bộ diện tương tác hiện đại của TPdfView trong một form gọn: mở hoặc tạo PDF mẫu, chuyển DisplayMode, áp FitMode, đổi PageColor, xoay trang, tô sáng các kết quả tìm kiếm trên trang hiện tại bằng HighlightSearchText, bật hoặc tắt chọn văn bản của người dùng, khảo sát SelectedText, gọi SelectAll, CopySelectionToClipboard, và ClearSelection, cùng theo dõi tọa độ DeviceToPage cập nhật trên thanh trạng thái khi chuột di chuyển trên trang

Tìm kiếm và chọn văn bản trong SearchAndSelect

Các dự án Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect, và Demo\Lazarus\SearchAndSelect biến quy trình tìm-kiếm-và-chọn của trình xem thành các mẫu độc lập. Chúng mở hoặc tạo một PDF, liệt kê kết quả tìm kiếm toàn tài liệu với số trang, chỉ số ký tự và văn bản xem trước, hỗ trợ dẫn hướng Previous / Next và nhảy tới kết quả bằng nhấp đúp, tô sáng các kết quả khớp trên trang hiện tại bằng HighlightSearchText, cung cấp tùy chọn phân biệt chữ hoa/thường và toàn từ, bật/tắt AllowUserTextSelection, xem trước SelectedText, điều khiển SelectAll, CopySelectionToClipboard, và ClearSelection từ các nút, cùng cho phép chuyển giữa chế độ hiển thị đơn và kiểu trải trang

Đọc tùy chọn in PDF trong PrintPreferences

Các dự án Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences, và Demo\Lazarus\PrintPreferences đọc tùy chọn in của tác giả trước khi in. Chúng hiển thị PrintCopies, PrintPageRanges, PrintScaling, và PrintPaperHandling, có thể sao chép số bản và dải trang do tác giả cung cấp vào các tham số in, và cho thấy SetPdfPrintPaperHandlingDevMode ánh ý định duplex của PDF vào một DEVMODE của Windows

In PDF chuẩn trong PrintPDF

Các dự án Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF, và Demo\Lazarus\PrintPDF thể hiện quy trình in chuẩn: mở một PDF, xem trước trang, chọn dải trang và số bản trong hộp thoại in của nền tảng, tôn trọng chế độ collate, kết xuất từng trang qua TPdf.RenderPage, và hiển thị tiến trình in kèm hủy bỏ. Mẫu Delphi còn nhận các tệp PDF kéo-thả để nạp nhanh

Xem nhiều trang nhanh trong MultiPageViewer

Các dự án Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer, và Demo\Lazarus\MultiPageViewer thực hành chế độ xem liên tục, trải trang và tính bìa với các quy trình dẫn hướng và thu phóng trang thông thường. Chúng cũng hữu ích để đo tốc độ mở tới trang đầu tiên và độ phản hồi kết xuất trên PDF rất lớn: trong các bài test tệp lớn, những tài liệu 2.x GB có thể khiến Acrobat hoặc Foxit mất 20-30 giây trước khi xem được lần đầu lại dùng được gần như ngay lập tức qua đường trình xem của PDFium Delphi Component

Rà soát song song trong SplitView

Các dự án Demo\Delphi\SplitView, Demo\CBuilder\SplitView, và Demo\Lazarus\SplitView thể hiện rà soát PDF song song. Các mẫu C++Builder và Lazarus tập trung vào so sánh hai hoặc ba khung, chọn view đang hoạt động, các lệnh dẫn hướng đồng bộ tùy chọn, bộ preset thu phóng dùng chung, và xoay cho view được chọn hoặc mọi view đã nạp. Hãy dùng các mẫu này để so sánh tài liệu lớn mà vẫn giữ lần xem đầu tiên dùng được và kết xuất các trang sau mượt mà; những tệp PDF 2.x GB mà Acrobat hoặc Foxit có thể mất 20-30 giây để mở lại dùng được gần như ngay lập tức trên đường trình xem của PDFium Delphi Component

Kiểm toán bề mặt rủi ro PDF trong SecurityAudit

Các dự án Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit, và Demo\Lazarus\SecurityAudit mở một PDF ở chế độ chỉ đọc và liệt kê trạng thái quyền, tệp đính kèm nhúng, các hành động JavaScript của tài liệu, chú thích liên kết URI và Launch, liên kết web, tóm tắt chữ ký, trạng thái XFA, khả dụng của trợ giúp V8, và các callback cho tính năng không được hỗ trợ. Các dòng tóm tắt và phát hiện đó có thể lưu hoặc sao chép thành báo cáo kiểm toán TXT hoặc lưu dạng JSON có cấu trúc. Chúng được thiết kế như các bảng rủi ro cho ứng dụng chủ, không phải công cụ phát hiện malware

Xử lý liên kết trên trình xem

Nhấp để theo liên kết an toàn với danh sách cho phép theo từng hành động

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

Chỉnh sửa widget biểu mẫu trên trình xem

Chọn và 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;

Khóa trình xem cho kiosk

Tắt các cử chỉ dẫn hướng trang

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

Bảng bên ảnh thu nhỏ

Gắn một TPdfThumbnailView với một 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;

Xuất hàng loạt có thể hủy theo kiểu hợp tác

PDF nhiều tệp sang JPG với tiến trình theo tệp và theo trang

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

Tọa độ trình xem và hit testing

Fit trang hiện tại và khảo sát văn bản dưới con trỏ chuột

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;

Xử lý lỗi

Các thao tác PDF bền vững

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 và khả năng tiếp cận

Phát hiện Tagged PDF và đi qua cây cấu trúc

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

Chọn chế độ in

Đặt chế độ in 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

Đọc cài đặt in do tác giả chỉ định

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

Các hành động JavaScript của tài liệu

Liệt kê JavaScript trong 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;

Mẹo hiệu năng

Tối ưu các thao tác 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;