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

C# PDF 생성 빠른 시작 가이드

.NET 프로젝트에서 보고서, 송장, 영수증과 같은 PDF 파일을 생성하는 방법을 읽어보세요. C# 또는 VB.NET을 사용하여 구조 요소를 구성하여 PDF 문서를 쉽게 만듭니다. 요소에는 머리글, 바닥글, 컨테이너, 표, 단락, 이미지 등이 포함됩니다. API는 자동으로 콘텐츠를 페이지로 나눕니다.

C# 및 VB.NET에서 PDF 문서 만들기 문서에 설명된 PDF 파일을 생성하는 몇 가지 다른 방법.

PDF 생성 라이브러리 소개

우리는 Docotic.Pdf 라이브러리의 무료 추가 기능으로 레이아웃 API를 제공합니다. 라이브러리와 Layout 추가 기능은 모두 NuGet 및 당사 사이트에서 사용할 수 있습니다. 평가 모드 제한 없이 라이브러리를 사용해 보려면 라이브러리 페이지에서 시간 제한이 있는 무료 라이센스 키를 받으세요.

Docotic.Pdf 라이브러리 9.3.17036-dev 레이아웃 애드온 9.3.17036-dev
회귀 테스트 14,665건의 테스트 통과 총 NuGet 다운로드 4,191,515

설치

NuGet에서 BitMiracle.Docotic.Pdf.Layout 패키지를 설치합니다. 이는 Layout 애드온을 설치하는 가장 쉽고 편리한 방법입니다.

대안으로 당사 사이트에서 라이브러리 바이너리가 포함된 ZIP을 다운로드할 수 있습니다. Layout API를 사용하려면 ZIP 패키지에서 다음 DLL에 대한 참조를 추가하세요:

  • BitMiracle.Docotic.Pdf.dll
  • Layout add-on/BitMiracle.Docotic.Pdf.Layout.dll

접근

진입점은 PdfDocumentBuilder 클래스입니다. PDF 생성을 시작하려면 클래스의 Generate 메서드를 호출합니다. 이 메서드에는 매개 변수 중 하나로 Action<Document> 유형의 대리자가 필요합니다. 라이브러리에서는 사용자가 대리자에 문서 내용을 배치할 것으로 예상합니다.

PdfDocumentBuilder.Create().Generate("output.pdf", (Document doc) =>
{
    // 여기에 문서 내용을 작성하세요.
});

Document 객체가 주어지면 위임자는 레이아웃을 정의해야 합니다. 전체 레이아웃은 더 작은 빌딩 블록으로 구성됩니다. 페이지, 머리글 및 바닥글, 컨테이너, 텍스트 블록이 이러한 블록의 예입니다.

많은 메서드 호출이 서로 연결되어 있습니다. 체인의 호출 순서는 중요합니다. 다음은 몇 번의 연쇄 호출로 페이지 콘텐츠를 설정하는 방법을 보여주는 예입니다.

PdfDocumentBuilder.Create()
    .Generate("output.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(24).AlignCenter().Text("Some text");
    }));

Intellisense는 API를 찾는 데 도움을 줄 것입니다. API를 직접 사용해 보세요. 쉽게 이용하시길 바랍니다.

안녕, 세상!

공통 구성 요소가 실제로 작동하는 모습을 보여주는 간단한 애플리케이션을 개발해 보겠습니다. 앱은 위에서 언급한 접근 방식을 사용합니다.

나는 Docotic.Pdf 샘플 코드 저장소에 완전한 애플리케이션 코드를 넣었습니다. 이 단계의 코드는 예시 앱의 HelloWorld 클래스에 있습니다.

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(page =>
    {
        page.Content().Text("Hello, world!");
    }));
}

코드가 많지 않은데 여기서 무슨 일이 일어나고 있는 걸까요?

단축번호에 대한 긴 설명

이 코드는 문서 빌더의 인스턴스를 생성하고 빌더에게 hello.pdf를 생성하도록 요청합니다. 빌더는 문서 콘텐츠 레이아웃 작업을 내 코드에 위임합니다.

대리자 코드에서 문서에 일부 페이지를 빌드하도록 요청합니다. 이제 문서는 페이지 내용을 내 코드에 배치하는 작업을 위임합니다.

페이지 대리자에서 페이지의 기본 콘텐츠에 대한 레이아웃 컨테이너에 액세스합니다. 나는 Content 메소드를 호출하여 이를 수행합니다. Text 에 대한 연쇄 호출은 유명한 텍스트 범위를 페이지 콘텐츠에 추가합니다.

이제 빌더가 제공된 레이아웃에 따라 일부 페이지가 포함된 문서를 생성할 차례입니다. 빌더는 기본 콘텐츠의 레이아웃 컨테이너에 추가한 데이터를 포함하는 데 필요한 정확한 수의 페이지를 생성합니다. 이 경우에는 한 페이지면 충분합니까? 좋아요.

향후 업데이트 준비

코드에 몇 가지 기능을 더 추가하겠습니다. 앱을 더욱 편리하게 개발할 수 있도록 코드를 다음과 같이 변경했습니다.

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

static void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

불필요해 보일 수도 있지만 코드를 메소드로 분할하면 최소한 더 읽기 쉬워집니다.

이 단계의 샘플 코드는 예제 앱HelloWorld2 클래스에 있습니다.

문서, 페이지, 컨테이너에 대한 자세한 내용은 다음 문서를 확인하세요.

글꼴 및 색상

기본 글꼴도 좋지만 다른 글꼴을 사용하는 방법을 보여 드리겠습니다. 주요 아이디어는 글꼴을 사용하여 텍스트 스타일을 만드는 것입니다. 그런 다음 필요한 경우 일부 선택적 속성을 스타일에 적용하고 마지막으로 텍스트 범위에서 스타일을 사용합니다.

운영 체제에 설치된 글꼴 모음에서 글꼴을 사용하거나 파일 또는 스트림에서 글꼴을 로드할 수 있습니다.

문서에 대한 서체를 설정하여 사전 정의된 스타일을 재정의하는 것이 좋습니다. 그러나 이는 필수는 아니며 텍스트 스타일을 직접 사용할 수 있습니다.

이 단계의 샘플 코드는 예제 앱HelloWorld3 클래스에 있습니다. 아래는 이전 단계에서 변경된 부분입니다.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildTextContent(pages.Content());
}

static void BuildTextContent(LayoutContainer content)
{
    var colorAccented = new PdfRgbColor(56, 194, 10);
    var styleAccented = TextStyle.Parent.FontColor(colorAccented);

    content.Text(t =>
    {
        t.Span("Hello, ").Style(styleAccented);
        t.Span("world!").Style(styleAccented.Underline());
    });
}

보시다시피 BuildDocumentBuildPages에 대한 코드를 업데이트했습니다. 또한 'BuildTextContent'라는 새 메서드를 추가했습니다.

BuildDocument에서 "Calibri"라는 시스템 글꼴로 텍스트 스타일을 만듭니다. 그런 다음 이 텍스트 스타일을 문서의 기본 텍스트 스타일로 설정했습니다.

이제 BuildPages 메소드에는 모든 페이지의 크기와 여백을 설정하는 코드가 포함됩니다. 또한 이 메서드는 'BuildTextContent'를 호출하여 페이지의 기본 콘텐츠에 대한 컨테이너를 매개변수로 전달합니다.

이제 기본 콘텐츠를 구성하는 방식이 달라졌습니다. 두 개의 텍스트 범위를 사용하고 각 범위에 서로 다른 텍스트 스타일을 적용합니다. 실제로 두 범위 모두 강조 색상을 사용하지만 두 번째 범위에도 밑줄이 있습니다.

이 코드는 사용자 정의 텍스트 글꼴 및 색상이 포함된 PDF를 생성합니다. 결과에 평가판 메시지 경고가 포함된 경우 Docotic.Pdf 라이브러리 페이지에서 무료 시간 제한 라이센스를 받으십시오.

보고서나 송장과 같은 실제 PDF 문서에는 머리글과 바닥글이 포함되어 있는 경우가 많습니다. PDF에 머리글과 바닥글을 추가하는 방법을 보여드리겠습니다.

이 단계의 샘플 코드는 예제 앱HelloWorld4 클래스에 있습니다. 아래는 이전 단계에서 변경된 부분입니다.

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());

    BuildTextContent(pages.Content());
}

static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

static void BuildPagesFooter(LayoutContainer footer)
{
    footer.Height(20)
        .Background(new PdfGrayColor(95))
        .AlignCenter()
        .Text(t => t.CurrentPageNumber());
}

헤더 컨테이너를 매개변수로 사용하여 BuildPagesHeader를 호출하도록 BuildPages 메서드를 변경했습니다. 또한 이 메서드는 바닥글 컨테이너를 전달하는 BuildPagesFooter를 호출합니다.

결과 머리글 및 바닥글이 포함된 PDF는 확실히 전문적인 PDF 문서에 더 가깝습니다.

BuildPagesHeader 메서드는 텍스트의 글꼴 크기를 더 작게 설정합니다. 헤더 내용에는 두 줄을 사용합니다. 하나는 현재 사용자의 이름이고 두 번째는 현재 날짜입니다. 텍스트가 오른쪽 정렬됩니다.

헤더의 크기를 명시적으로 지정하지 않았습니다. 왼쪽 및 오른쪽 여백을 제외한 전체 페이지 너비를 차지합니다. 높이는 텍스트 줄 높이에 따라 달라집니다.

바닥글은 높이가 명시적으로 지정된다는 점을 제외하면 비슷합니다. 그리고 배경색이 적용되어 있습니다. 흥미로운 부분은 PDF 바닥글에 현재 페이지 번호를 넣으려면 CurrentPageNumber 메서드를 사용하면 된다는 것입니다. 모든 계산은 라이브러리 내부에서 이루어집니다.

이미지

사진 한 장을 사용하면 많은 단어를 절약할 수 있다고 합니다. 견적이나 영수증에는 회사 로고나 기타 중요한 이미지를 사용하게 될 것입니다. 이 예제 코드에서는 보기 좋은 코드만 사용하겠습니다.

파일은 우리 사이트에 있으며, 결과 아름다운 이미지가 포함된 PDF가 어떻게 보이는지 확인할 수 있습니다.

이미지를 사용하려면 먼저 문서에 이미지를 추가해야 합니다. 라이브러리는 파일이나 스트림에서 이미지를 읽을 수 있습니다. 문서에 이미지를 추가하는 방법을 보여주기 위해 BuildDocument 메서드를 변경했습니다.

이 단계의 샘플 코드는 예제 앱HelloWorld5 클래스에 있습니다. 아래는 이전 단계에서 변경된 부분입니다.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });

    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
        });
    });
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());
}

static void BuildImageContent(LayoutContainer content, Image image)
{
    content.AlignCenter()
        .PaddingVertical(20)
        .Image(image);
}

보시다시피 BuildDocument 메소드에 더 많은 변경 사항이 있습니다. 이전에는 코드에서 Text 메서드를 사용하여 일부 텍스트를 페이지의 기본 콘텐츠로 설정했습니다. 이 문제는 BuildTextContent 메서드에서 계속 발생합니다. 하지만 이제는 콘텐츠에 이미지도 포함하고 싶습니다.

페이지의 기본 콘텐츠에 텍스트와 이미지를 모두 포함하려면 컨테이너가 필요합니다. 텍스트와 이미지를 세로로 차례로 추가하려면 Column 컨테이너를 사용합니다. 열 컨테이너의 Item 메소드는 하위 컨테이너를 제공합니다. 저는 Item 메소드에 대한 한 번의 호출을 사용하여 텍스트에 대한 컨테이너를 가져오고 또 다른 호출을 사용하여 이미지에 대한 컨테이너를 가져옵니다.

보시다시피 BuildTextContent를 조금 변경하지 않았습니다. 하지만 물론 BuildPages 메서드에서 BuildTextContent에 대한 호출을 제거해야 했습니다.

BuildImageContent는 세 줄로 중요한 작업을 수행합니다. 상단과 하단에 약간의 패딩이 있는 이미지를 추가합니다. 또한 이미지를 페이지 중앙에 배치합니다.

기울기

목록이란 무엇입니까? 이를 다른 항목 아래에 하나씩 적힌 번호가 매겨진 항목 그룹으로 생각해 봅시다. 이 아이디어는 목록을 구현하는 방법을 제안합니다.

이 단계의 샘플 코드는 예제 앱HelloWorld6 클래스에 있습니다. 아래는 이전 단계에서 변경된 부분입니다.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
        });
    });
}

static void BuildListContent(LayoutContainer content)
{
    var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
    var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

    content.Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t => {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}

BuildDocument 의 유일한 변경 사항은 새로운 메서드 호출입니다. BuildImageContent 호출 후에 BuildListContent에 대한 호출을 추가했습니다.

BuildListContent는 목록을 행 열로 생성합니다. 각 행에는 두 개의 항목이 있습니다. 첫 번째(왼쪽)는 품목 번호입니다. 또 다른 (오른쪽) 것은 항목 텍스트입니다. 코드는 행의 항목 사이의 간격을 명시적으로 설정합니다.

항목을 정렬하려면 Row 컨테이너가 미리 알고 있거나 각 항목의 크기를 계산해야 합니다. 이 예에서는 AutoItemRelativeItem 메서드를 사용합니다. 결과적으로 행 컨테이너는 첫 번째 항목을 포함하는 데 필요한 너비를 계산합니다. 그런 다음 컨테이너는 두 번째 항목에 사용 가능한 나머지 너비를 사용합니다.

목록을 추가한 후 결과 PDF의 내용이 더 이상 한 페이지에 맞지 않습니다. Layout API는 자동으로 두 번째 페이지를 추가하고 그 위에 목록을 배치합니다. 여러 페이지로 구성된 PDF 문서의 새 페이지에서 API가 머리글과 바닥글을 반복하는 것을 볼 수 있습니다.

테이블

많은 PDF 문서에는 표가 포함되어 있습니다. 테이블이 데이터의 명확성과 구성을 향상시키기 때문에 놀랄 일이 아닙니다. Layout API를 사용하여 PDF로 테이블을 만드는 방법을 보여드리겠습니다.

이 단계의 샘플 코드는 예제 앱HelloWorld7 클래스에 있습니다. 아래는 이전 단계에서 변경된 부분입니다.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
            BuildTableContent(c.Item());
        });
    });
}

static void BuildTableContent(LayoutContainer content)
{
    var color = new PdfGrayColor(75);

    content.PaddingTop(20).Table(t =>
    {
        t.Columns(c =>
        {
            c.RelativeColumn(4);
            c.RelativeColumn(1);
            c.RelativeColumn(4);
        });

        t.Header(h =>
        {
            h.Cell().Background(color).Text("Month in 2024");
            h.Cell().Background(color).Text("Days");
            h.Cell().Background(color).Text("First Day");
        });

        for (int i = 0; i < 12; i++)
        {
            var stats = GetMonthStats(2024, i);

            t.Cell().Text(stats.Item1);
            t.Cell().Text(stats.Item2);
            t.Cell().Text(stats.Item3);
        }
    });
}

static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex],
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

BuildTableContent에 대한 호출은 BuildDocument의 유일한 변경 사항입니다.

BuildTableContent에서 간단한 테이블을 만듭니다. 테이블에는 2024년의 월에 대한 간단한 정보가 표시됩니다. 먼저 상대 너비가 있는 세 개의 열을 정의합니다. 왼쪽 및 가장 오른쪽 열은 가운데 열보다 4배 더 넓습니다.

코드는 테이블의 헤더도 정의합니다. 헤더 셀을 추가하고 각 셀에 텍스트와 배경색을 지정하면 됩니다. 테이블이 한 페이지에 맞지 않으면 테이블이 차지하는 각 페이지에서 헤더가 반복됩니다. 결과 테이블이 포함된 PDF 문서에서 이를 확인할 수 있습니다. 문서에서 표는 두 번째 페이지에서 시작하여 세 번째 페이지에서 계속됩니다.

나는 행을 구성하기 위해 간단한 루프를 사용하고 있습니다. 루프의 코드는 매월 정보를 검색하는 것으로 시작됩니다. 그런 다음 정보 행을 구성하는 세 개의 셀을 추가합니다.

샘플 코드

위에서는 C#에서 PDF 생성의 가장 인기 있는 기능 중 일부를 소개했습니다. 다음 기사를 읽고 계속해서 API를 검색해 보시기 바랍니다. 또한 GitHub의 Docotic.Pdf.Samples 저장소에서 샘플 코드를 사용해 보세요. Layout API의 샘플 코드는 저장소의 Layout 폴더에 있습니다.

샘플 코드를 코드 플레이그라운드로 사용할 수 있습니다. 이렇게 하면 매번 처음부터 시작하지 않고도 몇 가지 아이디어를 시도해 볼 수 있습니다.

동일한 샘플 코드가 ZIP 패키지의 Samples 폴더에 있습니다. 두 가지 솔루션 파일이 있습니다. SamplesCSharp는 C# 언어를 사용하는 샘플 프로젝트용입니다. 그리고 SamplesVB.NET은 VB.NET 버전용입니다.