DEV Community

Cover image for SelectPdf in 2026: A Cross-Platform .NET PDF Library for Windows, Linux and macOS
Florentin Badea
Florentin Badea

Posted on Originally published at Medium

SelectPdf in 2026: A Cross-Platform .NET PDF Library for Windows, Linux and macOS

I'm the founder of SelectPdf. For most of its twelve years, our .NET PDF library ran on Windows only. If you needed HTML to PDF on Linux or macOS, we pointed you to our REST API. Articles written before October 2026 describe it that way, and for the library of that time they were right.

In October 2026 that changed. Version 26.4 introduced SelectPdf.Universal, a new edition of the library that runs locally on Windows, Linux and macOS. Version 26.5, released ten days later, added a remote engine that also takes it to Alpine Linux and Kubernetes. This article walks through what the library looks like now, with code.

Where it runs

SelectPdf.Universal runs on:

  • Windows x64 and x86
  • Linux x64 and ARM64
  • macOS on Apple Silicon
  • Docker containers, and through the remote engine, Alpine Linux and Kubernetes

It targets .NET 8, .NET 10 and .NET Standard 2.0, so it works on everything from .NET Framework 4.6.1 to the current .NET releases. We have verified it on Azure App Service, Azure Functions, AWS Lambda and Google Cloud Run.

Installing takes one package for the platform you deploy to:

dotnet add package SelectPdf.Universal.Linux
Enter fullscreen mode Exit fullscreen mode

The others are SelectPdf.Universal.Windows, SelectPdf.Universal.Linux.Arm64 and SelectPdf.Universal.MacOS. Each one installs the library together with the rendering engine for its platform. A build that targets several platforms references SelectPdf.Universal plus one SelectPdf.Universal.Native.<rid> package per runtime instead.

On Linux, the engine package brings the libraries Chromium needs, so a Docker image needs no apt-get step:

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -f net10.0 -o /out

FROM mcr.microsoft.com/dotnet/runtime:10.0-noble
# no apt-get: the Chromium closure ships in SelectPdf.Universal.Native.linux-x64
WORKDIR /app
COPY --from=build /out ./
ENTRYPOINT ["dotnet", "/app/PdfSample.dll"]
Enter fullscreen mode Exit fullscreen mode

The native packages need glibc 2.30 or later (Debian 11+, Ubuntu 20.04+, RHEL 9+). For musl-based images such as Alpine, there is the remote engine, below.

HTML to PDF

The API is the one SelectPdf users already know, in a new namespace:

using SelectPdf.Universal;

HtmlToPdf converter = new HtmlToPdf();

// A4 portrait with 20 mm margins (margins are in points: 20 mm = 57 pt)
converter.Options.PdfPageSize = PdfPageSize.A4;
converter.Options.PdfPageOrientation = PdfPageOrientation.Portrait;
converter.Options.MarginTop = 57;
converter.Options.MarginBottom = 57;
converter.Options.MarginLeft = 57;
converter.Options.MarginRight = 57;

// a web page, by URL
PdfDocument page = converter.ConvertUrl("https://selectpdf.com");
page.Save("webpage.pdf");
page.Close();
Enter fullscreen mode Exit fullscreen mode

The rendering engine is Chromium, so flexbox, grid, web fonts and current JavaScript render the way they do in the browser. There is one engine, so there is no engine to choose.

Async, with cancellation

The library has a Task-based API: ConvertUrlAsync and ConvertHtmlStringAsync on the HTML converter, async methods on the HTML to image converter, and AddAsync for HTML placed into an existing document. Each takes a CancellationToken:

using SelectPdf.Universal;

// in a web app or service: await the conversion, and give up after 30 seconds
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));

HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = await converter.ConvertUrlAsync("https://selectpdf.com", timeout.Token);

byte[] pdf = doc.Save();   // the PDF as bytes, for a response or a database
doc.Close();
Enter fullscreen mode Exit fullscreen mode

Up to eight conversions run in parallel by default, and the rest queue in arrival order. Version 26.5 made parallel conversions much faster.

New in 26.4: beyond HTML to PDF

Word to PDF. docx, doc, rtf, txt, WordML, HTML and Markdown files convert directly, on every platform:

using SelectPdf.Universal;

WordToPdf converter = new WordToPdf();
converter.Options.PageSetup = WordPageSetup.Document;   // each page keeps its Word size
converter.Options.Bookmarks = WordBookmarks.Headings;   // the PDF outline comes from the headings

PdfDocument doc = converter.ConvertFile("report.docx");
doc.Save("report.pdf");
doc.Close();
Enter fullscreen mode Exit fullscreen mode

Redaction. Text, images and form fields are removed from the file, not just covered. You can redact by area, by exact text or by regular expression:

using System.Text.RegularExpressions;
using SelectPdf.Universal;

var redactor = new PdfRedactionManager();
redactor.Load("statement.pdf");
redactor.AddRedaction(new Regex(@"\d{4}-\d{4}-\d{4}-\d{4}"));   // card numbers
redactor.OverlayText = "REDACTED";
redactor.Redact();
redactor.Save("statement-redacted.pdf");
redactor.Close();
Enter fullscreen mode Exit fullscreen mode

Signature validation, with trust and revocation checks, next to signing.

Standards. Accessible PDF/UA-1 and PDF/UA-2 output, on new, converted and loaded documents; PDF/A-4, 4e and 4f archiving; and ZUGFeRD / Factur-X electronic invoices. New documents are encrypted with AES-256 by default.

New in 26.5: the remote engine

SelectPdf 26.5 cover: C# code that sets RenderingEngine.ChromiumRemote, with tables of contents, fillable forms from HTML and hardware-key signing

The library can hand its conversions to an engine running in a container on another machine. Set the rendering engine and point the library at the engine's address:

using SelectPdf.Universal;

// convert in the SelectPdf engine container instead of on this machine
GlobalProperties.RenderingEngine = RenderingEngine.ChromiumRemote;
GlobalProperties.EngineEndpoint = "http://selectpdf-engine:8080";

HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertUrl("https://selectpdf.com");
doc.Save("output.pdf");
doc.Close();
Enter fullscreen mode Exit fullscreen mode

HTML to PDF, HTML to image, PDF to text and PDF to image then run in the engine container, and the application needs no native package at all. That is what brings SelectPdf to Alpine Linux and other musl-based systems. The engine image is published for linux/amd64 and linux/arm64 as ghcr.io/selectpdf/engine, with a Helm chart, and the documentation has tested setups for Docker Compose, Kubernetes, Azure Container Apps, Amazon ECS and Google Cloud Run.

Also new in 26.5

Tables of contents with page numbers, built while the HTML converts. The library can add leaders and page numbers to a table of contents the page already has, write one into the page from its headings, or insert separate table of contents pages:

HtmlToPdf converter = new HtmlToPdf();
converter.Options.TableOfContents.Mode = PdfTableOfContentsMode.GeneratePages;
converter.Options.TableOfContents.HeadingSelectors = new[] { "h1", "h2" };
PdfDocument doc = converter.ConvertHtmlString(html);
Enter fullscreen mode Exit fullscreen mode

Fillable PDF forms from HTML forms. With converter.Options.CreateFormFields = true, the text inputs, check boxes, radio buttons, drop-down lists and text areas of the page become PDF form fields, with their values, labels and options.

Signing with keys the library never sees. PKCS#11 tokens, smart cards and HSMs, the certificate store, or a remote signing service, with long-term validation (PAdES B-LT) and SHA-384 or SHA-512:

// the key stays on the token; the library sends it the digest to sign
var signer = new PdfPkcs11Signer("path/to/pkcs11-library", pin, "token label", "key label");
var signature = new PdfDigitalSignatureElement(50, 50, 240, 80, signer);
signature.DigestAlgorithm = PdfSignatureDigest.SHA384;
signature.EnableLtv = true;   // embed the revocation data (PAdES B-LT)
doc.Pages[0].Add(signature);
doc.Save("signed.pdf");
Enter fullscreen mode Exit fullscreen mode

More archiving levels: PDF/A-1a, PDF/A-2a and PDF/A-2u. Smaller files from pages with large photos, and text search in Chinese, Japanese, Korean, Hindi, Thai and Arabic.

For projects still on wkhtmltopdf

wkhtmltopdf is no longer maintained. Four new open-source packages, SelectPdf.Compat.DinkToPdf and SelectPdf.Compat.WkHtmlToPdfDotNet, each with a .Licensed version, keep the API of the DinkToPdf and Haukcode.WkHtmlToPdfDotNet wrappers. Existing converter code builds as it is and renders with Chromium. They are MIT licensed, on GitHub.

The free Community Edition

SelectPdf.HtmlToPdf.Universal is free, with no watermark, and converts HTML to PDF on the same platforms, up to 5 pages per document. It has the same one-package installs (SelectPdf.HtmlToPdf.Windows, .Linux, .Linux.Arm64 and .MacOS) and supports the remote engine. Your code can check whether the page limit cut a document:

PdfDocument doc = converter.ConvertHtmlString(html);
if (converter.ConversionResult.PdfPageCountBeforeLimit > converter.ConversionResult.PdfPageCount)
    Console.WriteLine("the 5-page limit cut this document");
Enter fullscreen mode Exit fullscreen mode

The classic library

The classic SelectPdf library for Windows is still maintained and receives selected updates; it is version 26.5 too. It remains the choice for .NET Framework 2.0 and later, and for projects that use the WebKit or Blink engines.

Moving an existing project to SelectPdf.Universal is mostly mechanical: new packages, a new namespace, and SelectPdf's own drawing types in place of System.Drawing. 82% of the classic library's public types and members carry over unchanged apart from the namespace, and the migration guide lists every change.

Licensing

One perpetual license per developer, from $499, covers both SelectPdf.Universal and the classic Windows library.

Links

If you have a question about running SelectPdf on your platform, ask in the comments. I read them all.

Top comments (0)