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

.NET PDF ジェネレーター

Docotic.Pdf を使うと、ページ、コンテナー、テキストスパン、画像、リンク、ヘッダー、フッター、テーブル、リストなどの構造要素を組み合わせて PDF ドキュメントを生成できます。これらの要素は、Docotic.Pdf の無料 Layout アドオンが提供する高レベルな Layout API から利用できます。API は再利用可能なカスタム コンポーネントもサポートします。

Docotic.Pdf が PDF を生成する仕組みの図。画像、テーブル、テキストなどの要素を配置すると、そのレイアウトからライブラリが PDF を生成します

Layout API を使うと、fluent なアプローチで C# または VB.NET のコードだけでドキュメント全体を定義できます。この説明どおり、アドオンが提供する PDF ジェネレーターは、単純なページから高度に構造化された PDF レポートまで、任意に複雑なレイアウトのドキュメントを生成できます。

PDF生成の基本

Layout API を使って PDF ドキュメントを生成するには、無料の Layout アドオンが必要です。コアライブラリとアドオンの使用にはライセンスキーも必要です。無料の試用キーまたは購入済みのキーのどちらかを使用できます。

アドオンのインストール

推奨される方法は、NuGet からアドオンをインストールすることです。

Install-Package BitMiracle.Docotic.Pdf.Layout

パッケージ マネージャーが依存関係を自動的に処理します。

手動でアドオンをインストールする場合は、まず Docotic.Pdf のバイナリを含む ZIP アーカイブをダウンロードします。アーカイブを展開し、次の DLL への参照を追加します。

  • BitMiracle.Docotic.Pdf.dll
  • BitMiracle.Docotic.Pdf.Layout.dll from the Layout add-on subfolder.

ライセンスキーの取得

ライブラリを試すには、Docotic.Pdf のダウンロードページのフォームに入力して、無料の期間限定ライセンス キーを申請します。すでにライセンスを購入済みの場合は、購入後に提供されたコードを使用します。

Layout アドオンは無料で、追加のライセンスは不要です。すでにお持ちの Docotic.Pdf ライセンスで Layout API を使用できます。

Layout API での Hello, world!

以下は、定番の「Hello, world!」フレーズを使って PDF を生成する API のサンプル コードです:

BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(pages =>
{
    pages.Content().Text("Hello, world!");
}));

このコードは、テキストが左上隅にある 1 ページの PDF を生成します。

コードサンプルの理解

サンプルはライセンスキーの追加から始まります。ライセンスがなければ、Docotic.Pdf ライブラリは何も生成しません。PDF を生成するコードは次の行から始まります。

このコードは、静的な PdfDocumentBuilder.Create() メソッドを呼び出してドキュメント ビルダーのインスタンスを作成します。Generate メソッドの呼び出しで PDF 生成処理が開始されます。このメソッドは 2 つの引数を受け取ります。生成するファイル名と、型 Action<Document> のデリゲートです。アドオンは Generate の結果として hello.pdf を生成します。

PDF にどのレイアウトを適用するかを判断するため、ドキュメント ビルダーは Document インスタンスを使ってデリゲートを呼び出します。デリゲートのコードがドキュメントの内容を組み立てます。このサンプルでは、Pages メソッドを使って、ドキュメント ページのレイアウトを定義する別のデリゲートをビルダーに渡しています。

ビルダーは、Pages メソッドに渡されたデリゲートを PageLayout インスタンスで呼び出します。このインスタンスは、ドキュメント内の 1 つ以上のページを表します。正確なページ数は、そこに追加されたコンテンツによって決まります。

ページのデリゲートは Content メソッドを呼び出して、ページの主コンテンツ用のレイアウト コンテナーにアクセスします。コンテナーの Text メソッドを連結して呼び出すことで、サンプルのテキストスパンがページに追加されます。動作の詳細については、レイアウト コンテナー のガイドを参照してください。

ドキュメント ビルダーは、コンテンツをページ間で自動的に分割し、主コンテンツのレイアウト コンテナーに追加されたすべてのデータを収めるのに必要なだけのページを作成します。このサンプルでは 1 ページで十分なため、出力はちょうど 1 ページになります。

基本サンプルを超えた一般的なタスク

サンプル コードはまず PdfDocumentBuilder インスタンスを作成し、その後 Generate メソッドを呼び出して PDF を生成します。PDF の生成を開始する前に、ビルダーを構成できます。

たとえば、暗号化ハンドラーをビルダーに渡すことで、暗号化された PDF を生成できます。生成ドキュメントに含めるメタデータも指定できます。ビルダーのカスタマイズ の詳細は、別の記事を参照してください。

サンプル コードでは主コンテンツ スロットのレイアウト コンテナーのみを使用していますが、他のコンテンツ スロットも利用できます。コンテンツ スロットにアクセスする PageLayout メソッドの完全な一覧は次のとおりです。

  • Background() - 他のコンテンツの下にある背景レイヤーを返します。
  • Header() - すべてのページに共通のヘッダーを返します。
  • Content() - メインのページ コンテンツ スロットを返します。
  • Footer() - すべてのページに共通のフッターを返します。
  • Foreground() - 他のコンテンツの上に表示される前景レイヤーを返します。

生成された PDF のページ数に影響するのは、メイン コンテンツ スロットの内容だけです。ドキュメント ビルダーは、Content() で提供されるスロット以外のすべてのスロットを各ページに繰り返し配置します。コンテンツ スロットの詳細は、ページ レイアウト の記事を参照してください。

多くのドキュメントでは、ページごとに異なるレイアウトが使われます。たとえば、最初のページは表紙風のデザインにし、その後のページはよりシンプルなレイアウトを使う場合があります。テーブル専用のページを含めたり、セクションに応じて異なる背景を適用したりするドキュメントもあります。Docotic.Pdf と Layout アドオンでこのようなドキュメントを生成するには、Document.Pages メソッドを複数回呼び出します。動作するサンプル はサンプル リポジトリにあります。

コードの整理

サンプルのような短いコードでは、呼び出しを連結し、ネストしたラムダを使って問題ありません。より大きなものを作るときは、コードを別メソッドに分割したほうが便利な場合があります。こうすると、コードは読みやすく、保守しやすくなります。

コードをメソッドに分割したときの Hello, world! サンプルは次のとおりです。

public void GeneratePdf()
{
    BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

private void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

private void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

Docotic.Pdf Layout API で PDF を生成する理由

Layout API は、PDF 形式の内部を理解しなくても、合成可能な構成要素から PDF を生成できる、開発者向けのモダンな方法です。高いパフォーマンスと決定的で予測可能な動作で PDF をレイアウトします。大量のドキュメント生成シナリオでも利用できます。

この API は、Docotic.Pdf と無料の Layout アドオンを併用すると利用できます。Docotic.Pdf と Layout アドオンはいずれも 100% マネージド コードの DLL であり、unsafe コードブロックは含まれません。Layout API は、ブラウザーや Skia バイナリのようなサードパーティの外部依存を使わずに実装されており、軽量でオーバーヘッドの少ない、展開と保守が容易なソリューションになっています。

API の概要

Layout API は、fluent で使いやすい API です。柔軟なレイアウト要素を使ってドキュメントのレイアウトをすべてコードで記述すると、ジェネレーターがコンテンツを流し込み、自動的にページ分割し、結果を PDF としてレンダリングします。

Layout アドオンは、宣言的なフローベースのレイアウト システムを使用します。レイアウト要素には、リスト、列、行、テーブル、画像、テキストスパン、ヘッダーとフッター、コンテナーなどがあります。要素を正確な座標に手動配置する代わりに、要素の振る舞いを記述します。するとドキュメント ビルダーが最終レイアウトを計算し、PDF を作成します。

フローベースのレイアウト システムにより、レイアウトはページサイズとコンテンツに適応します。この仕組みにより、アドオンは複雑で構造化されたルールベースのレイアウトで特に力を発揮します。異なる種類のコンテナーを入れ子にして、高度な構造を可読性を損なわずに構築できます。

Layout API では、ほとんどの呼び出しを連結できます。これにより、従来の API よりもコンパクトで表現力のあるコードになります。連結内の呼び出し順序は重要です。すべてが強く型付けされているため、コンパイル時の安全性が確保され、リファクタリングしやすくなります。Layout API を使用するコードは単体テストも可能です。

レイアウト実装をさらに簡潔にするには、独自メソッドで API を拡張し、その上にクリーンで表現力のある DSL を構築できます。

配置と DSL 作成 の詳細は、コンテナーのサイズ、位置、配置、レンダリング動作を制御する方法を説明した記事を参照してください。

.NET バージョンとプラットフォーム サポート

Layout API は、.NET Standard 2.1 以降を対象とするプロジェクトで使用できます。つまり、Layout アドオンは .NET 5 から .NET 10 まで互換です。さらに、.NET Core 3.0 以降もサポートされます。

Layout API を使って、ASP.NET Core、MAUI アプリ、Unity、Xamarin、コンソール アプリケーションで PDF を生成できます。Docotic.Pdf と Layout アドオンは Windows、macOS、Linux 上で PDF を作成できます。

クラウドプラットフォームと Docker イメージ

Docotic.Pdf と Layout アドオンは、サーバーレス構成を含む Azure および AWS のクラウド環境で実行できます。ライブラリとアドオンは、動的なハードウェア変更、自動スケーリング、その他のクラウドネイティブなランタイム機能を完全にサポートします。

ほとんどのクラウド シナリオでは、アンバウンド ライセンスが必要です。License FAQ では、クラウド アプリケーションに適したライセンス の選び方を説明しています。

Layout API は Docker コンテナー内でそのまま動作します。コンテナー内でライブラリと Layout アドオンを実行して PDF を生成するために、特別な設定は必要ありません。

PDF ファイルへのテキストの追加

テキストは、あらゆる PDF ドキュメントの基本要素です。LayoutContainer クラスの Text メソッドを使って、コンテンツ スロットにテキストを追加できます。

Title、Caption、Plain、Hyperlink、Strong、Footnote などのテキスト スタイルの例に囲まれたドキュメント ページの図

テキストスパン

Hello, world サンプルでは、ページの主コンテンツにテキストを追加するために LayoutContainer.Text(string) オーバーロードを使用しました。では、Text メソッドの別のオーバーロードを見てみましょう。

public static void GenerateTextPdf()
{
    PdfDocumentBuilder.Create().Generate("text-spans.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Text(AddTextSpans);
    }));
}

private static void AddTextSpans(TextContainer text)
{
    text.Line("About VB.NET")
        .Style(t => t.Strong);

    text.Span("VB.NET is a multi-paradigm, object-oriented language ");
    text.Span("that runs on .NET, Mono, and the ");
    text.Hyperlink(
        ".NET Framework",
        new Uri("https://dotnet.microsoft.com/download/dotnet-framework"));
    text.Line(".");

    text.Span("Released by Microsoft in 2002, ");
    text.Line("it continues the lineage of the original Visual Basic language.");
}

TextContainer クラスの Span および Line メソッドを使って、現在の行にテキストを追加しています。Line メソッドは、さらに現在の行を完了します。サンプル コードでは、Hyperlink メソッドを使って、特定のテキストスパンに外部リソースのリンクを関連付けています。

サンプル コードが Style メソッドを使って 1 行目に強調書式を適用している点に注目してください。テキスト スタイルの概念をもう少し詳しく見ていきましょう。

テキストスタイル

テキスト スタイルを使うと、テキストの見た目をカスタマイズできます。TextStyle クラスには、フォントサイズ、文字間隔、色、その他のテキスト プロパティを変更するメソッドがあります。TextStyle オブジェクトは不変なので、各メソッド呼び出しは新しいスタイル インスタンスを生成します。テキスト スタイルは、さまざまなレイアウト レベルで適用できます。

PdfDocumentBuilder.Create().Generate("text-styles.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.TextStyle(TextStyle.Parent.FontSize(30));

        pages.Content()
            .TextStyle(TextStyle.Parent.FontColor(new PdfRgbColor(0, 0, 255)))
            .Text(text =>
            {
                text.Span("Hello,");

                text.Span("World!")
                    .Style(TextStyle.Parent.Underline());
            });
    });
});

TextStyle.Parent プロパティは、すべてのテキスト プロパティが未定義の特別なスタイルを返します。上のサンプルでは、30 ポイントのフォントサイズで、2 番目の単語に下線を引いた青色の “Hello, World!” を描画しています。

これは、次のコードによるものです。

  • ページ レベルでフォントサイズを 30 ポイントに設定する
  • その後、主コンテンツ スロットのテキスト色を青に設定する
  • その後、最後のテキストスパンに下線スタイルを適用する

後続の各スタイルは、TextStyle.Parent プロパティを通じて前のスタイルを継承します。

TextStyle クラスのメソッドを使うと、たとえばテキストの方向を右から左に変更できます。サンプル リポジトリには、テキスト スタイル継承 を使う別の例があります。

タイポグラフィ

TextStyle クラスは、関連付けられたフォントを除くすべてのテキスト プロパティのカスタマイズをサポートします。既定のフォントを変更するには、Document.TextStyleWithFont メソッドを使って、特定のフォントに基づくテキスト スタイルを作成します。オペレーティング システムにインストールされているフォントを使うことも、ファイルやストリームから読み込むこともできます。

フォントベースのスタイルを作成した後で、追加の任意プロパティを適用し、その結果のスタイルをテキストスパンに使用できます。この C# サンプルでは、システム フォントの使い方を示します。

PdfDocumentBuilder.Create().Generate("text-style-with-font.pdf", doc =>
{
    var font = SystemFont.Family("Calibri");
    var style = doc.TextStyleWithFont(font).FontSize(30);

    doc.Pages(pages =>
    {
        pages.Content().Text(text =>
        {
            text.Span("Hello,");

            text.Span("World!")
                .Style(style);
        });
    });
});

TextStyleWithFont メソッドには、生成されるドキュメントにフォントをどのように埋め込むかを指定する任意パラメーターがあります。既定では、ライブラリは次のように動作します。

  • TrueType/OpenType フォントでは使用されたグリフのみを埋め込みます。
  • Type1 および CFF フォントではすべてのグリフを埋め込みます。
  • 組み込み PDF フォント (Base14 フォント) ではグリフを埋め込みません。

これらの既定値により、TrueType および OpenType フォントを使用するドキュメントは、大きなフォントを使ってもサイズを小さく保てる場合があります。カスタム フォント ローダー、フォールバック フォント、欠落グリフ用ハンドラーも指定できます。PDF ドキュメントでのフォント管理 の詳細は、examples リポジトリのサンプル コードを参照してください。

Layout API は、Typography クラスからアクセスして変更できる、定義済みスタイルのコレクションを提供します。

PdfDocumentBuilder.Create().Generate("typography.pdf", doc =>
{
    doc.Typography(t =>
    {
        var fontsPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
        var arialFont = new FileInfo(Path.Combine(fontsPath, "arial.ttf"));

        t.Document = doc.TextStyleWithFont(arialFont);
        t.Header = t.Parent.FontSize(20).FontColor(new PdfGrayColor(20));
        t.Footer = t.Footnote;
    });

    doc.Pages(pages =>
    {
        pages.Header().AlignCenter().Text("Header");

        pages.Content().Text(t =>
        {
            t.Line("Title").Style(t => t.Title);
            t.Line("Heading 1").Style(t => t.Heading1);
            t.Line("Regular");
        });

        pages.Footer()
            .Height(20)
            .AlignCenter()
            .Text(t => t.CurrentPageNumber());
    });
});

必須ではありませんが、ドキュメント全体のタイポグラフィを構成して定義済みスタイルを上書きすることを推奨します。テキストスパン レベルでも引き続きテキスト スタイルを適用できます。

Typography クラスを使えば、テキスト スタイルの参照を変数に保存する必要はありません。代わりに、必要なスタイルを Document.Typography メソッドで登録し、後から Typography オブジェクトのプロパティでアクセスします。PDF ドキュメント生成時に定義済みおよびカスタムのテキスト スタイル を使う方法は、Typography のサンプルを参照してください。

レポートや請求書のような実際の PDF ドキュメントには、ヘッダーとフッターが含まれることがよくあります。このセクションでは、両方を PDF に追加する方法を示します。

public static void GeneratePdfWithHeaderAndFooter()
{
    PdfDocumentBuilder.Create()
        .Generate("header-footer.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6).Margin(10);

        BuildPagesHeader(pages.Header());
        BuildPagesFooter(pages.Footer());

        pages.Content().Text("Hello, world!");
    }));
}

public static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

public static void BuildPagesFooter(LayoutContainer footer)
{
    footer.AlignRight().Text(text =>
    {
        text.Style(t => t.Parent.FontColor(new PdfRgbColor(255, 0, 0)));

        text.CurrentPageNumber();
        text.Span(" / ");
        text.PageCount();
    });
}

BuildPagesHeader メソッドは、ヘッダー テキストのフォントサイズを小さく設定します。ヘッダー内容は 2 行で、1 行目は現在のユーザー名、2 行目は現在の日付です。テキストは右揃えです。

コードではヘッダーの明示的なサイズを指定していないことに注目してください。ヘッダーは左右のマージンを除いたページ全幅を占有し、その高さはテキスト行の高さに依存します。

BuildPagesFooter メソッドは、PDF フッターに現在のページ番号を配置する方法を示します。ライブラリは現在ページ番号と総ページ数を自動的に計算します。これらの値には、TextContainer オブジェクトの CurrentPageNumber および PageCount メソッドでアクセスできます。

Layout API には、ページ番号の書式を設定する方法もあります。たとえば、16 進のページ番号は次のように描画できます。

text.CurrentPageNumber().Format(p => "0x" + p?.ToString("x2"));

サンプル リポジトリには、PDF にヘッダーとフッターを追加する 別の例があります。その例ではページ番号をローマ数字で書式設定します。

画像の挿入

ことわざにあるように、一枚の絵は多くの言葉に勝ります。見積書やレシートでは、会社のロゴや他の重要な画像を含めることがよくあります。この例では、シンプルで見栄えのよい画像を使います。

画像を使うには、まずそれをドキュメントに追加する必要があります。ライブラリは画像をファイルまたはストリームから読み込めます。サポートされるのはラスター形式のみで、PNG、JPEG、JPEG 2000、BMP、GIF、TIFF です。

Image オブジェクトを取得したら、コンテナーの Image メソッドを呼び出して、1 つ以上のレイアウト コンテナーの内容として設定できます。画像の拡大縮小、回転、パディングの追加、配置 は他のコンテンツと同様に行えます。

PdfDocumentBuilder.Create().Generate("image-with-text.pdf", doc =>
{
    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);
        pages.Content().Column(c =>
        {
            c.Spacing(20);

            c.Item()
                .AlignCenter()
                .Text("Hello, world!")
                .FontSize(20);

            c.Item()
                .AlignCenter()
                .MaxWidth(200)
                .Image(image);
        });
    });
});

サンプル コードでは、主コンテンツ スロットにテキストと画像の両方を使っています。ただし、レイアウト コンテナーにはテキストのみ、または画像のみを含めることもできます。ページの主コンテンツ スロットに両方を含めるには、複合コンテナー が必要です。

ここでは Column コンテナーを使って、テキストと画像を上下に順番に配置しています。列コンテナーの Item メソッドはサブコンテナーを提供します。Item を 1 回呼び出すとテキスト用コンテナーが作成され、もう 1 回呼び出すと画像用コンテナーが作成されます。すべてのサブコンテナーを持つ複合コンテナーがメイン コンテンツになります。

サンプル コードを実行するには、サンプル リポジトリから花の画像 をダウンロードし、アプリの作業ディレクトリに配置します。

オンライン画像

ファイルではなく画像 URL しかない場合は、画像をメモリ ストリームにダウンロードし、そのストリームから Image を作成します。次の例では、Layout API でオンライン画像を使う方法を示します。

public static async Task AddImageWithTextFromUrl()
{
    using var http = new HttpClient();
    using var stream = await http.GetStreamAsync("url/to/image");

    var memoryStream = new MemoryStream();
    await stream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;

    PdfDocumentBuilder.Create().Generate("online-image-with-text.pdf", doc =>
    {
        var image = doc.Image(memoryStream);

        doc.Pages(pages =>
        {
            pages.Size(PdfPaperSize.A6);
            pages.Content().Column(c =>
            {
                c.Spacing(20);

                c.Item()
                    .AlignCenter()
                    .Text("Hello, world!")
                    .FontSize(20);

                c.Item()
                    .AlignCenter()
                    .MaxWidth(200)
                    .Image(image);
            });
        });
    });
}

このメソッドは非同期処理 (URL から画像をダウンロード) を行うため、void ではなく async Task として宣言されています。呼び出し側は、PDF 生成が正しく完了するように await する必要があります。独自コードでも、同様のメソッドが値を返す場合があり、その場合は async Task<TResult> として宣言されます。

リストの作成

リストとは何でしょうか。番号付きの項目が縦に並んだグループとして考えられます。Layout API にはリスト専用のコンテナー型はありませんが、他の複合コンテナー を使って簡単に実装できます。

var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

PdfDocumentBuilder.Create().Generate("list.pdf", doc => doc.Pages(pages =>
{
    pages.Size(PdfPaperSize.A6);
    pages.Content().Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t =>
                {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}));

サンプル コードは、リストを行の列として作成します。各行には 2 つの項目があります。1 つ目 (左側) には項目番号、2 つ目 (右側) には項目テキストが入ります。コードでは、各行の項目間スペースを明示的に設定しています。

項目を配置するには、Row コンテナーで各項目のサイズを明示的に指定するか、計算させる必要があります。この例では AutoItem および RelativeItem メソッドを使用しています。その結果、行コンテナーは 1 つ目の項目に必要な幅を計算し、残りの利用可能な幅を 2 つ目の項目に使用します。

この方法を使い、必要に応じて行の見た目を調整することで、ドキュメントのレイアウトとスタイルに最も適したリストを作成できます。

テーブルの作成

多くの PDF ドキュメントにはテーブルが含まれます。テーブルはデータの明確さと整理性を高めるため、当然のことです。このセクションでは、Layout API を使って PDF にテーブルを作成する方法を示します。

public static void AddTable()
{
    PdfDocumentBuilder.Create().Generate("table.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);

        var color = new PdfGrayColor(75);
        pages.Content().Padding(20).Table(t =>
        {
            t.Columns(c =>
            {
                c.RelativeColumn(4);
                c.RelativeColumn(1);
                c.RelativeColumn(4);
            });

            t.Header(h =>
            {
                h.Cell().Background(color).Text("Month");
                h.Cell().Background(color).Text("Days");
                h.Cell().Background(color).Text("First Day");
            });

            var year = DateTime.Now.Year;
            for (int yearDiff = 0; yearDiff < 4; yearDiff++)
            {
                for (int i = 11; i >= 0; i--)
                {
                    var stats = GetMonthStats(year - yearDiff, i);

                    t.Cell().Text(stats.Item1);
                    t.Cell().Text(stats.Item2);
                    t.Cell().Text(stats.Item3);
                }
            }
        });
    }));
}

private static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        $"{DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex]} {year}",
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

サンプル コードは、今年と過去 3 年間の各月に関する基本情報を表示するシンプルなテーブルを作成します。コードでは、相対幅を持つ 3 列を定義しています。左端と右端の列は中央列の 4 倍の幅です。

コードでは、ヘッダー セルを追加し、各セルのテキストと背景色を指定してテーブル ヘッダーも定義しています。テーブルが 1 ページに収まらない場合、ヘッダーはテーブルがまたがるすべてのページで繰り返されます。この動作は、サンプル コードで生成される PDF で確認できます。

行の作成には 2 つの単純なループを使用します。外側のループは年を反復し、内側のループは月を逆順で反復します。内側のループは各月の情報を取得し、3 つのセルを追加して 1 行を形成します。

詳細については、Table コンテナー の機能を詳しく説明した記事を参照してください。

PDF ドキュメントは、同じファイル内の別の場所へ読者を移動できる内部リンクをサポートします。PDF ビューアーでは、これらのリンクはクリック可能なテキストまたは画像要素として表示されます。ハイパーリンクと同様に機能しますが、外部 Web サイトではなくドキュメント内を移動します。

リンクとターゲットのアイコンで接続されたドキュメント セクションを示す、PDF 内部リンクの図

多くの PDF ドキュメントでは、ブックマークや目次に内部リンクを使用し、読者がセクション間を素早く移動できるようにしています。Layout API を使ってドキュメント セクションへのリンクを追加する方法は次のとおりです:

PdfDocumentBuilder.Create().Generate("link.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.Content().Column(c =>
        {
            const string SectionName = "Chapter 1";
            c.Item().SectionLink(SectionName).Text("Link");

            c.Item().PageBreak();

            c.Item().Section(SectionName).Text("Target");
        });
    });
});

サンプル コードは、1 ページ目のテキストを、指定した名前のセクションへのリンクにします。これは、テキストを含むレイアウト コンテナーで SectionLink メソッドを呼び出して行います。この時点でセクションがまだ存在していなくても構いません。テキストは PDF ビューアーでクリック可能になります。

次に、コードは 2 ページ目のテキストを同じ名前のセクションの開始としてマークします。見た目と動作は変わりませんが、1 ページ目のリンクのターゲットになります。これは、対応するレイアウト コンテナーで Section メソッドを呼び出して行います。

この例では、リンクとそのターゲットはいずれもテキストスパンですが、任意のレイアウト コンテナーでセクションとリンクを作成できます。画像を含むコンテナー、テーブル コンテナー、または自分で構築したコンテナーでも構いません。

サンプル リポジトリには、PDF 目次の作成 の例があります。

複雑な PDF レイアウトの設計

Layout API は、組み合わせることで任意に複雑な PDF ドキュメントを作成できる、さまざまなレイアウト コンテナーを提供します。独自のカスタム コンポーネントで API を拡張することもできます。

このページの例は意図的にシンプルにしてあり、要点に集中できるよう、わかりやすいドキュメントを作成しています。さまざまなコンテナーを組み合わせてより複雑な PDF を生成する 方法を見たい場合は、GitHub のサンプル リポジトリにある対応する例を参照してください。

組み込みコンテナーを使うだけでなく、カスタム レイアウト コンポーネントを定義して使用できます。これらのコンポーネントは、データとレイアウト ロジックの両方を 1 つのクラスにカプセル化したい場合に特に役立ちます。すべてを 1 か所にまとめておくと、コンポーネントの理解、変更、再利用がしやすくなり、複雑なレイアウトを扱う柔軟な方法にもなります。

カスタム レイアウト コンポーネントを作成するには、クラスに ILayoutComponent インターフェイスを実装します。PDF を生成すると、ライブラリはインターフェイスの Compose メソッドを呼び出し、LayoutContext オブジェクトを提供します。このオブジェクトを使って、レイアウト要素を作成したり、生成中のドキュメントに関する情報にアクセスしたりできます。

レイアウトにカスタム レイアウト コンポーネントを追加するには、LayoutContainer クラスの Component メソッドを呼び出します。サイズ指定と位置指定の観点では、カスタム コンポーネントはテキストや画像と同様に動作します。

ILayoutComponent インターフェイスを使ったカスタム コンポーネント実装の例は、レイアウト コンポーネント のサンプルを参照してください。

PDF を作成するその他の方法

Docotic.Pdf には、用途に応じた複数の PDF 作成方法があります。このセクションでは、Layout API に頼るべき場合と、他の方法のほうが適している場合を説明します。

Layout API を優先すべき場合

Docotic.Pdf Layout API は、構造化されたレイアウトから PDF を生成する強力な方法です。テキスト、画像、コンテナー、テーブルを組み合わせて、任意に複雑な入れ子構造のドキュメントを構築できます。生成処理は高速で、メモリ使用量も適切で、動作は予測可能です。

Docotic.Pdf と Layout アドオンは、軽量で完全に自己完結したセットです。PDF 生成に外部ライブラリやブラウザーは不要です。そのため、.NET マイクロサービス、クラウド アプリケーション (特にサーバーレス)、およびフットプリント サイズが重要なその他の環境に適しています。

ドキュメント レイアウトはコードで定義されるため、その任意の部分を単体テストできます。レイアウト ロジックは他のコードと同様に再利用でき、データとレイアウト動作の両方をカスタム コンポーネント にカプセル化できます。

Layout API の代替手段

専用の記事で、Docotic.Pdf によるPDF 作成方法 をすべて詳しく比較しています。以下は、最も一般的に使われる代替手段の一部です。

  • HTML から PDF への変換
    既存の HTML/CSS テンプレートを再利用します。チームがすでに HTML/CSS でドキュメントを作成しており、その PDF 版が必要な場合は、HTML-to-PDF アプローチ を選択します。

  • 低レベルの PDF 生成
    PDF 構造とコンテンツに対して、ライブラリで可能な最大限の制御を提供します。ピクセル単位の配置や複雑なベクター グラフィックス が必要な場合に選択します。この方法は、パフォーマンスが重要なシナリオや、可能な限り小さいフットプリントが必要な場合に推奨されます。

  • テンプレートベースの PDF 生成
    要素を設計したり配置したりせずにドキュメントを埋める高速で予測可能な方法を提供します。承認済みまたはコンプライアンス管理されたテンプレートのような事前定義された PDF 構造があり、テキスト フィールドの変更、プレースホルダーの置換、関連ドキュメントの添付、その他同様の作業だけが必要な場合に選択します。

  • PDF の結合と構成
    ゼロから作成する代わりに、既存の要素から PDF を作成する ことができます。画像、スキャンしたページ、その他の PDF を 1 つのファイルにまとめる必要がある場合に選択します。

他の PDF 生成ソリューションとの比較

このセクションには 2 つの比較表があります。1 つは要点、もう 1 つは Docotic.Pdf と Layout アドオンが他の一般的な PDF 生成ソリューションとどう比較されるかを詳細かつ構造化して示した情報です。

主要な比較ポイント

無料のものを含め、PDF 生成には有力な選択肢があります。ただし、署名、暗号化、ドキュメントの結合などの機能は製品によって異なり、真にニーズに合うツールが限られる場合があります。

ソリューション 使用する場面 最適な用途
Docotic.Pdf と Layout アドオン 高品質で高性能なレイアウト エンジンを使い、最適化された PDF を生成したい場合。署名、暗号化、クロスプラットフォームの PDF 編集も高度にサポートします 請求書、レポート、明細書などの高品質生成。優れた開発者体験が得られます。エンタープライズ級の PDF 処理とプロフェッショナル サポートが必要な場合に最適です
PDFsharp + MigraDoc 基本的な PDF 生成のために無料の MIT ライセンス ライブラリを使いたく、デジタル署名や最新の暗号化アルゴリズムが不要な場合 オープンソースまたは予算制約のあるプロジェクトでのシンプルなドキュメント作成
QuestPDF PDF 生成専用のモダンなレイアウト エンジンを使いたく、編集、署名、暗号化が不要な場合 非生成機能をすべて別で処理できるなら、MIT または低コストの商用ライセンスで高品質な PDF を生成できます
iText PDF の生成と処理の両方に対応した成熟した多機能 PDF ツールキットが必要で、ソリューションを AGPL/GPLv3 ライセンスの下でオープンソース化するか、高価な商用ライセンスを購入する準備がある場合 iText API にすでに慣れており、その急な学習曲線が問題にならないチーム

詳細な比較

表を確認して、より広い文脈を理解し、独自の結論を導き出してください。

  Docotic.Pdf と Layout PDFsharp + MigraDoc QuestPDF iText
PDF 機能 多機能な PDF ライブラリ PDF 生成と限定的な編集 PDF 生成のみ 広範な PDF 機能
レンダリング モデル モダンで宣言的な、保持モードのレイアウト エンジン ボックスベースのレイアウト エンジン モダンで宣言的な、保持モードのレイアウト エンジン ボックスベースのレイアウト エンジン + レンダラー ツリー
API の種類 fluent API 命令型 API fluent API 命令型 API
開発者体験 優秀。API はクリーンでモダン、直感的 良好。API 設計は標準的 優秀。API はクリーンでモダン、直感的 可。API は冗長で過度に複雑
フォント サブセット化 サポートあり。既定では使用されたグリフのみを埋め込む サポートなし。不要に大きい PDF になる場合がある サポートあり。既定では使用されたグリフのみを埋め込む サポートあり。既定では使用されたグリフのみを埋め込む
右から左 (RTL) のコンテンツ方向 サポートあり 非対応 サポートあり サポートあり
デジタル署名 サポートあり。LTV と外部署名を含む 非対応 非対応 サポートあり。LTV と外部署名を含む
暗号化 / 権限 完全にサポート RC4 暗号化のみ。AES や証明書は非対応 非対応 完全にサポート
外部依存関係 なし なし SkiaSharp / Skia ベースのコンポーネント なし
サポート 見込み客と顧客向けのプロフェッショナル サポート。最上位ライセンスでは優先サポート コミュニティ サポート。プロフェッショナル サポートは別途購入可能 GitHub 経由のコミュニティ サポート AGPL 版ではコミュニティ サポート。商用ライセンス保持者向けのプロフェッショナル サポート
ライセンス 商用。対象のユースケースでは無料ライセンスあり MIT 個人および小規模企業向けは MIT。大規模企業は商用ライセンスが必要 オープンソース用途では AGPL、独自プロジェクトでは高価な商用ライセンス
開発者ライセンス すべてのライセンスで開発者数無制限 すべてのライセンスで開発者数無制限 MIT では開発者数無制限。Professional は 10 人。Enterprise は無制限 AGPL 版では開発者数無制限。商用ライセンスでは開発者ごとのライセンス

結論

Docotic.Pdf と Layout アドオンは、C# と VB.NET で PDF を生成するための、モダンで高性能、高品質な方法を提供します。ライブラリはレポート、明細書、請求書などのドキュメントを生成します。よく設計された fluent API は優れた開発者体験をもたらします。Docotic.Pdf とそのアドオンに対して Bit Miracle が提供するプロフェッショナル サポートを利用できます。

他の一部の PDF 生成ソリューションとは異なり、Docotic.Pdf は多機能な PDF API です。ライブラリは、LTV 対応署名を含むデジタル署名で生成された PDF に署名する ことができます。Docotic.Pdf は、USB トークンやスマートカードなどの安全なハードウェアに保存された証明書 を使用できます。Microsoft Azure Key Vault や AWS Key Management Service (KMS) のようなクラウドベースの Hardware Security Modules (HSMs) もサポートされます。

Docotic.Pdf を使うと、スプレッドシートや音声メモなどの補助ドキュメントを添付する ことができます。Web ページや同様のインターフェイスにドキュメントを表示するには、1 ページまたは複数ページからサムネイル画像を作成する ことができます。

次の手順: