VERGLEICH

Auswahl der besten C#-Bibliothek für die Dokumentenerstellung im Jahr 2026

Die Auswahl einer C#-PDF-Bibliothek wirkt sich auf die Lizenzierung Ihres Projekts, die Flexibilität bei der Bereitstellung und die langfristigen Wartungskosten aus. Die meisten Bibliotheken, die während der Evaluierung geeignet erscheinen, zeigen in der Produktion Einschränkungen - AGPL-Anforderungen, die Sie nicht erwartet haben, HTML-Rendering, das nicht zu Ihrem Browser passt, oder Speicherlecks, die nur unter Linux auftreten.

Dieser Artikel vergleicht die wichtigsten Optionen mit Codebeispielen, dokumentiert die Kompromisse, die in der Praxis von Bedeutung sind, und enthält einen Seite-an-Seite-Codevergleich, der dieselbe Rechnung in drei verschiedenen Bibliotheken generiert, damit Sie die API-Unterschiede direkt sehen können.

Schnellstart: HTML zu PDF in drei Zeilen

Installation über NuGet:

Installations-Paket IronPDF

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

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

Dies funktioniert unter Windows, Linux, macOS und Docker ohne zusätzliche Konfiguration. Die Ausgabe stimmt mit Chrome überein, daIronPDFdieselbe Chromium-Rendering-Engine einbettet.

Bewertungskriterien

Bevor Sie Bibliotheken vergleichen, sollten Sie wissen, was zu bewerten ist. Dies sind die Fragen, die Produktionsprobleme frühzeitig aufdecken:

Kriterium Was zu testen ist Warum das wichtig ist
HTML/CSS-Rendering Füttern Sie es mit Ihren aktuellen Vorlagen mit Flexbox/Grid Die meisten Bibliotheken behaupten, HTML zu unterstützen, geben aber bestenfalls CSS 2.1 wieder
JavaScript-Ausführung Test mit Chart.js oder dynamischem Tabelleninhalt Bibliotheken ohne JS-Unterstützung erzeugen leere Abschnitte
Lizenzierungsmodell Lesen Sie die vollständige Lizenz, nicht die Zusammenfassung AGPL erfordert das Open-Sourcing Ihrer gesamten Anwendung
Plattform-Unterstützung Bereitstellung in Ihrer Linux/Docker/ARM64-Zielumgebung Der Erfolg von Windows sagt nichts über das Verhalten von Linux aus
Speicher unter Last Generieren Sie 100+ Dokumente in einer Schleife Einzeldokumententests verbergen Lecks, die Produktionsserver zum Absturz bringen
Veröffentlichte Preise Prüfen Sie, ob die Preise auf der Website stehen "Kontaktverkauf" bedeutet oft $15K-$210K/Jahr

Vergleich von Bibliotheken

IronPDF- Eingebettetes Chromium, volle CSS/JS-Unterstützung

IronPDF bettet Chromium direkt in das NuGet-Paket ein. HTML-Rendering passt zu Chrome, weil es die Engine von Chrome ist. CSS Flexbox, Grid, benutzerdefinierte Eigenschaften und JavaScriptwerden alle wie erwartet ausgeführt.

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

Zu beachtende Nachteile: Das eingebettete Chromium fügt dem Bereitstellungspaket ~200 MB hinzu. Für Standard-Server- und Container-Implementierungen ist dies ein einmaliger Download, der keine Auswirkungen auf die Laufzeit hat. Bei Umgebungen mit Größenbeschränkungen wie dem Azure Functions-Verbrauchsplan sollten Sie das Größenlimit für die Bereitstellung überprüfen. Die erste PDF-Generierung in einem kalten Prozess dauert 2-5 Sekunden für die Chromium-Initialisierung; die nachfolgenden Generationen laufen in 100-500ms. Der Basisspeicher beträgt ~150-200 MB - planen Sie die Container-Ressourcen entsprechend ein.

Lizenzierung: Unbefristete Lizenzen ab 749 $ (1 Entwickler). Die Preise werden auf ironPdf.com veröffentlicht. Keine Gebühren pro Dokument, keine AGPL, keine Umsatzschwellen.

iText 7 (iTextSharp) - AGPL-Lizenzierung, eingeschränktes HTML

iText ist eine leistungsfähige Bibliothek zur PDF-Bearbeitung mit einer langen Geschichte. Das pdfHTML-Add-on bietet eine HTML-zu-PDF-Konvertierung, verwendet aber keine Browser-Engine, sondern nähert sich CSS 2.1 mit einem eigenen Parser an.

Ein Produktionsteam eines mittelständischen SaaS-Unternehmens stellte dies fest, als es seine Rechnungsvorlagen von Razor Views migrierte. Die Vorlagen verwenden CSS Flexbox für responsive Spaltenlayouts. Nach der Integration von pdfHTML von iText wurde jede Rechnung als einspaltiger vertikaler Stapel gerendert. Die Eigenschaften display: flex, gap, und justify-content wurden stillschweigend ignoriert. Drei Wochen Entwicklungszeit gingen ins Land, bevor das Team feststellte, dass pdfHTML das vorhandene CSS nicht darstellen konnte.

Die AGPL-Realität: iText verwendet die AGPL-Lizenz. Wenn Ihre Anwendung über das Netzwerk zugänglich ist - dazu gehören alle Webanwendungen, APIs und SaaS-Produkte - müssen Sie den gesamten Quellcode Ihrer Anwendung unter der AGPL veröffentlichen. Nicht nur das PDF-Modul. Alles. iText und die Muttergesellschaft Apryse setzen dies aktiv durch.

Kommerzielle Lizenzierung: iText stellt im Jahr 2024 auf eine abonnementbasierte Lizenzierung um. Die Preise werden nicht veröffentlicht - für ein Angebot wenden Sie sich bitte an den Vertrieb. Daten von Dritten deuten auf $15.000-$210.000 jährlich hin, je nach Nutzungsvolumen.

PdfSharp - MIT lizenziert, kein HTML

PdfSharp ist wirklich frei unter der MIT-Lizenz und wurde bereits 34,9 Millionen Mal von NuGet heruntergeladen. Der Kompromiss ist die Fähigkeit: Es bietet eine koordinatenbasierte Zeichen-API ohne HTML-Parser, ohne CSS-Engine und ohne Vorlagensystem.

Ein Team, das ein Reporting-Dashboard erstellt, wählte PdfSharp, weil es kostenlos und bekannt ist. Sie verbrachten vier Monate damit, koordinatenbasierten Layout-Code zu schreiben - X/Y-Positionen für jedes Textelement zu berechnen, Tabellenränder Pixel für Pixel zu zeichnen und Seitenumbrüche manuell zu bearbeiten. Als sie schließlich ihre Ausgabe mit dem verglichen, was die gleiche HTML-Vorlage in einem Browser erzeugte, stellten sie fest, dass sie eine schlechtere Version dessen erstellt hatten, was eine Chromium-basierte Bibliothek automatisch tut.

PdfSharp eignet sich gut zum Zusammenführen von PDFs, Hinzufügen von Wasserzeichen und Erstellen einfacher strukturierter Dokumente aus Daten. Wenn Sie kein HTML-Rendering benötigen, bleibt es eine legitime Option.

QuestPDF - Elegante API, kein HTML, Umsatzschwelle

QuestPDF bietet eine flüssige C#-API für die programmgesteuerte Erstellung von Dokumenten. Das API-Design ist wirklich gut - es ist eine der besseren .NET-Bibliotheks-APIs in jeder Kategorie.

Zwei Einschränkungen sind wichtig: QuestPDF rendert kein HTML (dies ist eine bewusste architektonische Entscheidung, keine fehlende Funktion), und die Community-Lizenz deckt Unternehmen mit einem jährlichen Bruttoumsatz von weniger als 1 Million Dollar ab. Sobald Ihr Unternehmen diese Schwelle überschreitet, wird eine Gewerbliche Lizenz erforderlich. Unternehmen, die sich der Schwelle nähern, sollten für diese Umstellung ein Budget einplanen, bevor es dringend wird.

Trotz der klaren Positionierung von QuestPDF gegenüber HTML stellen Entwickler dies regelmäßig fest, nachdem sie mit der Implementierung begonnen haben, da die Bibliothek in den Suchergebnissen für "C#-PDF-Bibliotheken" neben HTML-fähigen Bibliotheken erscheint.

wkhtmltopdf Wrappers - Verlassene, ungepatchte CVEs

die Zeit von wkhtmltopdf ist vorbei. Die GitHub Organisation wurde im Juli 2024 archiviert. Die zugrunde liegende QtWebKit-Engine wurde 2015 von Qt veraltet. Bekannte CVEs - einschließlich CVE-2022-35583(CVSS 9.8, SSRF, das die Exfiltration von AWS-Anmeldeinformationen ermöglicht) - werden nie gepatcht.

C#-Wrapper wie DinkToPdf, NReco.PdfGenerator und WkHtmlToXSharp verpacken alle das gleiche aufgegebene Binärprogramm. Die Rendering-Engine ist ungefähr auf dem Stand von Safari 2011 eingefroren: kein Flexbox, kein Grid, begrenztes JavaScript. Dies ist keine praktikable Option für neue Projekte.

PuppeteerSharp- Vollständiges Rendering, Betriebskomplexität

Puppeteer Sharp steuert Headless Chrome über .NET-Bindungen. Die Wiedergabequalität entspricht der von Chrome, weil es Chrome ist. Der Kompromiss ist operativ: Sie verwalten externe Browserprozesse, einschließlich Downloads, Pooling, Speicherüberwachung und Wiederherstellung bei Abstürzen.

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

In der Produktion benötigen Sie außerdem Browser-Prozess-Pooling, Überwachung von Speicherlecks (Chromium-Prozesse können auslaufen), Wiederherstellung bei Abstürzen und Bereinigung von Ressourcen. Die Docker-Bereitstellung erfordert die Installation von Chromium-Abhängigkeiten - eine umfangreiche Dockerdatei im Vergleich zu einem Standard-.NET-Image. PuppeteerSharpist realisierbar, wenn Ihr Team den operativen Aufwand tragen kann.

Aspose.PDF - Umfangreiche Funktionen, Linux-Speicherprobleme

Aspose.PDF bietet eine umfassende PDF-Funktionalität mit guter Dokumentation. Das wichtigste Problem ist die Linux-Stabilität: Asposehängt von System.Drawing.Common ab, das unter Linux libgdiplus benötigt - eine nicht gewartete Bibliothek mit dokumentierten Speicherlecks. Die Berichte für Entwickler umfassen mehrere Jahre:

Mehrere Dutzend Anfragen führen dazu, dass dem Dienst in der Unix-Umgebung der Speicher ausgeht, was aber in der Windows-Umgebung nicht der Fall ist

Für reine Windows-Einsätze ist Asposeweiterhin geeignet. Für plattformübergreifende oder containerisierte Implementierungen stellt die Abhängigkeit von System.Drawing.Common ein ständiges Risiko dar. Die Preise für kommerzielle Lizenzen beginnen bei etwa $999 pro Entwickler.

Funktionsvergleich

Feature IronPDF iText 7 PdfSharp QuestPDF wkhtmltopdf Puppeteer Aspose
HTML zu PDF Vollständig (Chromium) Begrenzt (CSS 2.1) Nein Nein Veraltet Vollständig (Chrome) Beschränkt
CSS Flexbox/Grid Ja Nein Nein Nein Nein Ja Nein
JavaScript Ja Nein Nein Nein Beschränkt Ja Nein
Linux (ohne libgdiplus) Ja Ja Teilweise* Ja Nicht anwendbar Ja Nein
Docker-Einsatz .NET-Standardbild Standard Teilweise* Standard Komplex Komplex Benötigt libgdiplus
Aktive Wartung Ja Ja Ja Ja Aufgegeben Ja Ja
Veröffentlichte Preise Ja ($749+) Nein ($15K-$210K/Jahr) Frei (MIT) Ja (kostenlos <$1M) Kostenlos Frei (MIT) Ja (ab $999)
Unbefristete Lizenz Ja Nein (Abonnement) Nicht anwendbar Nicht anwendbar Nicht anwendbar Nicht anwendbar Ja
AGPL-frei Ja Nein (erfordert Werbung) Ja Ja Ja Ja Ja

*PdfSharp hat plattformspezifische Probleme mit einigen Konfigurationen dokumentiert.

Leistungsvergleich

Getestet auf einer Mid-Tier-Cloud-VM (4 vCPU, 8 GB RAM) mit einer HTML-Rechnungsvorlage mit 200 Elementen, durchschnittlich über 50 Iterationen nach der Aufwärmphase:

Szenario IronPDF PuppeteerSharp iText pdfHTML wkhtmltopdf
Einfache HTML-Seite ~150ms ~500ms ~200ms ~200ms
Komplexes CSS-Layout (Flexbox/Grid) ~250ms ~600ms Misslungen/Teilweise ~400ms (abgebrochen)
JavaScript-lastige Seite ~350ms ~800ms Fällt aus Misslungen/Teilweise
Speicher pro Vorgang ~80MB ~150MB ~60MB ~50MB
Kaltstart (erste Generation) 2-5s 3-8s <1s <1s

iText und wkhtmltopdf zeigen schnellere Kaltstarts, weil sie keine Browser-Engine initialisieren - aber sie können auch nicht denselben Inhalt rendern. Der Leistungsvergleich ist nur für Szenarien aussagekräftig, in denen alle Bibliotheken eine korrekte Ausgabe liefern.

Code-Vergleich: Dieselbe Rechnung, drei Bibliotheken

Die Unterschiede zwischen diesen Bibliotheken werden beim Aufbau desselben Dokuments am deutlichsten. Hier ist eine auf drei Arten erstellte Rechnung.

IronPDF- HTML/CSS-Ansatz

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 - Fließender API-Ansatz

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 - Ansatz für Koordinatenzeichnungen

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

Die IronPDF-Version verwendet HTML/CSS - Fähigkeiten, über die die meisten Entwickler bereits verfügen. Die QuestPDF-Version erfordert das Erlernen einer domänenspezifischen, fließenden API, bietet aber Struktur. Bei der PdfSharp-Version muss jede Pixelposition manuell berechnet werden - jeder Spaltenversatz, jede Zeilenhöhe, jeder einzeln gezeichnete Rand.

Welche Bibliothek soll ich wählen?

Wenn ich diese Bibliotheken bewerte, ist der Entscheidungsbaum sehr einfach:

Brauchen Sie HTML-zu-PDF mit modernem CSS? Die praktischen Optionen sindIronPDFoder PuppeteerSharp.IronPDFarbeitet intern mit Chromium; Mit PuppeteerSharpmüssen Sie externe Browserprozesse verwalten. wkhtmltopdf ist keine Option für neue Projekte. pdfHTML von iText kann Flexbox oder Grid nicht darstellen.

Dokumente programmatisch aus Daten erstellen, kein HTML? Die flüssige API von QuestPDF ist produktiv und gut durchdacht. PdfSharp bietet eine niedrigere Kontrollebene, erfordert aber deutlich mehr Code für gleichwertige Layouts.

Plattformübergreifende Bereitstellung (Linux, Docker, Cloud)? IronPDF, QuestPDF und PuppeteerSharpfunktionieren unter Linux ohne libgdiplus-Abhängigkeiten. Aspose.PDF hat Speicherlecks unter Linux dokumentiert. PdfSharp hat teilweise Plattformunterstützung mit bekannten Problemen.

Lizenzbedingungen? PdfSharp (MIT) und PuppeteerSharp(MIT) sind kostenlos und ohne Bedingungen. QuestPDF ist unter $1M Umsatz kostenlos. iText erfordert entweder die Einhaltung der AGPL oder eine kommerzielle Lizenzierung ($15K-$210K/Jahr). Die unbefristete Lizenz vonIronPDFbeginnt bei $749. Asposebeginnt bei ~$999.

Bevor Sie sich festlegen

Testen Sie mit Ihrem tatsächlichen Inhalt, nicht mit "Hello World" Führen Sie die Bereitstellung auf Ihrer Zielplattform frühzeitig durch. Messen Sie das Gedächtnis über 100+ Dokumente, nicht nur eines. Lesen Sie den vollständigen Lizenztext zusammen mit Ihrem Rechtsteam. Prüfen Sie die Kaltstart-Latenz, wenn Sie auf Serverless abzielen.

IronPDF bietet eine Testversion mit vollem Funktionsumfang zur Evaluierung anhand Ihrer spezifischen Anforderungen.