iTextSharp로 PDF 작성
ASP.NET 기반의 업무용 웹 어플에서 PDF 출력 기능을 개발하면서 찾아본 내용등을 미래의 자신을 위해 간단히 정리한 내용입니다.
기본적으로 PDF 작성은 각 요소를 테이블로 작성하는 편이 조정할때 편한 것 같습니다.
using iTextSharp.text;
using iTextSharp.text.pdf;
/// <summary>
/// PDF 파일 작성
/// ※1. 포맷용의 PDF가 있을 경우
/// ※2. 복수 페이지의 PDF를 연속으로 출력할 경우도 대응가능
/// ※3. 웹 어플&응용 콘솔 양쪽에서 호출될 경우를 상정(공통로직)
/// </summary>
/// <param name="pdfStream">PDF스트림</param>
/// <param name="resPdfModelList">PDF작성용 모델(리스트)</param>
/// <param name="isCallFromBatch">웹/응용 콘솔 양쪽에서 호출된 경우의 판단용</param>
public void MakePdfStream(MemoryStream pdfStream, List<ResPdfModel> resPdfModelList, bool isCallFromBatch)
{
var currenAppDatatPath = string.Empty;
if (isCallFromBatch)
{
currenAppDatatPath = AppDomain.CurrentDomain.BaseDirectory + "App_Data";
}
else
{
currenAppDatatPath = HttpContext.Current.Request.PhysicalApplicationPath + "App_Data";
}
// Document 오브젝트 작성
var document = new Document();
var writer = PdfWriter.GetInstance(document, pdfStream);
// 폰트 준비
FontFactory.RegisterDirectories();
// 폰트 설정
var baseFont = FontFactory.GetFont("MS Gothic", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 8f);
// 초기 설정치
var paddingDefaultValue = 4f;
var BorderDefaultValue = 0f;
var leadingDefaultValue = 11f;
// 포맷 정보를 취득
string pdfFormatPath = string.Empty;
PdfReader reader = null;
int formatPageCount = 0;
PdfImportedPage page1 = null;
PdfImportedPage page2 = null;
PdfImportedPage page3 = null;
bool uniqueFormat = false;
if (resPdfModelList.Count == 1 || resPdfModelList.Count == resPdfModelList.Count(x => x.FormatPath == resPdfModelList[0].FormatPath))
{
// 모든 데이터의 포맷 값이 같을 경우엔 foreach를 하기 전에 PdfReader 인스턴스를 작성해 둔다.
// 만약 포맷 값이 다를 경우엔 foreach 내부에서 처리한다.
reader = new PdfReader(currenAppDatatPath + resPdfModelList[0].FormatPath);
formatPageCount = reader.NumberOfPages;
page1 = writer.GetImportedPage(reader, 1);
page2 = writer.GetImportedPage(reader, 2);
if (formatPageCount > 2)
{
page3 = writer.GetImportedPage(reader, 3);
}
uniqueFormat = true;
}
// 1P
document.Open();
var pdfContentByte = writer.DirectContent;
var listCount = 0;
var count = 1;
var tableBaseFont = baseFont;
var cell = new PdfPCell(new Phrase(string.Empty, baseFont));
var fixedHeightDefaultValueFor1page = 16.7f;
var fixedHeightDefaultValue = 21f;
var xPosDefaultValue = 119f;
var tableWidthDefaultValue = 434f;
foreach (var resPdfModel in resPdfModelList)
{
// 모든 데이터의 포맷이 같지 않을 경우엔 매번 다른 포맷을 읽어올 필요가 있다.
if (!uniqueFormat)
{
reader = new PdfReader(resPdfModel.FormatPath);
formatPageCount = reader.NumberOfPages;
page1 = writer.GetImportedPage(reader, 1);
page2 = writer.GetImportedPage(reader, 2);
if (formatPageCount > 2)
{
page3 = writer.GetImportedPage(reader, 3);
}
}
if (listCount != 0)
{
document.NewPage();
}
// PDF 배경에 템플릿을 설정
pdfContentByte.AddTemplate(page1, 0f, 0f);
pdfContentByte.BeginText();
pdfContentByte.SetFontAndSize(baseFont.BaseFont, 8);
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_LEFT, resPdfModel.AccountIDForHeader, 515f, 800f, 0);
pdfContentByte.EndText();
#region #### 샘플 [내용] ####
var infoTableWidths = new float[] { 0.41f, 0.165f, 0.425f };
var infoTable = new PdfPTable(3);
infoTable.SetWidths(infoTableWidths);
infoTable.TotalWidth = tableWidthDefaultValue;
var infoTableData = new List<string>();
infoTableData.Add(resPdfModel.AccountID);
infoTableData.Add("");
infoTableData.Add(resPdfModel.RegisteredPhoneNumber);
infoTableData.Add(resPdfModel.ContractName);
infoTableData.Add("");
infoTableData.Add(resPdfModel.PaymentMethod);
count = 1;
foreach (string title in infoTableData)
{
cell = new PdfPCell(new Phrase(GetChunk(title, tableBaseFont)));
cell.BorderWidth = BorderDefaultValue;
cell.BorderColor = new BaseColor(255, 153, 255);
cell.FixedHeight = fixedHeightDefaultValueFor1page;
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
cell.PaddingLeft = paddingDefaultValue;
infoTable.AddCell(cell);
count++;
}
infoTable.WriteSelectedRows(0, 10, xPosDefaultValue, 754f, pdfContentByte);
#endregion
// ==== 1P의 내용을 서술 ====
// 2P
document.NewPage();
pdfContentByte.AddTemplate(page2, 0f, 0f);
pdfContentByte.BeginText();
pdfContentByte.SetFontAndSize(baseFont.BaseFont, 8);
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_LEFT, resPdfModel.AccountIDForHeader, 525f, 812f, 0);
pdfContentByte.EndText();
// ==== 2P의 내용을 서술 ====
listCount++;
}
document.Close();
reader.Close();
}
/// <summary>
/// 문자 간격 설정용
/// </summary>
/// <param name="text"></param>
/// <param name="font"></param>
/// <returns></returns>
private Chunk GetChunk(string text, Font font)
{
Chunk chunk = new Chunk(text, font);
chunk.SetCharacterSpacing(-0.6f);
return chunk;
}
사각형의 외곽을 둥글게 표현할 경우에는 아래와 같이 썼습니다.
PdfContentByte contentByte = writer.DirectContent;
// x축, y측, 세로폭, 가로폭, 반영 범위 (0(완전한 사각)~10f(거의 원에 가까운 형태))※2~6f정도가 자연스러움.
contentByte.RoundRectangle(100f, 200f, 50f, 20f, 6f);
contentByte.SetLineWidth(0.5f);
contentByte.Stroke();