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 підтримує опції діапазону сторінок та роздільника сторінок для створення посторінкового текстового виводу в 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;

Сплющення заповнених значень форми на сторінку

// Без сплющення FormField[i] := value записує лише запис /V —
// видиме представлення береться з потоку /AP і може не оновитися
// GenerateFormAppearances перегенеровує /AP для кожного віджета, а потім
// 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;

Навігація і Bookmarks

Робота із Закладками

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

Збереження та стиснення

Збереження зі стисненням

// Створити стиснений PDF-документ
Pdf1.CreateDocument;
Pdf1.Compressed := True; // переконатися, що всі потоки стиснено через FlateDecode (типово)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // формат A4
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 := Типово(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 := Типово(TPdfASaveOptions);
    Opts.Conformance := pac1b;
    if Pdf1.SaveAsPdfAToStream(Stream, Opts) then
      UploadToArchive(Stream);
  finally
    Stream.Free;
  end;
end;

Validating PDF/A Conformance

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

The Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab, and Demo\Lazarus\StandardsLab projects load or create a PDF, run ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, and ValidatePdfVT, then show each detected conformance level and issue count in one grid. Their save buttons call SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR, and SaveAsPdfVT so the marker-output workflow is visible without writing a custom harness.

Створення звітів Preflight

The FPdfPreflightReport unit wraps ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, and ValidatePdfVT into one report object. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport, and Demo\CBuilder\PreflightReport let users choose standards, generate a summary, inspect status, priority, issue categories, category counts, next actions, issue codes, and recommended actions, switch the preview between text, Markdown, JSON, and CSV, and export TXT, HTML, Markdown, JSON, or CSV reports. The Delphi GUI sample also adds an Action Plan preview that condenses non-passing standards, priorities, issue codes, and recommendations into a remediation checklist. Demo\Delphi\PreflightReportCli exposes the same report path as a console workflow with input / output arguments, optional password, output format selection including plan / actionplan, standard filtering, optional single-file attach=output.pdf report embedding, failon= CI gating exit codes, batch=list.txt processing, batchdir=folder directory scanning with optional recursion and stable path-sorted processing, outdir=reports report routing, collision-safe report names for duplicate input file names, TXT / JSON / HTML summaries with aggregate and standard status totals, CSV row manifests with run settings repeated per item, and a no-argument sample mode for scripts and CI jobs. The generated report states that the built-in validators cover marker-level and selected file-level checks, so full content-level preflight can still be delegated to a dedicated validation engine.

Потокове завантаження та великі документи

Завантаження PDF на вимогу через LoadCustomDocument

// LoadCustomDocument дає змогу PDFium читати блоки з будь-якого TStream, що підтримує seek, на
// вимогу. Потік не копіюється в пам’ять, тож багатогігабайтні PDF, віддалені
// потоки HTTP body та джерела з базою даних усі придатні без
// попереднього буфера
var
  FileStream: TFileStream;
begin
// Потокове читання з файла без копії в пам’яті
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ право володіння передано — Pdf1 звільняє FileStream під час UnloadDocument
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Потік, яким володіє викликач (він звільняє його після 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;

Асинхронне скасування та поетапне відтворення

Cooperative Cancellation Tokens

// 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 дає змогу тривалому високодетальному відтворенню перерватися посеред сторінки, коли
// 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('Відтворення скасовано — доступний частковий результат');
      prsFailed:    Memo1.Lines.Add('PDFium повідомив 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;

Transform Matrices

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

Page Composition

Імпорт підмножини сторінок за індексом

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

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

Перевпорядкування сторінок In-Place

// 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.
//
// Приклад: 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('Передано недійсні індекси до MovePages');

Watermarking via Повторне використання сторінки як 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

Viewer Search Highlighting

Highlight Every Match on the Current Page

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;

Viewer Layout and Колір сторінки

Режим підгонки та фон сторінки

// FitMode keeps long documents framed during Resize or page changes.
// Setting Zoom directly cancels FitMode (it reverts to pfmNone).
PdfView1.FitMode := pfmFitPage;       // уся сторінка у вікні перегляду
// PdfView1.FitMode := pfmFitWidth;   // ширина сторінки відповідає ширині вікна
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor відокремлює відтворену сторінку PDF від Color елемента керування хоста
// — типова конфігурація переглядача в темному режимі лишає темною область
// прокручування, а сторінка PDF залишається папір-білою
PdfView1.Color     := clBlack;        // область прокручування
PdfView1.PageColor := clWhite;        // фон сторінки PDF

// Необов’язкова тінь і рамка сторінки, щоб підняти її над темним тлом
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, перелічують результати пошуку по всіх сторінках з номером сторінки, індексом символу та попереднім переглядом тексту, підтримують навігацію Previous / Next і перехід подвійним клацанням до результату, підсвічують збіги на поточній сторінці через 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 у Windows DEVMODE

Стандартний друк PDF у PrintPDF

Проєкти Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF і Demo\Lazarus\PrintPDF показують стандартний робочий процес друку: відкрити PDF, переглянути сторінки, вибрати діапазони сторінок і копії в системному діалозі друку, врахувати режим сортування, відтворити кожну сторінку через TPdf.RenderPage і показати прогрес друку з можливістю скасування. Зразок Delphi також приймає скинуті PDF-файли для швидкого завантаження

Швидкий перегляд багатосторінкових документів у MultiPageViewer

Проєкти Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer і Demo\Lazarus\MultiPageViewer перевіряють безперервний, розворотний і з урахуванням обкладинки перегляд із звичайною навігацією по сторінках і робочими процесами масштабу. Вони також корисні для перевірки швидкості відкриття до першої сторінки та чуйності відтворення на дуже великих 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 документа, анотації посилань URI і Launch, веб-посилання, зведення підписів, стан XFA, доступність допоміжних засобів V8 і зворотні виклики непідтримуваних функцій. Ті самі рядки зведення та знайдених елементів можна зберегти або скопіювати як TXT-звіт аудиту або зберегти як структурований JSON. Вони призначені для панелей ризиків у застосунку-хості, а не для виявлення шкідливого ПЗ

Viewer Link Handling

Safe Click-to-Follow with Per-Action Allow Lists

// LinkOptions керує автоматичною обробкою чотирьох типів дій PDF-посилань
// типів. Значення за замовчуванням вмикають лише goto та URI; launch (запуск програми) і
// вбудований GotoR (перехід до іншого файла) залишаються вимкненими, щоб випадкове клацання не могло
// виконати довільний код
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // safe defaults

// OnAnnotationLinkClick спрацьовує для призначень усередині документа (переходів між сторінками,
// іменованих призначень, ланцюжків дій). Установіть Handled = True, щоб запобігти
// автоматичному переходу переглядача за посиланням
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;  // придушити автоматичну обробку
  end;
end;

// OnWebLinkClick спрацьовує для рядків URI, виявлених сканером web-link PDFium
// сканер (автовиявлені URL у текстовому потоці, навіть без
// анотації /A <</S/URI...>>)
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

Viewer Form Widget Editing

Selection and 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;

Locking Down the Viewer for Kiosks

Вимкнення жестів навігації сторінками

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

Thumbnail Side Panel

Linking a TPdfThumbnailView to a 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;

Cooperatively-Cancellable Batch Export

Multi-File PDF to JPG with Per-File and Per-Page Progress

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

Viewer Coordinates and Hit Testing

Fit the Current Page and Inspect Text Under the Mouse

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;

Error Handling

Надійні операції з 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 — це дешевий запит каталогу: True, коли існує /StructTreeRoot
// StructureElements матеріалізує дерево у плоский масив
// записів TPdfStructureElement із типом, назвою, альтернативним текстом, фактичним
// текстом, текстом розгортання, мовою, рівнем, індексом батька та кількістю дочірніх / позначених
// елементів вмісту / атрибутів
var
  Elements: TPdfStructureElements;
  I: Integer;
begin
  if not Pdf1.IsTagged then
  begin
    Log.Add('Документ НЕ є Tagged PDF — засобам доступності може бути важко');
    Exit;
  end;
  if Pdf1.Language = '' then
    Log.Add('Tagged PDF без /Lang — не відповідає 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 — це глобальна функція, що відображається на
// FPDF_SetPrintMode PDFium. Використовуйте відповідне значення TPdfPrintMode для
// потрібного драйвера принтера
SetPdfPrintMode(pmEmf);                   // GDI EMF (типово)
// SetPdfPrintMode(pmTextOnly);           // відтворення лише тексту
// SetPdfPrintMode(pmPostScript2);        // PostScript рівня 2
// SetPdfPrintMode(pmPostScript3);        // PostScript рівня 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks);      // EMF + маски зображень
// SetPdfPrintMode(pmPostScript3Type42);  // PostScript 3 + шрифти Type 42

Читання призначених автором параметрів друку

// PrintCopies, PrintPageRanges, PrintScaling і PrintPaperHandling — це
// записи словника параметрів переглядача — вони кодують те, що
// автор документа хотів бачити як типові значення для PDF-читачів
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Масштабування друку: ' + 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 у дереві імен

// JavaScript на рівні документа зберігається в дереві імен /Names /JavaScript
// tree. JavaScriptAction[i] повертає один запис; JavaScriptActions повертає
// повний масив
var
  I: Integer;
  Action: TPdfJavaScriptAction;
begin
  for I := 0 to Pdf1.JavaScriptActionCount - 1 do
  begin
    Action := Pdf1.JavaScriptAction[I];
    Memo1.Lines.Add('Ім’я:   ' + string(Action.Name));
    Memo1.Lines.Add('Скрипт: ' + 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;