비교

2026년 문서 생성에 적합한 C# 라이브러리 선택하기

C# PDF 라이브러리를 선택하면 프로젝트의 라이선스 노출, 배포 유연성, 장기 유지 보수 비용에 영향을 미칩니다. 평가 중에 적합해 보이는 대부분의 라이브러리는 실제 사용에 제약이 있음을 드러냅니다 — 예상하지 못한 AGPL 요건, 브라우저와 일치하지 않는 HTML 렌더링, 리눅스에서만 표면화하는 메모리 누수.

이 문서는 주요 옵션을 코드 예제와 함께 비교하고, 실제로 중요하게 작용하는 절충점을 문서화하며, 서로 다른 세 라이브러리에서 동일한 인보이스를 생성하는 코드 비교를 포함합니다. 이를 통해 API 차이를 직접 확인할 수 있습니다.

빠른 시작: 3줄로 HTML을 PDF로 변환

NuGet을 통해 설치하세요:

Install-Package IronPDF

PDF 생성:

using IronPdf;

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");
$vbLabelText   $csharpLabel

이것은 추가 설정 없이 Windows, Linux, macOS, 및 Docker에서 작동합니다. 출력은 IronPDF가 같은 Chromium 렌더링 엔진을 내장했기 때문에 Chrome과 일치합니다.

평가 기준

라이브러리를 비교하기 전에 무엇을 평가할지 알아야 합니다. 이것들은 생산 문제를 미리 밝혀내는 질문들입니다:

기준무엇을 테스트해야 할까요왜 이것이 중요할까요
HTML/CSS 렌더링Flexbox/Grid와 함께 실제 템플릿을 입력하세요대부분의 라이브러리는 HTML 지원을 주장하지만, CSS 2.1만 최대로 렌더합니다
JavaScript실행Chart.js 또는 동적 표 내용을 테스트하세요JS 지원이 없는 라이브러리는 빈 섹션을 생성합니다
라이센싱 모델요약이 아닌 전체 라이선스를 읽으세요AGPL은 전체 애플리케이션의 오픈 소스를 요구합니다
플랫폼 지원목표 Linux/Docker/ARM64 환경에 배포하세요Windows의 성공이 Linux 동작을 예측하지는 않습니다
로드 상태에서의 메모리루프에서 100개 이상의 문서를 생성하세요단일 문서 테스트는 생산 서버를 중단시키는 누수를 숨깁니다
게시된 가격가격이 웹사이트에 있는지 확인하세요"연락처 영업"은 일반적으로 연간 $15K–$210K를 의미합니다

라이브러리 비교

IronPDF— 내장된 Chromium, 완전한 CSS/JS 지원

IronPDF는 Chromium을 NuGet 패키지에 직접 포함합니다. HTML 렌더링은 Chrome의 엔진이기 때문에 Chrome과 일치합니다. CSS Flexbox, Grid, 사용자 정의 속성,JavaScript모두 예상대로 실행됩니다.

using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;

var pdf = renderer.RenderHtmlAsPdf(@"
    <html>
    <head>
        <style>
            .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
            .card { background: linear-gradient(135deg, #f8fafc, #e2e8f0);
                    border-radius: 8px; padding: 20px; text-align: center; }
        </style>
    </head>
    <body>
        <div class='grid'>
            <div class='card'><h3>Revenue</h3><p>$1.2M</p></div>
            <div class='card'><h3>Users</h3><p>45,230</p></div>
            <div class='card'><h3>Uptime</h3><p>99.97%</p></div>
        </div>
    </body>
    </html>");
pdf.SaveAs("dashboard.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;

var pdf = renderer.RenderHtmlAsPdf(@"
    <html>
    <head>
        <style>
            .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
            .card { background: linear-gradient(135deg, #f8fafc, #e2e8f0);
                    border-radius: 8px; padding: 20px; text-align: center; }
        </style>
    </head>
    <body>
        <div class='grid'>
            <div class='card'><h3>Revenue</h3><p>$1.2M</p></div>
            <div class='card'><h3>Users</h3><p>45,230</p></div>
            <div class='card'><h3>Uptime</h3><p>99.97%</p></div>
        </div>
    </body>
    </html>");
pdf.SaveAs("dashboard.pdf");
$vbLabelText   $csharpLabel

고려할 트레이드오프: 내장된 Chromium은 배포 패키지에 약 200MB를 추가합니다. 표준 서버 및 컨테이너 배포의 경우, 이는 실행 시간의 영향을 받지 않는 일회성 다운로드입니다. Azure Functions 소비 계획과 같은 크기 제한 환경의 경우 배포 크기 제한을 확인하십시오. 콜드 프로세스에서 첫 번째 PDF 생성을 위해 Chromium 초기화에 2–5초가 소요됩니다. 이후 생성은 100–500ms 안에 실행됩니다. 메모리 기본값은 약 150–200MB입니다 — 컨테이너 자원을 적절히 계획하십시오.

라이센스: $749(개발자 1명)부터 영구 라이센스 시작. ironpdf.com에서 게시된 가격. 문서당 요금 없음, AGPL 없음, 수익 임계값 없음.

iText 7(iTextSharp) — AGPL 라이센스, 제한된 HTML

iText는 오랜 역사를 가진 유능한 PDF 조작 라이브러리입니다. pdfHTML 추가 기능은 HTML을 PDF로 변환하지만 브라우저 엔진을 사용하지 않으며 사용자 정의 파서를 사용하여 CSS 2.1을 근사합니다.

중간 규모의 SaaS 회사의 프로덕션 팀은 Razor 뷰에서 송장 템플릿을 마이그레이션할 때 이를 발견했습니다. 템플릿은 반응형 열 레이아웃에 CSS Flexbox를 사용했습니다. iText의 pdfHTML 통합 후, 모든 송장이 단일 열 세로 스택으로 렌더링되었습니다. display: flex, gap, 및 justify-content 속성은 조용히 무시되었습니다. 팀이 그들의 기존 CSS를 렌더할 수 없다는 것을 깨닫기까지 3주간의 개발 시간이 소요되었습니다.

AGPL 현실: iText는 AGPL 라이센스를 사용합니다. 네트워크로 접근 가능한 웹 앱, API 및 SaaS 제품을 포함한 애플리케이션은 AGPL에 따라 전체 애플리케이션 소스 코드를 공개해야 합니다. 단순히 PDF 모듈만이 아닙니다. 모든 것. iText와 모회사 Apryse는 이를 적극적으로 시행합니다.

상업적 라이센스: iText는 2024년 구독 기반 라이센스로 전환되었습니다. 가격은 게시되지 않았으며 견적을 위해 영업에 연락해야 합니다. 타사 데이터에 따르면 연간 사용량에 따라 $15,000–$210,000로 예상됩니다.

PdfSharp— MIT 라이센스, HTML 없음

PdfSharp는 MIT 라이선스 하에 진정으로 무료이며 3,490만 NuGet 다운로드 수를 기록했습니다. 이를 대신하여 기능이 있습니다: 이는 HTML 파서, CSS 엔진 및 템플릿 시스템 없이 좌표 기반 그리기 API를 제공합니다.

보고 대시보드를 구축하는 팀은 무료이면서 잘 알려진 PdfSharp를 선택했습니다. 그들은 4개월 동안 좌표 기반 레이아웃 코드를 작성하며 모든 텍스트 요소의 X/Y 위치를 계산하고 테이블 테두리를 픽셀 단위로 그리며 페이지 넘김을 수동으로 처리했습니다. 그들이 최종적으로 수출물을 브라우저에서 동일한 HTML 템플릿이 생산한 것과 비교해 보았을 때, 그들은 그 동안 자신들이 Chromium 기반 라이브러리가 자동으로 처리하는 것을 더 나쁜 버전으로 구축했다는 것을 깨달았습니다.

PdfSharp는 PDF 병합, 워터마크 추가 및 데이터에서 간단한 구조화 문서 작성에 잘 작동합니다. HTML 렌더링이 필요하지 않다면, 그것은 여전히 합법적인 옵션입니다.

QuestPDF— 우아한 API, HTML 없음, 수익 임계값

QuestPDF는 문서를 프로그래밍 방식으로 구축하기 위한 플루언트 C# API를 제공합니다. 이 API 디자인은 실제로 좋으며 .NET 라이브러리 API 중 어느 분야에서도 뛰어납니다.

두 가지 제약 조건이 중요합니다: QuestPDF는 HTML을 렌더하지 않으며(디자인으로 — 이는 누락된 기능이 아닌 고의적인 아키텍처 선택임), 커뮤니티 라이센스는 연간 총 수익이 $1M 미만인 비즈니스에 유효합니다. 회사가 그 임계값을 초과하면 상업적 라이센스가 필수가 됩니다. 그 임계값에 가까워지는 회사는 이를 급하게 만들기 전에 전환을 대비해야 합니다.

HTML에 대한 QuestPDF의 명확한 포지셔닝에도 불구하고, 개발자들은 이 라이브러리가 "C# PDF 라이브러리" 검색 결과에서 HTML 지원 라이브러리와 함께 나타나기 때문에 구현을 시작한 후 자주 이를 발견합니다.

wkhtmltopdf래퍼 — 사용 중단, 패치되지 않은 CVE

wkhtmltopdf의 시간은 지났습니다. GitHub 조직은 2024년 7월에 보관되었습니다. 기초가 되는 QtWebKit 엔진은 2015년에 Qt에 의해 더 이상 사용되지 않음으로 지정되었습니다. AWS 자격 증명 탈취를 가능하게 하는 SSRF 포함 CVE-2022-35583(CVSS 9.8)를 비롯한 알려진 CVE들은 결코 패치되지 않을 것입니다.

C# 래퍼인 DinkToPdf, NReco.PdfGenerator 및 WkHtmlToXSharp 모두 동일한 더 이상 사용되지 않는 바이너리를 랩핑합니다. 렌더링 엔진은 대략적으로 Safari 2011의 기능으로 고정되어 있습니다: Flexbox 없음, Grid 없음, 제한된 JavaScript. 이것은 새 프로젝트에 적합한 옵션이 아닙니다.

Puppeteer Sharp— 전체 렌더링, 운영 복잡도

Puppeteer Sharp는 .NET 바인딩을 통해 헤드리스 Chrome을 제어합니다. 렌더링 품질은 Chrome과 같으므로 Chrome입니다. 교환 조건은 운영입니다: 외부 브라우저 프로세스를 관리해야 하며, 다운로드, 풀링, 메모리 모니터링 및 크래시 복구가 포함됩니다.

using PuppeteerSharp;

// Downloads ~280MB Chromium on first run
await new BrowserFetcher().DownloadAsync();

await using var browser = await Puppeteer.LaunchAsync(
    new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync(new PdfOptions { Format = PaperFormat.A4, PrintBackground = true });
using PuppeteerSharp;

// Downloads ~280MB Chromium on first run
await new BrowserFetcher().DownloadAsync();

await using var browser = await Puppeteer.LaunchAsync(
    new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync(new PdfOptions { Format = PaperFormat.A4, PrintBackground = true });
$vbLabelText   $csharpLabel

생산 환경에서는 브라우저 프로세스 풀링, 메모리 누수 모니터링 (Chromium 프로세스는 누수될 수 있음), 크래시 복구 및 리소스 정리도 필요합니다. Docker 배포는 Chromium 종속성을 설치해야 합니다 — 표준 .NET 이미지에 비해 상당한 Dockerfile입니다. 운영 오버헤드를 소화할 수 있는 팀이라면PuppeteerSharp는 적합합니다.

Aspose.PDF — 광범위한 기능, Linux 메모리 문제

Aspose.PDF는 광범위한 PDF 기능을 제공하고 좋은 문서를 제공합니다. 중요한 문제는 Linux 안정성입니다: Aspose는 System.Drawing.Common에 의존하며, 이는 Linux에서 libgdiplus를 필요로 합니다 — 관리되지 않는 라이브러리로 문서에 따르면 메모리 누수가 발생합니다. 개발자 보고서는 수년간:

"몇 십여 개의 요청이 유닉스 환경에서 서비스의 메모리가 부족한 상태에 이르게 하지만, 이는 Windows 기반 환경에서 발생하지 않습니다." — Aspose 포럼, 2022년 3월

Windows 전용 배포에서는 Aspose가 계속 유능합니다. 크로스 플랫폼 또는 컨테이너화된 배포에서는 System.Drawing.Common 종속성이 지속적인 위험을 생성합니다. 상업용 라이선스는 개발자당 약 $999에서 시작합니다.

기능 비교

기능IronPDFiText 7PdfSharpQuestPDFwkhtmltopdfPuppeteerAspose
HTML to PDF전체 (Chromium)제한적 (CSS 2.1)아니요아니요더 이상 사용되지 않음전체 (Chrome)제한적
CSS Flexbox/Grid아니요아니요아니요아니요아니요
JavaScript아니요아니요아니요제한적아니요
Linux (libgdiplus 없음)부분적*해당 없음아니요
Docker 배포표준 .NET 이미지기준부분적*기준복잡함복잡함libgdiplus가 필요함
활발한 유지보수버려짐
게시된 가격예 ($749+)아니오 ($15K–$210K/yr)무료 (MIT)예 (무료 <$1M)무료무료 (MIT)예 ($999+)
영구 라이선스아니오 (구독)해당 없음해당 없음해당 없음해당 없음
AGPL-무료아니오 (상업용 필요)

*PdfSharp는 몇몇 구성에서 특정 플랫폼 관련 문제를 문서화했습니다.

성능 비교

중급 클라우드 VM (4 vCPU, 8GB RAM)에서 200개 요소의 HTML 송장 템플릿으로 테스트했으며, 워밍업 후 50회 반복 평균:

시나리오IronPDFPuppeteer SharpiText pdfHTMLwkhtmltopdf
간단한 HTML 페이지~150ms~500ms~200ms~200ms
복잡한 CSS 레이아웃 (Flexbox/Grid)~250ms~600ms실패/부분~400ms (손상됨)
JavaScript가 많은 페이지~350ms~800ms실패실패/부분
작업당 메모리~80MB~150MB~60MB~50MB
차가운 시작 (첫 번째 생성)2–5초3–8초<1s<1s

iText와 wkhtmltopdf는 브라우저 엔진을 초기화하지 않기 때문에 더 빠른 차가운 시작을 보여주지만 동일한 내용을 렌더링할 수 없습니다. 성능 비교는 모든 라이브러리가 올바른 출력을 생성하는 시나리오에서만 의미가 있습니다.

코드 비교: 동일한 인보이스, 세 가지 라이브러리

이러한 라이브러리 간의 차이점은 동일한 문서를 생성할 때 가장 잘 드러납니다. 여기 세 가지 방법으로 생성된 인보이스가 있습니다.

IronPDF— HTML/CSS 접근 방식

using IronPdf;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var renderer = new ChromePdfRenderer();

        string html = $@"
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                body {{ font-family: 'Segoe UI', sans-serif; margin: 40px; }}
                h1 {{ color: #2c3e50; }}
                table {{ width: 100%; border-collapse: collapse; }}
                th {{ background: #3498db; color: white; padding: 12px; text-align: left; }}
                td {{ border-bottom: 1px solid #e0e0e0; padding: 10px; }}
                .total {{ font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }}
            </style>
        </head>
        <body>
            <h1>Invoice #{data.InvoiceNumber}</h1>
            <table>
                <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
                {string.Join("", data.Items.Select(i =>
                    $"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.Price:F2}</td></tr>"))}
            </table>
            <p class='total'>Total: ${data.Total:F2}</p>
        </body>
        </html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        return pdf.BinaryData;
    }
}
using IronPdf;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var renderer = new ChromePdfRenderer();

        string html = $@"
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                body {{ font-family: 'Segoe UI', sans-serif; margin: 40px; }}
                h1 {{ color: #2c3e50; }}
                table {{ width: 100%; border-collapse: collapse; }}
                th {{ background: #3498db; color: white; padding: 12px; text-align: left; }}
                td {{ border-bottom: 1px solid #e0e0e0; padding: 10px; }}
                .total {{ font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }}
            </style>
        </head>
        <body>
            <h1>Invoice #{data.InvoiceNumber}</h1>
            <table>
                <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
                {string.Join("", data.Items.Select(i =>
                    $"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.Price:F2}</td></tr>"))}
            </table>
            <p class='total'>Total: ${data.Total:F2}</p>
        </body>
        </html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        return pdf.BinaryData;
    }
}
$vbLabelText   $csharpLabel

QuestPDF— 유창한 API 접근 방식

using QuestPDF.Fluent;
using QuestPDF.Infrastructure;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var document = Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.Margin(40);
                page.DefaultTextStyle(x => x.FontFamily("Segoe UI"));

                page.Header()
                    .Text($"Invoice #{data.InvoiceNumber}")
                    .FontSize(24).FontColor(Colors.Blue.Darken2);

                page.Content().Column(column =>
                {
                    column.Item().Table(table =>
                    {
                        table.ColumnsDefinition(cols =>
                        {
                            cols.RelativeColumn(3);
                            cols.RelativeColumn(1);
                            cols.RelativeColumn(1);
                        });

                        table.Header(header =>
                        {
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Item").FontColor(Colors.White);
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Qty").FontColor(Colors.White);
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Price").FontColor(Colors.White);
                        });

                        foreach (var item in data.Items)
                        {
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text(item.Name);
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text(item.Quantity.ToString());
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text($"${item.Price:F2}");
                        }
                    });

                    column.Item().AlignRight().PaddingTop(20)
                        .Text($"Total: ${data.Total:F2}").FontSize(16).Bold();
                });
            });
        });

        using var stream = new MemoryStream();
        document.GeneratePdf(stream);
        return stream.ToArray();
    }
}
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var document = Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.Margin(40);
                page.DefaultTextStyle(x => x.FontFamily("Segoe UI"));

                page.Header()
                    .Text($"Invoice #{data.InvoiceNumber}")
                    .FontSize(24).FontColor(Colors.Blue.Darken2);

                page.Content().Column(column =>
                {
                    column.Item().Table(table =>
                    {
                        table.ColumnsDefinition(cols =>
                        {
                            cols.RelativeColumn(3);
                            cols.RelativeColumn(1);
                            cols.RelativeColumn(1);
                        });

                        table.Header(header =>
                        {
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Item").FontColor(Colors.White);
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Qty").FontColor(Colors.White);
                            header.Cell().Background(Colors.Blue.Medium).Padding(8)
                                .Text("Price").FontColor(Colors.White);
                        });

                        foreach (var item in data.Items)
                        {
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text(item.Name);
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text(item.Quantity.ToString());
                            table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
                                .Padding(8).Text($"${item.Price:F2}");
                        }
                    });

                    column.Item().AlignRight().PaddingTop(20)
                        .Text($"Total: ${data.Total:F2}").FontSize(16).Bold();
                });
            });
        });

        using var stream = new MemoryStream();
        document.GeneratePdf(stream);
        return stream.ToArray();
    }
}
$vbLabelText   $csharpLabel

PdfSharp— 좌표 그리기 접근 방식

using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var document = new PdfDocument();
        var page = document.AddPage();
        var gfx = XGraphics.FromPdfPage(page);

        var titleFont = new XFont("Arial", 24);
        var headerFont = new XFont("Arial", 12, XFontStyleEx.Bold);
        var bodyFont = new XFont("Arial", 12);

        double y = 40;

        gfx.DrawString($"Invoice #{data.InvoiceNumber}", titleFont,
            XBrushes.DarkBlue, 40, y);
        y += 50;

        // Table header — manually positioned
        double[] colX = { 40, 300, 400 };
        double rowHeight = 30;

        gfx.DrawRectangle(XBrushes.SteelBlue, 40, y, 500, rowHeight);
        gfx.DrawString("Item", headerFont, XBrushes.White, colX[0] + 10, y + 20);
        gfx.DrawString("Qty", headerFont, XBrushes.White, colX[1] + 10, y + 20);
        gfx.DrawString("Price", headerFont, XBrushes.White, colX[2] + 10, y + 20);
        y += rowHeight;

        // Each row drawn individually with explicit coordinates
        foreach (var item in data.Items)
        {
            gfx.DrawRectangle(XPens.LightGray, 40, y, 500, rowHeight);
            gfx.DrawString(item.Name, bodyFont, XBrushes.Black, colX[0] + 10, y + 20);
            gfx.DrawString(item.Quantity.ToString(), bodyFont, XBrushes.Black, colX[1] + 10, y + 20);
            gfx.DrawString($"${item.Price:F2}", bodyFont, XBrushes.Black, colX[2] + 10, y + 20);
            y += rowHeight;
        }

        y += 20;
        gfx.DrawString($"Total: ${data.Total:F2}", headerFont, XBrushes.Black, 440, y);

        using var stream = new MemoryStream();
        document.Save(stream);
        return stream.ToArray();
    }
}
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;

public class InvoiceGenerator
{
    public byte[] GenerateInvoice(InvoiceData data)
    {
        var document = new PdfDocument();
        var page = document.AddPage();
        var gfx = XGraphics.FromPdfPage(page);

        var titleFont = new XFont("Arial", 24);
        var headerFont = new XFont("Arial", 12, XFontStyleEx.Bold);
        var bodyFont = new XFont("Arial", 12);

        double y = 40;

        gfx.DrawString($"Invoice #{data.InvoiceNumber}", titleFont,
            XBrushes.DarkBlue, 40, y);
        y += 50;

        // Table header — manually positioned
        double[] colX = { 40, 300, 400 };
        double rowHeight = 30;

        gfx.DrawRectangle(XBrushes.SteelBlue, 40, y, 500, rowHeight);
        gfx.DrawString("Item", headerFont, XBrushes.White, colX[0] + 10, y + 20);
        gfx.DrawString("Qty", headerFont, XBrushes.White, colX[1] + 10, y + 20);
        gfx.DrawString("Price", headerFont, XBrushes.White, colX[2] + 10, y + 20);
        y += rowHeight;

        // Each row drawn individually with explicit coordinates
        foreach (var item in data.Items)
        {
            gfx.DrawRectangle(XPens.LightGray, 40, y, 500, rowHeight);
            gfx.DrawString(item.Name, bodyFont, XBrushes.Black, colX[0] + 10, y + 20);
            gfx.DrawString(item.Quantity.ToString(), bodyFont, XBrushes.Black, colX[1] + 10, y + 20);
            gfx.DrawString($"${item.Price:F2}", bodyFont, XBrushes.Black, colX[2] + 10, y + 20);
            y += rowHeight;
        }

        y += 20;
        gfx.DrawString($"Total: ${data.Total:F2}", headerFont, XBrushes.Black, 440, y);

        using var stream = new MemoryStream();
        document.Save(stream);
        return stream.ToArray();
    }
}
$vbLabelText   $csharpLabel

IronPDF 버전은 대부분의 개발자가 이미 가지고 있는 HTML/CSS 기술을 사용합니다.QuestPDF버전은 도메인 별 유창한 API를 배우는 것이 필요하지만 구조를 제공합니다. PdfSharp 버전은 모든 픽셀 위치를 수동으로 계산해야 하며, 모든 열 오프셋, 모든 행 높이, 개별적으로 그린 모든 테두리를 계산해야 합니다.

어떤 라이브러리를 선택해야 할까요?

이 라이브러리를 평가할 때 의사 결정 트리는 간단합니다:

모던 CSS와 함께 HTML-to-PDF가 필요합니까? 실용적인 옵션은IronPDF또는PuppeteerSharp입니다. IronPDF는 Chromium을 내부 처리합니다;PuppeteerSharp는 외부 브라우저 프로세스를 관리해야 합니다. wkhtmltopdf는 새로운 프로젝트에 적합하지 않습니다. iText의 pdfHTML은 Flexbox나 Grid를 렌더링할 수 없습니다.

데이터에서 프로그램적으로 문서를 생성하고 HTML이 필요하지 않나요? QuestPDF의 유창한 API는 생산적이고 잘 설계되어 있습니다. PdfSharp는 더 낮은 수준의 제어를 제공하지만 동일한 레이아웃을 위해 상당히 더 많은 코드를 요구합니다.

크로스 플랫폼 배포 (Linux, Docker, 클라우드)? IronPDF, QuestPDF,PuppeteerSharp는 Linux에서 libgdiplus 의존성 없이 실행됩니다. Aspose.PDF는 Linux에서 문서된 메모리 누수가 있습니다. PdfSharp는 알려진 문제로 인해 일부 플랫폼 지원이 있습니다.

라이선스 제약? PdfSharp (MIT) 및Puppeteer Sharp(MIT)은 조건 없이 무료입니다. QuestPDF는 연간 $1M 미만의 수익에는 무료입니다. iText는 AGPL 준수나 상업적 라이센스를 요구합니다 ($15K–$210K/년). IronPDF의 영구 라이센스는 $749에서 시작합니다. Aspose는 약 $999에서 시작합니다.

커밋하기 전에

여러분의 실제 콘텐츠로 테스트하세요, 'Hello World'가 아닙니다. 초기 단계에서 대상 플랫폼에 배포하세요. 100개 이상의 문서에서 메모리를 측정하세요, 단 하나가 아닙니다. 법무 팀과 라이센스 전체 텍스트를 읽어보세요. 서버리스 대상으로 삼는 경우 콜드 스타트 지연 시간을 확인하세요.

IronPDF는 체험판을 제공합니다 특정 요구 사항에 대한 평가를 위해 완전한 기능을 갖추고 있습니다.