このページには自動翻訳されたテキストを含めることができます。

C#とVB.NETでPDFドキュメントを作成する方法

この記事では、Docotic.Pdfライブラリを使用して.NETでPDFドキュメントを作成するさまざまな方法について説明します。Docotic.Pdfは、PDFドキュメントの作成、編集、変換、処理を行うための、外部依存関係のない高性能な純粋なC# .NETライブラリです。

Docotic.Pdf を使用した PDF 作成と文書自動化を強調した図。

以下のセクションでは、Docotic.Pdf を使用して PDF を作成する主な方法について説明します。

  • Core API を使用する方法。テキスト、グラフィック、および PDF の内部構造を 低レベル制御 で制御できます。このオプションは、カスタムレイアウト、グラフィックを多用するドキュメント、および高度な機能に最適です。
  • 高レベル Layout API を使用する方法。段落、表、ヘッダー、フッター、および自動ページネーションをサポートします。この API は、位置を手動で計算することなく構造化されたドキュメントを作成したい場合に最適です。
  • HTML から PDF への変換。SVG やその他の Web フォーマットをサポートします。この方法は、ソリューションで既に HTML ドキュメントを生成しており、それらの HTML および CSS ファイルの PDF バージョンが必要な場合に特に便利です。
  • 画像からの PDF 作成。この方法は、スキャンされたドキュメント、画像ベースのレポート、領収書、およびラスター画像から始まるあらゆるワークフローに役立ちます。
  • PDF の結合または分割。これは、レポートの作成、ユーザーアップロードの処理、関連文書の結合、大規模PDFの再構築などに適した選択肢です。
  • テンプレートからのPDF作成。この方法は、領収書、税務申告書、雇用契約書など、繰り返し使用される文書のフォーマットを統一する必要がある場合に効果的です。

ガイドでは、以下のトピックも取り上げています。

Core API を使用した PDF の作成

Docotic.PdfにおけるPDF作成の基盤となるのは、コアAPIです。コアAPIのCanvas APIを使えば、PDFキャンバス上にテキスト、画像、ベクターグラフィックを配置するための、低レベルかつ完全な制御が可能になります。この描画APIはコアAPIのサブセットであり、キャンバスを持つページやその他のオブジェクトにコンテンツを追加するためのメソッドとプロパティを提供します。コアAPIはレンダリングだけでなく、注釈、フォームフィールド、レイヤー、ブックマークなど、その他のPDF機能もサポートしています。

以下は、テキストの描画、画像の配置、ベクターグラフィックのレンダリングという3つの基本的な操作を用いて、シンプルなPDFを作成するC#コードです。

using var pdf = new PdfDocument();
var canvas = pdf.Pages[0].Canvas;

canvas.Font = pdf.CreateFont(PdfBuiltInFont.HelveticaBold);
canvas.FontSize = 14;
canvas.DrawString(40, 100, "Core API demo: text, images, and vector graphics");

var image = pdf.CreateImage("image.png");
canvas.DrawImage(image, 40, 180, 120, 120, 0);

canvas.Pen.Color = new PdfRgbColor(30, 60, 160);
canvas.Pen.Width = 2;
canvas.Brush.Color = new PdfRgbColor(200, 230, 255);
canvas.DrawRectangle(new PdfRectangle(200, 200, 150, 80), PdfDrawMode.FillAndStroke);

pdf.Save("core-api-demo.pdf");

この概要では、Core API の機能のごく一部しか紹介していません。高度なトピック については、Core API を使用した PDF の作成に関する詳細な記事を参照してください。この記事では、テキストの計測、カラースペースの操作、クリッピングの適用、パターンによる領域の塗りつぶし、透明度の処理など、さまざまな機能について解説しています。

Layout APIを使用してPDFを生成する

Layout APIは、複雑でコンテンツ豊富なPDFを最も簡単かつ効率的に生成できる、高レベルのドキュメント構築エンジンです。

このAPIを使用すると、ページ、コンテナ、テキストスパン、画像、表、リンク、ヘッダー、フッターなどの構造要素からPDFを構成できます。座標を計算したり、ページネーションを手動で管理したりする代わりに、ドキュメントの構造を記述するだけで、レイアウトエンジンが残りの処理を担います。

この例では、Layout APIを使用してPDFを作成する方法を示します。手動による位置決めではなく、宣言的なレイアウトを使用します。

PdfDocumentBuilder.Create()
    .Info(info => info.Title = "Docotic.Pdf Layout API demo")
    .Generate("layout-api-demo.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(100).Text(text =>
        {
            text.Span("The Layout API lets you compose PDFs from structural elements ");
            text.Line("without manually calculating coordinates or handling pagination.")
                .Style(s => s.Strong);
        });
    }));

.NETアプリケーションでPDFを生成する際にLayout APIを使用する方法については、詳細ガイドをご覧ください。

HTML to PDF APIを使用してWebコンテンツを変換する

Docotic.Pdfは、無料のHtmlToPdfアドオンと組み合わせることで、Chromeベースの最新かつ高品質なHTML-PDF変換エンジンを提供します。このアドオンが提供するAPIを使用すれば、最新のHTMLやSVG、WebP画像などのWebコンテンツを高品質なPDFドキュメントに変換できます。

HTML-PDF APIは、完全なHTMLページまたはHTMLフラグメントからPDFを作成できます。URL、生のHTML文字列、ローカルのHTMLファイルからコンテンツを変換することも可能です。後者2つのオプションを使えば、HTMLテンプレートから簡単にPDFを生成できます。

HTMLテンプレートからPDFを生成する例をご覧ください。

public static async Task HelloHtmlTemplate()
{
    static string GetUserName()
    {
        // Replace with real logic: form input, API call, config, etc.
        return "World";
    }

    string html = $@"
        <h1>Hello, {GetUserName()}!</h1>
        <p>This PDF was generated from an HTML template.</p>";

    using var converter = await HtmlConverter.CreateAsync();
    using var pdf = await converter.CreatePdfFromStringAsync(html);
    pdf.Save("hello-html-template.pdf");
}

詳細や例については、弊社の詳細なHTMLからPDFへの概要をご覧ください。

画像からPDFを作成する

Docotic.Pdfは、画像をPDFに変換するための柔軟で開発者にとって使いやすい方法を提供します。このライブラリは、Core APIを通じてJPEG、BMP、GIF、PNG、TIFF、およびJPEG 2000の画像フォーマットをサポートしています。

Docotic.Pdfが複数の画像ファイルを1つのPDF文書に変換する様子を示す図。

PDF形式がサポートされている場合、Docotic.Pdfは画像バイトをそのまま埋め込み、ピクセルデコードや再エンコードを行わずに元の圧縮率を維持します。また、可能な限りカラースペースも保持します。

さらに、HTMLからPDFへのAPIを通じてSVGおよびWebP形式もサポートされています。ラベルや説明文の横に画像を配置する必要がある場合は、レイアウトAPIを使用することで、最小限の手間で要素の配置と整列を行うことができます。

複数の画像を1つのPDFに結合する方法

Docotic.Pdfを使えば、複数の画像を簡単に1つのPDFファイルに変換し、各ページに1枚ずつ配置できます。

以下の例では、ファイルから画像を読み込み、それぞれを個別のページに描画しています。各画像はページに合わせて拡大縮小され、中央揃えになるため、すっきりとした統一感のあるレイアウトになります。

public static void ImagesOnToPdf(string[] imagePaths, string outputPath)
{
    using var pdf = new PdfDocument();

    foreach (string path in imagePaths)
    {
        var image = pdf.CreateImage(path);

        var page = pdf.AddPage();
        var pageWidth = page.Width;
        var pageHeight = page.Height;

        var scale = Math.Min(pageWidth / image.Width, pageHeight / image.Height);
        var drawWidth = image.Width * scale;
        var drawHeight = image.Height * scale;
        var x = (pageWidth - drawWidth) / 2;
        var y = (pageHeight - drawHeight) / 2;

        page.Canvas.DrawImage(image, x, y, drawWidth, drawHeight, 0);
    }

    pdf.RemovePage(0);

    pdf.Save(outputPath);
}

複数ページのTIFFおよびGIF画像の取り扱い

Docotic.Pdfは、複数ページのTIFFファイルとGIFファイルを完全にサポートしています。PDFに画像を追加する際、画像が複数ページを含む可能性がある場合は、CreateImageメソッドではなくOpenImageメソッドを使用してください。

以下のコードは、TIFFファイルをPDFに変換する方法を示しており、単一ページ画像と複数ページ画像の両方に対応しています。

public static void OddFramesToPdf(string[] imagePaths, string outputPath)
{
    using var pdf = new PdfDocument();
    foreach (string path in imagePaths)
    {
        var imageFrames = pdf.OpenImage(path);
        for (int i = 0; i < imageFrames.Count; i++)
        {
            if (i % 2 != 0)
                continue;

            var image = pdf.CreateImage(imageFrames[i]);
            var page = pdf.AddPage();

            page.Width = image.Width;
            page.Height = image.Height;

            page.Canvas.DrawImage(image, 0, 0, image.Width, image.Height, 0);
        }
    }

    pdf.RemovePage(0);

    pdf.Save(outputPath);
}

GIFをPDFに変換する場合も、同じ方法が使えます。他の画像フォーマットにも適用できますが、単一フレームしか含まないフォーマットの場合は、必要以上に複雑な手順が必要になります。

PDFの結合と分割

Docotic.Pdfは、フル機能の.NETライブラリとして、既存のドキュメントからページを結合、抽出、再編成することで、新しいPDFを作成できます。

PDFを結合する際、ライブラリは別のドキュメントからページを追加するだけでなく、レイヤー、ブックマーク、ページラベル、共有JavaScript、リンク先(リンク先)、埋め込みファイルも追加します。詳細および結合後のPDFのサイズを縮小する方法については、PDFの結合に関する記事を参照してください。

デジタル署名されたPDFは、既存の署名を無効にせずに結合することはできません。署名を保持するには、ドキュメントを追加するのではなく、PDFポートフォリオを作成してください。別の方法として、まずPDFを結合してから、結合されたドキュメントに新しいデジタル署名を適用することもできます。

Docotic.Pdfでは、ページをコピーして新しいドキュメントに抽出することもできます。コピーされたページに関連付けられたすべてのコンテンツ(注釈、フォームコントロール、構造化コンテンツ、レイヤー、その他の関連データなど)は保持されます。具体的な例については、.NET で PDF を分割する に関する記事を参照してください。この記事では、ページの抽出や削除の方法も説明しています。

PDFテンプレートの操作

PDFテンプレートとは、新規ドキュメント作成のベース構造として使用される、あらかじめデザインされたPDFファイルです。異なるデータを入力しながらも、一貫したレイアウトのPDFを作成する必要がある場合に便利です。視覚的なデザインとデータ自体を分離したい場合にも、PDFテンプレートは有効な選択肢となります。

テンプレートには、フォームベースのPDFとフォームを含まない静的PDFの2種類があります。どちらのタイプも同じ目的で使用されます。さらに、フォームベースのテンプレートにはインタラクティブな要素が含まれており、フラット化されない限り、ユーザーから情報を収集することができます。

フォームベースのテンプレートからPDFを作成する

フォームベースのテンプレートには通常、標準的で広くサポートされている対話型PDFフォームであるAcroFormsが含まれています。このようなテンプレートからPDFを生成するには、通常、以下の手順が必要です。

  • 各プレースホルダーフィールドに値を入力する
  • フィールドをフラット化して、それ以上の編集を防止する
  • 結果を新しいPDFとして保存する

以下は、プレースホルダーテキストフィールドを名前で検索し、値を割り当て、フィールドをフラット化し、結果を保存してテンプレートからPDFを作成するC#コードです。

var nameOnCertificate = "Eva Marin";
using var pdf = new PdfDocument("certificate-template.pdf");
if (pdf.TryGetControl("name", out var field))
{
    if (field is PdfTextBox nameField)
    {
        nameField.Text = nameOnCertificate;
        nameField.Flatten();
    }
}

pdf.Save($"certificate-{nameOnCertificate}.pdf");

テンプレートにプレースホルダーが多数含まれている場合は、各フィールドを個別に入力する代わりに、FDFデータをインポートできます。また、PdfDocument.FlattenControlsを使用して、すべてのフィールドを一度にフラット化することもできます。

フォームを使用せずに静的テンプレートからPDFを作成する

テンプレートにフォームフィールドが含まれていない場合は、名前などのデータをページキャンバスに直接描画します。静的な PDF テンプレートには通常、テキスト、画像、空白領域などの固定された視覚的なプレースホルダーが含まれています。テンプレートから PDF を生成するには、これらの空白領域を埋め、プレースホルダーのテキストや画像をプログラムで置き換える必要があります。

空きスペース

テキストや画像を空いている領域に配置するには、Canvas API を利用しましょう。名前や写真を追加するなど、簡単な変更のみが必要な場合は、この方法が有効です。領域の座標とサイズを把握しておく必要があり、テキストを適切に配置するには、まずテキストの長さを測定してから、それに応じて位置を調整する必要があるかもしれません。

可変長テキストや複数行テキストを扱うのはより複雑ですが、それでも可能です。DrawTextDrawString、およびテキスト測定メソッドを組み合わせることで、必要に応じて行を折り返したり、位置を調整したりできます。テンプレートにこのような領域が複数ある場合は、Layout API を使用して PDF を生成するなど、別の方法を検討してください。

プレースホルダーテキスト

Docotic.Pdfには、テキストの検索と置換を行う機能も備わっています。しかし、テキスト検索をテンプレート機能として使用することは、通常、空のプレースホルダー領域を扱うのと大差ありません。新しいコンテンツを挿入する前に、正確なテキスト断片を特定し、それをきれいに削除する必要があります。

プレースホルダー画像

静的テンプレートには、ユーザーアバターや商品写真のプレースホルダー画像が含まれている場合があります。プレースホルダー画像を見つけるには、各ページに描画されている画像のコレクションを列挙します。描画されている各画像について、表示サイズと位置を取得できます。プレースホルダーを置き換えるには、PdfImage.ReplaceWithを使用します。

using var pdf = new PdfDocument("invoice-template.pdf");
var paintedImages = pdf.Pages[0].GetPaintedImages();

var placeholder = paintedImages.First();
placeholder.Image.ReplaceWith("company-logo.jpg");

pdf.Save($"invoice.pdf");

別の方法としては、プレースホルダー画像が占めている領域に新しい画像を描画する方法がありますが、これは通常、特に理由もなく結果として生成されるPDFのサイズを大きくしてしまいます。

簡単に交換できるプレースホルダーを設計する

静的テンプレートの場合、テキストと画像の両方について、予測可能で明確な領域を設定するレイアウト設計が効果的です。可変長のコンテンツが含まれる領域の周囲には十分な余白を設け、後で挿入する予定の縦横比に合った中立的なプレースホルダー画像を使用してください。

テンプレートに置換予定のプレースホルダーテキストが含まれている場合は、テキストボックスを使用することでワークフローを簡素化できます。テンプレートに読み取り専用の枠線なしテキストボックスを追加し、その中にプレースホルダーテキストを配置します。最終的なPDFを生成する際は、テンプレートを開き、テキストボックスを名前で指定して、box.Text = "new text"; のように新しい値を直接割り当てます。その後、テキストボックスをフラット化して、それ以上の編集を防ぎます。

インタラクティブな要素を追加する

インタラクティブ機能により、静的なPDFが、注釈やマークアップが追加された、動的で操作しやすいドキュメントへと変化します。アクションとJavaScriptを使用することで、PDF内で直接自動化を実行できます。

注釈

注釈とは、ページに添付されるオブジェクトで、コメント、ハイライト、ファイル添付、その他のインタラクティブなウィジェットなどを表します。これらはページコンテンツ内に表示され、レビューワークフローや共同作業をサポートします。

以下のC#の例は、Docotic.Pdfを使用してPDFページにテキスト注釈(付箋とも呼ばれます)を追加する方法を示しています。

using var pdf = new PdfDocument("example.pdf");
var page = pdf.Pages[0];

var textAnnot = page.AddTextAnnotation(new PdfPoint(50, 100), "Reviewer comment");
textAnnot.Contents = "Please check the figures on this page.";

pdf.Save("text-annotation.pdf");

次の例では、文書の重要な部分に注目を集めるために、テキストやその他のコンテンツを強調表示する方法を示します。

using var pdf = new PdfDocument("example.pdf");
var page = pdf.Pages[0];

var color = new PdfRgbColor(255, 255, 120);
var annotationText = "Please confirm this part.";
var bounds = new PdfRectangle(50, 250, 120, 40);
page.AddHighlightAnnotation(annotationText, bounds, color);

pdf.Save("highlight-annotation.pdf");

PDF規格では、いくつかの種類のPDFリンクが定義されています。最も重要で広く使われているのは、内部リンクとハイパーリンクです。

内部リンク(「GoTo」アクションとも呼ばれます)は、同じPDF内のページまたは指定された場所にジャンプすることを可能にします。相互参照や内部ナビゲーションに役立ちます。

以下は、最初のページからインデックスが5のページへのリンクを作成するC#コードです。

using var pdf = new PdfDocument();
var page = pdf.Pages[0];

int targetPageIndex = 5;
for (int i = 0; i < targetPageIndex; i++)
    pdf.AddPage();

var rect = new PdfRectangle(50, 50, 100, 40);
page.Canvas.DrawRectangle(rect);
page.AddLinkToPage(rect, targetPageIndex);

pdf.Pages[targetPageIndex].Canvas.DrawString(50, 50, "Glad to have you here.");

pdf.Save("link-to-page.pdf");

レイアウトAPIは、絶対位置指定を必要としない内部リンクを作成する別の方法を提供します。

外部リンク(URIアクションとも呼ばれます)は、Web URLを開きます。PdfPage.AddHyperlinkメソッドを使用すると、PDFページにハイパーリンクを追加できます。それ以外の方法は、内部リンクの場合と同じです。

ブックマーク

ブックマーク(アウトラインとも呼ばれます)は、読者が特定のセクションやページに素早く移動できるようにするための特別なショートカットまたはリンクです。読者がブックマークをクリックすると、ビューアアプリケーションはドキュメントの指定された箇所にジャンプします。

アウトラインはビューアのブックマークパネルに表示され、書籍の目次のような階層的なナビゲーションツリーを提供しますが、インタラクティブです。PDFのアウトラインには、メインブックマークとネストされたブックマークを含めることができ、これにより大規模なドキュメントの構造化が容易になります。

次の例は、C#とDocotic.Pdfを使用してPDFにブックマークを作成する方法を示しています。このコードは、3つのトップレベルブックマークを作成します。2番目のブックマークには、1つのネストされたブックマークが含まれています。

using var pdf = new PdfDocument();

for (int i = 0; i < 5; i++)
{
    var page = i == 0 ? pdf.Pages[0] : pdf.AddPage();

    var canvas = page.Canvas;
    canvas.FontSize = 14;
    canvas.DrawString(50, 50, $"Page {i + 1}");
}

var root = pdf.OutlineRoot;
root.AddChild("Getting Started", 1);

var child = root.AddChild("Things You Can Do", 2);
child.AddChild("Making Quick Improvements", 3);

root.AddChild("Keeping Everything Running Smoothly", 4);

pdf.PageMode = PdfPageMode.UseOutlines;

pdf.Save("bookmarks.pdf");

ブックマークは、紙の書籍のページに印刷された目次やPDFに表示される目次とは異なります。目次は、見出しの長さを計測し、ページ番号付きのエントリを書き込むことで、プログラム的に作成できます。

Layout API を使用した目次作成の別の方法については、サンプルリポジトリにある関連コードをご覧ください。

PDFスクリプト

JavaScriptアクションは、最も強力なインタラクティブ機能の一つです。PDF JavaScriptは、ドキュメントおよびビューアのAPIを公開するJavaScriptのサブセットです。フォームの検証、計算、ユーザーインターフェースダイアログ、および小規模な自動化タスクに使用されます。

スクリプトは、注釈、ブックマーク、フォームコントロール、または開くアクションに添付できます。Docotic.Pdfを使用すると、PDFにJavaScriptコードを埋め込むことができます。このコードは、フォーム入力の検証、値の計算、フィールドの表示/非表示、またはビューア操作を実行できます。

共有JavaScriptのコレクションには、ドキュメントレベルで保存されたスクリプトが含まれています。これらのスクリプトは、複数のアクションで再利用できます。つまり、共有スクリプトはユーティリティ関数や共有ロジックに役立ちます。重複を減らし、メンテナンスを簡素化するのに役立ちます。

以下のコードは、PDFビューアにアラートメッセージを表示する共有スクリプトを定義し、ボタンのクリックアクションに割り当ててそのスクリプトをトリガーする方法を示しています。

using var pdf = new PdfDocument();

pdf.SharedScripts.Add(
    pdf.CreateJavaScriptAction("function messageBox(message) { app.alert(message,3); }")
);

var button = pdf.Pages[0].AddButton(50, 50, 100, 40);
button.Text = "Click me";
button.OnMouseUp = pdf.CreateJavaScriptAction("messageBox('Hello, dear!');");

pdf.Save("shared-javascript.pdf");

例のスクリプトはシンプルですが、JavaScriptアクションはどんな複雑さでも作成できます。Adobe JavaScript APIリファレンスには、使用できるメソッドが多数記載されています。ただし、Adobe以外のビューアは通常、APIの一部の機能しかサポートしていないことに注意してください。

未解決のアクション

オープンアクションとは、PDFビューアがドキュメントを開いたときに実行するアクションのことです。一般的な用途としては、特定のページを開く、JavaScriptによる初期化ルーチンを実行する、ビューアの設定を行うなどが挙げられます。オープンアクションの種類に制限はありません。

以下の例は、GoToオープンアクションを作成する方法を示しています。このコードは2ページ目にテキストを追加し、PDFを開いたときにビューアが自動的にそのページに移動するようにオープンアクションを設定します。

using var pdf = new PdfDocument();

var canvas = pdf.AddPage().Canvas;
canvas.FontSize = 14;

var message =
    "If you see this immediately after opening the file, " +
    "your PDF viewer supports open actions.";
var options = new PdfTextDrawingOptions(new PdfRectangle(100, 100, 100, 150));
canvas.DrawText(message, options);

pdf.OnOpenDocument = pdf.CreateGoToPageAction(1, 0);

pdf.Save("open-action.pdf");

すべてのビューアがJavaScriptの開くアクションを実行するわけではないことに注意してください。一部のビューアはこれらのアクションを無視するか、ユーザーに確認を求めます。また、開くアクションを完全にブロックするビューアもあります。

PDFに開くアクションが含まれているかどうかを確認するには、PDFをPdfDocumentに読み込み、OnOpenDocumentプロパティを調べます。このプロパティがnullの場合、ドキュメントには開くアクションが定義されていません。

暗号化とデジタル署名の適用

暗号化とデジタル署名は、作成するPDFのセキュリティを確保する上で、互いに補完し合う2つの側面に対応します。暗号化は、ドキュメントを開くことができるユーザーとその操作を制御し、署名は、ファイルの作成者または承認者を証明し、改ざんされていないことを確認します。

パスワード保護を使用すると、作成時にアクセスルールを設定できます。閲覧を制限するためのオープンパスワードと、印刷、コピー、編集、フォームへの入力などの権限を定義するための所有者パスワードを設定できます。証明書による暗号化は、受信者ごとに強化された保護を提供し、共有パスワードに頼ることなく機密性の高いPDFを複数のユーザーに配布する場合に効果的です。詳細については、「パスワードと証明書を使用したPDFの暗号化」に関する記事を参照してください。

デジタル署名は、作成時に真正性と完全性を追加します。Docotic.Pdfは、ファイル、Windowsストア、ハードウェアトークン、HSM、またはクラウドキーサービスからの証明書を使用してPDFに署名できます。タイムスタンプと長期検証データを含めることで、ドキュメント作成後も署名の検証可能性を維持できます。 PKCS#11やクラウドKMSなどの外部署名ワークフローもサポートされています。

PDFメタデータの設定

PDFメタデータとは、タイトル、作成者、件名、キーワード、作成日など、文書に埋め込まれた記述情報のことです。これにより、ソフトウェア、検索エンジン、文書管理システムは、ファイルを開かなくてもその内容を理解することができます。

PDF文書は、以下の2つのシステムでメタデータを保持できます。

  • XMPメタデータ
  • 文書情報辞書(Info辞書)

Docotic.Pdf を使用して PDF 文書に XMP メタデータを追加するプロセスを示す図。

XMPは、記述メタデータを埋め込むための、より豊富で構造化された標準化フォーマットです。Info辞書はシンプルで広くサポートされていますが、機能が限られており、PDF 2.0規格(ISO 32000-2)ではXMPメタデータに置き換えられ、非推奨となっています。Docotic.Pdfは両方のシステムを読み書きでき、両者を同期させるためのヘルパーメソッドを提供しています。

Docotic.Pdfは、PDFファイルを保存する前に一部のメタデータを自動的に更新します。例えば、ライブラリはデフォルトでProducerとCreatorの値を設定します。保存オプションを使用すると、この動作を変更して明示的に設定したメタデータ値を保持できます。

XMPメタデータ

PdfDocument.Metadata プロパティを使用すると、PDF 内の XMP メタデータにアクセスして変更できます。このプロパティを使用すると、XMP Core、Dublin Core、PDF スキーマなどのよく知られたスキーマを操作できるだけでなく、独自のカスタムメタデータを管理することもできます。

using var pdf = new PdfDocument();
var xmp = pdf.Metadata;

xmp.Pdf.Creator = new XmpString("Second-line authoring terminal");
xmp.Pdf.Title = new XmpString("Quarterly Report");

var creators = new XmpArray(XmpArrayType.Ordered);
creators.Values.Add(new XmpString("Second-line authoring terminal"));
creators.Values.Add(new XmpString("Assistive authoring terminal"));
xmp.DublinCore.Creators = creators;

var descriptions = new XmpArray(XmpArrayType.Alternative);
descriptions.Values.Add(new XmpLanguageAlternative("x-default", "Quarterly Report"));
descriptions.Values.Add(new XmpLanguageAlternative("fr", "Rapport trimestriel"));
descriptions.Values.Add(new XmpLanguageAlternative("de", "Quartalsbericht"));
xmp.DublinCore.Descriptions = descriptions;

var author1 = new XmpString("First Author");
author1.Qualifiers.Add("role", "main author");

var author2 = new XmpString("Second Author");
author2.Qualifiers.Add("role", "co-author");

var authors = new XmpArray(XmpArrayType.Unordered);
authors.Values.Add(author1);
authors.Values.Add(author2);
xmp.Custom.Properties.Add("authors", authors);

pdf.Save("with-xmp-metadata.pdf");

XMPは配列、構造体、型付き値をサポートしているため、豊富なメタデータに適しています。上記のコードは、アプリケーション固有のプロパティをカスタムXMPスキーマに格納する方法も示しています。

文書情報辞書

Info辞書は主にテキスト文字列値を格納します。コンパクトで幅広いサポートがありますが、機能に制限があります。古いツールとの互換性が必要な場合はInfo辞書を使用し、それ以外の場合はXMPを使用することをお勧めします。

メタデータの同期

読み取りツールや自動化ツールを混乱させる可能性のある不整合を避けるため、両方のメタデータシステムを同期させておくことは推奨される方法です。

PdfDocument.SyncMetadata を使用して、XMP と Info の値を整合させ、対応するフィールドを一致させます。このメソッドは、不足している Info プロパティを XMP から取得し、同様に不足している XMP フィールドを Info から取得します。XMP を優先する場合は preferXmp: true を、Info ディクショナリを優先する場合は false を設定してください。

pdf.SyncMetadata(preferXmp: true);

SyncMetadata ドキュメントの「備考」セクションを参照して、メソッドが同期するプロパティ の詳細を確認してください。

ページラベルとビューア設定の構成

新しく作成されたPDFファイルは、明確なページ番号付け、細かな閲覧設定、そして文書の内容をより効果的に表示するためのページレイアウトによって、より見やすくなります。これらの設定は、読者がファイルを最初にどのように認識し、どのように操作するかに影響を与えます。

ページラベル

ページラベルは、PDFビューアに各ページに表示するラベルを指示するメタデータです。物理的なページ番号と表示番号を異なるものにする必要がある場合に使用します。例えば、PDFの冒頭部分に「i、ii、iii」、本文に「1、2、3」といった番号を付けたい場合などです。

このC#コードは、PDFページの最初の3ページに小文字のローマ数字、残りのページに1から始まるアラビア数字でラベルを付ける方法を示しています。

using var pdf = new PdfDocument();

for (int i = 0; i < 8; i++)
    pdf.AddPage();

pdf.PageLabels.AddRange(0, 2, PdfPageNumberingStyle.LowercaseRoman);
pdf.PageLabels.AddRange(3, PdfPageNumberingStyle.DecimalArabic);

pdf.Save("with-page-labels.pdf");

PDFビューアの設定

PDFビューアの設定は、ドキュメントに埋め込まれた推奨事項であり、ビューアがどのようにドキュメントを表示するかを示します。たとえば、ツールバーを非表示にする、ウィンドウを中央に配置する、ウィンドウをページに合わせるなどを指定できます。ビューアの設定は、ページレイアウトと開くアクションの設定を補完します。

Docotic.Pdfを使用してPDFの表示設定を変更する方法は次のとおりです。

using var pdf = new PdfDocument();

pdf.ViewerPreferences.DisplayTitle = false;
pdf.ViewerPreferences.FitWindow = true;
pdf.ViewerPreferences.HideToolBar = true;
pdf.ViewerPreferences.HideMenuBar = true;
pdf.ViewerPreferences.HideWindowUI = true;
pdf.ViewerPreferences.CenterWindow = true;

pdf.Save("with-viewer-prefs.pdf");

なお、Adobe Acrobatなどのビューアは、設定によってはこれらの設定を無視する場合がありますのでご注意ください。

ページレイアウトとページモード

ページレイアウトは、ドキュメントを開いたときのページの配置方法を決定します。1ページずつ表示するか、1段組で連続表示するか、見開き2ページを表示するかを選択できます。ページモードは、ドキュメントを開いたときに表示するUIパネルを制御します。ブックマーク/アウトライン、添付ファイル、サムネイル、または非表示を選択できます。

作成するPDFを、左ページを先に表示し、サムネイルパネルを表示した状態で、見開き2ページとして表示するように指定するには、次のように記述します。

using var pdf = new PdfDocument();

for (int i = 0; i < 7; i++)
{
    var page = i > 0 ? pdf.AddPage() : pdf.Pages[0];
    page.Canvas.FontSize = 36;
    page.Canvas.DrawString(100, 100, $"Page {i + 1}");
}

pdf.PageLayout = PdfPageLayout.TwoPageLeft;
pdf.PageMode = PdfPageMode.UseThumbs;

pdf.Save("with-layout-and-mode.pdf");

PDFを保存する

Docotic.Pdf は、作成または編集した同じドキュメントから、異なる PDF ファイルまたはストリームを生成できます。これらの出力は、異なるバージョンの PDF フォーマットに準拠し、バイト長が異なり、生成に必要なメモリ量も異なります。

ライブラリが PDF のバイト列を生成する方法は、保存オプションによって異なります。保存オプションを明示的に指定しない場合、PdfDocument オブジェクトの SaveSignAndSave、および TimestampAndSave メソッドはデフォルト設定を使用します。これらのデフォルト設定は慎重に選択されており、ほとんどのシナリオで適切に機能しますが、必要に応じて調整する必要がある場合もあります。

利用可能なオプションとそのデフォルト値の詳細については、PdfSaveOptions クラス のドキュメントを参照してください。以下のセクションでは、特に重要なオプションをいくつか取り上げ、実用的な推奨事項を示します。

PDF版

Docotic.Pdfは、生成されるファイルの圧縮率を高めるため、デフォルトでオブジェクトストリームを使用します。そのため、このライブラリはデフォルトでPDF 1.5ファイルとストリームを作成します。

PDF 1.5で生成されたドキュメントを表示するには、Adobe Reader 6(2003年リリース)以降のバージョンが必要です。これは通常問題になりませんが、古いバージョンのPDFしか受け付けないレガシーツール、古いビューア、または組み込みデバイスをサポートする必要がある場合は例外です。

古いバージョンのPDFファイルで保存する方法は以下のとおりです。

using var pdf = new PdfDocument();

var options = new PdfSaveOptions
{
    Version = PdfVersion.Pdf14,
    UseObjectStreams = false,
};
pdf.Save("version-1.4.pdf", options);

PDF 1.4 バージョンで保存するには、オブジェクト ストリームも無効にする必要があります。ドキュメントに後のバージョンで導入された機能が含まれている場合、ライブラリは古いバージョンを使用しません。

ファイルサイズの削減

Docotic.Pdf の保存オプションのうち、true に設定するとファイルサイズが小さくなるオプションがいくつかあります。具体的には、RemoveUnusedObjectsOptimizeIndirectObjectsWriteWithoutFormattingUseObjectStreams です。

参照されていないオブジェクトや余分な空白がなく、データがオブジェクトストリームに効率的に詰め込まれた PDF を作成する方法を以下に示します。

using var pdf = new PdfDocument();

var options = new PdfSaveOptions
{
    UseObjectStreams = true,
    RemoveUnusedObjects = true,
    OptimizeIndirectObjects = true,
    WriteWithoutFormatting = true,
};
pdf.Save("optimized.pdf", options);

これらのオプションは、PDFファイル全体を書き換える場合に最も効果を発揮します。増分保存の場合、これらのオプションは新しく追加されたリビジョンにのみ適用され、ファイルの以前の部分をクリーンアップまたは最適化することはできません。

段階的なアップデート

Docotic.Pdf は PDF ファイルを増分更新できます。WriteIncrementallytrue の場合、ライブラリは既存のファイルを書き換えるのではなく、変更内容を追記します。以前の相互参照データとオブジェクトデータはそのまま保持されます。追記されたデータは増分更新と呼ばれ、現在の更新とそれ以前のすべての更新を合わせて、ファイルの新しいリビジョンが構成されます。

新規作成されたドキュメントには追記する以前のリビジョンが存在しないため、増分更新はできません。ライブラリは新規ドキュメントに対してこのオプションを無視し、非増分モードで書き込みます。

増分更新が必要な場合

既に署名済みの文書に新たなデジタル署名を追加する場合は、ファイルを増分保存する必要があります。既に署名済みのファイルに新たな注釈やフォームデータを追加する場合も同様です。これらの場合、ファイル全体を書き換えると既存の署名が無効になります。

同時に、最初のデジタル署名を適用する前に、増分保存ではなく完全保存を行うことを推奨します。これにより、署名済みのベースラインは、完全に書き換えられたクリーンなファイルになります。以前の改訂版に構造上の問題が含まれている文書に署名すると、予期しない署名検証の問題が発生する可能性があります。

監査可能な改訂履歴を保持する必要があるワークフローや、追記専用の文書保存を強制するワークフローでも、増分保存が必要です。

増分更新を使用するメリット

増分更新方式では、同一ファイルへの複数署名が可能となり、既存の署名を無効にすることなく、フォームフィールドへの入力など、署名後の限定的な変更を行うことができます。

さらに、この方式では変更されたデータのみが書き込まれるため、小さな変更の保存が高速化されます。また、監査やその他のコンプライアンス関連のワークフローに不可欠な、文書の改訂履歴も保持されます。

避けるべき問題点と落とし穴

増分更新では、変更されたオブジェクトのみを追加するため、ファイル全体にグローバルな圧縮を適用したり、古いオブジェクトを削除したりすることはできません。そのため、一般的に、完全な書き換えよりもファイルサイズが大きく、最適化も不十分になります。

未使用のオブジェクトが存在しない場合でも、以前のすべてのリビジョンがファイルに埋め込まれたままになり、容量を占有し続けるため、リビジョンごとにファイルサイズが増加します。

以前のリビジョンに含まれる機密情報や誤った情報は復元可能であり、以前のリビジョンに存在するPDF形式の問題や構造上の欠陥は、新しいデータを追加しても修正されません。

最後に、一部のビューアや処理ツールは、複数リビジョンのPDFファイルを正しく処理できない場合があります。増分更新を使用する前に、ドキュメントのすべての利用者が複数リビジョンのファイルを処理できることを確認してください。

PDF出力のテスト

自動PDFテストは、生成されたPDFをリポジトリまたは成果物ストレージに保存されているベースラインPDFと比較することで、コンテンツやレイアウトの不具合によるリリースの不具合を防ぎます。ベースラインを使用することで、テキスト、フォント、画像、レイアウトにおける意図しない変更を検出でき、ビルドごとに手動で品質保証を行う必要性を軽減できます。

構造チェック、テキスト抽出、視覚的な比較を組み合わせることで、最も信頼性の高い結果が得られます。

アプローチの簡単な比較

方法 スピード 感度 最適
構造比較 速い 高:オブジェクトレベルの変更を検出します 同じ文書の2つのバージョンが構造的に同一であることを確認する必要がある回帰テスト
テキスト抽出 速い 中程度:通常はレイアウトの変更を無視します 意味内容と表の検証
視覚的な差異 もっとゆっくり 高:コンテンツとレンダリング/レイアウトの変更の両方を検出します 視覚的回帰を捉える

文書構造の比較

PdfDocument.DocumentsAreEqual を使用すると、PDF オブジェクトグラフ、PDF バージョン、およびドキュメント セキュリティ ストア (DSS) を比較できます。このメソッドは、時間依存のドキュメント プロパティを無視します。また、ドキュメント メタデータ、トレーラー ID、およびその他の自動生成プロパティも無視します。

このメソッドは、予期しないオブジェクトが追加または削除されていないことを確認する必要がある PDF ドキュメント テスト ワークフローに最適です。DocumentsAreEqual はファイルおよびストリームのオーバーロードをサポートし、暗号化された PDF を比較できます。

この手法を示す完全なサンプルは、Docotic.Pdf サンプルに含まれています。このサンプルでは、​​通常の .NET アプリケーションでメソッドを使用する方法だけでなく、ネイティブ AOT アプリケーションで DocumentsAreEqual を使用する方法も示しています。

抽出したテキストによるPDFの検証

文書全体から一度に、またはページごとにテキストを抽出し、文字列を比較できます。テキスト抽出オプションを使用すると、フッターを含む矩形領域を除外するなど、抽出プロセスを細かく調整できます。比較を容易にするために、抽出したテキストを行または単語ごとに分割することも可能です。

構造チェックを行う場合は、まずテキストを抽出し、その際に位置、フォント、および各チャンク、単語、文字に関するその他の詳細情報を取得します。次に、抽出した各要素を対応する基準要素と比較します。

視覚的な違いを検出する

まず、PDFページを画像にレンダリングし、各画像を基準画像と比較します。画像間の差異を検出するには、ImageSharp.CompareやMagick.NETなどの専用ライブラリを使用してください。

ピクセル単位での厳密な比較を推奨します。つまり、両方の画像で対応するすべてのピクセルが一致する必要があります。レンダリングのわずかな差異が許容される場合は、比較ロジックを調整して軽微な差異を許容することもできますが、ピクセル単位の完全な一致が最も信頼性の高い結果をもたらします。

ピクセル単位の完全な比較を行わずに、2つの画像が同一である可能性が高いかどうかを迅速に判断するための事前チェックとして、ハッシュ値の使用を検討してください。レンダリングされた各画像に対してSHA-256ハッシュ値を計算し、ハッシュ値が一致すれば、画像はほぼ確実に同一です。ハッシュ値が異なる場合は、ピクセル単位の完全な比較を実行してください。

結論

Docotic.Pdfは、.NETでPDFを作成・操作するための包括的で多層的なツールキットを提供します。開発者は、Core APIによる低レベル制御、Layout APIによる高レベルなドキュメント生成、またはWebテクノロジーを基盤としたワークフロー向けのHTMLからPDFへの変換など、様々な選択肢から最適な方法を選択できます。

このライブラリは、画像ベースのPDF、テンプレート駆動型生成、そして注釈、リンク、ブックマーク、JavaScriptアクション、開くアクションといった豊富なインタラクティブ機能もサポートしています。

信頼性を確保するため、Docotic.PdfにはPDF出力のテストメソッドが用意されており、アプリケーションの変更によって不具合や予期せぬ差異が発生するのを防ぎます。