PDFium Delphi Component Docs

PDFium VCL プログラミングサンプル

実行できるデモを選ぶ

デモは 32 トピックで、Delphi 32、C++Builder 31、Lazarus 32 の合計 95 ネイティブプロジェクトです。各プラットフォームのメインディレクトリには 22 の完全なワークフローデモがあります。10 の用途を絞ったトピックは Miscellaneous にあり、3 プラットフォーム合計で 29 プロジェクトです。XfaLab は Delphi と Lazarus で利用できます

まずはメインディレクトリのワークフローを開きます。機能ツアーには PDFViewer、ライブでの作成には CreatePDF、即時のインベントリには ContentExtractionLab、編集可能なフォームには FormFill、検証と変換には StandardsLab、自動取り込みには IntakeWorkbenchCli を使います。この 6 つの入口はすぐに動く結果を出せます。以下の特化サンプルは、1 つの API を詳しく調べたいときに役立ちます

メインのワークフローデモ

デモ用途関連デモとの違い
PDFViewer全般的な表示と機能評価メインビューアはナビゲーション、検索、サムネイル、表示モード、印刷、機能ショーケースを 1 つにまとめています
MultiPageViewer連続ページ、見開き、表紙ページのレイアウトビューア組み込みの選択 API を使ったレイアウト特化の例です。幅広いビューア体験には PDFViewer を使います
SplitView別々のペインでページやドキュメントを比較1 つのビューに 2 ページを並べるレイアウトではなく、独立したビュー位置とアクティブペインへのコマンドルーティングを示します
SearchAndSelectページをまたぐ検索結果と選択結果リストと検索オプションが、ビューアツールバーよりも検索ワークフローを直接的に見せます
PrintPDFプレビューと通常の印刷ジョブページ範囲、スケーリング、進捗、キャンセルによって標準的な印刷ワークフローを提供します
ContentExtractionLab複数のコンテンツカテゴリをまとめて調査TXT / JSON インベントリはオブジェクトを記述しテキストをプレビューしますが、完全なテキストや画像ファイルのエクスポートの代わりにはなりません
ExtractImages埋め込み画像オブジェクトのエクスポートページ全体のスクリーンショットではなく画像ビットマップを抽出します。内蔵フィクスチャは 2 ページそれぞれに 2 つの実画像オブジェクトを含みます
TableExtractionLabテーブル検出と CSV/JSON エクスポート罫線テーブルと空白整列テーブル、結合セル、連鎖グループ、信頼度コントロールは、プレーンテキスト抽出から独立しています
Attachment埋め込み添付ファイルの読み取り、追加、エクスポート添付ファイルのペイロード操作は、ContentExtractionLab での名前やサイズの一覧表示を超えます
BatchExport複数ドキュメントからのページ画像エクスポートストリーミング出力、ファイルごとのステータス、隔離された失敗により、単一変換ではなくバッチ処理を示します
CreatePDFライブプレビュー付きのページコンテンツ作成3 つのネイティブ版すべてで検索可能なテキスト、ベクター図形、実際のラスター画像、テーブル、レビューアノテーションを作成します。オプションを変更すると、明示的な保存の前にプレビューが再構築されます
ImageToPDF入力画像ファイルからのページ構築画像レイアウト、フィッティング、順序、JPEG 処理によってドキュメント変換ワークフローを提供します
MergePDF複数の PDF からのページ組み立てドキュメントのインポートと順序付き構成を示し、分割は逆方向のワークフローを扱います
SplitPDF選択したページグループの別々の PDF への書き出し出力命名とページグループの検証は、結合やページ画像レンダリングとは異なります
WatermarkStamp既存ページへの繰り返しオーバーレイの適用配置とバッチページスタンプが基本の作成 API を補完します
FormFill対話型の AcroForm 入力とフラット化フィールド入力と保存済みアピアランスは、XFDF 交換や XFA 固有の動作とは異なります
AccessibilityReader対話型の読み取りと音声GUI リーダーはコンプライアンス監査ではなく、読み取りナビゲーションとハイライトを示します
AccessibilityWorkbenchCliアクセシビリティの一括レビューとレポート生成検出項目、サマリー、レビューゲートは自動化向けですが、検出結果は手動のアクセシビリティレビューの代わりにはなりません
StandardsLab規格別の検証と変換対話型ラボは集計レポートを生成するだけでなく、操作とオプションを公開します
PreflightReport対話型の規格レポートGUI が手動での調査とレポートエクスポートをサポートします
PreflightReportCli自動化された規格レポートバッチ入力、レポート形式、終了コードゲートが、PreflightReport の自動化版を提供します
IntakeWorkbenchCli一括取り込み、トリアージ、ルーティング集計レポート、失敗ゲート、ルーティング判断により、SecurityAudit GUI を超える処理ワークフローになっています

Miscellaneous の特化サンプル

これらは Demo\Delphi\Miscellaneous、Demo\CBuilder\Miscellaneous、Demo\Lazarus\Miscellaneous から開いてください。メインディレクトリを一連の作業に集中させ、ここには実用的な API の使い方と用途を絞ったエクスポート処理を残しています。基本的な Unicode 検査と、固定の注釈作成/一覧手順は、文書作成、抽出、XFDF 交換、API テストで扱い、独立したデモプロジェクトは設けていません

デモ用途関連デモとの違い
SecurityAuditセキュリティ状態の対話的な調査GUI はファイルルーティングよりも、権限、スクリプト、署名、セキュリティ詳細に重点を置きます
XfdfLabXFDF データのインポートとエクスポートデータ交換と、インポート済み PDF の明示的な保存で永続化を示します。XFDF のエクスポートだけでは変更済み PDF は保存されません
AccessibilityLabコンソールでリーダー API を一通り確認コンパクトな実行形式のウォークスルーが、読み取り単位、単語ボックス、構造、音声スクリプト出力を公開します
ExtractText選択ページからの完全なテキストのエクスポートテキストレイヤー出力、ページ区切り、キャンセルはインベントリプレビューやテーブル検出とは別物で、OCR は行いません
FontPropertiesフォントと文字メトリクスの調査最初のテキストオブジェクトの実際のフォントと文字メトリクスを表示した状態で起動します。別の行を選べば、ドキュメントのペイロードをエクスポートせずにフォントを比較できます
NavigationLabブックマーク、デスティネーション、ナビゲーションアクションコンパクトな API ウォークスルーが PDFViewer の画面上のナビゲーションコントロールを補完します
PDFtoJpg1 つのドキュメントのページ画像へのレンダリングコンパクトな単一ドキュメントのレンダリング例です。Delphi 版は個別のページ範囲を公開し、BatchExport は複数入力ドキュメントに焦点を当てます
PrintPreferencesN-up シートと PDF 作成者の印刷設定部数、奇数 / 偶数ページのフィルタリング、作成者指定の設定を学習の中心に据え、PrintPDF と対を成すデモです
ViewerInteractionLabマウス、キーボード、座標インタラクションの診断インタラクションログとページ / デバイス座標変換により、API 診断ツールとして成り立っています
XfaLab静的 XFA パケットとランタイム機能通常のランタイムで template と datasets パケットを開きます。対話型 XFA レンダリングには XFA 対応ランタイムが必要で、Delphi 版と Lazarus 版があります

まずは動く機能ツアーから

PDFViewer は同梱の PDFiumDemoShowcase.pdf を開きます。これは検索可能なテキスト、ベクターチャート、2 枚の埋め込み画像、テーブル、4 つの AcroForm フィールド、章ブックマーク、リンク、レビューノート、2 つの添付ファイルを備えた 6 ページのドキュメントです。エクスペリエンスバーには Restart showcase、Find live text、2-page spread、Night reading、Inspect this PDF、Explore forms が用意され、どのアクションも現在開いているドキュメントに対して動作します

CreatePDF は 2 ページのレポートをライブプレビューで開きます。タイトルや作成者を編集したり、図形、テーブル、ウォーターマークをトグルしたりすると実際のページコンテンツが再構築され、Unicode オプションで 3 ページ目が追加されます。保存するまで何も書き込まれず、保存した PDF は検索可能なままです

MultiPageViewer、SearchAndSelect、ContentExtractionLab、FormFill は同じドキュメントをそれぞれのワークフロー用に開き、検索結果、抽出インベントリ、編集可能なフィールドがすぐに使える状態で揃っています。Miscellaneous\NavigationLab は独自のローカルコピーを使って、ブックマークとデスティネーションの API ウォークスルーを提供します

Attachment は 2 つの実際の埋め込みファイルから始まります。PrintPreferences は 4 ページのドキュメントと、部数 2 部、選択ページ範囲、倍率、両面印刷の作成者設定から始まります。現在の印刷ワークフローでは部数とページ範囲が適用され、倍率と両面印刷は検査のうえ報告されます。XfaLab は静的 XFA 検査用の実際の template と datasets パケットから始まります。対話型 XFA レンダリングには適切なランタイムが必要で、別の機能です

表示、フォーム、抽出、ナビゲーションの各プロジェクトは、それぞれ独自の PDFiumDemoShowcase.pdf を含んでいます。プロジェクトディレクトリをコピーして、この同梱入力を配布する実行可能ファイルの隣に置いてください。Lazarus ビルドは自動で配置します。ツアーはオフラインで動作し、ローカルの PDF が利用できない場合は汎用ビューアと抽出サンプルが生成済みのフォールバックコンテンツを提供します

ネイティブサンプルのコピーと保守

すべてのネイティブデモが独自のサンプル実装を含んでいます。AccessibilityLab、AccessibilityWorkbenchCli、IntakeWorkbenchCli、PreflightReportCli の Delphi と Lazarus のエントリーポイントは、コンパイラ固有の初期化や JPEG 書き込みを含む完全なワークフローをプロジェクトディレクトリ内に持っています。Attachment、印刷設定、XFA の各サンプルも、機能固有の入力をローカルで生成します。コピーしたプロジェクトの外側に必要なのは、ライブラリ、対応するネイティブランタイム、通常のコンパイラフレームワーク依存だけです

埋め込みビットマップが必要なときは画像オブジェクト抽出を、見えているページが必要なときはページレンダリングを、PDF に既にテキストレイヤーがあるときはテキスト抽出を選びます。これらのカテゴリを一覧するレポートは、その完全なペイロードをエクスポートしません。この境界を保つことで、焦点を絞った例は有用なまま、すべてのデモがもう 1 つの汎用ビューアやワークベンチへ膨らむことはありません

基本的なドキュメント操作

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\Miscellaneous\FontProperties、Demo\CBuilder\Miscellaneous\FontProperties、Demo\Lazarus\Miscellaneous\FontProperties プロジェクトは、TPdf と TPdfView の両方を介して同じメタデータを表示します

マルチカテゴリコンテンツ抽出レポートの構築

Demo\Delphi\ContentExtractionLab、Demo\CBuilder\ContentExtractionLab、Demo\Lazarus\ContentExtractionLab プロジェクトは、コンテンツ抽出を単一のワークフローに変えます。PDF を開いたり作成したりして、ユーザーがメタデータ、ページテキスト、ページオブジェクト、画像、添付ファイル、リンク、アノテーション、ブックマーク、フォント / 文字メトリクスを選択でき、サマリーと詳細な TXT または JSON レポートを生成します。Lab には、すべて / 何もカテゴリ選択、レポートのクリップボードへのコピー、元 PDF 保存アクションも含まれており、ユーザーは生成されたサンプル PDF を抽出レポートの隣に保持できます。ExtractImages と Attachment はペイロードエクスポートのワークフローを担い、Miscellaneous\ExtractText と Miscellaneous\FontProperties は特化した API サンプルとして残ります。ExtractText は Delphi、C++Builder、Lazarus にわたってページ範囲とページ区切りオプションをサポートし、ページごとのテキスト出力を生成します。ContentExtractionLab はクロスカッティングインベントリサンプルです

テーブルの検出と抽出

Demo\Delphi\TableExtractionLab、Demo\CBuilder\TableExtractionLab、Demo\Lazarus\TableExtractionLab の各プロジェクトは、テーブル検出を 1 つのワークフローにまとめます。ラボは、結合されたヘッダーセルを持つ罫線テーブル、罫線のない空白整列テーブル、ページ境界をまたいで分断される長いテーブルを含むサンプルドキュメントを構築し、現在のページには ExtractTables を、ドキュメント全体には ExtractDocumentTables を実行して、行数と列数、RowSpan と ColumnSpan の値、ヘッダーフラグ、検出モード、信頼度、連鎖グループを報告します。DetectRuledTables、DetectWhitespaceTables、DetectContinuations、DetectFilledRulings、MinConfidence の各オプションは UI で編集でき、選択したテーブルは TPdfTable.ToCsv と TPdfTable.ToJson でエクスポートされます。ラボはフォルダー単位のバッチ処理も行います。Batch folder はすべての PDF に ExtractDocumentTables を実行してドキュメントごとのテーブル数を報告し、テキストレイヤーのないスキャンにフラグを立て、空のドキュメントをコンパクト列プリセットで再試行します。Export all CSV は検出された全テーブルをソースファイルの隣に書き出します。各ドキュメントはそれぞれ専用の TPdf インスタンスで開かれるため、読み取れない 1 つのファイルがバッチの残りに影響することはありません

フォーム操作

フォームフィールドの操作

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] := '[email protected]';
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;

StandardsLab で複数の PDF 規格を検査

Demo\Delphi\StandardsLab、Demo\CBuilder\StandardsLab、Demo\Lazarus\StandardsLab プロジェクトは PDF を読み込みまたは作成し、ValidatePdfA、ValidatePdfUa、ValidatePdfE、ValidatePdfX、ValidatePdfR、ValidatePdfVT を実行し、検出された各準拠レベルと問題数を 1 つのグリッドに表示します。保存ボタンは SaveAsPdfA、SaveAsPdfUa、SaveAsPdfE、SaveAsPdfX、SaveAsPdfR、SaveAsPdfVT を呼び出すため、カスタムハーネスを書かなくてもマーカー出力ワークフローを確認できます

プリフライトレポートの生成

FPdfPreflightReport ユニットは、ValidatePdfA、ValidatePdfUa、ValidatePdfE、ValidatePdfX、ValidatePdfR、ValidatePdfVT を、Info / Warning / Error の重要度、ステータスと問題の集計、ルックアップ、検索、フィルタリング、安定した SHA-256 フィンガープリントを備えた 1 つの照会可能なレポートオブジェクトにまとめます。レポートはテキスト、HTML、Markdown、JSON、CSV、SARIF 2.1.0、JUnit XML、NDJSON、ネイティブ XML として出力できます。Demo\Delphi\PreflightReport、Demo\Lazarus\PreflightReport、Demo\CBuilder\PreflightReport は対話的な選択とプレビューのワークフローを提供し、Demo\Delphi\PreflightReportCli はスクリプト化可能な入出力引数、標準フィルタリング、オプションのレポート添付埋め込み、CI 終了コードゲーティング、バッチリストおよびディレクトリ処理、衝突を回避するレポートルーティング、集計サマリー、CSV マニフェストを追加します。生成されるレポートには、組み込みバリデータがマーカーレベルと選択されたファイルレベルのチェックを対象とすることが明記されているため、完全なコンテンツレベルのプリフライトは引き続き専用の検証エンジンに委任できます

ストリーミングロードと大規模なドキュメント

LoadCustomDocument によるオンデマンド PDF 読み込み

// 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 プロジェクトは、これを完全なバッチワークフローに変えます。複数の画像ファイルを選択し、選択した項目をプレビューし、画像ごとに 1 つの 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\Miscellaneous\ViewerInteractionLab、Demo\CBuilder\Miscellaneous\ViewerInteractionLab、Demo\Lazarus\Miscellaneous\ViewerInteractionLab プロジェクトは、モダンな TPdfView インタラクションサーフェスを 1 つのコンパクトなフォームに保ちます。サンプル PDF を開いたり作成したり、DisplayMode を切り替え、FitMode を適用し、PageColor を変更し、ページを回転し、HighlightSearchText で現在のページの検索ヒットをハイライトし、ユーザーテキスト選択を有効化または無効化し、SelectedText を検査し、SelectAll、CopySelectionToClipboard、ClearSelection を呼び出し、マウスがページ上を移動する間にステータスバーで DeviceToPage 座標が更新されるのを観察できます

SearchAndSelect でテキストの検索と選択

Demo\Delphi\SearchAndSelect、Demo\CBuilder\SearchAndSelect、Demo\Lazarus\SearchAndSelect プロジェクトは、検索および選択ビューアワークフローをスタンドアロンサンプルに変えます。PDF を開いたり作成したり、ページ番号、文字インデックス、プレビューテキスト付きの全ページ検索結果を一覧表示し、前 / 次ナビゲーションとダブルクリックによる結果ジャンプをサポートし、HighlightSearchText で現在のページのマッチをハイライトし、大文字小文字区別と単語単位オプションを公開し、AllowUserTextSelection を切り替え、SelectedText をプレビューし、ボタンから SelectAll、CopySelectionToClipboard、ClearSelection を駆動し、ユーザーが単一表示モードと見開き表示モードを切り替えられるようにします

PrintPreferences で PDF 印刷設定を読み取り

Demo\Delphi\Miscellaneous\PrintPreferences、Demo\CBuilder\Miscellaneous\PrintPreferences、Demo\Lazarus\Miscellaneous\PrintPreferences プロジェクトは、印刷前に作成者の印刷設定を読み取ります。PrintCopies、PrintPageRanges、PrintScaling、PrintPaperHandling を表示し、作成者が提供したコピーと範囲を印刷入力にコピーでき、SetPdfPrintPaperHandlingDevMode が PDF 両面印刷意図を Windows DEVMODE にマップする方法を示します

PrintPDF で標準 PDF 印刷

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 での最初のページを開く速度とレンダリング応答性のテストにも有用です。大規模ファイルテストでは、Acrobat や Foxit が最初の表示に 20-30 秒かかるような 2.x GB のドキュメントが、PDFium Delphi Component ビューアパスを通じてほぼ即座に使用可能になります

SplitView での並列レビュー

Demo\Delphi\SplitView、Demo\CBuilder\SplitView、Demo\Lazarus\SplitView プロジェクトは並列 PDF レビューを示します。C++Builder および Lazarus サンプルは、2 または 3 ペインの比較、アクティブビューの選択、オプションの同期ナビゲーションコマンド、共有ズームプリセット、選択されたビューまたはすべてのロードされたビューの回転に焦点を当てます。これらのサンプルを使用して、最初の使用可能なビューと後続のページレンダリングの応答性を保ちながら大規模ドキュメントを比較できます。Acrobat や Foxit で開くのに 20-30 秒かかる可能性のある同じ 2.x GB の PDF ファイルが、PDFium Delphi Component ビューアパスではほぼ即座に使用可能になります

SecurityAudit で PDF リスクサーフェスを監査

Demo\Delphi\Miscellaneous\SecurityAudit、Demo\CBuilder\Miscellaneous\SecurityAudit、Demo\Lazarus\Miscellaneous\SecurityAudit は PDF を読み取り専用で開き、権限、埋め込み添付、文書 JavaScript アクション、URI/Launch リンク注釈、web link、署名の概要、XFA の状態、V8 helper の利用可否、未対応機能の 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;

キオスク向けのビューアロックダウン

ページナビゲーションジェスチャの無効化

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

タグ付き PDF とアクセシビリティ

タグ付き 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 / イメージマスク印刷モードの設定

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