iTextSharp로 PDF 작성

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();

Pie's Tech Note

생계형 개발자의 메모장

comments powered by Disqus

    rss facebook twitter github youtube mail spotify instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora