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;
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;
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 レポートを生成します。Lab には、すべて / 何もカテゴリ選択、レポートのクリップボードへのコピー、元 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 はすべてのページのアノテーションとウィジェットを
// 永続的な編集不可コンテンツに焼き込みます。
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := 'john@example.com';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;
var
I: Integer;
Bitmap: TBitmap;
begin
for I := 0 to Pdf1.BitmapCount - 1 do
begin
Bitmap := Pdf1.Bitmap[I];
try
Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
finally
Bitmap.Free;
end;
end;
end;
var
JpegStream: TFileStream;
begin
JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
try
Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
finally
JpegStream.Free;
end;
Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
Pdf1.UpdatePage;
end;
var
I: Integer;
Annotation: TPdfAnnotation;
begin
for I := 0 to Pdf1.AnnotationCount - 1 do
begin
Annotation := Pdf1.Annotation[I];
ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
end;
end;
Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;
Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;
var
I: Integer;
Bookmarks: TBookmarks;
begin
Bookmarks := Pdf1.Bookmarks;
for I := 0 to Length(Bookmarks) - 1 do
ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;
var
I: Integer;
Destination: TDestination;
begin
for I := 0 to Pdf1.DestinationCount - 1 do
begin
Destination := Pdf1.Destination[I];
if Destination.Name = 'Chapter1' then
begin
Pdf1.PageNumber := Destination.PageNumber;
Break;
end;
end;
end;
var
I: Integer;
AttachmentData: TBytes;
AttachmentName: WString;
FileStream: TFileStream;
begin
for I := 0 to Pdf1.AttachmentCount - 1 do
begin
AttachmentName := Pdf1.AttachmentName[I];
AttachmentData := Pdf1.Attachment[I];
FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
try
if Length(AttachmentData) > 0 then
FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
finally
FileStream.Free;
end;
end;
if Pdf1.CreateAttachment('MyFile.txt') then
Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;
Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);
var
SourcePdf: TPdf;
begin
SourcePdf := TPdf.Create(nil);
try
SourcePdf.FileName := 'C:\SourceDocument.pdf';
SourcePdf.Active := True;
Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
finally
SourcePdf.Free;
end;
end;
if Pdf1.PageCount > 1 then
Pdf1.DeletePage(Pdf1.PageCount);
// 圧縮された 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;
// オプション付きで保存
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // インクリメンタル保存
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // セキュリティ削除
// 特定の PDF バージョンターゲットに保存。PDF バージョンバリデータは
// 選択されたターゲット (JBIG2Decode、MarkInfo、AES 暗号化、
// Polygon / Caret / Watermark / Redact アノテーションなど) より後に
// 導入された機能を拒否します。
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA は基本保存をインクリメンタル更新で後処理し、
// XMP メタデータストリーム、sRGB ICC OutputIntent、
// 更新されたドキュメント Catalog を注入することで結果を PDF/A-1b 準拠にします。
// カスタムプロファイルが提供されない場合、付属の sRGB IEC61966-2.1
// プロファイルが自動的に使用されます。
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
ShowMessage('Saved PDF/A-1b archive');
// カスタム ICC プロファイルを伴う PDF/A (例: CMYK 印刷パイプライン)
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;
// インメモリ PDF/A 出力
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;
var
Validation: TPdfAValidationResult;
Issue: TPdfAValidationIssue;
begin
Pdf1.FileName := 'C:\Incoming\report.pdf';
Pdf1.Active := True;
// 軽量なヘッダースタイルのプローブ — 完全なバリデータを反復処理することなく
// 準拠を読み取ります。結果: pacNone、pac1a、pac1b、pac2b、pac3b、または 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;
// 問題ごとの診断を伴う完全なバリデータ
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;
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 を 1 つのレポートオブジェクトにラップします。Demo\Delphi\PreflightReport、Demo\Lazarus\PreflightReport、Demo\CBuilder\PreflightReport はユーザーが規格を選択でき、サマリーを生成し、ステータス、優先度、問題カテゴリ、カテゴリ数、次のアクション、問題コード、推奨アクションを検査し、プレビューをテキスト、Markdown、JSON、CSV 間で切り替え、TXT、HTML、Markdown、JSON、CSV レポートをエクスポートできます。Delphi GUI サンプルでは、Action Plan のプレビューを追加し、合格しない標準、優先順位、問題コード、および推奨事項を修復チェックリストにまとめます。Demo\Delphi\PreflightReportCli は、入出力引数、オプションのパスワード、出力フォーマット選択に plan / actionplan を含める設定、規格フィルタリング、オプションの単一ファイル attach=output.pdf レポート埋め込み、failon= CI ゲーティング終了コード、batch=list.txt 処理、オプションの再帰と安定パスソート処理を伴う batchdir=folder ディレクトリスキャン、outdir=reports レポートルーティング、重複入力ファイル名向けの衝突安全レポート名、集計および規格ステータス合計付きの TXT / JSON / HTML サマリー、項目ごとに実行設定を繰り返す CSV 行マニフェスト、スクリプトと CI ジョブ向けの引数なしサンプルモードを伴うコンソールワークフローとして同じレポートパスを公開します。生成されたレポートには、組み込みバリデータがマーカーレベルと一部のファイルレベルチェックをカバーすると記載されているため、完全なコンテンツレベルのプリフライトは引き続き専用の検証エンジンに委任できます。
// LoadCustomDocument は PDFium が任意のシーク可能な TStream から
// ブロックをオンデマンドで取得できるようにします。ストリームはメモリにコピーされないため、
// 複数 GB の PDF、リモート HTTP ボディストリーム、データベースバックエンドソースは
// すべて事前バッファなしで実行可能です。
var
FileStream: TFileStream;
begin
// ファイルからの読み取りストリーミング (インメモリコピーなし)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ 所有権が移管されました — Pdf1 は UnloadDocument 時に FileStream を解放します。
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;
// FPdfAsync は、長時間実行されるエクスポートおよびレンダリングパスにわたって機能する
// UI からのキャンセルセマンティクス向けの IPdfCancellationToken /
// IPdfCancellationTokenSource を公開します。
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... ユーザーがキャンセルをクリック ...
CancelSource.Cancel;
// ワーカーは定期的に CancelToken.IsCancelled をチェックするか、
// レンダリングパスは PDFium の IFSDK_PAUSE コールバックを介してポーリングします。
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive は、ユーザーがキャンセルをクリックしたときに、
// 完了するまで呼び出し元をブロックする代わりに、長時間の高 DPI レンダリングを
// ページ途中で中止できるようにします。
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<T> はバックグラウンドスレッドでワーカーを起動し、
// TPdfFutureResult<T> エンベロープをメインスレッドに投稿します。
//
// 重要: TPdf はインスタンスごとにレンダリング呼び出しをシリアル化しますが、
// ワーカーは依然としてディスパッチとドキュメントライフタイムを所有します。
// レンダリング中の TPdf を変更しないでください。バッチエクスポートは通常、
// ファイルごとに 1 つの TPdf を保持します。
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;
// FPdfMatrix は PDFium の FS_MATRIX をラップするため、呼び出し元は
// 変換を宣言的に構築し、FPDFPageObj_SetMatrix に渡せます。
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // 1 インチ右、約 2.8 インチ上へ移動
M.Scale(0.5, 0.5); // ハーフサイズスタンプ
M.Rotate(15); // 15 度反時計回り回転
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // 別のマトリックスとの後乗算
// 任意の PDFium ページオブジェクト (テキスト / パス / 画像 / Form XObject) に適用
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex はソース PDF から明示的なゼロベースインデックス配列を
// このドキュメントにコピーします。InsertAt = 0 はページ 1 の前に挿入、
// PageCount は末尾に追加します。
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Source からページ 0、2、4 (1 ベース: ページ 1、3、5) をインポートし、
// Pdf1 の末尾に追加します。
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// 空配列はすべてのソースページをインポート
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne は、このドキュメントの NumX*NumY 合成ページを持つ
// 新しい TPdf を返します。OutputWidth/Height は PDF ユーザー単位
// (1 単位 = 1/72 インチ) です。
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 横向き: 842 x 595 pt、2 列 x 2 行
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages は移動するページのゼロベースインデックスと、
// 移動完了後に最初に移動したページの宛先インデックスを受け取ります。
// 他のページは移動したブロックの周りでシフトするため、ドキュメントの長さは
// 同じままになります。
//
// 例: [0,1,2,3,4] とラベル付けされたページを持つ 5 ページのドキュメントで、
// MovePages([2, 3], 0) は [2, 3, 0, 1, 4] に並べ替えます。
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage は他の任意の TPdf の任意のページから
// 再利用可能な Form XObject ハンドルを生成します。そのハンドルを
// このドキュメントの好きな数のページにスタンプできます — 各スタンプは
// マトリックス API を介して位置決め、スケール、回転が可能な
// 単一の PDFium ページオブジェクトです。
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;
// WatermarkSource のページ 0 を再利用可能な 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;
// ウォーターマークをページ中央に配置
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; // FPDF_XOBJECT ハンドルをクローズ
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(FileName) は VCL または LCL グラフィックスユニットに
// 登録された任意の形式 (PngImage 経由の BMP、PNG、JPG など) を受け付けます。
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) は TPicture 中間ステップをスキップします。
// ビットマップが既にレンダリングまたはバッチ生成作業から取得されている場合に最適です。
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
// ビューの現在のページのすべてのマッチに clYellow マスクを描画します。
// 別のページに切り替えるとハイライトは自動的にクリアされます。
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// ページ変更前にプログラム的にオーバーレイを削除
PdfView1.ClearHighlight;
end;
// FitMode は Resize 時またはページ変更時に長いドキュメントの
// フレーミングを維持します。Zoom を直接設定すると FitMode はキャンセルされます
// (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;
Demo\Delphi\ViewerInteractionLab、Demo\CBuilder\ViewerInteractionLab、Demo\Lazarus\ViewerInteractionLab プロジェクトは、モダンな TPdfView インタラクションサーフェスを 1 つのコンパクトなフォームに保ちます。サンプル PDF を開いたり作成したり、DisplayMode を切り替え、FitMode を適用し、PageColor を変更し、ページを回転し、HighlightSearchText で現在のページの検索ヒットをハイライトし、ユーザーテキスト選択を有効化または無効化し、SelectedText を検査し、SelectAll、CopySelectionToClipboard、ClearSelection を呼び出し、マウスがページ上を移動する間にステータスバーで DeviceToPage 座標が更新されるのを観察できます。
Demo\Delphi\SearchAndSelect、Demo\CBuilder\SearchAndSelect、Demo\Lazarus\SearchAndSelect プロジェクトは、検索および選択ビューアワークフローをスタンドアロンサンプルに変えます。PDF を開いたり作成したり、ページ番号、文字インデックス、プレビューテキスト付きの全ページ検索結果を一覧表示し、前 / 次ナビゲーションとダブルクリックによる結果ジャンプをサポートし、HighlightSearchText で現在のページのマッチをハイライトし、大文字小文字区別と単語単位オプションを公開し、AllowUserTextSelection を切り替え、SelectedText をプレビューし、ボタンから SelectAll、CopySelectionToClipboard、ClearSelection を駆動し、ユーザーが単一表示モードと見開き表示モードを切り替えられるようにします。
Demo\Delphi\PrintPreferences、Demo\CBuilder\PrintPreferences、Demo\Lazarus\PrintPreferences プロジェクトは、印刷前に作成者の印刷設定を読み取ります。PrintCopies、PrintPageRanges、PrintScaling、PrintPaperHandling を表示し、作成者が提供したコピーと範囲を印刷入力にコピーでき、SetPdfPrintPaperHandlingDevMode が PDF 両面印刷意図を Windows DEVMODE にマップする方法を示します。
Demo\Delphi\PrintPDF、Demo\CBuilder\PrintPDF、Demo\Lazarus\PrintPDF プロジェクトは標準印刷ワークフローを示します。PDF を開き、ページをプレビューし、プラットフォームの印刷ダイアログでページ範囲とコピーを選択し、丁合モードを尊重し、TPdf.RenderPage を介して各ページをレンダリングし、キャンセル付きで印刷進捗を公開します。Delphi サンプルはドロップされた PDF ファイルも素早く読み込めるよう受け付けます。
Demo\Delphi\MultiPageViewer、Demo\CBuilder\MultiPageViewer、Demo\Lazarus\MultiPageViewer プロジェクトは、通常のページナビゲーションとズームワークフローを伴う連続、見開き、表紙対応の表示を活用します。非常に大きな PDF での最初のページを開く速度とレンダリング応答性のテストにも有用です。大規模ファイルテストでは、Acrobat や Foxit が最初の表示に 20-30 秒かかるような 2.x GB のドキュメントが、PDFiumPas ビューアパスを通じてほぼ即座に使用可能になります。
Demo\Delphi\SplitView、Demo\CBuilder\SplitView、Demo\Lazarus\SplitView プロジェクトは並列 PDF レビューを示します。C++Builder および Lazarus サンプルは、2 または 3 ペインの比較、アクティブビューの選択、オプションの同期ナビゲーションコマンド、共有ズームプリセット、選択されたビューまたはすべてのロードされたビューの回転に焦点を当てます。これらのサンプルを使用して、最初の使用可能なビューと後続のページレンダリングの応答性を保ちながら大規模ドキュメントを比較できます。Acrobat や Foxit で開くのに 20-30 秒かかる可能性のある同じ 2.x GB の PDF ファイルが、PDFiumPas ビューアパスではほぼ即座に使用可能になります。
Demo\Delphi\SecurityAudit、Demo\CBuilder\SecurityAudit、Demo\Lazarus\SecurityAudit プロジェクトは PDF を読み取り専用で開き、権限状態、埋め込み添付ファイル、ドキュメント JavaScript アクション、URI および Launch リンクアノテーション、ウェブリンク、署名サマリー、XFA 状態、V8 ヘルパー利用可能性、未対応機能コールバックを一覧表示します。同じサマリーおよび検出行は TXT 監査レポートとして保存またはコピーするか、構造化された JSON として保存できます。ホストアプリケーションのリスクパネルとして意図されており、マルウェア検出としてではありません。
// LinkOptions は 4 つの PDF リンクアクションタイプの自動処理を制御します。
// デフォルトでは goto と URI のみが有効です。launch (プログラム実行) と
// embedded GotoR (別のファイルへのジャンプ) はオフのままなので、
// 誤クリックで任意のコードが実行されることはありません。
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // 安全なデフォルト
// 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 は PDFium のウェブリンクスキャナによって検出された URI 文字列に対して発生します
// (/A <</S/URI...>> アノテーションなしでも、テキストストリーム内の自動検出 URL)。
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Web link clicked: ' + string(Url));
end;
// フォーカスされた AcroForm ウィジェット上で、6 つの新しいメソッドが PDFium の
// フォーム編集履歴を駆動します。ウィジェットにフォーカスがないか、ドキュメントが
// AcroForm を持たない場合、6 つすべては安全にショートサーキットします。
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;
// 3 つすべてはデフォルトで True なので、既存のアプリは動作変更なしでアップグレードできます。
// TPdfView をサブクラス化することなくキオスク、プレビューペイン、読み取り専用埋め込みを
// ロックダウンするには、これらを False に切り替えます。
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // ホイール上下ジャンプ
PdfView1.AllowUserTextSelection := False; // マウス/キーボードテキスト選択
// Ctrl+ホイールはズーム用に予約されており、ChangePageOnMouseScrolling に関係なく
// ナビゲーションハンドラではスキップされます。
// TPdfThumbnailView (ユニット FPdfThumbnail) はスクロール可能なサイドパネルに
// 1 行ごとに 1 ページのサムネイルをレンダリングします。
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // 暖色系ハイライト
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex はゼロベース
end;
procedure TForm1.PdfView1PageNumber変更(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Demo\Delphi\BatchExport、Demo\CBuilder\BatchExport、
// Demo\Lazarus\BatchExport で使用されているパターン。ファイルごとに 1 つの TPdf、
// UI からのキャンセル用の IPdfCancellationToken、バッファ付きファイル読み込み用の TPdfStreamAdapter。
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;
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;
// IsTagged は軽量な Catalog プローブで、/StructTreeRoot が存在する場合に True です。
// StructureElements はツリーを TPdfStructureElement レコードのフラット配列に
// マテリアライズします。タイプ、タイトル、代替テキスト、実際のテキスト、
// 展開テキスト、言語、レベル、親インデックス、子 / マークコンテンツ / 属性数を持ちます。
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;
// SetPdfPrintMode は PDFium の FPDF_SetPrintMode にマップする
// グローバル関数です。使用するプリンタドライバに対して、
// 対応する TPdfPrintMode 値を使用します。
SetPdfPrintMode(pmEmf); // GDI EMF (デフォルト)
// SetPdfPrintMode(pmTextOnly); // テキストのみレンダリング
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 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('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // 1 ベース範囲
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 は /Names /JavaScript ネームツリーに
// 格納されています。JavaScriptAction[i] は 1 つのエントリを返し、
// JavaScriptActions は完全な配列を返します。
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;
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;
// ゼロコピーレンダーパス (v1.21.0 以降デフォルト): RenderPage は
// 呼び出し元が事前割り当てされたビットマップを渡すと宛先 TBitmap の
// DIB バッファに直接書き込み、幅 * 高さ * 4 バイトのコピーを 1 回節約します。
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;