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 IronPDFBir 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")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:
| Kriter | Ne Test Edilmeli | Neden Onemli |
|---|---|---|
| HTML/CSS oluşturma | Esas şablonlarınızı Flexbox/Grid ile doyurun | Cogu kutuphane HTML destegine sahip oldugunu soylese de, en fazla CSS 2.1'i oluşturur |
| JavaScriptçalıştırma | Chart.js veya dinamik tablo içeriği ile test edin | JS destegi olmayan kutuphaneler bos bölümler üretebilirken bu sorunu yok saymak daha kolaydir. |
| Lisans modeli | Tüm lisansi değil ozetini okuyun | AGPL, tüm başvurunuzu açık bir şekilde acmanizi gerektirir |
| Platform Desteği | Hedef Linux/Docker/ARM64 ortamınıza dağıtin | Windows'taki basari Linux davranisini tahmin etmez |
| Yuk altinda bellek | Bir dongude 100'den fazla belge oluşturun | Tek belge testleri uretim sunucularini kontrol edici bozulmalari gizler |
| Yayınlanmış fiyatlandırma | Fiyatlandirmanin 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")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Ü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ı
| Özellik | IronPDF | iText 7 | PdfSharp | QuestPDF | wkhtmltopdf | Puppeteer | Aspose |
|---|---|---|---|---|---|---|---|
| HTML'den PDF'ye | Tam (Chromium) | Sınırlı (CSS 2.1) | Hayır | Hayır | Kullanımdan kaldırılmış | Tam (Chrome) | Sınırlı |
| CSS Flexbox/Grid | Evet | Hayır | Hayır | Hayır | Hayır | Evet | Hayır |
| JavaScript | Evet | Hayır | Hayır | Hayır | Sınırlı | Evet | Hayır |
| Linux (libgdiplus yok) | Evet | Evet | Parçalı* | Evet | N/A | Evet | Hayır |
| Docker konuşlandırması | Standart .NET imajı | Standart | Parçalı* | Standart | Karmaşık | Karmaşık | libgdiplus gerekli |
| Aktif bakım | Evet | Evet | Evet | Evet | Terk edilmiş | Evet | Evet |
| Yayınlanmış fiyatlandırma | Evet (749 $+) | Hayır (15K-210K $/yıl) | Ücretsiz (MIT) | Evet (ücretsiz <1M $) | Ücretsiz | Ücretsiz (MIT) | Evet (999 $+) |
| Süresiz lisans | Evet | Hayır (abonelik) | N/A | N/A | N/A | N/A | Evet |
| AGPL ücretsiz | Evet | Hayır (ticari gerekir) | Evet | Evet | Evet | Evet | Evet |
*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ı:
| Senaryo | IronPDF | Puppeteer Sharp | iText pdfHTML | wkhtmltopdf |
|---|---|---|---|---|
| Basit HTML sayfası | ~150ms | ~500ms | ~200ms | ~200ms |
| Karmaşık CSS düzeni (Flexbox/Grid) | ~250ms | ~600ms | Başarısız/Parçalı | ~400ms (bozuk) |
| JavaScript ağırlıklı sayfa | ~350ms | ~800ms | Başarısız | Başarısız/Parçalı |
| Her işlem başına bellek | ~80MB | ~150MB | ~60MB | ~50MB |
| Soğuk başlangıç (ilk nesil) | 2–5s | 3–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 ClassQuestPDF — 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 ClassPdfSharp — 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 ClassIronPDF 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.
