PORóWNANIE

HTML do PDF w C# — Rzeczywistość opcji bibliotek

Konwersja HTML do PDF w C# wymaga biblioteki, która faktycznie renderuje HTML – a nie takiej, która analizuje tylko podzbiór znaczników i aproksymuje CSS 2.1. Większość bibliotek polecanych w wątkach na Stack Overflow i dyskusjach na Reddit albo nie potrafi renderować nowoczesnego CSS, albo mają ograniczenia licencyjne dyskwalifikujące ich do użytku komercyjnego, albo zostały porzucone z niezałatanymi lukami bezpieczeństwa.

Ten artykuł porównuje biblioteki, które programiści faktycznie napotykają, szukając "HTML to PDF C#", dokumentuje, co każda z nich może a czego nie może renderować, zawiera testy wydajności z metodologią i pokazuje rzeczywisty koszt operacyjny każdej z metod.

Quickstart: HTML do PDF in C

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

Zainstaluj za pomoca NuGet: Install-Package IronPdf. Działa na Windows, Linux, macOS i Docker bez zewnętrznych zależności.

Dłączego konwersja HTML do PDF jest trudna?

Poprawne renderowanie HTML do PDF wymaga zaimplementowania tych samych pięciu komponentów, co przeglądarka internetowa: parsera HTML, silnika CSS (w tym Flexbox, Grid, kaskadowania, specyficzności i zapytań medialnych), środowiska uruchomieniowego JavaScript, silnika układu i pipeline'u renderowania, który łączy to wszystko do PDF z dokładnością do sub-piksela.

Tradycyjne biblioteki PDF implementują pierwsze dwa komponenty częściowo i całkowicie pomijają JavaScript. Dlatego radzą sobie ze standardowym HTML, ale psują się na wszystkim, co nowoczesna przeglądarka renderuje poprawnie. Jedynym sposobem na dopasowanie się do wyjścia przeglądarki jest użycie silnika przeglądarki.

Które biblioteki faktycznie konwertują HTML do PDF?

Owijaczewkhtmltopdf— ekosystem błędów ładowania DLL

Najczęściej wyszukiwane zapytanie przyciągające programistów do tych artykułów to jakaś wariacja:

System.DllNotFoundException: Nie można załadować DLL 'libwkhtmltox'

Warianty specyficzne dla platformy obejmują:

Nie można załadować biblioteki współdzielonej 'wkhtmltox' lub jednej z jej zależności
    (Linux — libwkhtmltox.so not found)

Nie można odnaleźć określonego modułu. (0x8007007E)
    (Windows — wkhtmltox.dll path not configured)

dyld: Biblioteka nie załadowana: libwkhtmltox.dylib
    (macOS — not supported on ARM64/Apple Silicon)

Te błędy pochodzą z DinkToPdf, NReco.PdfGenerator, WkHtmlToXSharp i innych owijaczy C# wokół tej samej porzuconej binarki. Organizacjawkhtmltopdfna GitHub została zarchiwizowana w lipcu 2024. Podstawowy silnik QtWebKit został zdeprecjonowany przez Qt w 2015 roku. Strona statusu projektu wyraźnie oznacza go jako zdeprecjonowany.

Poza problemami z ładowaniem DLL, silnik renderujący jest zamrożony na poziomie możliwości około Safari 2011. Brak Flexbox, brak Grid, ograniczony CSS3, zawodny JavaScript. I są niezałatane krytyczne luki: CVE-2022-35583(CVSS 9.8) umożliwia ataki SSRF, które mogą wyciekają dane AWS przez spreparowany HTML.

Czaswkhtmltopdfjuż minął. Błędy ładowania DLL są symptomem głębszego problemu: polegasz na porzuconym oprogramowaniu bez drogi do przodu.

iText(pdfHTML Add-On) — ograniczone CSS, licencja AGPL

Moduł pdfHTMLiTextkonwertuje HTML do PDF, używając niestandardowego parsera — nie silnika przeglądarki. Obsługuje podstawowy HTML/CSS, ale nie renderuje Flexbox, Grid ani JavaScript.

Tryb awaryjny jest cichy: pdfHTML nie rzuca wyjątków, gdy napotyka nieobsługiwany CSS. Renderuje to, co moze, i ignoruje reszte. Pojemnik display: flex z gap: 20px i justify-content: space-between renderuje sie jako pionowo ulozone elementy bez odstępu. Programiści odkrywają to po integracji, a nie w trakcie.

Licencjonowanie: AGPL — wymaga udostępnienia kodu źródłowego całej aplikacji dostępnej z sieci, lub zakupu licencji komercyjnej. Ceny nie są opublikowane; dane zewnętrzne sugerują $15,000–$210,000 rocznie.

Jak zużycie pamięci się porównuje?

pdfHTML iTexta ładuje cały dokument do pamięci w celu przetworzenia. Dla typowych dokumentów biznesowych jest to do opanowania, ale duże raporty HTML z osadzonymi obrazami mogą powodować znaczną presję pamięci w porównaniu do podejść strumieniowych.

DlaczegoPDFSharpnie wspiera HTML?

PdfSharp pojawia sie w wynikach wyszukiwania "HTML to PDF" ze wzgledu na swoja popularnosc (34,9 mln pobran z NuGet) i czeste rekomendacje. Ale PdfSharp nie ma parsera HTML. Dostarcza API rysowania oparte na wspolrzednych: DrawString(), DrawRectangle(), DrawImage() z wyraznymi pozycjami X/Y.

Powszechnie sugerowane obejscie, HtmlRenderer.PdfSharp, wspiera tylko HTML 4.01 i CSS Poziom 2. Jesli twoj HTML uzywa jakiejkolwiek funkcji CSS wprowadzonej po 2010 — Flexbox (2012), Grid (2017), wlasnosci niestandardowe (2017), border-radius (2011) — nie bedzie rendu.

Deweloperzy, ktorzy wybieraja PdfSharp oczekujac wsparcia HTML, koncza na recznym pozycjonowaniu kazdego elementu za pomoca kodu opartego na wspolrzednych lub dodaja druga biblioteke do renderowania HTML — w tym momencie PdfSharp jest zbedny.

Co sprawia, że Puppeteer jest tak wymagający pod względem zasobów?

Puppeteer Sharp kontroluje Headless Chrome przez powiazania .NET. Dokładność renderowania odpowiada Chrome, ponieważ jest to Chrome. Koszt jest operacyjny: zarządzasz zewnętrznymi procesami przeglądarki.

Oto jak faktycznie wygląda wdrożeniePuppeteer Sharpw środowisku produkcyjnym — nie jest to 5-wierszowy przykład z samouczków, ale kod puli przeglądarek potrzebny do równoczesnego generowania plików PDF:

using PuppeteerSharp;
using System.Collections.Concurrent;

public class PdfBrowserPool : IAsyncDisposable
{
    private readonly ConcurrentBag<IBrowser> _available = new();
    private readonly SemaphoreSlim _semaphore;
    private readonly int _maxBrowsers;

    public PdfBrowserPool(int maxBrowsers = 4)
    {
        _maxBrowsers = maxBrowsers;
        _semaphore = new SemaphoreSlim(maxBrowsers, maxBrowsers);
    }

    public async Task InitializeAsync()
    {
        await new BrowserFetcher().DownloadAsync(); // ~280MB download
        for (int i = 0; i < _maxBrowsers; i++)
        {
            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = new[] { "--no-sandbox", "--disable-setuid-sandbox",
                               "--disable-dev-shm-usage" }
            });
            _available.Add(browser);
        }
    }

    public async Task<byte[]> ConvertHtmlToPdf(string html)
    {
        await _semaphore.WaitAsync();
        IBrowser browser = null;
        try
        {
            if (!_available.TryTake(out browser))
                throw new InvalidOperationException("No browser available");

            await using var page = await browser.NewPageAsync();
            await page.SetContentAsync(html, new NavigationOptions
            {
                WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
            });
            var result = await page.PdfAsync(new PdfOptions
            {
                Format = PaperFormat.A4,
                PrintBackground = true
            });
            return result;
        }
        catch (Exception ex) when (ex is NavigationException or TargetClosedException)
        {
            // Browser crashed — replace it
            browser?.Dispose();
            browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
            });
            throw; // Re-throw after recovery
        }
        finally
        {
            if (browser != null) _available.Add(browser);
            _semaphore.Release();
        }
    }

    public async ValueTask DisposeAsync()
    {
        foreach (var browser in _available)
        {
            await browser.CloseAsync();
            browser.Dispose();
        }
    }
}
using PuppeteerSharp;
using System.Collections.Concurrent;

public class PdfBrowserPool : IAsyncDisposable
{
    private readonly ConcurrentBag<IBrowser> _available = new();
    private readonly SemaphoreSlim _semaphore;
    private readonly int _maxBrowsers;

    public PdfBrowserPool(int maxBrowsers = 4)
    {
        _maxBrowsers = maxBrowsers;
        _semaphore = new SemaphoreSlim(maxBrowsers, maxBrowsers);
    }

    public async Task InitializeAsync()
    {
        await new BrowserFetcher().DownloadAsync(); // ~280MB download
        for (int i = 0; i < _maxBrowsers; i++)
        {
            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = new[] { "--no-sandbox", "--disable-setuid-sandbox",
                               "--disable-dev-shm-usage" }
            });
            _available.Add(browser);
        }
    }

    public async Task<byte[]> ConvertHtmlToPdf(string html)
    {
        await _semaphore.WaitAsync();
        IBrowser browser = null;
        try
        {
            if (!_available.TryTake(out browser))
                throw new InvalidOperationException("No browser available");

            await using var page = await browser.NewPageAsync();
            await page.SetContentAsync(html, new NavigationOptions
            {
                WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
            });
            var result = await page.PdfAsync(new PdfOptions
            {
                Format = PaperFormat.A4,
                PrintBackground = true
            });
            return result;
        }
        catch (Exception ex) when (ex is NavigationException or TargetClosedException)
        {
            // Browser crashed — replace it
            browser?.Dispose();
            browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
            });
            throw; // Re-throw after recovery
        }
        finally
        {
            if (browser != null) _available.Add(browser);
            _semaphore.Release();
        }
    }

    public async ValueTask DisposeAsync()
    {
        foreach (var browser in _available)
        {
            await browser.CloseAsync();
            browser.Dispose();
        }
    }
}
Imports PuppeteerSharp
Imports System.Collections.Concurrent
Imports System.Threading

Public Class PdfBrowserPool
    Implements IAsyncDisposable

    Private ReadOnly _available As New ConcurrentBag(Of IBrowser)()
    Private ReadOnly _semaphore As SemaphoreSlim
    Private ReadOnly _maxBrowsers As Integer

    Public Sub New(Optional maxBrowsers As Integer = 4)
        _maxBrowsers = maxBrowsers
        _semaphore = New SemaphoreSlim(maxBrowsers, maxBrowsers)
    End Sub

    Public Async Function InitializeAsync() As Task
        Await (New BrowserFetcher()).DownloadAsync() ' ~280MB download
        For i As Integer = 0 To _maxBrowsers - 1
            Dim browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {
                .Headless = True,
                .Args = New String() {"--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"}
            })
            _available.Add(browser)
        Next
    End Function

    Public Async Function ConvertHtmlToPdf(html As String) As Task(Of Byte())
        Await _semaphore.WaitAsync()
        Dim browser As IBrowser = Nothing
        Try
            If Not _available.TryTake(browser) Then
                Throw New InvalidOperationException("No browser available")
            End If

            Await Using page = Await browser.NewPageAsync()
                Await page.SetContentAsync(html, New NavigationOptions With {
                    .WaitUntil = New WaitUntilNavigation() {WaitUntilNavigation.Networkidle0}
                })
                Dim result = Await page.PdfAsync(New PdfOptions With {
                    .Format = PaperFormat.A4,
                    .PrintBackground = True
                })
                Return result
            End Using
        Catch ex As Exception When TypeOf ex Is NavigationException OrElse TypeOf ex Is TargetClosedException
            ' Browser crashed — replace it
            browser?.Dispose()
            browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {
                .Headless = True,
                .Args = New String() {"--no-sandbox", "--disable-setuid-sandbox"}
            })
            Throw ' Re-throw after recovery
        Finally
            If browser IsNot Nothing Then _available.Add(browser)
            _semaphore.Release()
        End Try
    End Function

    Public Async Function DisposeAsync() As ValueTask Implements IAsyncDisposable.DisposeAsync
        For Each browser In _available
            Await browser.CloseAsync()
            browser.Dispose()
        Next
    End Function
End Class
$vbLabelText   $csharpLabel

To około 60 wierszy kodu infrastruktury, zanim wygenerujesz pojedynczy plik PDF. Potrzebne jest również monitorowanie wycieków pamięci (procesy Chromium z czasem gromadzą pamięć), kontrole stanu oraz plik Dockerfile z ponad 20 zależnościami Chromium. Rozmiar obrazu Docker wzrasta o 300–400 MB.

Porównaj to z odpowiednim podejściem IronPDF:

using IronPdf;

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Browser pooling, process management, crash recovery — handled internally
using IronPdf;

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Browser pooling, process management, crash recovery — handled internally
Imports IronPdf

Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)
' Browser pooling, process management, crash recovery — handled internally
$vbLabelText   $csharpLabel

Puppeteer Sharp jest opłacalny, jeśli Twój zespół jest w stanie pokryć związane z nim koszty operacyjne. Dla zespołów, które chcą skupić się na swojej aplikacji, a nie na infrastrukturze przeglądarki,IronPDFobsługuje to samo renderowanie wewnętrznie.

DłączegoQuestPDFnie może konwertować plików HTML?

QuestPDF pojawia się praktycznie w każdej dyskusji na temat "HTML do PDF w C#" na Reddicie i Stack Overflow. Powstaje w ten sposób spójny schemat: programiści kupują lub integrują QuestPDF, oczekując konwersji do HTML, a następnie odkrywają, że w ogóle nie renderuje on HTML.

QuestPDF to płynnie działający interfejs API w języku C# do programowego tworzenia dokumentów. Jego pozycjonowanie wyraźnie brzmi: "przestań zmagać się z konwersją HTML na PDF" — zastępuje ono podejście oparte na HTML kodem C#. Jest to świadomy wybór projektowy. Dyskusje na GitHubie z lat 2022–2024 pokazują, że programiści odkrywają to po rozpoczęciu wdrażania. Twórcy konsekwentnie potwierdzają, że obsługa HTML nie jest planowana.

Jeśli obecny proces wykorzystuje szablony HTML — widoki Razor dla faktur, kod HTML pulpitu nawigacyjnego dla raportów, treści internetowe do archiwizacji —QuestPDFwymaga przepisania każdego szablonu w płynnym kodzie API języka C#. W przypadku nowych projektów, w których tworzysz układy dokumentów od podstaw przy użyciu danych strukturalnych, APIQuestPDFjest dobrze zaprojektowane i wydajne.

Licencja społecznościowa obejmuje firmy o rocznych przychodach brutto poniżej 1 mln USD. Ponadto wymagana jest licencja komercyjna.

A co z Aspose.PDF?

Aspose.PDF zapewnia szeroki zakres funkcji związanych z plikami PDF w ramach licencji komercyjnej (ceny zaczynają się od ~999 USD/programista). Konwersja HTML wykorzystuje niestandardowy silnik, a nie przeglądarkę — podobnie jak iText, obsługuje podstawowy HTML, ale nie renderuje poprawnie nowoczesnych funkcji CSS.

Głównym problemem jest stabilność platformy:Asposeopiera się na System.Drawing.Common, co wymaga libgdiplus w systemie Linux. W .NET 6+ firma Microsoft wycofała tę funkcję dla platform innych niż Windows. Programiści zgłaszają wycieki pamięci charakterystyczne dla wdrożeń w systemie Linux, które nie występują w systemie Windows. W środowiskach wyłącznie dla systemu Windows sprawdzi się Aspose. W przypadku wdrożeń wielopłatformowych lub kontenerowych łańcuch zależności stwarza ciągłe ryzyko.

W jaki sposóbIronPDFobsługuje konwersję HTML do PDF?

IronPDF osadza Chromium bezpośrednio w pakiecie NuGet. CSS Flexbox, Grid, wlasnosci niestandardowe, @font-face, zapytania medialne i JavaScript dzialaja tak samo, jak w Chrome. Wynik jest zgodny z przeglądarką, ponieważ wykorzystuje ten sam silnik renderujący.

using IronPdf;

var renderer = new ChromePdfRenderer();

string html = @"
<!DOCTYPE html>
<html>
<head>
    <style>
        :root { --primary: #2563eb; }
        body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
        .card {
            background: linear-gradient(135deg, #f8fafc, #e2e8f0);
            border-radius: 12px; padding: 24px; text-align: center;
        }
        .card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
        .card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
        table { width: 100%; border-collapse: collapse; margin-top: 30px; }
        th { background: var(--primary); color: white; padding: 12px; text-align: left; }
        td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
    </style>
</head>
<body>
    <div class='grid'>
        <div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
        <div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
        <div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
    </div>
    <table>
        <tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
        <tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
        <tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
    </table>
</body>
</html>";

var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();

string html = @"
<!DOCTYPE html>
<html>
<head>
    <style>
        :root { --primary: #2563eb; }
        body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
        .card {
            background: linear-gradient(135deg, #f8fafc, #e2e8f0);
            border-radius: 12px; padding: 24px; text-align: center;
        }
        .card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
        .card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
        table { width: 100%; border-collapse: collapse; margin-top: 30px; }
        th { background: var(--primary); color: white; padding: 12px; text-align: left; }
        td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
    </style>
</head>
<body>
    <div class='grid'>
        <div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
        <div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
        <div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
    </div>
    <table>
        <tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
        <tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
        <tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
    </table>
</body>
</html>";

var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

Dim html As String = "
<!DOCTYPE html>
<html>
<head>
    <style>
        :root { --primary: #2563eb; }
        body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
        .card {
            background: linear-gradient(135deg, #f8fafc, #e2e8f0);
            border-radius: 12px; padding: 24px; text-align: center;
        }
        .card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
        .card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
        table { width: 100%; border-collapse: collapse; margin-top: 30px; }
        th { background: var(--primary); color: white; padding: 12px; text-align: left; }
        td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
    </style>
</head>
<body>
    <div class='grid'>
        <div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
        <div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
        <div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
    </div>
    <table>
        <tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
        <tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
        <tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
    </table>
</body>
</html>"

Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("report.pdf")
$vbLabelText   $csharpLabel

To uzywa CSS Grid z minmax, wlasnosciami niestandardowymi, linear-gradient, border-radius, oraz :root selektorami. Kazda z tych funkcji zawodzi w pdfHTML iTexta, lamie sie w wkhtmltopdf, i nie istnieje w PdfSharp lub QuestPDF.

Jak przeprowadzić migrację z innych bibliotek?

Dla zespolow migrujacych ziTextlub wkhtmltopdf,IronPDFakceptuje URL-e bezposrednio — przydatne, gdy istniejacy proces generuje pliki HTML lub serwuje strony:

using IronPdf;

var renderer = new ChromePdfRenderer();

// Convert from URL — useful when migrating fromwkhtmltopdfURL-based workflows
var pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly");
pdf.SaveAs("report.pdf");

// Convert from local HTML file
var pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html");
pdfFromFile.SaveAs("invoice.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();

// Convert from URL — useful when migrating fromwkhtmltopdfURL-based workflows
var pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly");
pdf.SaveAs("report.pdf");

// Convert from local HTML file
var pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html");
pdfFromFile.SaveAs("invoice.pdf");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

' Convert from URL — useful when migrating from wkhtmltopdf URL-based workflows
Dim pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly")
pdf.SaveAs("report.pdf")

' Convert from local HTML file
Dim pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html")
pdfFromFile.SaveAs("invoice.pdf")
$vbLabelText   $csharpLabel

Wdrożenie

IronPDF działa na systemach Windows (x64), Linux (x64, ARM64), macOS (x64, Apple Silicon) oraz w kontenerach Docker. Konfiguracja Docker to standardowy obraz .NET Standard:

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Bez instalacji Chromium, bez zależności od bibliotek natywnych, bez konfiguracji piaskownicy.

Licencjonowanie: Licencje wieczyste zaczynaja sie od $2,998. Ceny opublikowane na stronie IronPDF.com. Bez licencji AGPL, bez opłat za dokument, bez progów przychodów.

Testy wydajności

Przetestowano na maszynie wirtualnej Azure Standard_D4s_v3 (4 vCPU, 16 GB pamięci RAM) z systemem Ubuntu 22.04 i platformą .NET 8. Dokument testowy: 200-elementowy szablon faktury HTML z układem CSS Grid, osadzonymi obrazami i wykresem wygenerowanym przez JavaScript. Każdy pomiar jest średnią z 50 iteracji po 5-iteracyjnym okresie rozgrzewki.

ScenariuszIronPDFPuppeteer SharpiText pdfHTMLwkhtmltopdf
Prosty HTML (bez JS)~150ms~500 ms~200 ms~200 ms
Złożone CSS (Flexbox/Grid)~250ms~600 msUszkodzony wynikUszkodzony wynik
Treści renderowane w JavaScript~350ms~800msBłędy (brak silnika JS)Nie powiodlo sie/Czesciowe
Pamieć na operacje~80 MB~150 MB~60MB~50MB
Zimny start (pierwsza generacja)2–5s3–8s<1s<1s

iText iwkhtmltopdfcharakteryzują się szybszym uruchamianiem, ponieważ nie inicjują silnika przeglądarki. Jednak porównanie to ma znaczenie tylko w scenariuszach, w których wszystkie biblioteki generują poprawny wynik — a w przypadku złożonej zawartości CSS lub JavaScript tylkoIronPDFiPuppeteer Sharpzapewniają użyteczne wyniki.

Uwaga: Są to typowe obserwacje dotyczące określonego sprzętu. Wydajność będzie zależała od złożoności kodu HTML, długości dokumentu oraz zasobów serwera. Przed podjęciem decyzji przetestuj rozwiązanie na rzeczywistych obciążeniach.

Porównanie funkcji

FunkcjaIronPDFiTextPuppeteer SharpwkhtmltopdfPDFSharpQuestPDFAspose
HTML do PDFTak (Chromium)Ograniczony (CSS 2.1)Tak (Chrome)PrzestarzalyNieNieOgraniczone
CSS Flexbox/GridTakNieTakNieNieNieNie
Wykonanie kodu JavaScriptTakNieTakOgraniczoneNieNieNie
Wielopłatformowe (bez libgdiplus)TakTakTakNie dotyczyCzęścioweTakNie
Opublikowane ceny$2,998+Nie (od $15K–$210K/rok)Bezpłatne (MIT)BezpłatneBezpłatne (MIT)Bezpłatne <$1M$999+
Aktywna konserwacjaTakTakTakPorzuconyTakTakTak

Którą bibliotekę wybrać?

Szablony HTML z nowoczesnym CSS → IronPDF zapewnia wbudowany Chromium bez zewnętrznego zarządzania procesami. Jeśli Twój zespół potrafi zarządzać infrastrukturą przeglądarek,Puppeteer Sharpjest realną alternatywą.

Programowe generowanie dokumentów na podstawie danych, bez HTML → QuestPDF oferuje elegancki, płynny interfejs API. Nie wybieraj tego, oczekując konwersji HTML.

Prosta manipulacja PDF (laczenie, dzielenie, znak wodny) → PDFSharp jest darmowy i zdolny do zadan niezaleznych od HTML.

Unikaj w nowych projektach:wkhtmltopdf(porzucony, CVE),iTextbez licencji komercyjnej (pułapka AGPL),Asposena Linuksie (wycieki pamięci).

Kluczowe pytanie brzmi: czy w Twoim procesie pracy wykorzystujesz szablony HTML? W takim przypadku tylko rozwiązania oparte na Chromium generują poprawny wynik przy użyciu nowoczesnego CSS. Jeśli tak nie jest, wybór zależy od preferencji dotyczących API i ograniczeń licencyjnych.

Zwróć uwagęAspose, DinkToPdf, NReco, PDFSharp, PuppeteerSharp, QuestPDF,iTextiwkhtmltopdfsą zarejestrowanymi znakami towarowymi ich właścicieli. Ta strona nie jest zwiazana, rekomendowana ani sponsorowana przezAsposePty Ltd, CodeFlint, DinkToPdf, NReco, PuppeteerSharp, empira Software GmbH,iTextGroup lub wkhtmltopdf. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.