KARşıLAşTıRMA

2026'da Belge Üretimi için En İyi C# Kütüphanesini Seçmek

Bir C# PDF kutuphanesi secmek, projenizin lisanslama maruziyetini, dağıtim esnekligini ve uzun vadeli bakım maliyetini etkiler. Degerlendirme esnasinda dogru görünen kutuphanelerin cogu, uretimde kisitlamalari ortaya koyar - beklenmedik AGPL gereksinimleri, tarayicinizla eslesmeyen HTML oluşturma veya sadece Linux'ta ortaya cikan bellek sizintilari.

Bu makale, uretimde onemli olan ticari gorisimleri belgelendir ve uclu bir kutuphanelle ayni fatura oluşturan yan yana bir kod karsilastirmasi yaparak API farkliklarini dogrudan görebilmeniz için buyuk seçenekleri karsilastirır.

Hizli Başlangic: Uc Satirda HTML'den PDF'ye

NuGet ile kurun:

Install-Package IronPDF

Bir PDF oluştur:

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");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>")
pdf.SaveAs("output.pdf")
$vbLabelText   $csharpLabel

Ek ayarlama gerektirmeden Windows, Linux, macOS ve Docker'da calisir. Cikti, IronPDF'in ayni Chromium islecini icermesi nedeniyle Chrome ile uyumludur.

Degerlendirme Kriterleri

Kutuphaneleri karsilastirmadan once, neyin degerlendirilecegini bilin. Bunlar, ureti pak problemi sorunlarini erkenden ortaya cikaran sorular:

KriterNe Test EdilmeliNeden Onemli
HTML/CSS oluşturmaEsas şablonlarınızı Flexbox/Grid ile doyurunCogu kutuphane HTML destegine sahip oldugunu soylese de, en fazla CSS 2.1'i oluşturur
JavaScriptçalıştırmaChart.js veya dinamik tablo içeriği ile test edinJS destegi olmayan kutuphaneler bos bölümler üretebilirken bu sorunu yok saymak daha kolaydir.
Lisans modeliTüm lisansi değil ozetini okuyunAGPL, tüm başvurunuzu açık bir şekilde acmanizi gerektirir
Platform DesteğiHedef Linux/Docker/ARM64 ortamınıza dağıtinWindows'taki basari Linux davranisini tahmin etmez
Yuk altinda bellekBir dongude 100'den fazla belge oluşturunTek belge testleri uretim sunucularini kontrol edici bozulmalari gizler
Yayınlanmış fiyatlandırmaFiyatlandirmanin web sitesinde olup olmadığını kontrol et"Satışla iletişim", sıklıkla, $15K–$210K/yıl anlamına gelir

Kutuphane Karsilastirmasi

IronPDF — Gomulu Chromium, Tam CSS/JS Destegi

IronPDF, NuGet paketinde Chromium'u dogrudan gomer. HTML oluşturma, tarayici motoru oldugu icin Chrome ile uyumludur. CSS Flexbox, Grid, ozel özellikler veJavaScriptbeklenen sekilde calisir.

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");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print

Dim 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

Dusunulmesi gereken takaslar: Gomulu Chromium, dağıtim paketine ~200MB ekler. Standart sunucu ve konteyner dağıtimlari icin bu, calisma suresi etkisi olmadan bir kerelik indirme işlemi. Azure Functions tuketim plani gibi boyut kisitli ortamlar icin dağıtim boyutu limitini kontrol edin. Soguk bir prosesle ilk PDF oluşturma, Chromium başlatilmasi icin 2-5 saniye sürer; sonraki oluşturmalar 100-500 ms arasinda calisir. Bellek temeli ~150–200MB — konteyner kaynaklarinizi buna gore planlayin.

Lisanslama: $749 (1 geliştirici) ile başlayan suresi olmaksizin lisanslar. ironpdf.com adresinde yayinlanan fiyatlar. Doküman basina ucret yok, AGPL yok, gelir esiklari yok.

iText 7 (iTextSharp) — AGPL Lisanslama, Sinirli HTML

iText, uzun bir geçmişe sahip yetenekli bir PDF manipulasyon kutuphanesidir. pdfHTML eklentisi HTML'den PDF'e donusum saglar, ancak bir tarayici motoru kullanmaz - CSS 2.1'i bir özgün parser ile tahmini olarak sunar.

Orta olcekli bir SaaS şirketinde bir uretim ekibi bunu, fiş sablonlarini Razor gorunumlerinden goc ettirdiginde kesfetti. Sablonlar, duyarlı kolon yerlesimleri icin CSS Flexbox kullaniyordu. iText'in pdfHTML'ini entegre ettikten sonra, her fatura tek bir sutun dikey yigin olarak oluşturuldu. display: flex, gap ve justify-content özellikleri fark edilmeden görmezden gelindi. Takimin pdfHTML'nin mevcut CSS'lerini oluşturamadığını fark etmeden once uc haftalik geliştirme zamani harcandi.

AGPL gerceği: iText, AGPL lisansi kullanmaktadır. Uygulamanız ağa erişilebiliyorsa — bu, her web uygulaması, API ve SaaS ürününü içerir — uygulamanızın tüm kaynak kodunu AGPL altında yayınlamanız gerekir. Sadece PDF modülü değil. Her şey. iText ve ana şirketi Apryse bunu aktif olarak uygular.

Ticari lisanslama: iText abonelik tabanlı lisanslamaya geçti 2024 yılında. Fiyatlandırması yayınlanmıyor — fiyat teklifi almak için satış ekibi ile iletişime geçersiniz. Üçüncü taraf verileri, kullanım hacmine bağlı olarak yıllık 15.000 $-210.000 $ arasında olduğunu öne sürüyor.

PdfSharp — MIT Lisanslı, HTML Yok

PdfSharp MIT lisansi altinda gercekten ucretsizdir ve 34,9 milyon NuGet indirilmesi vardir. Değiş tokuş zenginlikte: HTML ayrıştırıcısı, CSS motoru ve şablon sistemi olmadan koordinat tabanlı bir çizim API'sı sunar.

Bir raporlama paneli insa eden bir ekip, ücretsiz ve iyi bilindigi icin PdfSharp'i tercih etti. Her metin elemanı için X/Y pozisyonlarını hesaplamak, tablo kenarlarını piksel piksel çizmek, sayfa sonlarını manuel olarak ele almak için dört ay harcadılar. Sonunda çıktılarının bir tarayıcıda üretilen aynı HTML şablonuyla karşılaştırıldığında, Chromium tabanlı bir kitaplığın otomatik olarak yaptığı şeyin daha kötü bir versiyonunu oluşturduklarını fark ettiler.

PdfSharp, PDF'leri birlestirmek, filigran eklemek ve verilerden basit yapılandırilmis belgeler oluşturmak icin iyi calisir. HTML işleme ihtiyaçınız yoksa, bu geçerli bir seçenektir.

QuestPDF — Zarif API, HTML Yok, Gelir Eşiği

QuestPDF belgeleri programlı olarak oluşturmak için akıcı bir C# API sunar. API tasarımı gerçekten iyi — herhangi bir kategori içinde daha iyi .NET kitaplığı API'lerinden biridir.

Öngörülen iki sınırlama:QuestPDFHTML'i işleme yapmaz (tasarım gereği — bu kasıtlı bir mimari seçimdir, eksik bir özellik değildir) ve Topluluk Lisansı yıllık brüt geliri 1 milyon doların altında olan işletmeleri kapsar. Şirketiniz bu eşiği geçtiğinde bir ticari lisans zorunlu hale gelir. Bu eşiğe yaklaşan şirketler, bu geçiş için acil hale gelmeden önce bütçe ayırmalıdır.

QuestPDF'nin HTML'e karşı net konumuna rağmen, geliştiriciler düzenli olarak bu durumu uygulamayı başlatmadan sonra keşfeder çünkü kitaplık, HTML yeteneği olan kitaplıklarla birlikte 'C# PDF kütüphanesi' arama sonuçlarında görünür.

wkhtmltopdf Sarıcılar — Terk Edilmiş, Yamalanmamış CVE'ler

wkhtmltopdf'nin zamanı geçti. GitHub organizasyonu Temmuz 2024'te arşivlendi. Altındaki QtWebKit motoru 2015 yılında Qt tarafından kullanımdan kaldırıldı. Bilinen CVE'ler — CVE-2022-35583 (CVSS 9.8, AWS kimlik bilgileri dışa çıkarımını sağlayan SSRF dahil) — asla yamalanmayacak.

DinkToPdf, NReco.PdfGenerator ve WkHtmlToXSharp gibi C# sarıcılarının hepsi aynı terk edilmiş ikiliyi sarar. İşleme motoru yaklaşık olarak Safari 2011 kapasitesine dondurulmuştur: Flexbox yok, Grid yok, sınırlı JavaScript. Bu, yeni projeler için geçerli bir seçenek değildir.

Puppeteer Sharp — Tam İşleme, Operasyonel Karmaşıklık

Puppeteer Sharp başsız Chrome'u .NET bağlamaları aracılığıyla kontrol eder. İşleme kalitesi, Chrome ile aynı çünkü o Chrome'dur. Takas operasyoneldir: dış tarayıcı süreçlerini, indirmeleri, havuzu, bellek izlemeyi ve çökme kurtarmayı yönetirsiniz.

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 });
Imports PuppeteerSharp

' Downloads ~280MB Chromium on first run
Await (New BrowserFetcher()).DownloadAsync()

Await Using browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {.Headless = True})
    Await Using page = Await browser.NewPageAsync()
        Await page.SetContentAsync(html)
        Return Await page.PdfAsync(New PdfOptions With {.Format = PaperFormat.A4, .PrintBackground = True})
    End Using
End Using
$vbLabelText   $csharpLabel

Üretimde, ayrıca tarayıcı süreç havuzu, bellek sızıntısı izleme (Chromium süreçler sızabilir), çökme kurtarma ve kaynak temizleme ihtiyaçınız olacak. Docker konuşlandırması, Chromium bağımlılıklarının yüklenmesini gerektirir — standart bir .NET imajına kıyasla geniş bir Dockerfile gerektirir. Ekibiniz operasyonel yükleri absorbe edebilirsePuppeteer Sharpuygulanabilir.

Aspose.PDF — Geniş Özellikler, Linux Bellek Sorunları

Aspose.PDF, iyi belgelenmiş geniş PDF işlevselliği sunar. Önemli sorun Linux istikrarıdır: Aspose, System.Drawing.Common'a bağlıdır ki bu, Linux'ta libgdiplus gerektirir — belgelenmiş bellek sızıntılarına sahip, bakımsız bir kütüphane. Geliştirici raporları yıllardır yayılıyordu:

"Birkaç düzine istek, Unix ortamında hizmetin bellekten tükenmesine neden olur, ancak bu, Windows tabanlı ortamda gerçekleşmez." — Aspose Forum, Mart 2022

Sadece Windows tabanlı konuşlandırmalar için,Asposeyetenekli olmaya devam eder. Çapraz platform veya konteynerize konuşlandırmalar için, System.Drawing.Common bağımlılığı sürekli bir risk yaratır. Ticari lisanslama yaklaşık olarak geliştirici başına 999 $'dan başlar.

Özellik Karşılaştırması

ÖzellikIronPDFiText 7PdfSharpQuestPDFwkhtmltopdfPuppeteerAspose
HTML'den PDF'yeTam (Chromium)Sınırlı (CSS 2.1)HayırHayırKullanımdan kaldırılmışTam (Chrome)Sınırlı
CSS Flexbox/GridEvetHayırHayırHayırHayırEvetHayır
JavaScriptEvetHayırHayırHayırSınırlıEvetHayır
Linux (libgdiplus yok)EvetEvetParçalı*EvetN/AEvetHayır
Docker konuşlandırmasıStandart .NET imajıStandartParçalı*StandartKarmaşıkKarmaşıklibgdiplus gerekli
Aktif bakımEvetEvetEvetEvetTerk edilmişEvetEvet
Yayınlanmış fiyatlandırmaEvet (749 $+)Hayır (15K-210K $/yıl)Ücretsiz (MIT)Evet (ücretsiz <1M $)ÜcretsizÜcretsiz (MIT)Evet (999 $+)
Süresiz lisansEvetHayır (abonelik)N/AN/AN/AN/AEvet
AGPL ücretsizEvetHayır (ticari gerekir)EvetEvetEvetEvetEvet

*PdfSharp, bazı konfigürasyonlarla ilgili platforma özgü sorunları belgelenmiştir.

Performans Karşılaştırması

200 elemanlı bir HTML fatura şablonuyla orta sınıf bir bulut VM'de (4 vCPU, 8GB RAM) test edildi, 50 yineleme ortalamasını alarak ısınma sonrası:

SenaryoIronPDFPuppeteer SharpiText pdfHTMLwkhtmltopdf
Basit HTML sayfası~150ms~500ms~200ms~200ms
Karmaşık CSS düzeni (Flexbox/Grid)~250ms~600msBaşarısız/Parçalı~400ms (bozuk)
JavaScript ağırlıklı sayfa~350ms~800msBaşarısızBaşarısız/Parçalı
Her işlem başına bellek~80MB~150MB~60MB~50MB
Soğuk başlangıç (ilk nesil)2–5s3–8s<1s<1s

iText ve wkhtmltopdf, tarayıcı motoru başlatmadıkları için daha hızlı soğuk başlatmalar gösteriyor — ancak aynı içeriği işleyemezler. Performans karşılaştırması, sadece tüm kütüphanelerin doğru çıktı ürettiği senaryolar için anlamlıdır.

Kod Karşılaştırması: Aynı Fatura, Üç Kütüphane

Bu kütüphaneler arasındaki farklar, aynı belgeyi oluştururken en net şekilde ortaya çıkar. İşte üç şekilde oluşturulmuş bir fatura.

IronPDF — HTML/CSS Yaklaşımı

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;
    }
}
Imports IronPdf

Public Class InvoiceGenerator
    Public Function GenerateInvoice(data As InvoiceData) As Byte()
        Dim renderer = New ChromePdfRenderer()

        Dim html As String = $"
        <!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(Function(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>"

        Dim pdf = renderer.RenderHtmlAsPdf(html)
        Return pdf.BinaryData
    End Function
End Class
$vbLabelText   $csharpLabel

QuestPDF — Akıcı API Yaklaşımı

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();
    }
}
Imports QuestPDF.Fluent
Imports QuestPDF.Infrastructure
Imports System.IO

Public Class InvoiceGenerator
    Public Function GenerateInvoice(data As InvoiceData) As Byte()
        Dim document = Document.Create(Sub(container)
                                           container.Page(Sub(page)
                                                              page.Size(PageSizes.A4)
                                                              page.Margin(40)
                                                              page.DefaultTextStyle(Function(x) x.FontFamily("Segoe UI"))

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

                                                              page.Content().Column(Sub(column)
                                                                                       column.Item().Table(Sub(table)
                                                                                                               table.ColumnsDefinition(Sub(cols)
                                                                                                                                           cols.RelativeColumn(3)
                                                                                                                                           cols.RelativeColumn(1)
                                                                                                                                           cols.RelativeColumn(1)
                                                                                                                                       End Sub)

                                                                                                               table.Header(Sub(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)
                                                                                                                            End Sub)

                                                                                                               For Each 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}")
                                                                                                               Next
                                                                                                           End Sub)

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

        Using stream As New MemoryStream()
            document.GeneratePdf(stream)
            Return stream.ToArray()
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

PdfSharp — Koordinat Çizim Yaklaşımı

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();
    }
}
Imports PdfSharpCore.Drawing
Imports PdfSharpCore.Pdf
Imports System.IO

Public Class InvoiceGenerator
    Public Function GenerateInvoice(data As InvoiceData) As Byte()
        Dim document As New PdfDocument()
        Dim page = document.AddPage()
        Dim gfx = XGraphics.FromPdfPage(page)

        Dim titleFont As New XFont("Arial", 24)
        Dim headerFont As New XFont("Arial", 12, XFontStyleEx.Bold)
        Dim bodyFont As New XFont("Arial", 12)

        Dim y As Double = 40

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

        ' Table header — manually positioned
        Dim colX As Double() = {40, 300, 400}
        Dim rowHeight As Double = 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
        For Each 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
        Next

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

        Using stream As New MemoryStream()
            document.Save(stream)
            Return stream.ToArray()
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

IronPDF versiyonu HTML/CSS kullanır — çoğu geliştiricinin zaten sahip olduğu beceriler.QuestPDFversiyonu, öğrenilmesi gereken bir alan özgü akıcı API gerektirir ancak yapı sağlar. PdfSharp versiyonu, her piksel konumunu manuel olarak hesaplamayi gerektirir - her sutun ofseti, her satir yuksekligi, her sinir bireysel olarak cizilir.

Hangi Kütüphaneyi Seçmeliyim?

Bu kütüphaneleri değerlendirdiğimde, karar ağacı basittir:

HTML'den PDF'e modern CSS ile mi ihtiyaçınız var? Pratik seçeneklerIronPDFveyaPuppeteerSharp'dır. IronPDF, Chromium'u dahili olarak işler;PuppeteerSharp, harici tarayıcı süreçlerini yönetmenizi gerektirir. Yeni projeler içinwkhtmltopdfbir seçenek değildir. iText'in pdfHTML'i Flexbox veya Grid işleyemez.

Verilerden programlamalı olarak belge oluşturmak, HTML yok mu? QuestPDF'nin akıcı API'si üretken ve iyi tasarlanmıştır. PdfSharp daha dusuk seviyeli kontrol sunar ancak esdeger duzenler icin çok daha fazla kod gerektirir.

Çapraz platform dağıtımı (Linux, Docker, bulut)? IronPDF,QuestPDFvePuppeteerSharp, Linux'ta libgdiplus bağımlılıkları olmadan çalışır. Aspose.PDF'in Linux'ta belgelenmiş bellek sızıntıları vardır. PdfSharp bilinen sorunlarla kisitli platform destegine sahiptir.

Lisans sinirlari mi? PdfSharp (MIT) vePuppeteer Sharp(MIT) kosulsuz ucretsizdir. QuestPDF, $1M gelirin altında ücretsizdir. iText, ya AGPL uyumunu ya da ticari lisansı ($15K–$210K/yıl) gerektirir. IronPDF'in sürekli lisansı $749'dan başlar.Aspose~999$'dan başlar.

Bağlılık Öncesi

"Hello World" yerine gerçek içeriğinizle test edin. Hedef platformunuza erken dağıtım yapın. 100'den fazla belge üzerinde bellek ölçün, tek bir belge üzerinde değil. Hukuk ekibinizle tam lisans metnini okuyun. Sunucusuz hedefliyorsanız, soğuk başlatma gecikmesinin kontrol edin.

IronPDF, özel gereksinimlerinizi değerlendirmek için tam işlevsellik sunan bir deneme sunar.