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

複合コンテナー

複雑なコンテナーは、コンテンツを構造化し整理するうえで重要な役割を果たします。適切なコンテナーを使うことで、テキストや画像を使いやすい形で簡単に配置できます。

単純なテキスト コンテナーと画像コンテナーだけで文書を構成することは確かに可能です。ただし、単純なコンテナーだけでは実装が難しい、または不可能な要件もあります。複合コンテナーはそのような場合に役立ちます。また、複雑なコンテナーを使うと、より少ないコードで目的を達成できます。

複合コンテナー

LayoutContainer クラスのメソッドを使用して、RowColumn のような複合コンテナーを文書ページに追加します。これらのコンテナーを使うと、グリッドやリストなどの他のコンテナーを実装できます。あまり一般的ではないものの重要なケース向けに、InlinedLayers メソッドもあります。

この記事は、PDF 生成向けの Layout API に関するシリーズの一部です。API を初めて使う場合は、先に Layout API の入門 をお読みください。

Row

Row コンテナーは、項目を横方向に1行で並べるための領域を提供します。行内の各項目はコンテナーです。つまり、1つの行に異なる種類のコンテンツを配置できます。項目間の間隔は Spacing メソッドで指定できます。

行内のすべての項目の高さは同じです。ライブラリは最も高い項目の高さを行の高さとして使用します。項目の幅を指定する方法は3つあります。項目を作成するときに、いずれか1つを選択する必要があります。行には、異なる方法で作成した項目を含められます。

Row.AutoItem メソッドは、幅を明示的に指定しない項目を作成します。このような項目では、ライブラリがコンテンツの固有サイズを計算します。計算されたコンテンツ幅が項目の幅になります。AutoItem で作成された項目は長い行を折り返さないことに注意してください。

正確なポイント数に等しい幅の項目を作成するには、ConstantItem メソッドを使用します。

RelativeItem は、行内の項目の正確な幅が分からず、固有サイズも使いたくない場合に便利です。代わりに、行内の項目に相対幅を指定できます。このメソッドは、項目が占有する部分数を受け取ります。部分の合計数は、この行で呼び出されたすべての RelativeItem の数値の合計です。

たとえば、RelativeItem の呼び出しが1つだけなら、その数値は重要ではありません。項目は利用可能な幅全体を占有します。2つ以上の項目がある場合は、数値が比率を定義します。

row.RelativeItem(2)
row.RelativeItem(3)
row.RelativeItem(1)

上記のコードで作成されるすべての項目は 6 部分分を使用します(2 + 3 + 1 = 6)。各項目はそれぞれ 6 分の 2、6 分の 3、6 分の 1 を占有します。

Layout API は、1部分の幅を計算するために次の式を使用します。

PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts

ここで:
RowWidth = 行コンテナーの幅
AutoWidth = AutoItem メソッドで作成されたすべての項目の幅
ConstantWidth = ConstantItem メソッドで作成されたすべての項目の幅

次は、3種類すべての項目を含む行を作成する例です。

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames;
var groups = new[]
{
    string.Join(", ", monthNames.Take(4)),
    string.Join(", ", monthNames.Skip(4).Take(4)),
    string.Join(", ", monthNames.Skip(8).Take(4)),
};

PdfDocumentBuilder.Create().Generate("compounds-row.pdf", doc => doc.Pages(page =>
{
    page.Content()
        .Padding(20)
        .MinimalBox()
        .Row(row =>
        {
            row.ConstantItem(100)
                .Background(new PdfRgbColor(187, 237, 237))
                .Text("100 points wide");

            for (int i = 0; i < groups.Length; i++)
            {
                row.AutoItem().LineVertical(0.1);

                var numberOfParts = groups.Length - i + 1;
                row.RelativeItem(numberOfParts)
                    .PaddingHorizontal(5)
                    .Text(t =>
                    {
                        t.Line($"{numberOfParts} parts wide");
                        t.Line();
                        t.Line(groups[i]);
                    });
            }
        });
}));

コードの結果は compounds-row.pdf で確認できます。

Column

項目を縦方向に1つずつ並べるには、Column コンテナーを使用します。列内の各項目はコンテナーです。そのため、1つの列に異なる種類のコンテンツを配置できます。

各項目の幅は列の幅と同じです。各項目の高さは、その項目のコンテンツとプロパティによって決まります。Column コンテナーはページ分割をサポートしているため、Layout add-on は列の項目を複数ページにわたって描画できます。

既定では、Column コンテナーにはヘッダーやフッターのコンテンツはありません。対応するコンテナーにアクセスして設定するには、HeaderFooter メソッドを使用します。列の項目が複数ページにまたがる場合、ライブラリは各ページにヘッダーとフッターの両方を繰り返し配置します。

列の項目間に縦方向の余白を追加するには Spacing メソッドを使用します。なお、ライブラリはヘッダーと最初の項目の間には間隔を適用しません。また、フッターの前にも余白を追加しません。

PdfDocumentBuilder.Create().Generate("compounds-column.pdf", doc => doc.Pages(page =>
{
    page.Size(PdfPaperSize.A6);

    page.Content()
        .Padding(20)
        .Column(column =>
        {
            column.Header()
                .Background(new PdfRgbColor(187, 237, 237))
                .Padding(5)
                .Text("Month names");

            for (int i = 0; i < 12; i++)
            {
                column.Item().Background(
                    new PdfGrayColor(i % 2 == 0 ? 90 : 100))
                    .Padding(5)
                    .Text(DateTimeFormatInfo.InvariantInfo.MonthNames[i]);
            }

            column.Footer().LineHorizontal(1);
        });
}));

コードの結果は compounds-column.pdf で確認できます。

グリッド

グリッド レイアウトは、項目を列と行に整理します。この点ではテーブルと似ています。Layout API には、グリッド専用のコンテナー型はありません。Column コンテナーと Row コンテナーを使ってグリッド レイアウトを実装できます。

グリッドは、各項目が行である列として考えると分かりやすくなります。ColumnRow の両方のコンテナーには、項目間の間隔を設定する機能があります。必要であれば、ヘッダーとフッターを追加できます。

各行は独立したレイアウトにできます。行ごとに項目数を変えられます。項目は幅と高さをそれぞれ異ならせることができます。行内の項目の前後や間に余分なスペースを追加することも可能です。そのためには、コンテンツや装飾のない項目を使用します。

var blue = new PdfRgbColor(187, 237, 237);
var darkerBlue = blue.Darken(50);
PdfDocumentBuilder.Create().Generate("compounds-grid.pdf", doc => doc.Pages(page =>
{
    page.Size(300, 200);

    page.Content().Padding(15).Column(column =>
    {
        column.Spacing(10);

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.ConstantItem(100).Background(darkerBlue).Height(40);
            row.RelativeItem(4).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(2).Background(blue).Height(60);
            row.RelativeItem(1);
            row.RelativeItem(2).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(1).Background(blue).Height(50);
            row.RelativeItem(3).Background(blue);
            row.ConstantItem(50).Background(darkerBlue);
        });
    });
}));

コードの結果は compounds-grid.pdf で確認できます。

リスト

リストは、情報を簡潔な項目に分けることで可読性を高めます。リスト項目には、テキストの横に番号、箇条書き記号、その他の記号を付けられます。Column コンテナーと Row コンテナーを使えば、リスト レイアウトを簡単に実装できます。Layout API には、リスト専用のコンテナー型はありません。

季節ごとの月のリストを作成するサンプルコードを確認してください。このリストにはヘッダーがあることに注意してください。項目に次の行へ折り返せるテキストが含まれる場合は、項目のテキスト部分に RelativeItem または ConstantItem メソッドを使用します。

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames.ToList();
monthNames.Insert(0, monthNames[11]);

PdfDocumentBuilder.Create().Generate("compounds-list.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 200);

    page.Content().Padding(5).Column(column =>
    {
        column.Header()
            .Text("Months by seasons:")
            .Style(TextStyle.Parent.Underline());

        for (int i = 0; i < 4; i++)
        {
            var season = string.Join(", ", monthNames.Skip(i * 3).Take(3));
            column.Item().Row(row =>
            {
                row.Spacing(2);

                row.AutoItem().Text("•");
                row.RelativeItem().Text(season);
            });
        }
    });
}));

コードの結果は compounds-list.pdf で確認できます。

Table

テーブル レイアウトは、項目を列と行に整理します。Table コンテナー型は豊富な機能を備えており、最も高度なケースにも対応できます。すべての機能については テーブル コンテナー 記事を参照してください。

InlineContainer

1つのコンテナーに、他のコンテナーのコレクションを入れることができます。まず LayoutContainer.Inlined メソッドを呼び出します。次に、提供される InlineContainerItem メソッドを呼び出して子コンテナーを追加します。

Layout add-on は、コンテナーを1つずつ横に並べます。項目を配置するスペースがない場合、ライブラリは新しい行を開始します。項目間に余白を追加するには Spacing/HorizontalSpacing/VerticalSpacing メソッドを使用します。

配置メソッドを使用すると、コンテナー内の項目位置を調整できます。AlignTop/AlignMiddle/AlignBottom メソッドは項目を縦方向に整列します。水平方向には AlignLeft/AlignCenter/AlignRight/AlignJustify メソッドを使用します。

特殊なケースもあります。AlignSpaceAround メソッドは項目を水平方向に整列し、最初の項目の前と最後の項目の後ろにも余分な間隔を追加します。

var orange = new PdfRgbColor(250, 123, 5);
var brown = orange.Darken(50);
var itemProps = new (int Width, PdfColor Color)[] {
    (20, orange), (30, orange), (50, brown), (50, orange), (50, orange),
    (30, orange), (20, brown), (30, orange), (50, brown), (10, brown)
};

PdfDocumentBuilder.Create().Generate("compounds-inlined.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 120);

    page.Content().Inlined(c =>
    {
        c.Spacing(5);

        foreach (var (Width, Color) in itemProps)
            c.Item().Height(30).Width(Width).Background(Color);
    });
}));

コードの結果は compounds-inlined.pdf で確認できます。

LayerContainer

メインのページ コンテンツの下や上にコンテンツを配置したい場合があります。典型的な用途は、PDF ページの上にウォーターマークを追加することです。

まず、LayoutContainer.Layers メソッドを呼び出して LayerContainer オブジェクトを取得します。そのオブジェクトを使ってレイヤーを追加していきます。Layer メソッドを呼び出すと、補助レイヤーが追加されます。PrimaryLayer メソッドを呼び出すと、メイン コンテンツ レイヤーが追加されます。プライマリ レイヤーは必ず1つだけ追加する必要があります。

Layout API は、レイヤーを作成した順序のまま合成します。プライマリ レイヤーの前に追加されたレイヤーは背景になります。プライマリ レイヤーの後に追加されたレイヤーは、すべてメイン コンテンツの上に配置されます。コンテナーは、メイン コンテンツが配置されるすべてのページで補助レイヤーを繰り返します。

次は、PDF ページにウォーターマークを追加するサンプルコードです。

PdfDocumentBuilder.Create().Generate("compounds-layers.pdf", doc => doc.Pages(page =>
{
    var largeRedText = TextStyle.Parent.FontSize(48)
        .FontColor(new PdfRgbColor(235, 64, 52));

    page.Size(400, 250);

    page.Content()
        .Padding(25)
        .Layers(layers =>
        {
            layers.Layer()
                .AlignCenter()
                .Text(text => text.CurrentPageNumber().Style(largeRedText));

            layers.PrimaryLayer()
                .Background(new PdfGrayColor(85), 65)
                .Padding(25)
                .Text(new string('_', 790));

            layers.Layer()
                .AlignCenter()
                .AlignMiddle()
                .Text("Watermark")
                .Style(largeRedText);
        });
}));

コードの結果は compounds-layers.pdf で確認できます。

ウォーターマーク

一般的な要件の1つに、PDF にウォーターマークを追加することがあります。この要件にはさまざまな理由があります。所有権を示すため、または PDF 内の情報の機密性や重要性を示すためにウォーターマークを追加できます。

PDF にウォーターマークを付ける方法の1つは、レイヤーを使うことです。前の節の例を参照してください。ここでは別の方法を示します。

PDF にウォーターマークを付けるために、ページの背景コンテナーと前景コンテナーを使用します。Layout add-on は、これらのコンテナーを次のページでも繰り返し配置します。メインの文書コンテンツがある各ページには、背景コンテナーと前景コンテナーも含まれます。そのため、この用途に適しています。

まず、背景コンテナー に画像を追加します。ロゴを PDF に追加する場合も同じ方法です。画像はページ コンテンツの背後に表示されます。文書の何ページで背景画像を使用していても問題ありません。API は、画像バイト列を1コピーだけ生成された PDF に追加します。

メインの文書コンテンツには、任意の内容を指定できます。このサンプルコードでは、有名な Lorem Ipsum のテキストを使用します。

テキストのウォーターマークは 前景コンテナー に入れます。テキスト自体は任意で、色やフォントも自由に選べます。ここでは、やや透明な赤い文字を使い、回転させた大きめのテキストを描画しています。

サンプルコードでは、画像とテキストの両方をサンプルコード リポジトリから非同期にダウンロードします。もちろん、ローカル画像を使うことも、ファイルからテキストを読み込むこともできます。

var urlPrefix =
    "https://raw.githubusercontent.com/BitMiracle/Docotic.Pdf.Samples/master/Samples/Sample%20Data/";

using var client = new HttpClient();
using var imageResponse = await client.GetAsync(urlPrefix + "watermark-background.png");
using var imageStream = await imageResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

var loremIpsum = string.Empty;
using (var textResponse = await client.GetAsync(urlPrefix + "lorem-ipsum.txt"))
     loremIpsum = await textResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

PdfDocumentBuilder.Create().Generate("compounds-watermarks.pdf", doc =>
{
    var image = doc.Image(imageStream);

    doc.Pages(page =>
    {
        page.Size(400, 250);

        page.Background()
            .AlignMiddle()
            .Image(image);

        page.Content()
            .Padding(25)
            .Text(loremIpsum);

        var largeRedText = TextStyle.Parent.FontSize(48)
            .FontColor(new PdfRgbColor(235, 64, 52), 50);
        page.Foreground()
            .Rotate(-30)
            .Translate(-50, 180)
            .Text("DO NOT COPY")
            .Style(largeRedText);
    });
});

コードの結果は compounds-watermarks.pdf で確認できます。

サンプル コード

前述の機能をより詳しく扱うサンプル アプリがいくつかあります。ぜひ時間を取って確認してください。