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

C# 및 VB.NET에서 PDF 문서를 만드는 방법

이 문서에서는 Docotic.Pdf 라이브러리를 사용해 .NET에서 PDF 문서를 만드는 다양한 방법을 설명합니다. 이 라이브러리는 외부 종속성 없이 PDF 문서를 생성, 편집, 변환 및 처리할 수 있는 고성능의 순수 C# .NET 라이브러리입니다.

Docotic.Pdf를 이용한 PDF 생성 및 문서 자동화를 강조한 그림.

다음 섹션에서는 Docotic.Pdf로 PDF를 만드는 주요 접근 방식을 소개합니다:

  • 텍스트, 그래픽, PDF 내부에 대한 저수준 제어를 제공하는 Core API 사용. 이 옵션은 사용자 지정 레이아웃, 그래픽이 많은 문서, 고급 기능에 가장 적합합니다.
  • 단락, 표, 머리글, 바닥글, 자동 페이지 나누기를 지원하는 고수준 Layout API 사용. 이 API는 위치를 수동으로 계산하지 않고 구조화된 문서를 만들 때 이상적입니다.
  • SVG 및 기타 웹 형식을 지원하는 HTML을 PDF로 변환. 이 접근 방식은 이미 HTML 문서를 생성하고 있고 해당 HTML 및 CSS 파일의 PDF 버전이 필요할 때 특히 유용합니다.
  • 이미지에서 PDF 생성. 이 방법은 스캔 문서, 이미지 기반 보고서, 영수증, 그리고 래스터 이미지로 시작하는 모든 워크플로에 유용합니다.
  • PDF 병합 또는 분할. 보고서 조합, 사용자 업로드 처리, 관련 문서 결합, 대형 PDF 재구성에 적합한 선택입니다.
  • 템플릿에서 PDF 생성. 이 접근 방식은 영수증, 세금 양식, 고용 계약서, 기타 반복 가능한 문서 유형처럼 일관된 형식이 필요한 일괄 생성 문서에 잘 맞습니다.

가이드에서 다루는 추가 주제는 다음과 같습니다:

Core API를 사용한 PDF 생성

Core API는 Docotic.Pdf에서 PDF 생성의 기반입니다. 이 API는 Canvas API를 통해 PDF canvas 위에 텍스트, 이미지, 벡터 그래픽을 배치하는 작업에 대한 완전한 저수준 제어를 제공합니다. 이 그리기 API는 Core API의 하위 집합이며, canvas가 있는 페이지와 다른 개체에 콘텐츠를 추가하는 데 사용하는 메서드와 속성을 제공합니다. 렌더링을 넘어, Core API는 주석, 양식 필드, 레이어, 북마크 및 기타 PDF 기능도 지원합니다.

다음은 세 가지 기본 작업, 즉 텍스트 그리기, 이미지 배치, 페이지 canvas에서 벡터 그래픽 렌더링을 사용해 간단한 PDF를 만드는 C# 코드입니다.

using var pdf = new PdfDocument();
var canvas = pdf.Pages[0].Canvas;

canvas.Font = pdf.CreateFont(PdfBuiltInFont.HelveticaBold);
canvas.FontSize = 14;
canvas.DrawString(40, 100, "Core API demo: text, images, and vector graphics");

var image = pdf.CreateImage("image.png");
canvas.DrawImage(image, 40, 180, 120, 120, 0);

canvas.Pen.Color = new PdfRgbColor(30, 60, 160);
canvas.Pen.Width = 2;
canvas.Brush.Color = new PdfRgbColor(200, 230, 255);
canvas.DrawRectangle(new PdfRectangle(200, 200, 150, 80), PdfDrawMode.FillAndStroke);

pdf.Save("core-api-demo.pdf");

이 개요는 Core API가 수행할 수 있는 기능의 일부만 소개합니다. 고급 주제는 Core API로 PDF를 만드는 상세 문서를 참고하세요. 이 문서는 텍스트 측정, 색 공간 처리, 클리핑 적용, 패턴으로 영역 채우기, 투명도 처리 및 기타 기능을 다룹니다.

Layout API를 사용한 PDF 생성

Layout API는 복잡하고 콘텐츠가 풍부한 PDF를 가장 쉽고 효율적으로 생성할 수 있게 해주는 고수준 문서 작성 엔진입니다.

API를 사용할 때는 페이지, 컨테이너, 텍스트 span, 이미지, 표, 링크, 머리글, 바닥글 등의 구조적 요소로 PDF를 구성합니다. 좌표를 계산하거나 페이지 나누기를 수동으로 관리하는 대신, 문서의 구조를 설명하고 나머지는 레이아웃 엔진에 맡기면 됩니다.

이 예제는 수동 배치 대신 선언적 레이아웃에 의존하여 Layout API로 PDF를 만드는 방법을 보여줍니다.

PdfDocumentBuilder.Create()
    .Info(info => info.Title = "Docotic.Pdf Layout API demo")
    .Generate("layout-api-demo.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(100).Text(text =>
        {
            text.Span("The Layout API lets you compose PDFs from structural elements ");
            text.Line("without manually calculating coordinates or handling pagination.")
                .Style(s => s.Strong);
        });
    }));

자세한 가이드를 확인하여 .NET에서 PDF를 생성할 때 Layout API를 사용하는 방법을 알아보세요.

HTML을 PDF로 API를 사용한 웹 콘텐츠 변환

Docotic.Pdf는 무료 HtmlToPdf 추가 기능과 함께 최신의 고품질 Chrome 기반 HTML-to-PDF 엔진을 제공합니다. 이 추가 기능이 제공하는 API를 사용하여 SVG 또는 WebP 이미지와 같은 최신 HTML 및 기타 웹 콘텐츠를 고품질 PDF 문서로 변환할 수 있습니다.

HTML to PDF API는 전체 HTML 페이지나 HTML 조각에서 PDF를 만들 수 있습니다. URL, 원시 HTML 문자열, 로컬 HTML 파일의 콘텐츠를 변환할 수 있습니다. 마지막 두 옵션은 HTML 템플릿에서 PDF를 쉽게 생성할 수 있게 해줍니다.

HTML 템플릿에서 PDF를 만드는 예는 다음과 같습니다:

public static async Task HelloHtmlTemplate()
{
    static string GetUserName()
    {
        // 실제 로직으로 교체: 폼 입력, API 호출, 구성 등.
        return "World";
    }

    string html = $@"
        <h1>Hello, {GetUserName()}!</h1>
        <p>This PDF was generated from an HTML template.</p>";

    using var converter = await HtmlConverter.CreateAsync();
    using var pdf = await converter.CreatePdfFromStringAsync(html);
    pdf.Save("hello-html-template.pdf");
}

자세한 내용과 예제는 심층적인 HTML-to-PDF 개요를 참고하세요.

이미지에서 PDF 생성

Docotic.Pdf는 이미지를 PDF로 변환하는 유연하고 개발자 친화적인 방법을 제공합니다. 이 라이브러리는 Core API를 통해 JPEG, BMP, GIF, PNG, TIFF, JPEG 2000 이미지 형식을 지원합니다.

Docotic.Pdf가 여러 이미지 파일을 하나의 PDF 문서로 변환하는 방법을 보여주는 그림.

PDF 형식에서 지원하는 경우, Docotic.Pdf는 이미지 바이트를 있는 그대로 포함하여 픽셀 디코딩과 재인코딩을 피하고 원래 압축을 보존합니다. 또한 가능한 경우 색 공간도 유지합니다.

추가로 SVG 및 WebP 형식은 HTML to PDF API를 통해 지원됩니다. 이미지를 레이블과 함께 배치하거나 설명과 함께 배치해야 할 때는 Layout API가 최소한의 노력으로 요소를 정렬하고 배치하는 데 도움을 줍니다.

여러 이미지를 하나의 PDF로 결합하는 방법

Docotic.Pdf를 사용하면 이미지 집합을 하나의 PDF로 쉽게 변환하여 각 페이지에 이미지 하나씩 배치할 수 있습니다.

아래 예제는 파일에서 이미지를 불러와 각 이미지를 자체 페이지에 그립니다. 각 이미지는 페이지에 맞도록 크기가 조정되고 깔끔하고 일관된 레이아웃을 위해 가운데에 배치됩니다.

public static void ImagesOnToPdf(string[] imagePaths, string outputPath)
{
    using var pdf = new PdfDocument();

    foreach (string path in imagePaths)
    {
        var image = pdf.CreateImage(path);

        var page = pdf.AddPage();
        var pageWidth = page.Width;
        var pageHeight = page.Height;

        var scale = Math.Min(pageWidth / image.Width, pageHeight / image.Height);
        var drawWidth = image.Width * scale;
        var drawHeight = image.Height * scale;
        var x = (pageWidth - drawWidth) / 2;
        var y = (pageHeight - drawHeight) / 2;

        page.Canvas.DrawImage(image, x, y, drawWidth, drawHeight, 0);
    }

    pdf.RemovePage(0);

    pdf.Save(outputPath);
}

여러 페이지 TIFF 및 GIF 이미지 처리

Docotic.Pdf는 다중 페이지 TIFF 및 GIF 파일을 완전히 지원합니다. PDF에 이미지를 추가할 때, 이미지 중 하나라도 여러 페이지를 포함할 수 있다면 CreateImage 대신 OpenImage 메서드를 사용하세요.

다음 코드는 TIFF를 PDF로 변환하는 방법을 보여주며, 단일 페이지 이미지와 다중 페이지 이미지 모두에서 작동합니다:

public static void OddFramesToPdf(string[] imagePaths, string outputPath)
{
    using var pdf = new PdfDocument();
    foreach (string path in imagePaths)
    {
        var imageFrames = pdf.OpenImage(path);
        for (int i = 0; i < imageFrames.Count; i++)
        {
            if (i % 2 != 0)
                continue;

            var image = pdf.CreateImage(imageFrames[i]);
            var page = pdf.AddPage();

            page.Width = image.Width;
            page.Height = image.Height;

            page.Canvas.DrawImage(image, 0, 0, image.Width, image.Height, 0);
        }
    }

    pdf.RemovePage(0);

    pdf.Save(outputPath);
}

같은 접근 방식을 사용해 GIF를 PDF로 변환할 수도 있습니다. 다만 단일 프레임만 포함하는 형식에서는 굳이 필요하지 않을 만큼 더 복잡합니다.

PDF 병합 및 분할

기능이 완전한 .NET 라이브러리로서 Docotic.Pdf는 기존 문서의 페이지를 병합, 추출, 재구성하여 새 PDF를 만들 수 있습니다.

PDF를 병합할 때 라이브러리는 다른 문서의 페이지를 추가할 뿐 아니라 레이어, 북마크, 페이지 레이블, 공유 JavaScript, 목적지(링크 대상), 임베디드 파일도 함께 추가합니다. 자세한 내용과 결합된 PDF의 크기를 줄이는 방법은 PDF 병합 문서를 참고하세요.

디지털 서명된 PDF는 기존 서명을 무효화하지 않고는 병합할 수 없습니다. 서명을 보존하려면 문서를 추가하는 대신 PDF portfolio를 만드세요. 다른 방법으로는 먼저 PDF를 병합한 다음 결합된 문서에 새 디지털 서명 적용을 수행할 수 있습니다.

또한 Docotic.Pdf는 페이지를 새 문서로 복사하고 추출할 수 있습니다. 복사된 페이지와 연결된 모든 콘텐츠는 주석, 양식 컨트롤, 구조화 콘텐츠, 레이어 및 기타 관련 데이터를 포함하여 보존됩니다. 실제 예제는 .NET에서 PDF 분할 문서를 참고하세요. 이 문서는 페이지를 추출하거나 제거하는 방법도 설명합니다.

PDF 템플릿 작업하기

PDF 템플릿은 새 문서를 만들기 위한 기본 구조로 사용되는 사전 설계된 PDF 파일입니다. 서로 다른 데이터를 공급하면서도 일관된 레이아웃의 PDF를 만들어야 할 때 유용합니다. 시각적 디자인과 데이터 자체를 분리하고 싶을 때도 PDF 템플릿은 좋은 선택입니다.

템플릿은 양식 기반 PDF이거나 양식이 없는 정적 PDF일 수 있습니다. 두 유형은 같은 목적을 수행합니다. 또한 양식 기반 템플릿에는 플래튼하지 않으면 사용자로부터 정보를 수집할 수 있는 대화형 요소가 포함됩니다.

양식 기반 템플릿에서 PDF 생성하기

양식 기반 템플릿에는 일반적으로 AcroForms가 포함되어 있습니다. AcroForms는 표준이며 널리 지원되는 대화형 PDF 양식 유형입니다. 이러한 템플릿에서 PDF를 생성하려면 일반적으로 다음이 필요합니다:

  • 각 자리 표시자 필드 채우기
  • 추가 편집을 방지하기 위해 필드 플래튼하기
  • 결과를 새 PDF로 저장하기

다음은 이름으로 자리 표시자 텍스트 필드를 찾아 값을 할당하고, 필드를 플래튼한 다음 결과를 저장하여 템플릿에서 PDF를 만드는 C# 코드입니다:

var nameOnCertificate = "Eva Marin";
using var pdf = new PdfDocument("certificate-template.pdf");
if (pdf.TryGetControl("name", out var field))
{
    if (field is PdfTextBox nameField)
    {
        nameField.Text = nameOnCertificate;
        nameField.Flatten();
    }
}

pdf.Save($"certificate-{nameOnCertificate}.pdf");

템플릿에 자리 표시자가 많다면 각 필드를 개별적으로 채우는 대신 FDF 데이터를 가져올 수 있습니다. PdfDocument.FlattenControls를 사용하여 모든 필드를 한 번에 플래튼할 수도 있습니다.

양식 없는 정적 템플릿에서 PDF 생성하기

템플릿에 양식 필드가 없다면, 이름과 기타 데이터를 페이지 canvas에 직접 그리게 됩니다. 정적 PDF 템플릿에는 일반적으로 텍스트, 이미지 또는 빈 영역과 같은 고정된 시각적 자리 표시자가 포함됩니다. 템플릿에서 PDF를 만들려면 이러한 빈 영역을 채우고 자리 표시자 텍스트와 이미지를 프로그래밍 방식으로 교체해야 합니다.

빈 영역

Canvas API를 사용하여 빈 영역에 텍스트와 이미지를 배치하세요. 이름과 사진 추가처럼 작은 변경만 필요한 단순한 경우에 이 접근 방식은 잘 작동합니다. 영역의 좌표와 크기를 알아야 하며, 텍스트를 올바르게 배치하려면 먼저 측정한 다음 그에 맞게 정렬해야 할 수 있습니다.

가변 길이 또는 여러 줄 텍스트를 다루는 것은 더 어렵지만 여전히 가능합니다. DrawText, DrawString, 텍스트 측정용 메서드를 결합하면 필요에 따라 줄을 감싸고 배치할 수 있습니다. 템플릿에 이러한 영역이 몇 개보다 많다면 Layout API를 사용한 PDF 생성 같은 대안적 접근 방식을 고려하세요.

자리 표시자 텍스트

Docotic.Pdf는 텍스트를 찾아 바꾸는 메서드도 제공합니다. 그러나 텍스트 검색을 템플릿 메커니즘으로 사용하는 것은 일반적으로 빈 자리 표시자 영역을 다루는 것보다 더 쉽지 않습니다. 새 콘텐츠를 삽입하기 전에 정확한 텍스트 조각을 찾아 깔끔하게 제거해야 합니다.

자리 표시자 이미지

정적 템플릿에는 사용자 아바타 또는 제품 사진용 자리 표시자 이미지가 포함될 수 있습니다. 자리 표시자 이미지를 찾으려면 각 페이지에 그려진 이미지 컬렉션을 열거하세요. 그려진 각 이미지에 대해 표시 크기와 위치를 얻을 수 있습니다. 자리 표시자를 교체하려면 PdfImage.ReplaceWith를 사용하세요.

using var pdf = new PdfDocument("invoice-template.pdf");
var paintedImages = pdf.Pages[0].GetPaintedImages();

var placeholder = paintedImages.First();
placeholder.Image.ReplaceWith("company-logo.jpg");

pdf.Save($"invoice.pdf");

다른 방법으로는 자리 표시자 이미지가 차지하는 영역 위에 새 이미지를 그릴 수 있지만, 이 방식은 보통 특별한 이유 없이 결과 PDF의 크기를 증가시킵니다.

쉽게 교체할 수 있도록 자리 표시자 설계하기

정적 템플릿의 경우 텍스트와 이미지 모두에 대해 예측 가능하고 명확하게 정의된 영역으로 레이아웃을 설계하는 것이 도움이 됩니다. 가변 길이 콘텐츠를 담을 영역 주변에는 충분한 여백을 두고, 나중에 삽입할 것으로 예상되는 비율에 맞는 중립적인 자리 표시자 이미지를 사용하세요.

템플릿에서 교체할 자리 표시자 텍스트를 사용한다면 텍스트를 원시 텍스트 대신 텍스트 상자로 처리하여 작업 흐름을 단순화할 수 있습니다. 템플릿에 읽기 전용의 테두리 없는 텍스트 상자를 추가하고 그 안에 자리 표시자 텍스트를 배치하세요. 최종 PDF를 생성할 때는 템플릿을 열고 이름으로 텍스트 상자를 찾아 box.Text = "new text";로 새 값을 직접 할당하세요. 그런 다음 추가 편집을 방지하기 위해 텍스트 상자를 플래튼합니다.

대화형 요소 추가하기

대화형 기능은 정적 PDF를 주석과 마크업이 풍부한, 동적으로 탐색하기 쉬운 문서로 바꿉니다. Actions와 JavaScript는 PDF 내부에서 직접 자동화를 가능하게 합니다.

주석

주석은 페이지에 부착되는 개체로, 댓글, 강조 표시, 파일 첨부 및 기타 대화형 위젯을 나타냅니다. 페이지 콘텐츠에 표시되며 검토 워크플로와 협업을 지원합니다.

다음 C# 예제는 Docotic.Pdf를 사용하여 PDF 페이지에 스티키 노트라고도 하는 텍스트 주석을 추가하는 방법을 보여줍니다.

using var pdf = new PdfDocument("example.pdf");
var page = pdf.Pages[0];

var textAnnot = page.AddTextAnnotation(new PdfPoint(50, 100), "Reviewer comment");
textAnnot.Contents = "Please check the figures on this page.";

pdf.Save("text-annotation.pdf");

다음 예제는 문서의 핵심 부분에 주의를 끌기 위해 텍스트와 기타 콘텐츠를 강조하는 방법을 보여줍니다.

using var pdf = new PdfDocument("example.pdf");
var page = pdf.Pages[0];

var color = new PdfRgbColor(255, 255, 120);
var annotationText = "Please confirm this part.";
var bounds = new PdfRectangle(50, 250, 120, 40);
page.AddHighlightAnnotation(annotationText, bounds, color);

pdf.Save("highlight-annotation.pdf");

PDF 표준은 여러 유형의 PDF 링크를 정의합니다. 가장 중요하고 널리 사용되는 것은 내부 링크와 하이퍼링크입니다.

내부 링크는 GoTo 작업이라고도 하며, 같은 PDF 안의 페이지나 명명된 목적지로 이동할 수 있게 합니다. 교차 참조와 내부 탐색에 유용합니다.

다음은 첫 페이지에서 인덱스가 5인 페이지로 링크를 만드는 C# 코드입니다:

using var pdf = new PdfDocument();
var page = pdf.Pages[0];

int targetPageIndex = 5;
for (int i = 0; i < targetPageIndex; i++)
    pdf.AddPage();

var rect = new PdfRectangle(50, 50, 100, 40);
page.Canvas.DrawRectangle(rect);
page.AddLinkToPage(rect, targetPageIndex);

pdf.Pages[targetPageIndex].Canvas.DrawString(50, 50, "Glad to have you here.");

pdf.Save("link-to-page.pdf");

Layout API는 절대 위치 지정을 요구하지 않는 내부 링크를 만드는 또 다른 방법을 제공합니다.

외부 링크는 URI 작업이라고도 하며 웹 URL을 엽니다. PdfPage.AddHyperlink 메서드를 사용하여 PDF 페이지에 하이퍼링크를 추가할 수 있습니다. 그 외에는 접근 방식이 내부 링크와 동일합니다.

북마크

북마크는 개요라고도 하며, 독자가 특정 섹션이나 페이지로 빠르게 이동하도록 돕는 특수한 바로가기 또는 링크입니다. 독자가 북마크를 클릭하면 뷰어 애플리케이션이 문서의 지정된 부분으로 이동합니다.

개요는 뷰어의 북마크 패널에 표시되며, 책의 목차와 유사하지만 대화형인 계층 구조 탐색 트리를 제공합니다. PDF 개요에는 주요 북마크와 중첩 북마크가 포함될 수 있어 대형 문서를 구조화하기가 더 쉬워집니다.

다음 예제는 C#과 Docotic.Pdf를 사용하여 PDF에 북마크를 만드는 방법을 보여줍니다. 이 코드는 최상위 북마크 3개를 만듭니다. 두 번째 북마크에는 중첩 북마크 1개가 포함됩니다.

using var pdf = new PdfDocument();

for (int i = 0; i < 5; i++)
{
    var page = i == 0 ? pdf.Pages[0] : pdf.AddPage();

    var canvas = page.Canvas;
    canvas.FontSize = 14;
    canvas.DrawString(50, 50, $"Page {i + 1}");
}

var root = pdf.OutlineRoot;
root.AddChild("Getting Started", 1);

var child = root.AddChild("Things You Can Do", 2);
child.AddChild("Making Quick Improvements", 3);

root.AddChild("Keeping Everything Running Smoothly", 4);

pdf.PageMode = PdfPageMode.UseOutlines;

pdf.Save("bookmarks.pdf");

북마크는 실제 책의 페이지나 PDF에 표시된 목차와는 다릅니다. 제목을 측정하고 페이지 번호와 함께 항목을 작성하여 목차를 프로그래밍 방식으로 만들 수 있습니다.

Layout API를 사용해 목차를 만드는 대체 접근 방식을 보려면 샘플 저장소의 관련 코드를 참고하세요.

PDF 스크립팅

JavaScript 작업은 가장 강력한 대화형 기능 중 하나입니다. PDF JavaScript는 문서 및 뷰어 API를 노출하는 JavaScript의 하위 집합입니다. 양식 유효성 검사, 계산, 사용자 인터페이스 대화상자, 소규모 자동화 작업에 사용됩니다.

스크립트를 주석, 북마크, 양식 컨트롤 또는 열기 작업에 연결할 수 있습니다. Docotic.Pdf를 사용하면 PDF에 JavaScript 코드를 포함할 수 있습니다. 코드는 양식 입력을 검증하고, 값을 계산하고, 필드를 표시하거나 숨기고, 뷰어 상호작용을 수행할 수 있습니다.

공유 JavaScript 컬렉션에는 문서 수준에 저장된 스크립트가 들어 있습니다. 이러한 스크립트는 여러 작업에서 재사용할 수 있습니다. 즉, 공유 스크립트는 유틸리티 함수와 공유 로직에 유용합니다. 중복을 줄이고 유지 관리를 단순화하는 데 도움이 됩니다.

아래 코드는 PDF 뷰어에 경고 메시지를 표시하는 공유 스크립트를 정의한 다음, 해당 스크립트를 버튼의 클릭 작업에 할당하여 실행하는 방법을 보여줍니다.

using var pdf = new PdfDocument();

pdf.SharedScripts.Add(
    pdf.CreateJavaScriptAction("function messageBox(message) { app.alert(message,3); }")
);

var button = pdf.Pages[0].AddButton(50, 50, 100, 40);
button.Text = "Click me";
button.OnMouseUp = pdf.CreateJavaScriptAction("messageBox('Hello, dear!');");

pdf.Save("shared-javascript.pdf");

예제의 스크립트는 단순하지만, 복잡도에 관계없이 JavaScript 작업을 만들 수 있습니다. Adobe JavaScript API 참조에는 사용할 수 있는 많은 메서드가 있습니다. Adobe 이외의 뷰어는 일반적으로 API의 일부만 지원한다는 점을 기억하세요.

열기 작업

열기 작업은 PDF 뷰어가 문서를 열 때 실행하는 작업입니다. 일반적인 용도에는 특정 페이지에서 열기, JavaScript 초기화 루틴 실행, 뷰어 환경 설정 지정 등이 포함됩니다. 열기 작업의 유형에는 제한이 없습니다.

다음 예제는 GoTo 열기 작업을 만드는 방법을 보여줍니다. 이 코드는 두 번째 페이지에 텍스트를 추가하고, PDF가 열릴 때 뷰어가 자동으로 그 페이지로 이동하도록 하는 열기 작업을 설정합니다.

using var pdf = new PdfDocument();

var canvas = pdf.AddPage().Canvas;
canvas.FontSize = 14;

var message =
    "If you see this immediately after opening the file, " +
    "your PDF viewer supports open actions.";
var options = new PdfTextDrawingOptions(new PdfRectangle(100, 100, 100, 150));
canvas.DrawText(message, options);

pdf.OnOpenDocument = pdf.CreateGoToPageAction(1, 0);

pdf.Save("open-action.pdf");

모든 뷰어가 JavaScript 열기 작업을 실행하는 것은 아니라는 점에 유의하세요. 일부는 이를 무시하거나 먼저 사용자에게 묻습니다. 일부 뷰어는 열기 작업을 완전히 차단합니다.

PDF에 열기 작업이 포함되어 있는지 확인하려면 PdfDocument에 로드하고 OnOpenDocument 속성을 검사하세요. 속성이 null이면 문서에 정의된 열기 작업이 없습니다.

암호화 및 디지털 서명 적용하기

암호화와 디지털 서명은 생성하는 PDF를 보호하는 두 가지 상호 보완적인 측면을 다룹니다. 암호화는 누가 문서를 열 수 있고 문서로 무엇을 할 수 있는지 제어하고, 서명은 누가 파일을 만들거나 승인했는지 증명하며 파일이 변경되지 않았음을 확인합니다.

암호로 보호하면 생성 중에 접근 규칙을 설정할 수 있습니다. 열기 암호를 지정해 보기 권한을 제한하고, 소유자 암호를 지정해 인쇄, 복사, 편집, 양식 작성과 같은 권한을 정의할 수 있습니다. 인증서 암호화는 더 강력하고 수신자별 보호를 제공하며, 공유 암호에 의존하지 않고 여러 사람에게 기밀 PDF를 배포할 때 유용합니다. 자세한 내용은 암호와 인증서를 사용한 PDF 암호화 문서를 참고하세요.

디지털 서명은 생성 시점에 진위성과 무결성을 추가합니다. Docotic.Pdf는 파일, Windows store, 하드웨어 토큰, HSM, 클라우드 키 서비스의 인증서를 사용해 PDF에 서명할 수 있습니다. 타임스탬프와 장기 검증 데이터를 포함하여 문서 생성 후 오랜 시간이 지나도 서명을 검증할 수 있게 할 수 있습니다. PKCS#11 및 클라우드 KMS를 포함한 외부 서명 워크플로도 지원됩니다.

PDF 메타데이터 설정하기

PDF 메타데이터는 제목, 작성자, 주제, 키워드, 생성 날짜 및 유사한 필드처럼 문서에 포함된 설명 정보입니다. 소프트웨어, 검색 엔진, 문서 관리 시스템이 파일을 열지 않고도 그 내용을 이해하는 데 도움이 됩니다.

PDF 문서는 두 개의 공존하는 시스템에 메타데이터를 담을 수 있습니다:

  • XMP 메타데이터
  • 문서 정보 사전 (Info 사전)

Docotic.Pdf를 사용하여 PDF 문서에 XMP 메타데이터를 추가하는 과정을 보여주는 그림.

XMP는 설명 메타데이터를 포함하기 위한 더 풍부하고 구조화되어 있으며 표준화된 형식입니다. Info 사전은 단순하고 널리 지원되지만 제한적이며, PDF 2.0 표준(ISO 32000-2)에서는 XMP 메타데이터를 우선하도록 더 이상 권장되지 않습니다. Docotic.Pdf는 두 시스템을 읽고 쓸 수 있으며, 서로 동기화 상태를 유지하는 도우미 메서드를 제공합니다.

Docotic.Pdf는 PDF 파일을 저장하기 전에 일부 메타데이터를 자동으로 업데이트합니다. 예를 들어 라이브러리는 기본적으로 Producer 및 Creator 값을 설정합니다. 이 동작을 변경하고 명시적으로 설정한 메타데이터 값을 보존하려면 저장 옵션을 사용하세요.

XMP 메타데이터

PdfDocument.Metadata 속성을 사용하여 PDF의 XMP 메타데이터에 접근하고 수정하세요. 이 속성을 통해 XMP Core, Dublin Core, PDF 스키마와 같은 잘 알려진 스키마를 사용할 수 있으며, 자체 사용자 지정 메타데이터도 관리할 수 있습니다.

using var pdf = new PdfDocument();
var xmp = pdf.Metadata;

xmp.Pdf.Creator = new XmpString("Second-line authoring terminal");
xmp.Pdf.Title = new XmpString("Quarterly Report");

var creators = new XmpArray(XmpArrayType.Ordered);
creators.Values.Add(new XmpString("Second-line authoring terminal"));
creators.Values.Add(new XmpString("Assistive authoring terminal"));
xmp.DublinCore.Creators = creators;

var descriptions = new XmpArray(XmpArrayType.Alternative);
descriptions.Values.Add(new XmpLanguageAlternative("x-default", "Quarterly Report"));
descriptions.Values.Add(new XmpLanguageAlternative("fr", "Rapport trimestriel"));
descriptions.Values.Add(new XmpLanguageAlternative("de", "Quartalsbericht"));
xmp.DublinCore.Descriptions = descriptions;

var author1 = new XmpString("First Author");
author1.Qualifiers.Add("role", "main author");

var author2 = new XmpString("Second Author");
author2.Qualifiers.Add("role", "co-author");

var authors = new XmpArray(XmpArrayType.Unordered);
authors.Values.Add(author1);
authors.Values.Add(author2);
xmp.Custom.Properties.Add("authors", authors);

pdf.Save("with-xmp-metadata.pdf");

XMP는 배열, 구조체, 형식이 지정된 값을 지원하므로 풍부한 메타데이터에 적합합니다. 위 코드는 사용자 지정 XMP 스키마에 애플리케이션별 속성을 저장하는 방법도 보여줍니다.

문서 정보 사전

Info 사전은 주로 텍스트 문자열 값을 저장합니다. 간결하고 널리 지원되지만 제한적입니다. 이전 도구와의 호환성이 필요할 때는 Info 사전을 사용하고, 다른 경우에는 XMP를 선호하세요.

메타데이터 동기화

독자와 자동화 도구를 혼동시킬 수 있는 불일치를 피하려면 두 메타데이터 시스템을 동기화된 상태로 유지하는 것이 좋습니다.

PdfDocument.SyncMetadata를 사용하여 XMP와 Info 값을 정렬하고 대응하는 필드가 일치하도록 하세요. 이 메서드는 XMP에서 누락된 Info 속성을 채우고, 반대로 Info에서 누락된 XMP 필드도 채웁니다. XMP가 권위 있는 원본이면 preferXmp: true를, Info 사전이 우선해야 하면 false를 설정하세요.

pdf.SyncMetadata(preferXmp: true);

메서드가 어떤 속성을 동기화하는지에 대한 자세한 정보는 SyncMetadata 설명의 Remarks 섹션을 참고하세요.

페이지 레이블 및 뷰어 환경 설정 구성하기

새로 만든 PDF는 명시적인 페이지 번호, 세밀하게 조정된 뷰어 환경 설정, 그리고 문서 콘텐츠를 더 효과적으로 표시하기 위해 선택한 페이지 레이아웃의 이점을 얻을 수 있습니다. 이러한 설정은 독자가 파일을 처음 볼 때와 탐색할 때의 방식에 영향을 줍니다.

페이지 레이블

페이지 레이블은 PDF 뷰어가 각 페이지에 표시할 레이블을 알려주는 메타데이터입니다. 표시되는 번호가 실제 페이지 인덱스와 달라야 할 때 사용하세요. 예를 들어 PDF의 서두에는 i, ii, iii, 본문에는 1, 2, 3을 사용하고 싶을 때입니다.

다음 C# 코드는 처음 세 페이지에는 소문자 로마 숫자를, 나머지에는 1부터 시작하는 아라비아 숫자를 사용해 PDF 페이지에 레이블을 지정하는 방법을 보여줍니다.

using var pdf = new PdfDocument();

for (int i = 0; i < 8; i++)
    pdf.AddPage();

pdf.PageLabels.AddRange(0, 2, PdfPageNumberingStyle.LowercaseRoman);
pdf.PageLabels.AddRange(3, PdfPageNumberingStyle.DecimalArabic);

pdf.Save("with-page-labels.pdf");

PDF 뷰어 환경 설정

PDF 뷰어 환경 설정은 뷰어가 문서를 어떻게 표시해야 하는지에 대한 문서 내 권장 사항입니다. 예를 들어 도구 모음을 숨기고, 창을 가운데 정렬하고, 창을 페이지에 맞추도록 지정할 수 있습니다. 뷰어 환경 설정은 페이지 레이아웃과 열기 작업 설정을 보완합니다.

Docotic.Pdf를 사용해 PDF 보기 환경 설정을 변경하는 방법은 다음과 같습니다:

using var pdf = new PdfDocument();

pdf.ViewerPreferences.DisplayTitle = false;
pdf.ViewerPreferences.FitWindow = true;
pdf.ViewerPreferences.HideToolBar = true;
pdf.ViewerPreferences.HideMenuBar = true;
pdf.ViewerPreferences.HideWindowUI = true;
pdf.ViewerPreferences.CenterWindow = true;

pdf.Save("with-viewer-prefs.pdf");

구성에 따라 Adobe Acrobat 및 기타 뷰어는 이러한 환경 설정을 무시할 수 있다는 점에 유의하세요.

페이지 레이아웃 및 페이지 모드

페이지 레이아웃은 문서가 열릴 때 페이지를 어떻게 배치할지 결정합니다. 한 번에 한 페이지, 한 열 연속, 또는 두 페이지 펼침 방식으로 표시할 수 있습니다. 페이지 모드는 열릴 때 어떤 UI 패널이 보일지 제어합니다. 북마크/개요, 첨부 파일, 축소판 또는 없음이 될 수 있습니다.

다음은 생성된 PDF가 두 페이지 펼침, 왼쪽 페이지 우선으로 표시되고, 열릴 때 축소판 패널이 보이도록 지정하는 방법입니다:

using var pdf = new PdfDocument();

for (int i = 0; i < 7; i++)
{
    var page = i > 0 ? pdf.AddPage() : pdf.Pages[0];
    page.Canvas.FontSize = 36;
    page.Canvas.DrawString(100, 100, $"Page {i + 1}");
}

pdf.PageLayout = PdfPageLayout.TwoPageLeft;
pdf.PageMode = PdfPageMode.UseThumbs;

pdf.Save("with-layout-and-mode.pdf");

PDF 저장하기

Docotic.Pdf는 생성하거나 편집한 동일한 문서에서 서로 다른 PDF 파일이나 스트림을 만들 수 있습니다. 이러한 출력은 PDF 형식의 다른 버전에 맞출 수 있고, 바이트 길이가 달라질 수 있으며, 생성에 필요한 메모리 양도 다를 수 있습니다.

라이브러리가 PDF 바이트를 생성하는 방식은 저장 옵션에 따라 달라집니다. 저장 옵션을 명시적으로 지정하지 않으면 PdfDocument 객체의 Save, SignAndSave, TimestampAndSave 메서드는 기본 설정을 사용합니다. 이 기본값은 신중하게 선택되었고 대부분의 시나리오에서 잘 작동하지만, 경우에 따라 조정이 필요할 수 있습니다.

사용 가능한 옵션과 기본값에 대한 자세한 정보는 PdfSaveOptions 클래스 문서를 참고하세요. 아래 섹션에서는 더 중요한 몇 가지 옵션을 강조하고 실용적인 권장 사항을 제공합니다.

PDF 버전

Docotic.Pdf는 생성하는 파일의 압축률을 높이기 위해 기본적으로 오브젝트 스트림을 사용합니다. 그 결과 기본적으로 PDF 1.5 파일과 스트림을 생성합니다.

PDF 1.5는 생성된 문서를 보려면 Adobe Reader 6(2003년 출시) 이상이 필요합니다. 오래된 도구, 구형 뷰어, 또는 이전 PDF 버전만 허용하는 임베디드 장치를 지원해야 하는 경우가 아니라면 보통 문제되지 않습니다.

다음은 더 오래된 PDF 파일 버전으로 저장하는 방법입니다:

using var pdf = new PdfDocument();

var options = new PdfSaveOptions
{
    Version = PdfVersion.Pdf14,
    UseObjectStreams = false,
};
pdf.Save("version-1.4.pdf", options);

PDF 1.4 버전으로 저장하려면 오브젝트 스트림도 비활성화해야 합니다. 문서에 이후 버전에서 도입된 기능이 포함되어 있으면 라이브러리는 더 오래된 버전을 사용하지 않습니다.

파일 크기 줄이기

여러 저장 옵션은 true로 설정하면 Docotic.Pdf가 더 작은 파일(바이트 기준)을 생성하게 합니다: RemoveUnusedObjects, OptimizeIndirectObjects, WriteWithoutFormatting, UseObjectStreams.

다음은 참조되지 않은 개체와 추가 공백 없이, 데이터를 오브젝트 스트림에 촘촘하게 압축해 PDF를 만드는 방법입니다:

using var pdf = new PdfDocument();

var options = new PdfSaveOptions
{
    UseObjectStreams = true,
    RemoveUnusedObjects = true,
    OptimizeIndirectObjects = true,
    WriteWithoutFormatting = true,
};
pdf.Save("optimized.pdf", options);

이 옵션들은 PDF가 완전히 다시 작성될 때 가장 효과적입니다. 증분 저장 중에는 새로 추가된 리비전에는만 적용되며, 파일의 이전 부분을 정리하거나 최적화할 수는 없습니다.

증분 업데이트

Docotic.Pdf는 PDF를 증분 방식으로 업데이트할 수 있습니다. WriteIncrementallytrue이면 라이브러리는 기존 파일을 다시 쓰지 않고 변경 사항을 덧붙입니다. 이전 교차 참조와 개체 데이터는 그대로 유지됩니다. 덧붙여진 데이터는 증분 업데이트라고 하며, 현재 업데이트와 모든 이전 업데이트가 함께 파일의 새 리비전을 구성합니다.

새로 만든 문서는 덧붙일 이전 리비전이 없으므로 증분 업데이트가 불가능합니다. 라이브러리는 새 문서에서는 이 옵션을 무시하고 비증분 모드로 저장합니다.

증분 업데이트가 필요한 경우

이미 서명이 포함된 문서에 새 디지털 서명 추가를 할 때는 파일을 증분 방식으로 저장해야 합니다. 이전에 서명된 파일을 새 주석이나 양식 데이터로 업데이트하는 경우도 마찬가지입니다. 이런 경우 전체 파일을 다시 쓰면 기존 서명이 무효화됩니다.

동시에, 서명된 기준 파일이 깔끔하고 완전히 다시 작성된 파일이 되도록 첫 번째 디지털 서명을 적용하기 전에 비증분(전체) 저장을 수행하는 것이 가장 좋습니다. 이전 리비전에 구조적 문제가 있는 문서에 서명하면 예상치 못한 서명 검증 문제가 발생할 수 있습니다.

감사 가능한 리비전 기록을 보존해야 하거나 덧붙이기만 가능한 문서 저장을 강제해야 하는 워크플로에서도 증분 추가가 필요합니다.

증분 업데이트의 장점

증분 업데이트를 사용하면 동일한 파일에 여러 서명을 적용할 수 있고, 기존 서명을 무효화하지 않으면서 양식 필드 채우기 같은 제한된 범위의 서명 후 수정도 허용할 수 있습니다.

또한 수정된 데이터만 기록되므로 작은 변경의 저장 속도가 더 빠릅니다. 아울러 문서의 리비전 기록을 보존하므로 감사 및 기타 규정 준수 중심 워크플로에 필수적입니다.

피해야 할 문제와 함정

증분 업데이트는 수정된 개체만 덧붙이므로 전체 파일에 전역 압축을 적용하거나 오래된 개체를 제거할 수 없습니다. 그 결과 일반적으로 전체 다시쓰기보다 크고 최적화가 덜 된 파일이 생성됩니다.

사용되지 않는 개체가 없더라도 파일 크기는 각 리비전마다 커집니다. 이전 리비전이 모두 파일 내부에 포함되어 공간을 계속 차지하기 때문입니다.

이전 리비전의 민감하거나 잘못된 정보는 여전히 복구 가능하며, 기존 PDF 형식 문제나 이전 리비전의 구조적 결함도 새 데이터를 덧붙인다고 해서 수정되지는 않습니다.

마지막으로 일부 뷰어와 처리 도구는 다중 리비전 PDF를 제대로 다루지 못합니다. 증분 업데이트에 의존하기 전에 문서를 소비하는 모든 도구가 여러 리비전이 있는 파일을 처리할 수 있는지 확인하세요.

PDF 출력 테스트

자동화된 PDF 테스트는 생성된 PDF를 저장소나 아티팩트 저장소에 보관된 기준 PDF와 비교하여 콘텐츠와 레이아웃의 회귀를 방지합니다. 기준 파일은 텍스트, 글꼴, 이미지 또는 레이아웃의 우발적 변경을 감지하는 데 도움이 되며, 매 빌드마다 수동 QA의 필요성을 줄여줍니다.

가장 신뢰할 수 있는 결과를 위해 구조 검사, 텍스트 추출, 시각적 비교를 함께 사용하세요.

접근 방식의 빠른 비교

방법 속도 민감도 가장 적합한 경우
구조 비교 빠름 높음: 개체 수준 변경 감지 같은 문서의 두 버전이 구조적으로 동일한지 확인해야 하는 회귀 테스트
텍스트 추출 빠름 중간: 일반적으로 레이아웃 변경은 무시 의미적 콘텐츠와 표 검증
시각적 비교 느림 높음: 콘텐츠와 렌더링/레이아웃 변경 모두 감지 시각적 회귀 탐지

문서 구조 비교

PdfDocument.DocumentsAreEqual를 사용하여 시간에 의존하는 문서 속성을 무시하면서 PDF 객체 그래프, PDF 버전, 문서 보안 저장소(DSS)를 비교하세요. 이 메서드는 문서 메타데이터, trailer ID 및 기타 자동 생성 속성도 무시합니다.

이 메서드는 예상치 못한 개체 추가 또는 제거가 없어야 하는 PDF 문서 테스트 워크플로에 이상적입니다. DocumentsAreEqual은 파일 및 스트림 오버로드를 지원하며 암호화된 PDF도 비교할 수 있습니다.

이 기법을 보여주는 완전한 예제는 Docotic.Pdf 샘플에 있습니다. 일반 .NET 애플리케이션에서 메서드를 사용하는 방법을 보여줄 뿐 아니라, 샘플은 Native AOT에서 DocumentsAreEqual 사용하기도 시연합니다.

추출한 텍스트로 PDF 검증

문서 전체를 한 번에 또는 개별 페이지를 순서대로 텍스트 추출한 다음 문자열을 비교하세요. 바닥글이 있는 사각형 제외처럼 추출 과정을 세밀하게 조정하는 텍스트 추출 옵션을 사용할 수 있습니다. 비교를 쉽게 하려면 추출한 텍스트를 줄이나 단어로 나눌 수 있습니다.

구조화 검사를 위해 먼저 각 조각, 단어 또는 문자에 대한 위치, 글꼴 및 기타 상세 정보를 포함해 텍스트를 추출하세요. 그런 다음 각 추출 요소를 해당 기준 요소와 비교합니다.

시각적 차이 감지

먼저 PDF 페이지를 이미지로 렌더링하고 각 이미지를 기준 이미지와 비교하세요. 이미지의 차이를 감지하려면 ImageSharp.Compare 또는 Magick.NET 같은 특수 라이브러리를 사용하세요.

엄격한 픽셀 단위 비교를 선호하여 두 이미지의 모든 대응 픽셀이 일치해야 하도록 하세요. 요구 사항상 약간의 렌더링 차이를 허용할 수 있다면 비교 로직을 조정하여 미세한 차이를 허용할 수 있지만, 정확한 픽셀 동일성은 가장 신뢰할 수 있는 결과를 제공합니다.

전체 픽셀 비교를 수행하지 않고 두 이미지가 동일할 가능성이 높은지 빠르게 판단하기 위해 해싱을 사전 검사로 사용하는 것을 고려하세요. 렌더링된 각 이미지에 대해 SHA-256 해시를 계산하고 해시가 일치하면 두 이미지는 거의 확실히 동일합니다. 해시가 다르면 전체 픽셀 단위 비교를 실행하세요.

결론

Docotic.Pdf는 .NET에서 PDF를 만들고 조작하기 위한 포괄적인 다층 도구 모음을 제공합니다. 개발자는 Core API를 통한 저수준 제어, Layout API를 통한 고수준 문서 생성, 또는 이미 웹 기술을 기반으로 구축된 워크플로를 위한 HTML-to-PDF 변환 중에서 선택할 수 있습니다.

이 라이브러리는 이미지 기반 PDF, 템플릿 기반 생성, 그리고 주석, 링크, 북마크, JavaScript 작업, 열기 작업 같은 풍부한 대화형 기능도 지원합니다.

신뢰성을 보장하기 위해 Docotic.Pdf는 PDF 출력 테스트용 메서드도 포함하므로 애플리케이션의 변경이 회귀나 예상치 못한 차이를 유발하지 않도록 할 수 있습니다.