이 페이지에는 자동 번역된 텍스트가 포함될 수 있습니다.

복합 컨테이너

복합 컨테이너는 콘텐츠를 구조화하고 구성하는 데 중요한 역할을 합니다. 적절한 컨테이너를 사용하면 텍스트와 이미지를 사용자 친화적인 방식으로 쉽게 배치할 수 있습니다.

간단한 텍스트 및 이미지 컨테이너만으로 문서를 구성하는 것은 분명 가능합니다. 하지만 단순한 컨테이너만으로는 구현하기 어렵거나 불가능한 요구 사항도 있습니다. 이러한 경우 복합 컨테이너가 도움이 됩니다. 또한 복합 컨테이너는 더 적은 코드로 목표를 달성하는 데 도움이 됩니다.

복합 컨테이너

LayoutContainer 클래스의 메서드를 사용하여 RowColumn 같은 복합 컨테이너를 문서 페이지에 추가합니다. 이러한 컨테이너를 사용하면 그리드와 목록 같은 다른 컨테이너를 구현할 수 있습니다. 덜 일반적이지만 여전히 중요한 경우를 위한 InlinedLayers 메서드도 있습니다.

이 문서는 PDF 생성을 위한 Layout API 시리즈의 일부입니다. API를 처음 사용하는 경우 먼저 Layout API로 시작하기 부분을 읽으십시오.

Row

Row 컨테이너는 항목을 가로로 한 줄에 배치할 수 있는 공간을 제공합니다. 행의 각 항목은 컨테이너입니다. 즉, 서로 다른 유형의 콘텐츠를 한 행에 넣을 수 있습니다. Spacing 메서드를 사용하여 항목 간 간격을 지정할 수 있습니다.

행의 모든 항목은 높이가 같습니다. 라이브러리는 가장 높은 항목의 높이를 행의 높이로 사용합니다. 항목 너비를 지정하는 방법은 세 가지가 있습니다. 항목을 만들 때 반드시 하나를 선택해야 합니다. 행에는 서로 다른 방식으로 만들어진 항목을 포함할 수 있습니다.

Row.AutoItem 메서드는 명시적으로 너비를 지정하지 않은 항목을 만듭니다. 이러한 항목에 대해 라이브러리는 콘텐츠의 고유 크기를 계산합니다. 계산된 콘텐츠 너비가 항목의 너비가 됩니다. AutoItem으로 만든 항목은 긴 줄을 줄바꿈하지 않는다는 점에 유의하십시오.

ConstantItem 메서드를 사용하면 너비가 정확히 지정한 포인트 수와 같은 항목을 만들 수 있습니다.

RelativeItem은 행의 항목 너비를 정확히 알 수 없고 고유 크기를 사용하고 싶지 않을 때 유용합니다. 대신 행의 항목에 상대 너비를 지정할 수 있습니다. 이 메서드는 항목이 차지해야 하는 부분 수를 받습니다. 전체 부분 수는 이 행의 모든 RelativeItem 호출에 있는 모든 숫자의 합입니다.

예를 들어 RelativeItem 호출이 하나만 있으면 숫자는 중요하지 않습니다. 항목은 사용 가능한 전체 너비를 차지합니다. 두 개 이상의 항목이 있으면 숫자가 비율을 정의합니다.

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

위 코드로 만든 모든 항목은 6부분(2 + 3 + 1 = 6)을 차지합니다. 각 항목은 각각 6분의 2, 6분의 3, 6분의 1을 차지합니다.

Layout API는 다음 공식을 사용하여 한 부분의 너비를 계산합니다.

PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts

여기서:
RowWidth = 행 컨테이너의 너비
AutoWidth = AutoItem 메서드로 만든 모든 항목의 너비
ConstantWidth = ConstantItem 메서드로 만든 모든 항목의 너비

다음은 세 가지 종류의 항목을 모두 포함하는 행을 만드는 예입니다.

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

항목을 세로로 하나씩 배치하려면 Column 컨테이너를 사용합니다. 열의 각 항목은 컨테이너입니다. 따라서 서로 다른 유형의 콘텐츠를 한 열에 넣을 수 있습니다.

각 항목의 너비는 열 너비와 같습니다. 각 항목의 높이는 항목의 콘텐츠와 속성에 따라 달라집니다. Column 컨테이너는 페이징을 지원하므로 Layout 추가 기능은 열의 항목을 둘 이상의 페이지에 렌더링할 수 있습니다.

기본적으로 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는 그리드용 특수 컨테이너 형식을 제공하지 않습니다. ColumnRow 컨테이너를 사용하여 그리드 레이아웃을 구현할 수 있습니다.

그리드를 각 항목이 행인 열로 생각하면 이해하기 쉽습니다. 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에서 확인할 수 있습니다.

기울기

목록은 정보를 간결한 항목으로 나누어 가독성을 높입니다. 목록 항목은 텍스트 옆에 숫자, 글머리 기호 및 기타 기호를 둘 수 있습니다. ColumnRow 컨테이너를 사용하면 목록 레이아웃을 쉽게 구현할 수 있습니다. 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

컨테이너를 다른 컨테이너 컬렉션으로 채울 수 있습니다. LayoutContainer.Inlined 메서드를 호출하는 것부터 시작합니다. 그런 다음 제공된 InlineContainerItem 메서드를 호출하여 하위 컨테이너를 추가합니다.

Layout 추가 기능은 컨테이너를 한 줄에 하나씩 차례로 배치합니다. 항목을 배치할 공간이 없으면 라이브러리는 새 줄을 시작합니다. 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 메서드를 호출하여 मुख्य 콘텐츠 레이어를 추가합니다. 기본 레이어는 정확히 하나만 추가해야 합니다.

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에서 확인할 수 있습니다.

워터마크

흔한 요구 사항 중 하나는 PDF에 워터마크를 추가하는 것입니다. 이 요구 사항은 여러 이유로 발생할 수 있습니다. PDF에서 소유권을 식별하거나 정보의 기밀성 또는 민감성을 나타내기 위해 워터마크를 추가할 수 있습니다.

PDF 워터마킹의 한 가지 방법은 레이어를 사용하는 것입니다. 이전 섹션의 예를 참조하십시오. 이제 다른 방법을 보여드리겠습니다.

PDF를 워터마크하기 위해 페이지 배경 및 전경 컨테이너를 사용하겠습니다. Layout 추가 기능은 다음 페이지에서도 이러한 컨테이너를 반복합니다. 기본 문서 콘텐츠가 있는 모든 페이지에는 배경 및 전경 컨테이너도 포함됩니다. 따라서 이 작업에 적합합니다.

먼저 background container에 이미지를 추가합니다. 같은 방식으로 로고를 PDF에 추가할 수 있습니다. 이미지는 페이지 콘텐츠 뒤에 표시됩니다. 문서의 여러 페이지에서 배경 이미지를 사용하더라도 상관없습니다. API는 생성된 PDF에 이미지 바이트의 복사본을 하나만 추가합니다.

주 문서 콘텐츠는 무엇이든 될 수 있습니다. 이 샘플 코드에서는 유명한 Lorem Ipsum 텍스트를 사용합니다.

텍스트 워터마크는 foreground container에 넣습니다. 텍스트 자체는 무엇이든 될 수 있으며 어떤 색상이나 글꼴도 사용할 수 있습니다. 저는 더 큰 크기의 반투명 빨간색 글자로 그린 회전된 텍스트를 사용했습니다.

샘플 코드는 샘플 코드 저장소에서 이미지와 텍스트를 모두 비동기로 다운로드합니다. 물론 로컬 이미지를 사용하거나 텍스트를 파일에서 읽을 수도 있습니다.

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에서 확인할 수 있습니다.

샘플 코드

앞서 언급한 기능을 더 자세히 다루는 샘플 앱이 몇 가지 있습니다. 시간을 내어 확인해 보십시오.