이 페이지에는 자동 번역된 텍스트가 포함될 수 있습니다.
표 컨테이너
Table 컨테이너를 사용해 가장 복잡한 데이터를 레이아웃할 수 있습니다. 표는 PDF 문서에서 중요한 역할을 하며, 명확성과 효과를 높여 줍니다.
표는 데이터를 구조화된 형식으로 제시합니다. 복잡한 세부 정보를 간결하고 시각적으로 보기 좋게 표현할 수 있습니다. 긴 문단 대신 표를 사용해 사실, 수치, 추세를 간명하게 보여줄 수 있습니다.
잘 설계된 표는 가독성을 높입니다. 열과 행을 정렬하면 읽는 사람의 시선을 안내하는 구조를 만들 수 있습니다. 배경, 테두리, 글꼴 스타일을 사용해 특정 셀이나 제목을 강조할 수 있습니다.

LayoutContainer.Table 메서드를 호출해 표 컨테이너를 가져옵니다. 그런 다음 컨테이너에 최소한 하나의 열을 정의해야 합니다. 가장 단순한 경우에는 Cell 메서드를 여러 번 호출해 모든 열과 행을 채울 수 있습니다.
열을 정의하고 표에 머리글 및/또는 바닥글을 추가하는 방법은 계속 읽어 보세요. 이 글에서는 Table 컨테이너의 다른 모든 기능도 설명합니다.
이 문서는 PDF 생성을 위한 Layout API 시리즈의 일부입니다. API가 처음이라면 먼저 Getting Started with Layout API 부분을 읽어 보세요.
열과 행
모든 표에는 최소한 하나의 열이 있어야 합니다. Table.Columns 메서드를 사용해 열을 정의합니다. 이 메서드는 Action<TableColumnContainer> 형식의 대리자를 받습니다. 대리자 코드에서 제공된 개체의 메서드를 호출해 표에 열을 추가합니다.
ConstantColumn 메서드는 정확한 포인트 수와 같은 너비의 열을 추가합니다.
RelativeColumn 메서드는 상대 너비의 열을 추가합니다. 이 메서드는 열이 차지해야 하는 비율 수를 받습니다. 전체 비율 수는 이 표에서 모든 RelativeColumn 호출에 사용된 숫자의 합입니다. 대부분 이 메서드는 Row container의 RelativeItem과 같은 방식으로 동작합니다. 자세한 설명은 링크를 읽어 보세요.
행을 명시적으로 정의할 필요는 없습니다. 행 수는 열과 셀 속성에 따라 달라집니다. 셀 수도 행 수에 영향을 줄 수 있습니다. Table의 높이는 모든 행 높이의 합보다 클 수 있습니다. 행에 포함된 셀 수가 열 수보다 적을 수도 있습니다. 또한 열에 포함된 셀 수가 행 수보다 적을 수도 있습니다.
PdfDocumentBuilder.Create().Generate("table-basic.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(300, 100);
page.Content()
.Padding(10)
//.MinimalBox()
.Border(b => b.Thickness(1).Color(new PdfRgbColor(250, 123, 5)))
.Table(table =>
{
table.Columns(columns =>
{
columns.ConstantColumn(100);
columns.RelativeColumn(1);
columns.RelativeColumn(3);
});
for (int i = 0; i < 10; i++)
{
table.Cell()
.Border(b => b.Thickness(0.1))
.AlignCenter()
.Text($"Cell {i + 1}");
}
});
});
});
코드 결과는 table-basic.pdf에서 볼 수 있습니다.
생성된 PDF에서 표는 부모 컨테이너의 전체 공간을 차지합니다. 셀 수가 영역을 채우기에 충분하지 않아도 그렇습니다. MinimalBox 호출의 주석을 해제하면 표 높이를 행 높이의 합으로 제한할 수 있습니다.
이 코드는 3개의 열을 정의하고 표에 10개의 셀을 추가합니다. 표는 셀을 순서대로 배치하고, 필요할 때 새 행을 추가합니다. 한 행에 셀이 하나만 있어도 전혀 문제없습니다.
현재 페이지에 공간이 충분하지 않으면 표는 셀을 다음 페이지에 배치합니다. 추가하는 셀 수를 늘리면 이를 확인할 수 있습니다.
셀
Layout API가 표 셀 작업에 제공하는 내용을 설명하겠습니다.
위치
표 셀을 만드는 방법은 두 가지입니다.
- 명시적인 위치 정보 없이
- 위치를 적어도 일부 지정해서
이전 섹션의 코드에서는 첫 번째 방법을 사용했습니다. 매개 변수를 받지 않는 Cell 메서드 오버로드를 호출합니다. 이 메서드는 마지막으로 추가된 셀 뒤의 열에 새 셀을 추가합니다. 오른쪽에 공간이 없으면 셀은 다음 행으로 이동합니다. RTL 모드에서는 마지막으로 추가된 셀의 왼쪽에 공간이 있는지 확인합니다.
새 셀의 정확한 위치를 알고 있으면 Cell 메서드의 다른 오버로드를 사용합니다. 이 오버로드는 Action<TableCell> 형식의 대리자를 받습니다. 대리자 코드에서 제공된 개체의 RowIndex 및 ColumnIndex 메서드를 호출해 셀의 행과 인덱스를 지정합니다.
새 셀에 대해 행 또는 열 인덱스만 지정할 수도 있습니다. 행 인덱스만 지정하면 열 인덱스는 0입니다. 반대도 마찬가지입니다.
셀을 만들 때 두 Cell 오버로드를 어떤 순서로든 섞어서 호출할 수 있습니다.
이 문서의 다음 코드 예제에서는 아래 확장 메서드를 사용하겠습니다. 이렇게 하면 예제가 더 간결하고 읽기 쉬워집니다.
static class LayoutHelpers
{
public static TextSpan BoxWithText(this LayoutContainer cell, string caption)
=> cell.Border(b => b.Thickness(0.5))
.Background(new PdfGrayColor(75))
.ShowOnce()
.MinWidth(50)
.MinHeight(50)
.Padding(10)
.AlignCenter()
.AlignMiddle()
.Text(caption);
}
다음은 명시적으로 배치된 셀과 암시적으로 배치된 셀을 모두 사용하는 표를 만드는 예입니다. 셀에 스타일과 텍스트를 적용하기 위해 확장 메서드를 사용했습니다. 셀의 숫자는 셀이 생성된 순서를 나타냅니다.
PdfDocumentBuilder.Create().Generate("table-cells.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(300, 200);
page.Content()
.Padding(10)
.MinimalBox()
.Border(b => b.Thickness(0.1))
.Table(table =>
{
table.Columns(columns =>
{
for (int i = 0; i < 4; i++)
columns.RelativeColumn();
});
table.Cell().BoxWithText("1");
table.Cell(c => c.RowIndex(1).ColumnIndex(1)).BoxWithText("2");
table.Cell().BoxWithText("3");
table.Cell(c => c.RowIndex(2).ColumnIndex(2)).BoxWithText("4");
table.Cell(c => c.RowIndex(0).ColumnIndex(3)).BoxWithText("5");
});
});
});
코드 결과는 table-cells.pdf에서 볼 수 있습니다.
행 및 열 병합
셀은 여러 행 및/또는 여러 열에 걸칠 수 있습니다. 셀이 차지하는 행 수와 열 수를 지정하려면 대리자를 받는 Cell 오버로드를 사용합니다.
PdfDocumentBuilder.Create().Generate("table-cellspans.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(300, 150);
page.Content()
.Padding(10)
.MinimalBox()
.Border(b => b.Thickness(0.1))
.Table(table =>
{
table.Columns(columns =>
{
for (int i = 0; i < 4; i++)
columns.RelativeColumn();
});
table.Cell(c => c.RowSpan(2).ColumnSpan(2)).BoxWithText("1");
table.Cell(c => c.ColumnSpan(2)).BoxWithText("2");
table.Cell().BoxWithText("3");
table.Cell().BoxWithText("4");
});
});
});
코드 결과는 table-cellspans.pdf에서 볼 수 있습니다.
겹침
셀은 서로를 덮을 수 있습니다. 이런 경우 생성 순서에 따라 어떤 셀이 위에 표시될지가 결정됩니다.
셀을 여러 열이나 여러 행에 걸치게 하면 일부 셀이 겹칠 수 있습니다. 또는 셀을 명시적 및 암시적으로 모두 배치할 때도 겹칠 수 있습니다.
PdfDocumentBuilder.Create().Generate("table-overlapping.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(300, 300);
page.Content()
.Padding(10)
.MinimalBox()
.Border(b => b.Thickness(0.1))
.Table(table =>
{
table.Columns(columns =>
{
for (int i = 0; i < 4; i++)
columns.RelativeColumn();
});
for (int i = 0; i < 16; i++)
table.Cell().BoxWithText($"{i + 1}");
table.Cell(c => c.RowIndex(2).ColumnIndex(1).ColumnSpan(3))
.Background(new PdfRgbColor(250, 123, 5), 50);
});
});
});
코드 결과는 table-overlapping.pdf에서 볼 수 있습니다.
아래쪽까지 확장
표의 레이아웃이 복잡하면 페이지 나누기 메커니즘 때문에 일부 열의 아래쪽에 빈 공간이 생길 수 있습니다. 이 빈 공간을 없애려면 ExtendCellsToBottom 메서드를 사용하세요. 이 메서드는 각 열의 마지막 셀을 아래쪽까지 확장해 셀이 표의 하단에서 끝나도록 합니다.
다음 예제에서는 첫 번째 페이지에 기본 동작이 표시됩니다. 두 번째 페이지에는 ExtendCellsToBottom 호출이 열에 어떤 영향을 주는지 표시됩니다.
PdfDocumentBuilder.Create().Generate("table-extendcells.pdf", doc =>
{
for (int take = 0; take < 2; take++)
{
doc.Pages(page =>
{
page.Size(300, 200);
page.Content()
.Padding(10)
.MinimalBox()
.Border(b => b.Thickness(0.1))
.Table(table =>
{
if (take != 0)
table.ExtendCellsToBottom();
table.Columns(columns =>
{
for (int i = 0; i < 4; i++)
columns.RelativeColumn();
});
table.Cell(c => c.RowIndex(0).ColumnIndex(0)).BoxWithText("1");
table.Cell(c => c.RowIndex(2).ColumnIndex(0)).BoxWithText("2");
table.Cell(c => c.RowIndex(1).ColumnIndex(1)).BoxWithText("3");
table.Cell(c => c.RowIndex(2).ColumnIndex(2)).BoxWithText("4");
table.Cell(c => c.RowIndex(1).ColumnIndex(3)).BoxWithText("5");
});
});
}
});
코드 결과는 table-extendcells.pdf에서 볼 수 있습니다.
머리글 및 바닥글
표에는 머리글과 바닥글을 둘 수 있습니다. Header 및 Footer 메서드를 사용해 대응하는 TableCellContainer를 가져오고 설정합니다. 이 컨테이너는 셀을 만들기 위한 Cell 메서드를 제공합니다. 이 메서드들은 본문 표 콘텐츠용 메서드와 완전히 동일하게 동작합니다.
머리글, 바닥글, 본문 표 셀은 동일한 열 정의를 사용합니다. 하지만 세 셀 컬렉션은 모두 서로 독립적이며 서로에게 영향을 주지 않습니다.
표가 한 페이지에 들어가지 않으면 머리글과 바닥글이 표가 차지하는 각 페이지마다 반복됩니다.
이전 예제에서는 BoxWithText 확장 메서드를 사용했습니다. 다음 코드에서도 WhiteBoxWithText 확장 메서드를 사용합니다.
static class LayoutHelpers
{
public static TextSpan WhiteBoxWithText(
this LayoutContainer cell, string caption, bool center = true)
{
return cell.Border(b => b.Thickness(0.5))
.ShowOnce()
.MinWidth(50)
.MinHeight(50)
.Padding(10)
.Container(l => center ? l.AlignCenter() : l)
.AlignMiddle()
.Text(caption);
}
}
다음 예제는 머리글이 있는 표를 만드는 방법을 보여 줍니다. 다섯 가지 핵심 화학 원소를 담은 표를 사용합니다. 각 원소에 대해 이름과 섭씨 및 켈빈 기준의 융점이 있습니다.
var elements = new (string Name, double Celsius, double Kelvin)[] {
("Oxygen", -218.79, 54.36),
("Carbon", double.NaN, double.NaN),
("Hydrogen", -259.16, 13.99),
("Nitrogen", -209.86, 63.23),
("Sulfur", 115.21, 388.36),
};
static string formatDouble(double val)
{
return double.IsNaN(val)
? string.Empty
: val.ToString(CultureInfo.InvariantCulture);
}
PdfDocumentBuilder.Create().Generate("table-header.pdf", doc =>
{
doc.Pages(page =>
{
page.Size(400, 500);
page.Content()
.Padding(10)
.MinimalBox()
.Border(b => b.Thickness(0.1))
.Table(table =>
{
table.Columns(columns =>
{
columns.RelativeColumn();
columns.ConstantColumn(100);
columns.ConstantColumn(100);
});
table.Header(header =>
{
header.Cell(c => c.RowSpan(2)).BoxWithText("Chemical element");
header.Cell(c => c.ColumnSpan(2)).BoxWithText("Melting point");
header.Cell().BoxWithText("Celsius");
header.Cell().BoxWithText("Kelvin");
});
foreach (var (Name, Celsius, Kelvin) in elements)
{
table.Cell().WhiteBoxWithText(Name, false);
table.Cell().WhiteBoxWithText(formatDouble(Celsius));
table.Cell().WhiteBoxWithText(formatDouble(Kelvin));
}
});
});
});
코드 결과는 table-header.pdf에서 볼 수 있습니다.
샘플 코드
PDF 문서에 표 추가 예제를 한번 살펴보세요. 머리글이 있는 표를 만드는 방법도 보여 주지만, 확장 메서드는 사용하지 않습니다. VB.NET 버전도 있습니다.