A PuppeteerSharp alternative for .NET.
PuppeteerSharp prints PDFs by driving a Chrome browser that your application downloads and launches. SelectPdf ships Chromium in a NuGet package and converts with one class: no browser download at run time, nothing to launch, and no Linux packages to install. In the same open benchmark it completed three times as many conversions a minute as PuppeteerSharp on Windows and on Linux, kept its memory flat over 1,000 conversions, and produced every PDF/A and PDF/UA level valid.
using SelectPdf.Universal;
HtmlToPdf converter = new HtmlToPdf();
converter.Options.PdfPageSize = PdfPageSize.Letter;
converter.Options.CssMediaType =
HtmlToPdfCssMediaType.Print;
PdfDocument doc = converter.ConvertHtmlString(html);
doc.Save("report.pdf");
doc.Close();
HTML to PDF in SelectPdf, with PuppeteerSharp's Letter paper and print media.
Both libraries at a glance.
PuppeteerSharp is a free .NET port of Puppeteer: it downloads Chrome for Testing and controls it over the DevTools protocol, for testing, scraping and printing pages to PDF. SelectPdf.Universal is the cross-platform .NET PDF library from SelectPdf: it converts URLs and HTML to PDF with a Chromium engine shipped in a NuGet package, and also creates, edits and reads PDF documents.
| SelectPdf | PuppeteerSharp | |
|---|---|---|
| HTML engine | Chromium 154.0.8037.58, in a NuGet engine package | Chrome for Testing, downloaded at run time (25.13.1: 154.0.8037.57; 25.14.0: 155) |
| Setup in code | new HtmlToPdf() | BrowserFetcher.DownloadAsync(), Puppeteer.LaunchAsync(), NewPageAsync() |
| Disk per application | the engine package in the publish output | Chrome for Testing plus Chrome Headless Shell, about 435 MB + 273 MB on Windows x64 |
| Linux | no OS packages (glibc 2.30 or later) | the Debian/Ubuntu libraries from Puppeteer's troubleshooting guide |
| PDF tools | merge, split, watermarks, forms, text extraction, PDF to image, signatures, PDF/A, PDF/UA | printing to PDF only |
| License | commercial, perpetual, from USD 499 | free, MIT (the downloaded Chrome build is Google's) |
Moving over replaces the browser lifecycle with one converter class and turns PdfOptions into converter.Options, with margins in points and timeouts in seconds. The sections below show where SelectPdf comes out ahead, how to migrate PuppeteerSharp code to SelectPdf, and what it costs.
Where SelectPdf is ahead.
Measurements from the 2026-10-15 run of the open pdf-benchmarks harness: PuppeteerSharp 25.13.1 and SelectPdf.Universal 26.5.0 on the same machine.
| SelectPdf | PuppeteerSharp | |
|---|---|---|
| Browser download and launch | none | at run time, in your code |
| Conversions a minute, 8 at once, Windows | 1,078 | 325 |
| Conversions a minute, 8 at once, Linux | 2,597 | 798 |
| Test pages converted faster, Windows | 18 of 18 | 0 of 18 |
| Memory after 1,000 conversions, Windows | 140 → 156 MB | 325 → 584 MB |
| Memory after 1,000 conversions, Linux | 369 → 389 MB | 398 → 683 MB |
| Memory, 8 conversions at once, Windows | 467 MB | 1,461 MB |
| PDF/A, PDF/UA and PDF/X levels valid | all 17 | none produced |
| Docker on the official .NET image | runs, root and non-root | does not run without extra packages |
| Merge, watermark, forms, signatures, text extraction | included | not available |
Every number above, with the rest of the measurements, is in the full table below.
Migrate from PuppeteerSharp.
The same task in both: an HTML string converted to PDF. Install the library and the engine package for your platform:
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.win-x64
PuppeteerSharp:
using PuppeteerSharp;
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
await page.PdfAsync("report.pdf");
SelectPdf:
using SelectPdf.Universal;
HtmlToPdf converter = new HtmlToPdf();
converter.Options.PdfPageSize = PdfPageSize.Letter;
converter.Options.CssMediaType = HtmlToPdfCssMediaType.Print;
converter.Options.WebPageWidth = 816;
converter.Options.Tagged = true;
PdfDocument doc = converter.ConvertHtmlString(html);
doc.Save("report.pdf");
doc.Close();
The four options reproduce PuppeteerSharp's defaults (Letter paper, print media, the page laid out at the paper width, tagged output); without them SelectPdf uses A4, screen media and a 1,024 px layout width. Six tasks side by side, including URLs, page setup, headers and footers and an ASP.NET Core endpoint, each built and checked, are in the migration guide on GitHub.
Map PuppeteerSharp to SelectPdf.
SelectPdf margins and header heights are in points (20 mm is 57 pt), the layout width in pixels, and timeouts in seconds.
| PuppeteerSharp | SelectPdf |
|---|---|
BrowserFetcher.DownloadAsync(), Puppeteer.LaunchAsync() | nothing to call |
page.SetContentAsync(html) + page.PdfAsync(path) | ConvertHtmlString(html) + doc.Save(path) |
page.GoToAsync(url) + page.PdfAsync(path) | ConvertUrl(url) + doc.Save(path) |
EmulateMediaTypeAsync(MediaType.Screen) | Options.CssMediaType |
SetViewportAsync(...) | Options.WebPageWidth / WebPageHeight |
Format, Landscape | Options.PdfPageSize, Options.PdfPageOrientation |
MarginOptions ("20mm") | MarginTop and the others (points) |
HeaderTemplate, FooterTemplate | Header / Footer with PdfHtmlSection or PdfTextSection |
pageNumber, totalPages classes | {page_number}, {total_pages} |
WaitForFunctionAsync(...) | Options.WindowStatus or selectpdf.start() |
EvaluateExpressionAsync(...) | Options.PostLoadingScript |
SetCookieAsync, SetExtraHttpHeadersAsync | Options.HttpCookies, Options.HttpHeaders |
Tagged, Outline | Options.Tagged, PdfBookmarkOptions |
The complete mapping is in the migration guide; every class is in the documentation.
Pricing compared.
PuppeteerSharp is free under the MIT license; the Chrome for Testing build it downloads is Google's. SelectPdf is commercial: a Single Developer license is USD 499 and a Single Developer OEM license, for SaaS and software sold to customers, USD 799, both perpetual with one year of updates (SelectPdf pricing). The SelectPdf Community Edition converts HTML to PDF for free for documents up to 5 pages, and the full library runs as a 30-day trial without a key. One license covers SelectPdf.Universal and the classic Windows library.
The full measurement.
PuppeteerSharp 25.13.1 and SelectPdf.Universal 26.5.0 on every axis of the 2026-10-15 run, on one machine under Windows and Linux, generated from the raw data. The method, per-page numbers and eight other .NET PDF libraries are on the results page; the harness is open source, so every number can be re-run.
| PuppeteerSharp | SelectPdf | |
|---|---|---|
| HTML to PDF, geometric mean of 18 page medians, ms (Windows) | 486 | 212 |
| Pages converted faster than the other library (Windows) | 0 of 18 | 18 of 18 |
| Cold start, ms (Windows) | 1,003 | 714 |
| Throughput, conversions/min, 1 / 4 / 8 at once, sync (Windows) | 125 / 297 / 325 | 353 / 948 / 1,078 |
| Throughput, conversions/min, 1 / 4 / 8 at once, async (Windows) | 127 / 309 / 346 | 323 / 926 / 1,158 |
| Memory, one page / 8 at once, MB (Windows) | 418 / 1,461 | 205 / 467 |
| Memory over 1,000 conversions, MB (Windows) | 325 → 584, 0 failures | 140 → 156, 0 failures |
| Page that never loads: gives up after, s (Windows) | 30 | 60 |
| Parallel output equals sequential (Windows) | 32 / 32 | 32 / 32 |
| HTML to PDF, geometric mean of 18 page medians, ms (Linux) | 150 | 134 |
| Pages converted faster than the other library (Linux) | 4 of 18 | 14 of 18 |
| Cold start, ms (Linux) | 528 | 745 |
| Throughput, conversions/min, 1 / 4 / 8 at once, sync (Linux) | 450 / 797 / 798 | 619 / 1,929 / 2,597 |
| Throughput, conversions/min, 1 / 4 / 8 at once, async (Linux) | 471 / 798 / 838 | 603 / 1,872 / 2,639 |
| Memory, one page / 8 at once, MB (Linux) | 498 / 789 | 462 / 727 |
| Memory over 1,000 conversions, MB (Linux) | 398 → 683, 0 failures | 369 → 389, 0 failures |
| Page that never loads: gives up after, s (Linux) | 30 | 60 |
| Parallel output equals sequential (Linux) | 32 / 32 | 32 / 32 |
| Content checks passed (Windows) | 38 / 42 | 46 / 48 |
| Content checks passed (Linux) | 38 / 42 | 46 / 48 |
| Fidelity vs Chrome, mean SSIM (Windows) | 0.949 | 0.984 |
| PDF/A, PDF/UA, PDF/X: levels produced / valid (of 17) | 0 / 0 | 17 / 17 |
| Tagged structure checks | – | 112 / 112 |
| Text extraction, lowest F1 | not supported | 0.97 |
| Search | not supported | supported |
| PDF to image | not supported | supported |
| Word to PDF | not supported | supported |
| Redaction | not supported | supported |
| Merge | not supported | supported |
| Split | not supported | supported |
| Watermark | not supported | supported |
| Digital signature | not supported | supported |
| Form filling | not supported | supported |
| ZUGFeRD / Factur-X | not supported | supported |
| Docker, minimal image (Linux) | does not run, 79 MB | runs, 270 MB |
| Docker, vendor-documented image (Linux) | – | not needed (the minimal image runs) |
| Publish size win-x64 / linux-x64, MB | 8.4 / 8.3 | 477.0 / 449.6 |
Linux, Docker and ARM64.
PuppeteerSharp's Chrome needs the system libraries listed in Puppeteer's troubleshooting guide, and in Docker it either runs as a non-root user with --cap-add=SYS_ADMIN or turns Chrome's sandbox off. SelectPdf's engine package bundles the Chromium libraries, so the official .NET images work without apt-get lines or extra docker run flags, as root or as a non-root user. Linux x64 and ARM64 each have their own package; Linux needs glibc 2.30 or later. On Alpine, conversions run in the SelectPdf engine container.
Step by step: HTML to PDF in Docker on Linux. Both libraries' Dockerfiles are side by side in the C# PDF libraries comparison.
Questions, answered.
Something else? Write to support@selectpdf.com with your settings and, if you can, the HTML.