PDF 문서를 프로그래밍 방식으로 생성해야 하는 상황에서 iTextSharp 라이브러리는 강력한 기능을 제공합니다. 복잡한 레이아웃과 다양한 서식 요구사항을 처리하는 방법을 단계별로 살펴봅니다.
환경 설정 및 기본 구조
NuGet 패키지 관리자를 통해 iTextSharp를 설치한 후, 필요한 네임스페이스를 임포트합니다.
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
PDF 문서의 기본 골격은 다음과 같이 구성됩니다. Rectangle로 용지 크기를 정의하고, Document로 문서 객체를 생성한 뒤 PdfWriter를 통해 파일로 출력합니다.
var paperSize = new Rectangle(800, 600);
var pdfDoc = new Document(paperSize, 36, 36, 72, 72);
var outputPath = @"C:\output\sample.pdf";
using (var fileStream = new FileStream(outputPath, FileMode.Create))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, fileStream);
pdfDoc.Open();
pdfDoc.Add(new Paragraph("안녕하세요, iTextSharp!"));
pdfDoc.Close();
}
문서 메타데이터 설정
검색 가능성과 문서 정보 관리를 위해 메타데이터를 설정할 수 있습니다.
pdfDoc.AddTitle("프로젝트 보고서");
pdfDoc.AddSubject("2024년 1분기 성과 분석");
pdfDoc.AddKeywords("보고서, 분석, PDF, 자동화");
pdfDoc.AddCreator("ReportGenerator v2.1");
pdfDoc.AddAuthor("개발팀");
이미지 삽입 및 위치 제어
로고나 도장 이미지를 정확한 위치에 배치해야 할 때 DirectContent를 활용합니다.
string logoPath = Server.MapPath("~/assets/logo.png");
var companyLogo = iTextSharp.text.Image.GetInstance(logoPath);
// 좌측 하단 기준 좌표 (x, y)
companyLogo.SetAbsolutePosition(50, 750);
companyLogo.ScalePercent(60);
pdfWriter.DirectContent.AddImage(companyLogo);
동적 테이블 생성
가변적인 열 너비와 복잡한 셀 서식이 필요한 테이블을 구성합니다. 중국어/한국어 등 유니코드 문자 표시를 위해 폰트 설정이 필수적입니다.
// 폰트 설정
string fontPath = Server.MapPath("~/fonts/NanumGothic.ttf");
BaseFont unicodeFont = BaseFont.CreateFont(
fontPath,
BaseFont.IDENTITY_H,
BaseFont.EMBEDDED
);
Font headerFont = new Font(unicodeFont, 11, Font.BOLD, BaseColor.WHITE);
Font dataFont = new Font(unicodeFont, 10, Font.NORMAL, BaseColor.BLACK);
// 테이블 초기화 (7열, 각 열 너비 지정)
float[] columnWidths = { 120f, 100f, 100f, 110f, 120f, 100f, 150f };
var dataTable = new PdfPTable(columnWidths);
dataTable.TotalWidth = 800f;
dataTable.LockedWidth = true;
// 헤더 셀 생성
var headerCell = new PdfPCell(new Phrase("제품명", headerFont));
headerCell.HorizontalAlignment = Element.ALIGN_CENTER;
headerCell.VerticalAlignment = Element.ALIGN_MIDDLE;
headerCell.BackgroundColor = new BaseColor(51, 122, 183);
headerCell.PaddingTop = 8f;
headerCell.PaddingBottom = 8f;
dataTable.AddCell(headerCell);
// 데이터 행 추가 (반복)
for (int i = 0; i < productList.Count; i++)
{
var cell = new PdfPCell(new Phrase(productList[i].Name, dataFont));
cell.HorizontalAlignment = Element.ALIGN_LEFT;
cell.PaddingLeft = 5f;
dataTable.AddCell(cell);
// ... 추가 셀
}
pdfDoc.Add(dataTable);
절대 좌표 텍스트 배치
템플릿 기반 문서나 정밀한 위치 제어가 필요한 경우 ColumnText를 사용합니다.
var canvas = pdfWriter.DirectContent;
var positionedText = new Phrase("기밀문서", new Font(unicodeFont, 14, Font.BOLD, BaseColor.RED));
// 좌측 정렬, x=400, y=300 위치에 0도 회전
ColumnText.ShowTextAligned(
canvas,
Element.ALIGN_LEFT,
positionedText,
400,
300,
0
);
페이지 관리
새 페이지 강제 시작 및 페이지 번호 재설정 기능입니다.
pdfDoc.NewPage(); // 새 페이지 생성
pdfDoc.ResetPageCount(); // 페이지 카운터 초기화
고급: 페이지 이벤트로 헤더/푸터/워터마크 구현
가장 복잡한 요구사항은 IPdfPageEvent 인터페이스를 구현하여 해결합니다. 배경색, 워터마크, 머리글/바닥글을 레이어 순서대로 제어합니다.
public class PageDecorator : PdfPageEventHelper
{
private string watermarkImagePath;
private BaseFont documentFont;
public PageDecorator(string watermarkPath)
{
this.watermarkImagePath = watermarkPath;
}
public override void OnEndPage(PdfWriter writer, Document document)
{
base.OnEndPage(writer, document);
// 폰트 초기화 (지연 로딩)
if (documentFont == null)
{
documentFont = BaseFont.CreateFont(
@"C:\fonts\malgun.ttf",
BaseFont.IDENTITY_H,
BaseFont.EMBEDDED
);
}
var contentByte = writer.DirectContent;
var underContent = writer.DirectContentUnder;
// ===== 레이어 1: 배경색 (최하위) =====
using (var bgBitmap = new System.Drawing.Bitmap(1, 1))
using (var graphics = System.Drawing.Graphics.FromImage(bgBitmap))
{
var bgColor = System.Drawing.Color.FromArgb(240, 248, 255); // AliceBlue
graphics.Clear(bgColor);
var bgImage = iTextSharp.text.Image.GetInstance(
bgBitmap,
System.Drawing.Imaging.ImageFormat.Png
);
bgImage.ScaleAbsolute(document.PageSize.Width, document.PageSize.Height);
bgImage.SetAbsolutePosition(0, 0);
underContent.AddImage(bgImage);
}
// ===== 레이어 2: 워터마크 (중간) =====
try
{
var watermark = iTextSharp.text.Image.GetInstance(watermarkImagePath);
watermark.RotationDegrees = 45;
watermark.SetAbsolutePosition(200, 300);
// 투명도 설정
var graphicState = new PdfGState();
graphicState.FillOpacity = 0.15f;
underContent.SetGState(graphicState);
// 타일링 패턴으로 전체 페이지에 배포
float xPos = -200;
for (int col = 0; col < 8; col++)
{
xPos += 250;
float yPos = -200;
float xOffset = xPos;
for (int row = 0; row < 6; row++)
{
yPos += 200;
xOffset += 30; // 대각선 오프셋
watermark.SetAbsolutePosition(xOffset - 100, yPos);
underContent.AddImage(watermark);
}
}
// 투명도 복원
underContent.SetGState(new PdfGState { FillOpacity = 1.0f });
}
catch { /* 워터마크 로드 실패 시 무시 */ }
// ===== 레이어 3: 머리글/바닥글 (최상위) =====
var headerText = new Phrase("회사 기밀 문서", new Font(documentFont, 9, Font.ITALIC, BaseColor.GRAY));
ColumnText.ShowTextAligned(
contentByte,
Element.ALIGN_RIGHT,
headerText,
document.Right,
document.Top + 20,
0
);
var footerText = new Phrase(
$"페이지 {writer.PageNumber}",
new Font(documentFont, 9, Font.NORMAL, BaseColor.DARK_GRAY)
);
ColumnText.ShowTextAligned(
contentByte,
Element.ALIGN_CENTER,
footerText,
(document.Right + document.Left) / 2,
document.Bottom - 30,
0
);
}
}
이벤트 핸들러를 작성기에 연결합니다.
var decorator = new PageDecorator(Server.MapPath("~/images/watermark.png"));
pdfWriter.PageEvent = decorator;
이 구조를 통해 배경색 → 워터마크 → 본문 콘텐츠의 올바른 쌓임 순서를 보장하며, 모든 페이지에 일관된 서식을 적용할 수 있습니다.