<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Florentin Badea</title>
    <description>The latest articles on DEV Community by Florentin Badea (@florentin_badea).</description>
    <link>https://dev.to/florentin_badea</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948041%2Fb69049a6-b977-4157-9634-d3fab9658a92.jpg</url>
      <title>DEV Community: Florentin Badea</title>
      <link>https://dev.to/florentin_badea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/florentin_badea"/>
    <language>en</language>
    <item>
      <title>SelectPdf vs IronPDF. An IronPDF alternative for .NET.</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Thu, 22 Oct 2026 14:03:45 +0000</pubDate>
      <link>https://dev.to/florentin_badea/-5dj3</link>
      <guid>https://dev.to/florentin_badea/-5dj3</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i" class="crayons-story__hidden-navigation-link"&gt;SelectPdf vs IronPDF in 2026, measured on Windows and Linux&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/florentin_badea" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948041%2Fb69049a6-b977-4157-9634-d3fab9658a92.jpg" alt="florentin_badea profile" class="crayons-avatar__image" width="551" height="659"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/florentin_badea" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Florentin Badea
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Florentin Badea
                
                
              
              &lt;div id="story-author-preview-content-4896380" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/florentin_badea" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948041%2Fb69049a6-b977-4157-9634-d3fab9658a92.jpg" class="crayons-avatar__image" alt="" width="551" height="659"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Florentin Badea&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i" id="article-link-4896380"&gt;
          SelectPdf vs IronPDF in 2026, measured on Windows and Linux
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dotnet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dotnet&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/csharp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;csharp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/pdf"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;pdf&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/benchmark"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;benchmark&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>SelectPdf vs IronPDF in 2026, measured on Windows and Linux</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:26:04 +0000</pubDate>
      <link>https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i</link>
      <guid>https://dev.to/florentin_badea/selectpdf-vs-ironpdf-in-2026-measured-on-windows-and-linux-491i</guid>
      <description>&lt;p&gt;&lt;em&gt;Most comparisons still describe the Windows-only SelectPdf of 2025. Here is what changed, and what an open benchmark measured on the same machine under both systems.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm the founder of SelectPdf. The numbers below come from an open benchmark that anyone can re-run, and this article also says where IronPDF comes out ahead.&lt;/p&gt;

&lt;p&gt;Search for "SelectPdf vs IronPDF" today and most results will tell you that SelectPdf runs only on Windows, renders with WebKit and cannot redact PDFs or convert Word files. Until October 2026 that was a fair description of the classic SelectPdf library. It does not describe the current one, so this article compares the two libraries as they stand in October 2026, with measurements instead of adjectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in SelectPdf in 2026
&lt;/h2&gt;

&lt;p&gt;SelectPdf 26.4, released in October 2026, introduced SelectPdf.Universal, a new library on NuGet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It runs on Windows (x64 and x86), Linux (x64 and ARM64), macOS and in Docker.&lt;/li&gt;
&lt;li&gt;It targets .NET 8, .NET 10 and .NET Standard 2.0.&lt;/li&gt;
&lt;li&gt;It renders HTML with Chromium only: Chromium 154 in version 26.5.&lt;/li&gt;
&lt;li&gt;Besides HTML to PDF, it creates, merges, splits and edits PDF documents, fills forms, extracts text, redacts text, images and form fields, and signs with certificates, USB tokens, smart cards and HSMs, with long-term validation. It also converts Word documents to PDF and produces PDF/A and PDF/UA output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Version 26.5 added a remote engine, so the library also runs on Alpine Linux and in Kubernetes.&lt;/p&gt;

&lt;p&gt;The classic SelectPdf library is still maintained for Windows projects, with its WebKit, Blink and Chromium engines. One license covers both libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same task in both libraries
&lt;/h2&gt;

&lt;p&gt;Converting an HTML string looks much the same in both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SelectPdf.Universal&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// IronPDF&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The differences show up in the details. SelectPdf sets margins in points where IronPDF uses millimetres, and its page-number placeholders are {page_number} and {total_pages} rather than {page} and {total-pages}. Moving a project across is mostly renaming and converting units; a migration guide on GitHub shows each change on a working project.&lt;/p&gt;

&lt;p&gt;Both libraries render HTML with Chromium, but not the same version. SelectPdf.Universal 26.5 ships Chromium 154, which has been stable since September 2026. IronPDF 2026.10.3 ships Chromium 109, stable since January 2023, according to the libcef binary in its native packages. CSS and JavaScript features added to Chromium since then will not appear in IronPDF's output.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it was measured
&lt;/h2&gt;

&lt;p&gt;The numbers come from an open benchmark of ten .NET PDF libraries, run on 2026-10-15. It used SelectPdf.Universal 26.5.0 and IronPdf 2026.10.3, the latest versions on NuGet that day.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One machine (Intel Core Ultra 9 275HX, 24 logical cores) ran Windows, and Ubuntu 24.04 under WSL2 on the same hardware.&lt;/li&gt;
&lt;li&gt;Speed is the median of 9 conversions per page in a warm process, over 18 test pages, with the libraries interleaved so that machine drift is spread out.&lt;/li&gt;
&lt;li&gt;Throughput is the number of conversions a minute, measured for 60 seconds with 8 conversions running at once.&lt;/li&gt;
&lt;li&gt;Memory is measured across the library's whole process tree, so a browser engine's helper processes count too.&lt;/li&gt;
&lt;li&gt;Fidelity compares each PDF, tile by tile, with a Chrome screenshot of the same page (SSIM, where 1.0 means identical).&lt;/li&gt;
&lt;li&gt;Every PDF/A and PDF/UA file is validated by veraPDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The harness, the test pages and the raw results are all public, and the links are at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Windows, IronPDF is faster
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTML to PDF, average over 18 pages&lt;/td&gt;
&lt;td&gt;212 ms&lt;/td&gt;
&lt;td&gt;186 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pages converted faster&lt;/td&gt;
&lt;td&gt;4 of 18&lt;/td&gt;
&lt;td&gt;14 of 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start (new process, one page)&lt;/td&gt;
&lt;td&gt;714 ms&lt;/td&gt;
&lt;td&gt;657 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversions a minute, 8 at once&lt;/td&gt;
&lt;td&gt;1,078&lt;/td&gt;
&lt;td&gt;1,735&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory, 8 conversions at once&lt;/td&gt;
&lt;td&gt;467 MB&lt;/td&gt;
&lt;td&gt;788 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory over 1,000 conversions (MB)&lt;/td&gt;
&lt;td&gt;140 → 156&lt;/td&gt;
&lt;td&gt;226 → 630&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On Windows, IronPDF converted most pages faster, started faster and completed more conversions a minute. SelectPdf was faster on four pages: long documents, HTML strings, web fonts and CJK text. It also used less memory, and its memory barely grew over 1,000 conversions in one process.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Linux, SelectPdf is faster
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Linux (Ubuntu 24.04, WSL2)&lt;/th&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTML to PDF, average over 18 pages&lt;/td&gt;
&lt;td&gt;134 ms&lt;/td&gt;
&lt;td&gt;169 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pages converted faster&lt;/td&gt;
&lt;td&gt;14 of 18&lt;/td&gt;
&lt;td&gt;4 of 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start (new process, one page)&lt;/td&gt;
&lt;td&gt;745 ms&lt;/td&gt;
&lt;td&gt;1,090 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversions a minute, 8 at once&lt;/td&gt;
&lt;td&gt;2,597&lt;/td&gt;
&lt;td&gt;1,520&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory, 8 conversions at once&lt;/td&gt;
&lt;td&gt;727 MB&lt;/td&gt;
&lt;td&gt;1,322 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory over 1,000 conversions (MB)&lt;/td&gt;
&lt;td&gt;369 → 389&lt;/td&gt;
&lt;td&gt;451 → 1,076&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On Linux the order reverses. SelectPdf converted 14 of the 18 pages faster, started faster and completed more conversions a minute at every concurrency the benchmark tested (1, 4 and 8, with both the synchronous and the asynchronous API). IronPDF was faster on a page with a JavaScript chart, on lazy-loaded and large images, and on the SelectPdf home page.&lt;/p&gt;

&lt;p&gt;The averages are geometric means of the per-page medians.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output quality
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Content checks passed&lt;/td&gt;
&lt;td&gt;46 of 48&lt;/td&gt;
&lt;td&gt;41 of 48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fidelity vs Chrome (Windows)&lt;/td&gt;
&lt;td&gt;0.984&lt;/td&gt;
&lt;td&gt;0.928&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fidelity vs Chrome (Linux)&lt;/td&gt;
&lt;td&gt;0.984&lt;/td&gt;
&lt;td&gt;0.919&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/A, PDF/UA, PDF/X files compliant&lt;/td&gt;
&lt;td&gt;17 of 17&lt;/td&gt;
&lt;td&gt;5 of 17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accessibility tag checks&lt;/td&gt;
&lt;td&gt;112 of 112&lt;/td&gt;
&lt;td&gt;42 of 112&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word to PDF, 8-page report: pages&lt;/td&gt;
&lt;td&gt;8 of 8&lt;/td&gt;
&lt;td&gt;6 of 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word to PDF, 8-page report: fidelity&lt;/td&gt;
&lt;td&gt;0.98&lt;/td&gt;
&lt;td&gt;0.47&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The content checks look for things such as missing text, cut-off content, embedded fonts, loaded images, page numbers, bookmarks, form fields and encryption. SelectPdf missed one wide-content page. IronPDF missed bookmarks, the requested encryption algorithm, lazy-loaded images, the wide-content page and an isolation check.&lt;/p&gt;

&lt;p&gt;IronPDF offers fewer standard levels (it has no PDF/A-2u or PDF/A-3u option, for example), and several of the files it did produce failed veraPDF validation.&lt;/p&gt;

&lt;p&gt;Both libraries merge, split, watermark, fill forms, sign, redact and produce Factur-X invoices, and every one of those checks passed for both. IronPDF was faster at redaction, signing and Factur-X; SelectPdf was faster at extracting text from long documents and at converting the 8-page Word report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;On the official mcr.microsoft.com/dotnet/runtime:8.0 image, with nothing installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SelectPdf converted pages both as root and as a non-root user. Its native package bundles the Chromium libraries it needs, so the Dockerfile has no apt-get step. Linux needs glibc 2.30 or later.&lt;/li&gt;
&lt;li&gt;IronPDF converted pages as root, but failed as a non-root user. Following IronPDF's own Docker guide, which installs 24 OS packages, it worked as both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IronPDF has the smaller footprint: its published app is 315 MB on linux-x64, against 450 MB for SelectPdf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Price
&lt;/h2&gt;

&lt;p&gt;Both libraries sell perpetual licenses with one year of updates and support included, and both charge a yearly renewal for updates after that. The prices below are in USD, read from both vendors' pages on 2026-10-22.&lt;/p&gt;

&lt;p&gt;SelectPdf licenses count developers and deployment machines. One license covers both SelectPdf.Universal and the classic library.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;th&gt;Developers&lt;/th&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Renewal/year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single Developer&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 machine&lt;/td&gt;
&lt;td&gt;$499&lt;/td&gt;
&lt;td&gt;$299&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single Developer OEM&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;unlimited, OEM&lt;/td&gt;
&lt;td&gt;$799&lt;/td&gt;
&lt;td&gt;$499&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-Developers&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;up to 10 machines&lt;/td&gt;
&lt;td&gt;$799&lt;/td&gt;
&lt;td&gt;$499&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-Developers OEM&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;unlimited, OEM&lt;/td&gt;
&lt;td&gt;$1,099&lt;/td&gt;
&lt;td&gt;$699&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td&gt;up to 100 machines&lt;/td&gt;
&lt;td&gt;$1,199&lt;/td&gt;
&lt;td&gt;$799&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise OEM&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td&gt;unlimited, OEM&lt;/td&gt;
&lt;td&gt;$1,599&lt;/td&gt;
&lt;td&gt;$999&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The renewal prices apply when you renew before the maintenance plan expires; after it has lapsed, renewal costs $399 to $1,299. Licenses can also be bought with a 3-year or 5-year maintenance plan.&lt;/p&gt;

&lt;p&gt;IronPDF licenses count developers, locations and projects. Using it in SaaS or in software you redistribute needs the OEM Redistribution add-on, which the licensing page offers with Plus, Professional and Unlimited, but not with Lite.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;th&gt;Developers&lt;/th&gt;
&lt;th&gt;Locations/projects&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Renewal/year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lite&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 / 1&lt;/td&gt;
&lt;td&gt;$999&lt;/td&gt;
&lt;td&gt;$599&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plus&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3 / 3&lt;/td&gt;
&lt;td&gt;$1,499&lt;/td&gt;
&lt;td&gt;$899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Professional&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;10 / 10&lt;/td&gt;
&lt;td&gt;$2,399*&lt;/td&gt;
&lt;td&gt;$1,799&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td&gt;$4,799*&lt;/td&gt;
&lt;td&gt;$3,599&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OEM Redistribution add-on (Plus and up)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+$1,999&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Shown as a 20% discount from $2,999 and $5,999 on 2026-10-22; Iron Software announces those prices from November 1, 2026&lt;/p&gt;

&lt;p&gt;The two price lists are not cut the same way, so here are the closest matches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 developer&lt;/td&gt;
&lt;td&gt;$499&lt;/td&gt;
&lt;td&gt;$999 (Lite)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 developer, SaaS or redistributed&lt;/td&gt;
&lt;td&gt;$799 (OEM)&lt;/td&gt;
&lt;td&gt;$3,498 (Plus + OEM add-on)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 developers&lt;/td&gt;
&lt;td&gt;$799&lt;/td&gt;
&lt;td&gt;$2,399 (Professional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited developers&lt;/td&gt;
&lt;td&gt;$1,199&lt;/td&gt;
&lt;td&gt;$4,799 (Unlimited)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited developers, OEM&lt;/td&gt;
&lt;td&gt;$1,599&lt;/td&gt;
&lt;td&gt;$6,798 (Unlimited + OEM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Renewal for 1 developer, a year&lt;/td&gt;
&lt;td&gt;$299&lt;/td&gt;
&lt;td&gt;$599&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The matches are approximate: IronPDF's smallest license with the OEM add-on is Plus, for three developers; its smallest for five developers is Professional, for ten; and SelectPdf's Enterprise license covers up to 100 deployment machines, where IronPDF's Unlimited has no location limit.&lt;/p&gt;

&lt;p&gt;SelectPdf also has a free Community Edition that converts HTML to PDF with no watermark, including in production, for documents of up to 5 pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one to choose
&lt;/h2&gt;

&lt;p&gt;Choose IronPDF if your servers run Windows and raw conversion speed matters most to you, or if you need a smaller deployment.&lt;/p&gt;

&lt;p&gt;Choose SelectPdf if you deploy to Linux or Docker, if you need a recent Chromium, or if you need PDF/A, PDF/UA or accessible output that passes validation. The same applies if you would rather not install OS packages in your images, or if license cost matters: SelectPdf costs less in each of the matches above.&lt;/p&gt;

&lt;p&gt;In either case, convert a few of your own documents with both libraries before you decide. The benchmark pages are not your pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There was one machine and one run per operating system. Linux ran in WSL2 on the same hardware, not on a separate server, so treat the numbers as relative.&lt;/li&gt;
&lt;li&gt;The commercial libraries ran on their trials, with evaluation watermarks masked before the images were compared.&lt;/li&gt;
&lt;li&gt;Each library was configured the way its vendor documents. If the setup is wrong for either library, the adapter code is public and corrections are welcome.&lt;/li&gt;
&lt;li&gt;The benchmark is maintained by SelectPdf. Public code and raw data make it checkable; they do not make it independent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SelectPdf vs IronPDF comparison: &lt;a href="https://selectpdf.com/comparisons/ironpdf-alternative/" rel="noopener noreferrer"&gt;https://selectpdf.com/comparisons/ironpdf-alternative/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full benchmark results (2026-10-15): &lt;a href="https://github.com/csharp-pdf/pdf-libraries-compared/blob/main/benchmarks/2026-10-15.md" rel="noopener noreferrer"&gt;https://github.com/csharp-pdf/pdf-libraries-compared/blob/main/benchmarks/2026-10-15.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark harness and methodology: &lt;a href="https://github.com/csharp-pdf/pdf-benchmarks" rel="noopener noreferrer"&gt;https://github.com/csharp-pdf/pdf-benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Migration guide from IronPDF: &lt;a href="https://github.com/selectpdf/migrate-to-selectpdf/tree/main/from-ironpdf" rel="noopener noreferrer"&gt;https://github.com/selectpdf/migrate-to-selectpdf/tree/main/from-ironpdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SelectPdf 26.4 and 26.5 release notes: &lt;a href="https://selectpdf.com/pdf-library/html/Release-Notes-v26-4.htm" rel="noopener noreferrer"&gt;https://selectpdf.com/pdf-library/html/Release-Notes-v26-4.htm&lt;/a&gt; and &lt;a href="https://selectpdf.com/pdf-library/html/Release-Notes-v26-5.htm" rel="noopener noreferrer"&gt;https://selectpdf.com/pdf-library/html/Release-Notes-v26-5.htm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SelectPdf.Universal on NuGet: &lt;a href="https://www.nuget.org/packages/SelectPdf.Universal" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/SelectPdf.Universal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>benchmark</category>
    </item>
    <item>
      <title>We compared 10 .NET PDF libraries on the same 14 C# tasks: here is the open benchmark</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Mon, 19 Oct 2026 12:41:10 +0000</pubDate>
      <link>https://dev.to/florentin_badea/we-compared-10-net-pdf-libraries-on-the-same-14-c-tasks-here-is-the-open-benchmark-4ibi</link>
      <guid>https://dev.to/florentin_badea/we-compared-10-net-pdf-libraries-on-the-same-14-c-tasks-here-is-the-open-benchmark-4ibi</guid>
      <description>&lt;p&gt;Choosing a PDF library for .NET usually means reading vendor pages, running a hello world and hoping the result holds up on real documents. We wanted something more useful: ten C# PDF libraries put through the same 14 tasks, each with a compilable sample, and measured on the same machine under Windows and Linux for speed, throughput, memory, output correctness, fidelity to Chrome, PDF/A and PDF/UA output, and Docker deployment.&lt;/p&gt;

&lt;p&gt;Everything is public: the harness, the 18 test pages, the raw results and every sample, so you can check any number or re-run it on your own hardware. This post explains how the measurements work, where each library comes out ahead (it is not the same library every time), and what the results can and cannot tell you.&lt;/p&gt;

&lt;p&gt;Disclosure: we make SelectPdf, one of the ten libraries in this comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why PDF library comparisons mislead
&lt;/h2&gt;

&lt;p&gt;Most comparisons you find measure one library on one page with its defaults, and the defaults differ more than you would expect. Some libraries print with print CSS media and drop backgrounds; some lay the page out at 1,024 pixels and scale it; some use Letter paper, others A4. One converts HTML with Chromium, another with its own layout engine that does not run JavaScript. A "hello world" timing tells you little about an invoice with web fonts, a chart drawn by script or a 40-page report.&lt;/p&gt;

&lt;p&gt;So we built the comparison around the same work for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fourteen tasks, one sample each
&lt;/h2&gt;

&lt;p&gt;Every library gets one compilable console project per task: hello world, an HTML string, a URL, page size and margins, a header and footer with page numbers, merge, watermark, form filling, text extraction, PDF to image, a digital signature, PDF/A output, an ASP.NET Core endpoint and a Linux Docker deployment. A task a library cannot do is marked &lt;em&gt;not supported&lt;/em&gt;, with a link to the vendor page that says so, instead of being left blank.&lt;/p&gt;

&lt;p&gt;Each sample is built and run, and a checker opens the output: the PDF must open, have the expected number of pages and contain the expected text. Licences, prices, platforms and engine versions on each library page link to the vendor's own page or the NuGet package, with the date they were checked.&lt;/p&gt;

&lt;p&gt;The ten: ABCpdf, Docotic.Pdf, IronPDF, iText with pdfHTML, PDFsharp / MigraDoc, Playwright, PuppeteerSharp, QuestPDF, SelectPdf and wkhtmltopdf (through the Haukcode.WkHtmlToPdfDotNet wrapper).&lt;/p&gt;

&lt;h2&gt;
  
  
  How the harness measures
&lt;/h2&gt;

&lt;p&gt;Each library runs behind a small adapter program, and one runner drives them all over the same 18 test pages: Bootstrap layouts, an invoice, web fonts, grid and flexbox, a chart drawn by JavaScript, lazy-loaded and large images, right-to-left and CJK text, a long document and more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; 9 conversions per page in a warm process after a discarded warm-up, with the libraries interleaved (A, B, C, A, B, C…) so machine drift is spread out. Medians are reported, and the summary uses the geometric mean over the pages every HTML library converted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput:&lt;/strong&gt; conversions a minute for 60 seconds at 1, 4 and 8 at once, through the synchronous API and the asynchronous one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; the proportional set size of the library's whole process tree, so browser-based engines with many processes are counted fairly, plus 1,000 conversions in one process to see whether memory grows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 48 content checks (text present, nothing cut off, fonts embedded, page numbers, bookmarks, form fields, encryption, isolation between conversions) and fidelity: each PDF is compared, tile by tile, with a Chrome screenshot of the same page using SSIM, with evaluation watermarks masked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standards:&lt;/strong&gt; every PDF/A and PDF/UA level a library offers is requested and validated twice by veraPDF; PDF/X-1a, which veraPDF does not cover, is checked structurally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; each published app runs in &lt;code&gt;mcr.microsoft.com/dotnet/runtime:8.0&lt;/code&gt; as root and as a non-root user, first with nothing added, then following the vendor's own Docker instructions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Windows and Linux on one machine
&lt;/h2&gt;

&lt;p&gt;The run used one machine (Intel Core Ultra 9 275HX, 24 logical cores): Windows, and Ubuntu 24.04 under WSL2 on the same hardware. That keeps the two operating systems comparable with each other, but WSL2 is not a separate Linux server, and your production machine is not this one. Treat the numbers as relative, not as capacity planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different libraries win different measures
&lt;/h2&gt;

&lt;p&gt;There is no overall score, because the winners change from one measure to the next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastest HTML to PDF per page on Windows: iText (183 ms), just ahead of IronPDF (186 ms).&lt;/li&gt;
&lt;li&gt;Fastest per page on Linux: Docotic.Pdf (107 ms), with Playwright at 108 ms.&lt;/li&gt;
&lt;li&gt;Most conversions a minute with 8 at once: IronPDF on Windows (1,735), SelectPdf on Linux (2,597).&lt;/li&gt;
&lt;li&gt;Least memory growth over 1,000 conversions: iText on both systems.&lt;/li&gt;
&lt;li&gt;Smallest publish size: PDFsharp (2.1 MB on linux-x64).&lt;/li&gt;
&lt;li&gt;Most content checks passed, closest to Chrome, most PDF/A, PDF/UA and PDF/X levels valid, and most PDF tasks offered: SelectPdf (46 of 48, 0.984, 17 of 17, 11 of 11).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some results are about deployment rather than speed. On the plain .NET runtime image, SelectPdf, iText, PDFsharp and QuestPDF ran as root and as a non-root user with nothing installed; ABCpdf, Docotic.Pdf and IronPDF needed the OS packages from their own instructions; Playwright, PuppeteerSharp and wkhtmltopdf did not run on the plain image.&lt;/p&gt;

&lt;p&gt;The full tables, with every page and every failed check, are on the results page linked below. If you only need documents built in C# without HTML, QuestPDF or PDFsharp may be all you need; if you already run Playwright for end-to-end tests, printing with it may be enough. The point of publishing everything is that you can make that call yourself. The same summary, with each library's licence and price, is on our &lt;a href="https://selectpdf.com/comparisons/csharp-pdf-libraries/" rel="noopener noreferrer"&gt;C# PDF libraries compared&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-run it
&lt;/h2&gt;

&lt;p&gt;The harness needs the .NET 8 SDK, Python 3 and veraPDF. Libraries that need a trial key read it from an environment variable and are skipped without one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run.sh             &lt;span class="c"&gt;# Linux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/run.ps1&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c"&gt;# Windows&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/run.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Linux&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c"&gt;# Windows, then the same run inside WSL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A full run takes about half an hour per operating system on an otherwise idle machine. The published Linux results came from the second Windows command, which runs the same harness inside WSL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One machine and one run per operating system; Linux is WSL2, not a separate host.&lt;/li&gt;
&lt;li&gt;Commercial libraries run on their free trial or evaluation; watermarks are masked before images are compared.&lt;/li&gt;
&lt;li&gt;Each library is configured the way its vendor documents. If we got your library's setup wrong, open an issue or a pull request: the adapter code for every library is in the harness repository, and corrections are welcome.&lt;/li&gt;
&lt;li&gt;The benchmark is maintained by a vendor whose library is included. Public code and raw data make it checkable; they do not make it independent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Comparison, samples and library pages: &lt;a href="https://github.com/csharp-pdf/pdf-libraries-compared" rel="noopener noreferrer"&gt;https://github.com/csharp-pdf/pdf-libraries-compared&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark harness and methodology: &lt;a href="https://github.com/csharp-pdf/pdf-benchmarks" rel="noopener noreferrer"&gt;https://github.com/csharp-pdf/pdf-benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full results of the 2026-10-15 run: &lt;a href="https://github.com/csharp-pdf/pdf-libraries-compared/blob/main/benchmarks/2026-10-15.md" rel="noopener noreferrer"&gt;https://github.com/csharp-pdf/pdf-libraries-compared/blob/main/benchmarks/2026-10-15.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>htmltopdf</category>
    </item>
    <item>
      <title>SelectPdf in 2026: A Cross-Platform .NET PDF Library for Windows, Linux and macOS</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Fri, 16 Oct 2026 08:01:57 +0000</pubDate>
      <link>https://dev.to/florentin_badea/selectpdf-in-2026-a-cross-platform-net-pdf-library-for-windows-linux-and-macos-4da0</link>
      <guid>https://dev.to/florentin_badea/selectpdf-in-2026-a-cross-platform-net-pdf-library-for-windows-linux-and-macos-4da0</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it runs
&lt;/h2&gt;

&lt;p&gt;SelectPdf.Universal runs on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows x64 and x86&lt;/li&gt;
&lt;li&gt;Linux x64 and ARM64&lt;/li&gt;
&lt;li&gt;macOS on Apple Silicon&lt;/li&gt;
&lt;li&gt;Docker containers, and through the remote engine, Alpine Linux and Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Installing takes one package for the platform you deploy to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package SelectPdf.Universal.Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;SelectPdf.Universal.Native.&amp;lt;rid&amp;gt;&lt;/code&gt; package per runtime instead.&lt;/p&gt;

&lt;p&gt;On Linux, the engine package brings the libraries Chromium needs, so a Docker image needs no apt-get step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-f&lt;/span&gt; net10.0 &lt;span class="nt"&gt;-o&lt;/span&gt; /out

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/runtime:10.0-noble&lt;/span&gt;
&lt;span class="c"&gt;# no apt-get: the Chromium closure ships in SelectPdf.Universal.Native.linux-x64&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /out ./&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "/app/PdfSample.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML to PDF
&lt;/h2&gt;

&lt;p&gt;The API is the one SelectPdf users already know, in a new namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// A4 portrait with 20 mm margins (margins are in points: 20 mm = 57 pt)&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Portrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginTop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginLeft&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginRight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// a web page, by URL&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"webpage.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async, with cancellation
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// in a web app or service: await the conversion, and give up after 30 seconds&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrlAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// the PDF as bytes, for a response or a database&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Up to eight conversions run in parallel by default, and the rest queue in arrival order. Version 26.5 made parallel conversions much faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  New in 26.4: beyond HTML to PDF
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Word to PDF.&lt;/strong&gt; docx, doc, rtf, txt, WordML, HTML and Markdown files convert directly, on every platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;WordToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WordToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WordPageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// each page keeps its Word size&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bookmarks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WordBookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// the PDF outline comes from the headings&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.docx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Redaction.&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;redactor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfRedactionManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"statement.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRedaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"\d{4}-\d{4}-\d{4}-\d{4}"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// card numbers&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OverlayText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"REDACTED"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Redact&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"statement-redacted.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;redactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Signature validation&lt;/strong&gt;, with trust and revocation checks, next to signing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standards.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  New in 26.5: the remote engine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8swm3hzc8mfur4gjz1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8swm3hzc8mfur4gjz1c.png" alt="SelectPdf 26.5 cover: C# code that sets RenderingEngine.ChromiumRemote, with tables of contents, fillable forms from HTML and hardware-key signing" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// convert in the SelectPdf engine container instead of on this machine&lt;/span&gt;
&lt;span class="n"&gt;GlobalProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromiumRemote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;GlobalProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EngineEndpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://selectpdf-engine:8080"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;ghcr.io/selectpdf/engine&lt;/code&gt;, with a Helm chart, and the documentation has tested setups for Docker Compose, Kubernetes, Azure Container Apps, Amazon ECS and Google Cloud Run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also new in 26.5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tables of contents with page numbers&lt;/strong&gt;, 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableOfContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTableOfContentsMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GeneratePages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableOfContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HeadingSelectors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"h2"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fillable PDF forms from HTML forms.&lt;/strong&gt; With &lt;code&gt;converter.Options.CreateFormFields = true&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signing with keys the library never sees.&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the key stays on the token; the library sends it the digest to sign&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;signer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfPkcs11Signer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/pkcs11-library"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"token label"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"key label"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfDigitalSignatureElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigestAlgorithm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfSignatureDigest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SHA384&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnableLtv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// embed the revocation data (PAdES B-LT)&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"signed.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  For projects still on wkhtmltopdf
&lt;/h2&gt;

&lt;p&gt;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, &lt;a href="https://github.com/selectpdf/selectpdf-compat-wkhtmltopdf" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free Community Edition
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConversionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageCountBeforeLimit&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConversionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the 5-page limit cut this document"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The classic library
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licensing
&lt;/h2&gt;

&lt;p&gt;One perpetual license per developer, from $499, covers both SelectPdf.Universal and the classic Windows library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://selectpdf.com/pdf-library-cross-platform/" rel="noopener noreferrer"&gt;SelectPdf.Universal overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selectpdf.com/pdf-library/html/Release-Notes-v26-4.htm" rel="noopener noreferrer"&gt;Release notes 26.4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selectpdf.com/pdf-library/html/Release-Notes-v26-5.htm" rel="noopener noreferrer"&gt;Release notes 26.5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selectpdf.com/pdf-library/html/Remote-Engine.htm" rel="noopener noreferrer"&gt;Remote engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://selectpdf.com/how-to/" rel="noopener noreferrer"&gt;How-to guides&lt;/a&gt;, with complete C# programs compiled and run on Windows and Linux&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selectpdf.com/community-edition/" rel="noopener noreferrer"&gt;Community Edition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have a question about running SelectPdf on your platform, ask in the comments. I read them all.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>docker</category>
    </item>
    <item>
      <title>HTML to PDF in a .NET Docker container, with no apt-get</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Mon, 05 Oct 2026 13:29:13 +0000</pubDate>
      <link>https://dev.to/florentin_badea/html-to-pdf-in-a-net-docker-container-with-no-apt-get-3fkb</link>
      <guid>https://dev.to/florentin_badea/html-to-pdf-in-a-net-docker-container-with-no-apt-get-3fkb</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work on SelectPdf, the library used in this article. It's a commercial library with a free Community Edition; both are covered at the end.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've ever generated PDFs from HTML in a Linux container, you probably know the drill: a headless browser needs a long list of shared libraries, so the Dockerfile grows an &lt;code&gt;apt-get install&lt;/code&gt; line with twenty-odd packages, then fonts, then sometimes &lt;code&gt;--no-sandbox&lt;/code&gt; or a bigger &lt;code&gt;--shm-size&lt;/code&gt;. Every base-image update is a chance for that list to break.&lt;/p&gt;

&lt;p&gt;This article builds a small ASP.NET Core service that converts web pages and HTML to PDF in a stock .NET runtime container, with none of that. It uses &lt;strong&gt;SelectPdf.Universal&lt;/strong&gt;, which renders with Chromium and ships the engine, with everything it needs, inside a NuGet package.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;

&lt;p&gt;A minimal API with two endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /pdf?url=…&lt;/code&gt; returns any web page as a PDF&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /pdf&lt;/code&gt; with an HTML body returns that HTML as a PDF&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. The project
&lt;/h2&gt;

&lt;p&gt;Create a web project and add two packages: the library, and the native engine for the platform the container runs on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-n&lt;/span&gt; PdfService
&lt;span class="nb"&gt;cd &lt;/span&gt;PdfService
dotnet add package SelectPdf.Universal
dotnet add package SelectPdf.Universal.Native.linux-x64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native package is the part that makes the container story simple: it carries the complete Chromium shared-library closure, including the modules Chromium loads at run time for HTTPS. &lt;code&gt;dotnet publish&lt;/code&gt; copies it into the output, so the image needs nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The code
&lt;/h2&gt;

&lt;p&gt;Replace &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// GET /pdf?url=https://example.com  -&amp;gt;  the page as a PDF download&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrlAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"page.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// POST /pdf with an HTML body  -&amp;gt;  that HTML as a PDF&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StreamReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ReadToEndAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"document.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The async methods take a &lt;code&gt;CancellationToken&lt;/code&gt;, so a client that disconnects cancels its conversion. &lt;code&gt;doc.Save()&lt;/code&gt; with no arguments returns the PDF as a byte array; &lt;code&gt;doc.Save("file.pdf")&lt;/code&gt; writes it to disk instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Dockerfile
&lt;/h2&gt;

&lt;p&gt;A standard multi-stage build. Note what's &lt;strong&gt;not&lt;/strong&gt; in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish PdfService.csproj &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:10.0&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app .&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; ASPNETCORE_URLS=http://+:8080&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "PdfService.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;apt-get&lt;/code&gt;, no fonts step, no Xvfb. The engine renders headless, so there's no display server. It also runs as the image's built-in non-root &lt;code&gt;app&lt;/code&gt; user: when the app is published on Linux, as it is inside this build, the native package makes the engine files readable and executable for every user.&lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;.dockerignore&lt;/code&gt; so local build output stays out of the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/
obj/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Build and run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; pdfservice &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 pdfservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A plain &lt;code&gt;docker run&lt;/code&gt;: no &lt;code&gt;--no-sandbox&lt;/code&gt;, no added capabilities, no privileged mode, no GPU and no &lt;code&gt;--shm-size&lt;/code&gt;. Docker's default 64 MB of shared memory is enough.&lt;/p&gt;

&lt;p&gt;Try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; page.pdf &lt;span class="s2"&gt;"http://localhost:8080/pdf?url=https://example.com"&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: text/html"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--data-binary&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h1&amp;gt;Hello from Docker&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-o&lt;/span&gt; hello.pdf http://localhost:8080/pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Fonts for every language
&lt;/h2&gt;

&lt;p&gt;Base images ship with no fonts at all. The library carries the Liberation fonts, so Latin, Greek and Cyrillic text renders correctly out of the box. For anything else (Chinese, Japanese, Korean, Arabic, Hebrew, Hindi, Thai and more, plus color emoji), add one package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package SelectPdf.Universal.Fonts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It copies the Noto fonts into your publish output at build time, so nothing gets installed in the image. With it referenced, this renders correctly in the same container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: text/html; charset=utf-8"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--data-binary&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;中文 日本語 العربية 😀&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-o&lt;/span&gt; languages.pdf http://localhost:8080/pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. ARM64, and building on a Mac
&lt;/h2&gt;

&lt;p&gt;For ARM64 containers (AWS Graviton, Ampere, or Docker on an Apple Silicon Mac), reference &lt;code&gt;SelectPdf.Universal.Native.linux-arm64&lt;/code&gt; instead of &lt;code&gt;linux-x64&lt;/code&gt;. Nothing else changes: the &lt;code&gt;mcr.microsoft.com/dotnet&lt;/code&gt; tags are multi-architecture, so the same &lt;code&gt;FROM&lt;/code&gt; lines pick the right image.&lt;/p&gt;

&lt;p&gt;One thing that catches people on a Mac: Docker Desktop runs &lt;strong&gt;Linux&lt;/strong&gt; containers, so the image needs &lt;code&gt;linux-arm64&lt;/code&gt;, not &lt;code&gt;osx-arm64&lt;/code&gt;. The macOS package is for apps running natively on macOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works, and what doesn't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Base images.&lt;/strong&gt; Any glibc-based .NET image. The engines need glibc 2.30 or newer, which covers Ubuntu 20.04+, Debian 11+ and RHEL 9+. Verified end to end, with a real HTTPS page and no packages installed: Debian 12, Ubuntu 22.04, Ubuntu 24.04, and Amazon Linux 2023 through a self-contained publish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not supported: Alpine.&lt;/strong&gt; The native engines need glibc, and Alpine uses musl. Stay on the default Debian or Ubuntu based tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources.&lt;/strong&gt; Chromium-based rendering likes CPU and memory. Plan for at least 1 core and 2 GB of RAM per container, and 2+ cores if you convert a lot under load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows containers&lt;/strong&gt; work too, on Windows Server Core, but they need a step to install the core Windows fonts. If Windows isn't a requirement, Linux containers are smaller and simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licensing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SelectPdf.Universal&lt;/strong&gt; is commercial. Without a license the output carries a trial watermark; with one, set &lt;code&gt;GlobalProperties.LicenseKey&lt;/code&gt; at startup. Licenses are perpetual and per developer, from $499, and one license covers both SelectPdf.Universal and the classic Windows library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SelectPdf.HtmlToPdf.Universal&lt;/strong&gt; is the free Community Edition: HTML to PDF only, free for commercial use, no watermark, up to 5 pages per document. The code in this article runs unchanged with it: reference &lt;code&gt;SelectPdf.HtmlToPdf.Universal&lt;/code&gt; instead of &lt;code&gt;SelectPdf.Universal&lt;/code&gt;, keep the same native package, and longer documents stop at five pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The whole container story comes down to two &lt;code&gt;dotnet add package&lt;/code&gt; lines and an ordinary Dockerfile. The documentation has more: a Docker deployment guide with Windows containers and batch jobs, and step-by-step pages for Azure Container Apps, AWS Lambda and Google Cloud Run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-platform library: &lt;a href="https://selectpdf.com/pdf-library-cross-platform/" rel="noopener noreferrer"&gt;https://selectpdf.com/pdf-library-cross-platform/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Launch post (what's new in 26.4): &lt;a href="https://selectpdf.com/selectpdf-universal-our-new-flagship-net-pdf-library-on-linux-macos-and-docker/" rel="noopener noreferrer"&gt;https://selectpdf.com/selectpdf-universal-our-new-flagship-net-pdf-library-on-linux-macos-and-docker/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and hit something this article doesn't cover, let me know in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>docker</category>
      <category>csharp</category>
      <category>pdf</category>
    </item>
    <item>
      <title>HTML to PDF in C#: The Complete Guide</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Tue, 07 Jul 2026 15:39:39 +0000</pubDate>
      <link>https://dev.to/florentin_badea/html-to-pdf-in-c-the-complete-guide-4b4p</link>
      <guid>https://dev.to/florentin_badea/html-to-pdf-in-c-the-complete-guide-4b4p</guid>
      <description>&lt;p&gt;This is a complete, hands-on guide to converting HTML to PDF in C# with the &lt;a href="https://selectpdf.com/pdf-library-for-net/" rel="noopener noreferrer"&gt;SelectPdf library for .NET&lt;/a&gt;. It works up from a three-line conversion to a production-ready workflow: every one of the 19 code steps is a full, compiling example taken straight from this project and rendered with the Chromium engine, so the code you copy is code that actually runs. Along the way you will build a real invoice from a data model, render complex CSS and paginating tables, control layout, handle dynamic and authenticated pages, secure and archive your output, integrate with ASP.NET Core, and finish with notes on running in production and troubleshooting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Add the SelectPdf packages, then build and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Select.Pdf.NetCore
dotnet add package Select.Pdf.NetCore.Chromium.Windows
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the project writes each result PDF to &lt;code&gt;output/&lt;/code&gt; and the preview images shown below into &lt;code&gt;images/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1 - Getting started&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert an HTML string to PDF&lt;/li&gt;
&lt;li&gt;Convert a URL to PDF&lt;/li&gt;
&lt;li&gt;Convert a local HTML file to PDF&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 2 - Build a real document&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build an invoice from a template&lt;/li&gt;
&lt;li&gt;Render complex HTML that paginates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 3 - Control the page layout&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set page size, orientation and margins&lt;/li&gt;
&lt;li&gt;Add headers and footers with page numbers&lt;/li&gt;
&lt;li&gt;Use HTML for headers and footers&lt;/li&gt;
&lt;li&gt;Generate bookmarks from HTML headings&lt;/li&gt;
&lt;li&gt;Produce a single-page PDF&lt;/li&gt;
&lt;li&gt;Add a watermark to every page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 4 - Dynamic and remote content&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wait for JavaScript-rendered content&lt;/li&gt;
&lt;li&gt;Inject custom CSS and run a script&lt;/li&gt;
&lt;li&gt;Control the browser viewport width&lt;/li&gt;
&lt;li&gt;Send HTTP headers and cookies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 5 - Secure, archive, integrate&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the document properties&lt;/li&gt;
&lt;li&gt;Password-protect and restrict permissions&lt;/li&gt;
&lt;li&gt;Create an archivable PDF/A-3 document&lt;/li&gt;
&lt;li&gt;Generate a PDF in ASP.NET Core&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 6 - Run it in production&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Running in production&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;FAQ&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frequently asked questions&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 1 - Getting started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Convert an HTML string to PDF
&lt;/h3&gt;

&lt;p&gt;The simplest conversion: render a raw HTML string into a PDF document in a few lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create the HTML to PDF converter and select the Chromium (CEF) engine.&lt;/span&gt;
&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Convert a raw HTML string into a PDF document.&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Hello, SelectPdf!&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;This PDF was generated from an HTML string in C#.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Save the document to disk, then release the native resources.&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flprgpwgvx9tycdh7m0j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flprgpwgvx9tycdh7m0j5.png" alt="Convert an HTML string to PDF" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Convert a URL to PDF
&lt;/h3&gt;

&lt;p&gt;Point the converter at any public web address and capture the rendered page as a PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Convert a live web page to a PDF document.&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9a9874y17oxw8h79wpj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9a9874y17oxw8h79wpj3.png" alt="Convert a URL to PDF" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Convert a local HTML file to PDF
&lt;/h3&gt;

&lt;p&gt;Render an HTML file from disk - together with its local CSS and images - into a PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ConvertUrl also accepts a local file path; relative CSS and images&lt;/span&gt;
&lt;span class="c1"&gt;// referenced by the file are resolved automatically.&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnlgmgyffhjmv9qywtin4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnlgmgyffhjmv9qywtin4.png" alt="Convert a local HTML file to PDF" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2 - Build a real document
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4. Build an invoice from a template
&lt;/h3&gt;

&lt;p&gt;Turn a C# data model into a styled, real-world invoice: branded header, a line-item table, VAT totals and a page-numbered footer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1) Your data - normally from a database or an order.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;LineItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SelectPdf Developer License"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Qty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UnitPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;499m&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;LineItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Priority Support (1 year)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;Qty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UnitPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;199m&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;LineItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"On-site training (per day)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;Qty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UnitPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;900m&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// 2) Render the data into an HTML template (see BuildInvoiceHtml in the source).&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildInvoiceHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INV-1042"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3) Convert to PDF, adding a footer with page numbers.&lt;/span&gt;
&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginTop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayFooter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;28&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PdfTextSection&lt;/span&gt; &lt;span class="n"&gt;pageNo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfTextSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Invoice INV-1042 - page {page_number} of {total_pages}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Arial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;pageNo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTextHorizontalAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageNo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjaor90b3617sc0e3guza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjaor90b3617sc0e3guza.png" alt="Build an invoice from a template" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Render complex HTML that paginates
&lt;/h3&gt;

&lt;p&gt;Prove the engine handles modern CSS - grid layout and a live web font - plus a long table whose header repeats on every page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Give the web font a moment to download before the page is captured.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MinPageLoadTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The report uses CSS grid, a Google web font, and a 30-row table.&lt;/span&gt;
&lt;span class="c1"&gt;// The table header repeats on every page (thead { display: table-header-group })&lt;/span&gt;
&lt;span class="c1"&gt;// and rows are kept whole (page-break-inside: avoid) - all controlled from CSS.&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;BuildReportHtml&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbjzv0ytpz5cbe1ovmfqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbjzv0ytpz5cbe1ovmfqc.png" alt="Render complex HTML that paginates" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3 - Control the page layout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6. Set page size, orientation and margins
&lt;/h3&gt;

&lt;p&gt;Control the physical page: choose A4, switch to landscape, and set the page margins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Page setup (margins are expressed in points; 72 points = 1 inch).&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Landscape&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginLeft&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;36&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginRight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;36&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginTop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;36&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;36&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;A4 Landscape&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;This page uses a custom size, orientation and half-inch margins.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyspdue0ru9ae0s7r1lpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyspdue0ru9ae0s7r1lpp.png" alt="Set page size, orientation and margins" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Add headers and footers with page numbers
&lt;/h3&gt;

&lt;p&gt;Draw a repeating header on every page and a footer that shows "Page X of Y".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Enable and populate the header.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayHeader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PdfTextSection&lt;/span&gt; &lt;span class="n"&gt;headerText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfTextSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SelectPdf Tutorial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Arial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FontStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bold&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;headerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTextHorizontalAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headerText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Enable and populate the footer with page numbering tokens.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayFooter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PdfTextSection&lt;/span&gt; &lt;span class="n"&gt;footerText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfTextSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Page {page_number} of {total_pages}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Arial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;footerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTextHorizontalAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;footerText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjjm7f272c6c7fcrjlz7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjjm7f272c6c7fcrjlz7m.png" alt="Add headers and footers with page numbers" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Use HTML for headers and footers
&lt;/h3&gt;

&lt;p&gt;Build a fully styled header or footer from an HTML fragment instead of plain text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Render the header from an HTML fragment so it can be fully styled with CSS.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayHeader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfHtmlSection&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfHtmlSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;div style='font-family:Arial;color:#b45309;font-size:18px;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"border-bottom:2px solid #b45309;padding:6px 0;'&amp;gt;Acme Corporation&amp;lt;/div&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoFitHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HtmlToPdfPageFitMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoFit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Report body&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;The header above is rendered from HTML.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhv6olyhq5qflau0o9xke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhv6olyhq5qflau0o9xke.png" alt="Use HTML for headers and footers" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Generate bookmarks from HTML headings
&lt;/h3&gt;

&lt;p&gt;Turn the document's headings into a clickable PDF bookmarks (outline) tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Build PDF bookmarks from the elements matching these CSS selectors.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfBookmarkOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CssSelectors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"h2"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Open the document with the bookmarks (outline) panel visible.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewerPreferences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfViewerPageMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseOutlines&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Chapter 1&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Introduction.&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h2&amp;gt;Section 1.1&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Details.&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Chapter 2&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;More content.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3l9rajspdzwf5xmz11o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3l9rajspdzwf5xmz11o.png" alt="Generate bookmarks from HTML headings" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Produce a single-page PDF
&lt;/h3&gt;

&lt;p&gt;Fit the whole document onto one continuous page with no page breaks - handy for receipts and tickets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Produce one continuous page sized to the content, instead of paginating.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenerateSinglePagePdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Single-page output&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;Section one.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Section two.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Section three.&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;All content stays on a single, taller page with no page breaks.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx1lryf4a00pkwdytsmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx1lryf4a00pkwdytsmg.png" alt="Produce a single-page PDF" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Add a watermark to every page
&lt;/h3&gt;

&lt;p&gt;Stamp a semi-transparent text watermark - such as CONFIDENTIAL or DRAFT - behind the content of every page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Draft report&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Every page of this document carries a watermark.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw a large, light "CONFIDENTIAL" behind the content of every page,&lt;/span&gt;
&lt;span class="c1"&gt;// using a background template that repeats on all pages.&lt;/span&gt;
&lt;span class="n"&gt;PdfFont&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PdfStandardFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;RectangleF&lt;/span&gt; &lt;span class="n"&gt;pageRect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;ClientRectangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PdfTemplate&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageRect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfTextElement&lt;/span&gt; &lt;span class="n"&gt;watermark&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfTextElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"CONFIDENTIAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;watermark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;210&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;watermark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTextHorizontalAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;watermark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frb3l0wd0pqc8rnbs8sgm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frb3l0wd0pqc8rnbs8sgm.png" alt="Add a watermark to every page" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4 - Dynamic and remote content
&lt;/h2&gt;

&lt;h3&gt;
  
  
  12. Wait for JavaScript-rendered content
&lt;/h3&gt;

&lt;p&gt;Give client-side scripts time to run so dynamic content is captured instead of a half-loaded page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Give client-side scripts time to run before the page is captured (seconds).&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MinPageLoadTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Dynamic content&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p id='status'&amp;gt;Loading...&amp;lt;/p&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;script&amp;gt;setTimeout(function(){"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"document.getElementById('status').innerHTML = 'Loaded by JavaScript!';"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"}, 800);&amp;lt;/script&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29wudz89gscu4jrxce3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29wudz89gscu4jrxce3m.png" alt="Wait for JavaScript-rendered content" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Inject custom CSS and run a script
&lt;/h3&gt;

&lt;p&gt;Restyle the page with your own CSS and execute JavaScript after load, without touching the source HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Inject additional CSS into the page before rendering.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomCSS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;"body { font-family: Arial; background: #fef3c7; }"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"h1 { color: #b45309; }"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Run JavaScript after the page has loaded (Chromium/Blink only).&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostLoadingScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;"document.body.insertAdjacentHTML('beforeend', '&amp;lt;p&amp;gt;Added by script.&amp;lt;/p&amp;gt;');"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Custom styled page&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;The amber background and heading color come from injected CSS.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4dom9y0r3r0igeh6h4gj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4dom9y0r3r0igeh6h4gj.png" alt="Inject custom CSS and run a script" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Control the browser viewport width
&lt;/h3&gt;

&lt;p&gt;Set the virtual browser width the HTML is laid out at, then shrink oversized layouts to fit the PDF page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Lay the HTML out at a fixed 1200px browser width...&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebPageWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebPageFixedSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...then shrink it so the wide layout fits the PDF page width.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoFitWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HtmlToPdfPageFitMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ShrinkOnly&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;div style='width:1200px;background:#e0f2fe;padding:24px'&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Wide layout&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;Rendered at a 1200px viewport, then shrunk to fit the page.&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2a66xlgtgikkogg3e75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2a66xlgtgikkogg3e75.png" alt="Control the browser viewport width" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Send HTTP headers and cookies
&lt;/h3&gt;

&lt;p&gt;Attach custom request headers and cookies to reach authenticated or localized pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Attach custom request headers (e.g. language, bearer token) and cookies.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accept-Language"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpCookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"demo-cookie-value"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.selectpdf.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuarkw11t32zg1icayf4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuarkw11t32zg1icayf4p.png" alt="Send HTTP headers and cookies" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 5 - Secure, archive, integrate
&lt;/h2&gt;

&lt;h3&gt;
  
  
  16. Set the document properties
&lt;/h3&gt;

&lt;p&gt;Fill in the PDF title, author, subject and keywords shown in the viewer and used by search indexes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Quarterly Report&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Prepared for Acme Corporation.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// These appear in the viewer's "Document Properties" dialog and in search indexes.&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Quarterly Report"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SelectPdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Q3 financial results"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keywords&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"report, quarterly, finance"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreationDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffwvv4li2gf2rl3vbhlrs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffwvv4li2gf2rl3vbhlrs.png" alt="Set the document properties" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  17. Password-protect and restrict permissions
&lt;/h3&gt;

&lt;p&gt;Encrypt the PDF with an owner password and disable copying and editing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Encrypt with an owner password and restrict what readers may do.&lt;/span&gt;
&lt;span class="c1"&gt;// (Set UserPassword as well to also require a password just to open the file.)&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OwnerPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"owner-secret"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanPrint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanCopyContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanEditContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Confidential&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Copying and editing are disabled on this document.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m171an3gvvkgatjmpk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m171an3gvvkgatjmpk4.png" alt="Password-protect and restrict permissions" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  18. Create an archivable PDF/A-3 document
&lt;/h3&gt;

&lt;p&gt;Emit a PDF/A-3B file suitable for long-term archiving and compliance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// PDF/A (and tagged/accessible) output requires the Chromium engine.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Emit a PDF/A-3B document suitable for long-term archiving.&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfStandard&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfStandard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfA3B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Archivable document&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;p&amp;gt;Saved as PDF/A-3B for long-term archiving and compliance.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPdf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F66juk1eseotfulsawi7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F66juk1eseotfulsawi7h.png" alt="Create an archivable PDF/A-3 document" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  19. Generate a PDF in ASP.NET Core
&lt;/h3&gt;

&lt;p&gt;Build the PDF in a controller action and stream it straight to the browser as a downloadable file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In an ASP.NET Core app, generate the PDF inside a controller action and stream it back.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/invoice/{id:int}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;HtmlToPdf&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RenderingEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"&amp;lt;h1&amp;gt;Invoice #&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Thank you for your order.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Save straight to a byte array - no temp file needed.&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Return it as a downloadable file (omit the file name to show it inline).&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"invoice-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F18oia6d0dlcg2a6qhshv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F18oia6d0dlcg2a6qhshv.png" alt="Generate a PDF in ASP.NET Core" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 6 - Run it in production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  20. Running in production
&lt;/h3&gt;

&lt;p&gt;SelectPdf runs entirely in-process and is Windows-only (x86 and x64) - there is no Linux or macOS build, and the host needs full trust. Factor that into your hosting choice before you commit.&lt;/p&gt;

&lt;p&gt;A converter instance is not thread-safe: create one HtmlToPdf per conversion rather than sharing one across threads, and cap parallelism with your own queue or semaphore - the library has no built-in concurrency limit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt; - ship Select.Pdf.dll together with Select.Html.dep and Select.Tools.dep; for the Chromium engine also deploy its runtime folder (the Chromium.Windows package copies it on build).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process bitness&lt;/strong&gt; - match the package to the process or you get a BadImageFormatException. Under IIS, Enable 32-Bit Applications = True runs x86, False runs x64.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt; - on 64-bit servers use the x64 package - it can allocate far more memory for image-heavy pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeouts&lt;/strong&gt; - the default navigation timeout is 60 seconds; raise Options.MaxPageLoadTime for slow pages and use MinPageLoadTime to wait for late assets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licensing&lt;/strong&gt; - set GlobalProperties.LicenseKey once at startup, before any conversion; the trial watermarks every page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Untrusted HTML&lt;/strong&gt; - disable scripts (Options.JavaScriptEnabled = false) and local file access (Options.DenyLocalFileAccess = true) when converting input you do not control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure&lt;/strong&gt; - App Service needs the Chromium engine (v26.2+, Basic plan or above); Virtual Machines and Cloud Services have no restrictions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  21. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;A handful of problems come up again and again - here is the quick fix for each.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blank page, or images and CSS missing from an HTML string&lt;/strong&gt; - pass a base URL: ConvertHtmlString(html, baseUrl) so relative URLs resolve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic content missing&lt;/strong&gt; - the page needs more time or a signal - set Options.MinPageLoadTime, or use WindowStatus / PostLoadingScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BadImageFormatException&lt;/strong&gt; - the x86 or x64 package does not match the process bitness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Could not find Select.Html.dep&lt;/strong&gt; - copy Select.Html.dep and Select.Tools.dep next to Select.Pdf.dll.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation timeout&lt;/strong&gt; - the page is slower than 60s, the URL is not resolvable from the server, or a proxy is required - raise MaxPageLoadTime or set Options.ProxyOptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An image splits across two pages&lt;/strong&gt; - set Options.KeepImagesTogether = true, or style the img with page-break-inside: avoid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OutOfMemoryException&lt;/strong&gt; - an image-heavy page - use the x64 package and optimize the images.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  22. Frequently asked questions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Is SelectPdf free?&lt;/strong&gt; - The &lt;a href="https://selectpdf.com/community-edition/" rel="noopener noreferrer"&gt;Community Edition&lt;/a&gt; (Select.HtmlToPdf) is free for HTML to PDF with a 5-page cap. This tutorial uses the commercial &lt;a href="https://selectpdf.com/pdf-library-for-net/" rel="noopener noreferrer"&gt;Select.Pdf library&lt;/a&gt; - a paid license with the full feature set and no page limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it run on Linux, macOS or in Docker?&lt;/strong&gt; - No. The library is Windows-only (x86 and x64). To generate PDFs from Linux, host it on Windows or use the &lt;a href="https://selectpdf.com/html-to-pdf-api/" rel="noopener noreferrer"&gt;SelectPdf online HTML-to-PDF REST API&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which rendering engine should I use?&lt;/strong&gt; - Chromium (used throughout this tutorial) for modern HTML5, CSS3 and web fonts. WebKit is the embedded default with nothing extra to deploy; Blink is an alternative Chromium-based engine. Chromium and Blink each need their companion NuGet package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How do I remove the trial watermark?&lt;/strong&gt; - Set GlobalProperties.LicenseKey once at application startup, before any conversion. See &lt;a href="https://selectpdf.com/pricing/" rel="noopener noreferrer"&gt;.NET library pricing&lt;/a&gt; for license options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can it convert a page that requires login?&lt;/strong&gt; - Yes - send authentication cookies or headers (see Send HTTP headers and cookies), or post form data with HttpPostParameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there a limit on the number of pages?&lt;/strong&gt; - Not in the commercial library. The 5-page cap applies only to the free &lt;a href="https://selectpdf.com/community-edition/" rel="noopener noreferrer"&gt;Community Edition&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can I convert charts or content drawn by JavaScript?&lt;/strong&gt; - Yes, with the Chromium engine. Give scripts time to run with Options.MinPageLoadTime, or wait for a signal via WindowStatus (see Wait for JavaScript-rendered content).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the converter thread-safe?&lt;/strong&gt; - Use one HtmlToPdf instance per conversion and throttle concurrency yourself; see Running in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That is the full arc of HTML to PDF with SelectPdf - from a three-line string conversion to a real invoice, complex paginating layouts, security, archival compliance, ASP.NET Core integration and production hardening. Because the same Chromium engine powers every example, what you preview in the browser is what lands in the PDF. Start with a &lt;a href="https://selectpdf.com/downloads/" rel="noopener noreferrer"&gt;free trial&lt;/a&gt;, browse the &lt;a href="https://selectpdf.com/pdf-library/" rel="noopener noreferrer"&gt;full API reference&lt;/a&gt;, or try the &lt;a href="https://selectpdf.com/demo/" rel="noopener noreferrer"&gt;live C# demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>htmltopdf</category>
    </item>
    <item>
      <title>wkhtmltopdf is archived. A straight .NET migration guide (Windows, Linux, Docker and Azure)</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Sun, 05 Jul 2026 06:53:30 +0000</pubDate>
      <link>https://dev.to/florentin_badea/wkhtmltopdf-is-archived-a-straight-net-migration-guide-azure-windows-and-the-linux-caveat-2lc6</link>
      <guid>https://dev.to/florentin_badea/wkhtmltopdf-is-archived-a-straight-net-migration-guide-azure-windows-and-the-linux-caveat-2lc6</guid>
      <description>&lt;p&gt;&lt;em&gt;Updated October 2026 for SelectPdf 26.4: the library now runs on Linux, in Docker and on macOS on Apple Silicon as well, through the new SelectPdf.Universal packages. The Linux sections below are rewritten accordingly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If your .NET app still calls &lt;code&gt;DinkToPdf&lt;/code&gt;, &lt;code&gt;NReco.PdfGenerator&lt;/code&gt;, &lt;code&gt;Rotativa&lt;/code&gt;, or &lt;code&gt;WkHtmlToXSharp&lt;/code&gt; in production, you're shipping a wrapper around a binary that hasn't had a security patch in years — and one of its open holes is a &lt;strong&gt;CVSS 9.8&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;wkhtmltopdf's repository has been &lt;strong&gt;frozen and read-only since January 2023&lt;/strong&gt;, and the whole project org was archived by mid-2024. Development has stopped; nobody is accepting patches. The underlying QtWebKit engine forked from upstream around 2012 — no modern Flexbox, no Grid, no modern JavaScript, no ICU updates. And &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2022-35583" rel="noopener noreferrer"&gt;&lt;strong&gt;CVE-2022-35583&lt;/strong&gt;&lt;/a&gt; — an SSRF that lets an attacker reach your internal network by injecting an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; pointing at an internal IP — sits at &lt;strong&gt;CVSS 9.8, unpatched, and permanently so.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your app takes any user-influenced HTML and turns it into a PDF behind a public endpoint, that's not a "someday" cleanup. This post is the migration I'd actually run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I work on &lt;a href="https://selectpdf.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;SelectPdf&lt;/strong&gt;&lt;/a&gt;, a .NET PDF library, and the code below is SelectPdf because that's what I can vouch for line by line. I'll be honest about where it fits and where it doesn't. The broader point stands no matter what you pick: &lt;strong&gt;stop wrapping the dead binary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  First: Windows, Linux or Docker?
&lt;/h2&gt;

&lt;p&gt;I'm putting this at the top because it decides which packages you install, and nothing annoys an engineer more than finding it in paragraph 30. The good news: since version 26.4 there is a SelectPdf library for each case.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux, Docker, macOS on Apple Silicon — or any new project on modern .NET&lt;/strong&gt; — use &lt;strong&gt;SelectPdf.Universal&lt;/strong&gt;, the new flagship edition. It runs on Windows (x64, x86), Linux (x64, ARM64) and macOS on Apple Silicon, targets .NET 8 and .NET 10 plus .NET Standard 2.0, and renders with Chromium. You reference the library plus one native package per runtime you deploy to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An existing Windows or .NET Framework app&lt;/strong&gt; (IIS, a VM, Windows App Service) — the classic &lt;strong&gt;SelectPdf&lt;/strong&gt; library is a direct NuGet reference with one API call, supports .NET Framework 2.0 and later, and keeps its four rendering engines. It is still maintained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not on .NET, or you want nothing to deploy at all&lt;/strong&gt; — SelectPdf's &lt;strong&gt;&lt;a href="https://selectpdf.com/html-to-pdf-api/" rel="noopener noreferrer"&gt;Online REST API&lt;/a&gt;&lt;/strong&gt;: your app POSTs HTML and gets back PDF bytes over HTTPS, from any OS and any language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linux is where wkhtmltopdf hurts most today (newer glibc/libssl combos crash it, and it's no one's job to fix), so it's worth being specific. The &lt;code&gt;SelectPdf.Universal.Native.linux-x64&lt;/code&gt; and &lt;code&gt;linux-arm64&lt;/code&gt; packages carry the shared libraries Chromium needs, so a stock .NET runtime image converts pages with no &lt;code&gt;apt-get&lt;/code&gt; step, no X server and no special &lt;code&gt;docker run&lt;/code&gt; flags. That was verified with real HTTPS conversions on Debian 12, Ubuntu 22.04, Ubuntu 24.04 and Amazon Linux 2023. Latin, Greek and Cyrillic text prints with the fonts built into the library; for other scripts (Arabic, Chinese, Japanese, Korean, Hindi, Thai and more) add the optional &lt;code&gt;SelectPdf.Universal.Fonts&lt;/code&gt; package — also just a NuGet reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Why not just use Playwright or PuppeteerSharp?"
&lt;/h2&gt;

&lt;p&gt;Fair question, and for some teams that's the right call — they're free, cross-platform, and render with real Chromium. Here's the honest trade:&lt;/p&gt;

&lt;p&gt;With Playwright/PuppeteerSharp &lt;strong&gt;you operate the browser yourself.&lt;/strong&gt; You run a provisioning step (&lt;code&gt;playwright install&lt;/code&gt;, or bundling the binaries) and version-manage Chromium; you own the headless browser's lifecycle (zombie processes are real under load); you write your own concurrency throttling; and you write and maintain the automation script. On Linux you also chase down the font and shared-library packages so text doesn't render as boxes. On Windows App Service specifically, a raw headless browser tends to fight the sandbox too — a similar restriction to the Win32k/GDI wall that bites wkhtmltopdf (more on that below).&lt;/p&gt;

&lt;p&gt;Now the honest part, about what is &lt;em&gt;not&lt;/em&gt; a difference: SelectPdf also ships a bundled Chromium (CEF) runtime of broadly similar size — so the disk/image footprint is roughly a wash. Chromium is Chromium. What differs is &lt;strong&gt;who provisions and babysits it.&lt;/strong&gt; SelectPdf ships the matched runtime as a NuGet package that the build copies into your output automatically (no separate install step, no version drift), manages the browser for you, and caps parallelism through one built-in setting (&lt;code&gt;MaximumConcurrentConversions&lt;/code&gt;) — all behind a single call that returns the PDF. On Linux the native package already carries the shared libraries, so there is nothing to &lt;code&gt;apt-get&lt;/code&gt;. The engine is also built to run inside the App Service sandbox where a raw headless browser struggles. You don't write automation, and you don't provision a browser fleet.&lt;/p&gt;

&lt;p&gt;And there's a &lt;strong&gt;scope&lt;/strong&gt; difference that outlasts the migration: Playwright and Puppeteer &lt;em&gt;only print HTML to PDF.&lt;/em&gt; The moment your requirements grow past "render this page" — merge the invoice with a cover sheet, stamp a watermark, fill an AcroForm, digitally sign it, encrypt it, or extract the text back out for search — you're bolting on a &lt;em&gt;second&lt;/em&gt; PDF library (PdfSharp, iText, QuestPDF) and gluing two toolchains together. SelectPdf's &lt;strong&gt;commercial&lt;/strong&gt; library does the rendering &lt;em&gt;and&lt;/em&gt; that post-processing in one dependency: create PDFs from scratch, merge/split, stamp, fill and flatten forms, sign, encrypt, extract text and images, and emit tagged PDF/UA or PDF/A for accessibility and archival. (The free Community Edition is HTML→PDF only — these extras are the paid library.) If rendering HTML is genuinely all you'll ever do, that breadth doesn't matter and Puppeteer is fine. If you can feel a "can you also…" ticket coming, one library beats two.&lt;/p&gt;

&lt;p&gt;If your team is happy operating headless Chrome directly and never needs more than print-to-PDF, Puppeteer is a perfectly defensible choice. Pick with eyes open.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(One more note on the "is it even alive?" question, since wkhtmltopdf just taught everyone to ask it: SelectPdf shipped **v26.4 in October 2026&lt;/em&gt;* — the release that added SelectPdf.Universal for Linux, Docker and macOS on Apple Silicon — and the packages sit at &lt;strong&gt;23M+ NuGet downloads&lt;/strong&gt;. Actively maintained, with a paid tier where a human answers.)*&lt;/p&gt;




&lt;h2&gt;
  
  
  What "archived" actually means for your stack
&lt;/h2&gt;

&lt;p&gt;Most teams know wkhtmltopdf is "old" but haven't priced in the archive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No more CVE patches. Ever.&lt;/strong&gt; CVE-2022-35583 is filed, triaged, and will never be fixed upstream. Any SSRF reported after the repo froze lands in a read-only tracker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QtWebKit is frozen near 2012.&lt;/strong&gt; CSS Grid, container queries, modern JS, modern font features — anything your designers shipped in the last decade renders unreliably or not at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The .NET wrappers fix none of this.&lt;/strong&gt; &lt;code&gt;DinkToPdf&lt;/code&gt;, &lt;code&gt;NReco.PdfGenerator&lt;/code&gt;, &lt;code&gt;Rotativa.AspNetCore&lt;/code&gt;, &lt;code&gt;WkHtmlToXSharp&lt;/code&gt; all P/Invoke into the same &lt;code&gt;libwkhtmltox&lt;/code&gt; native binary. If wkhtmltopdf isn't patched, neither are they. (DinkToPdf itself hasn't shipped a release since 2017.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The migration, in code
&lt;/h2&gt;

&lt;p&gt;Here's a typical DinkToPdf setup — note the injected &lt;code&gt;IConverter&lt;/code&gt;, which DinkToPdf &lt;em&gt;requires&lt;/em&gt; as a singleton because the native lib can't be loaded repeatedly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Old: DinkToPdf&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DinkToPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DinkToPdf.Contracts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConverter&lt;/span&gt; &lt;span class="n"&gt;_converter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;InvoiceController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConverter&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;RenderInvoiceHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HtmlToPdfDocument&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;GlobalSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;PaperSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PaperKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Orientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Orientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Portrait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Margins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MarginSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Bottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Objects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ObjectSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"invoice-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same endpoint with SelectPdf.Universal — the same code runs on Windows, Linux and macOS on Apple Silicon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// New: SelectPdf.Universal&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SelectPdf.Universal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;RenderInvoiceHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageSize&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageOrientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Portrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginTop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// parameterless Save() returns the PDF bytes&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"invoice-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the classic Windows library the code is the same apart from &lt;code&gt;using SelectPdf;&lt;/code&gt; and one extra line to pick the engine: &lt;code&gt;converter.Options.RenderingEngine = RenderingEngine.Chromium;&lt;/code&gt; (SelectPdf.Universal renders with Chromium only, so it needs no such line).&lt;/p&gt;

&lt;h3&gt;
  
  
  About that lifecycle change
&lt;/h3&gt;

&lt;p&gt;You probably noticed the DI singleton is gone. That's deliberate, and it's the question a careful reviewer should ask: &lt;em&gt;is &lt;code&gt;new HtmlToPdf()&lt;/code&gt; per request safe?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes. Unlike DinkToPdf's converter, there's no "load the native lib exactly once" dance — you construct a converter per conversion. Parallelism is governed by a single knob, &lt;code&gt;HtmlToPdfOptions.MaximumConcurrentConversions&lt;/code&gt; (default &lt;code&gt;8&lt;/code&gt; in SelectPdf.Universal, &lt;code&gt;4&lt;/code&gt; in the classic library), which you set &lt;strong&gt;once at startup, before the first conversion&lt;/strong&gt;; requests beyond the limit queue for a slot rather than thrashing the box. So the mental model is: &lt;code&gt;new&lt;/code&gt; freely per request, cap total concurrency globally. If you'd rather keep a DI registration for testability, register the converter as transient — it doesn't need to be a singleton. SelectPdf.Universal also has &lt;code&gt;ConvertHtmlStringAsync&lt;/code&gt; and &lt;code&gt;ConvertUrlAsync&lt;/code&gt;, with cancellation, if your controllers are async.&lt;/p&gt;

&lt;h3&gt;
  
  
  NuGet packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SelectPdf.Universal — Windows, Linux, Docker, macOS on Apple Silicon.&lt;/span&gt;
&lt;span class="c"&gt;# The library, plus one native package per runtime you deploy to&lt;/span&gt;
&lt;span class="c"&gt;# (win-x64, win-x86, linux-x64, linux-arm64, osx-arm64).&lt;/span&gt;

&lt;span class="c"&gt;# Free Community Edition — HTML-&amp;gt;PDF only, 5-page cap;&lt;/span&gt;
&lt;span class="c"&gt;# no PDF post-processing (merge/split, forms, signing, encryption, extraction)&lt;/span&gt;
dotnet add package SelectPdf.HtmlToPdf.Universal
dotnet add package SelectPdf.Universal.Native.linux-x64

&lt;span class="c"&gt;# Commercial library once you outgrow 5 pages (from $499, perpetual)&lt;/span&gt;
dotnet add package SelectPdf.Universal
dotnet add package SelectPdf.Universal.Native.linux-x64

&lt;span class="c"&gt;# Optional on Linux: fonts for non-Latin scripts&lt;/span&gt;
dotnet add package SelectPdf.Universal.Fonts

&lt;span class="c"&gt;# Classic library on Windows (.NET Framework 4.6.1+ and modern .NET;&lt;/span&gt;
&lt;span class="c"&gt;# for .NET Framework 2.0/4.0 use Select.HtmlToPdf / Select.Pdf)&lt;/span&gt;
dotnet add package Select.HtmlToPdf.NetCore                    &lt;span class="c"&gt;# free CE&lt;/span&gt;
dotnet add package Select.HtmlToPdf.NetCore.Chromium.Windows
dotnet add package Select.Pdf.NetCore                          &lt;span class="c"&gt;# commercial&lt;/span&gt;
dotnet add package Select.Pdf.NetCore.Chromium.Windows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference several native packages if you build for several targets — developing on Windows and deploying to a Linux container is the common case. Be honest with yourself about that &lt;strong&gt;5-page cap&lt;/strong&gt;: it's free forever, but a typical multi-page invoice or report blows past it, so for real workloads the CE is effectively an evaluation and you'll want a license (&lt;a href="https://selectpdf.com/pricing/" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;). One perpetual license covers both the classic and the Universal library. The classic packages still target &lt;code&gt;net20&lt;/code&gt; and &lt;code&gt;net40&lt;/code&gt; (&lt;code&gt;Select.Pdf&lt;/code&gt;, &lt;code&gt;Select.HtmlToPdf&lt;/code&gt;) and &lt;code&gt;net461&lt;/code&gt;, &lt;code&gt;net472&lt;/code&gt; and &lt;code&gt;netstandard2.0&lt;/code&gt; (the &lt;code&gt;.NetCore&lt;/code&gt; packages), so migrating a .NET Framework app doesn't force a TFM bump.&lt;/p&gt;




&lt;h2&gt;
  
  
  Migrating your wkhtmltopdf flags
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ConvertHtmlString&lt;/code&gt; call is the easy 20%. The part that actually took you an afternoon in wkhtmltopdf was the CLI flags — headers, footers, page numbers, margins. Here's the mapping so you're not reverse-engineering it (it is the same in both libraries):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;wkhtmltopdf&lt;/th&gt;
&lt;th&gt;SelectPdf&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--page-size A4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Options.PdfPageSize = PdfPageSize.A4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--orientation Landscape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Options.PdfPageOrientation = PdfPageOrientation.Landscape&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--margin-top 20&lt;/code&gt; (etc.)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Options.MarginTop = 20&lt;/code&gt; (&lt;code&gt;MarginBottom/Left/Right&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--header-html header.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Options.DisplayHeader = true;&lt;/code&gt; then &lt;code&gt;converter.Header.Add(new PdfHtmlSection(url))&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--footer-html footer.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Options.DisplayFooter = true;&lt;/code&gt; then &lt;code&gt;converter.Footer.Add(...)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[page] / [topage]&lt;/code&gt; footer tokens&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{page_number}&lt;/code&gt; / &lt;code&gt;{total_pages}&lt;/code&gt; in a &lt;code&gt;PdfTextSection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--print-media-type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Options.CssMediaType = HtmlToPdfCssMediaType.Print&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Footer with page numbers, end to end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayFooter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageNo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfTextSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Page {page_number} of {total_pages}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PdfFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateStandardFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PdfStandardFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;pageNo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfTextHorizontalAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageNo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(On the classic library, &lt;code&gt;PdfTextSection&lt;/code&gt; takes a &lt;code&gt;System.Drawing.Font&lt;/code&gt; instead, for example &lt;code&gt;new System.Drawing.Font("Arial", 9)&lt;/code&gt;. SelectPdf.Universal has its own font types because System.Drawing is Windows-only on current .NET.)&lt;/p&gt;

&lt;p&gt;That — headers, footers, page numbering — all works in the &lt;strong&gt;free&lt;/strong&gt; Community Edition too, within the 5-page cap.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Azure App Service angle
&lt;/h2&gt;

&lt;p&gt;This is the one I get asked about most, so I'll be explicit.&lt;/p&gt;

&lt;p&gt;Azure App Service on Windows runs your app in a sandbox that blocks most User32/GDI32 (Win32k.sys) system calls — exactly what a GDI-based browser engine bundled in a .NET app needs. That's what makes wkhtmltopdf flaky there: P/Invoke load failures, missing GDI calls, font issues. Bundled headless-Chrome setups tend to struggle in the same sandbox too, for related reasons (process/GPU restrictions rather than GDI specifically).&lt;/p&gt;

&lt;p&gt;SelectPdf's Chromium engine — on &lt;strong&gt;Chromium 154&lt;/strong&gt; as of v26.4 — is built to run inside that sandbox. With SelectPdf.Universal the same code runs on &lt;strong&gt;App Service and Azure Functions, on both Windows and Linux&lt;/strong&gt;, on the Basic plan or above. One caveat worth knowing up front: on Windows, don't deploy with "Run From Package" (&lt;code&gt;WEBSITE_RUN_FROM_PACKAGE&lt;/code&gt;) — the engine cannot load its libraries from the mounted package; Zip Deploy and Web Deploy are fine. The classic library runs on Windows App Service (Basic and above), and on Azure Functions when hosted on a Premium or App Service plan. From your code it's the same call you already saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/report"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No "but it works locally" Slack thread at 11pm.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing the SSRF loop
&lt;/h2&gt;

&lt;p&gt;We opened on CVE-2022-35583, so let's not hand-wave the fix. Two things change when you migrate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You get a kill-switch for local-file exfiltration.&lt;/strong&gt; Set &lt;code&gt;HtmlToPdfOptions.DenyLocalFileAccess = true&lt;/code&gt; per conversion, or &lt;code&gt;GlobalProperties.ForceDenyLocalFileAccess = true&lt;/code&gt; process-wide, and &lt;code&gt;file://&lt;/code&gt; reads are blocked — shutting off the local-file-read half of the classic wkhtmltopdf attack. Both libraries have both switches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The internal-IP SSRF half is a network problem, and the right answer depends on trust.&lt;/strong&gt; If you convert genuinely untrusted, user-supplied HTML, don't render it on your own infrastructure next to your internal network at all — push it to the &lt;strong&gt;&lt;a href="https://selectpdf.com/html-to-pdf-api/" rel="noopener noreferrer"&gt;Online REST API&lt;/a&gt;&lt;/strong&gt;, so the conversion runs off your infrastructure, away from your internal network. And unlike the archived binary, when a rendering-side issue &lt;em&gt;is&lt;/em&gt; found, there's a maintained product that can actually ship a fix.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the real difference: not "SelectPdf is magically SSRF-proof," but "you get controls, isolation options, and someone who patches."&lt;/p&gt;




&lt;h2&gt;
  
  
  What to actually do this week
&lt;/h2&gt;

&lt;p&gt;A pragmatic checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;grep&lt;/code&gt; the solution for &lt;code&gt;DinkToPdf&lt;/code&gt;, &lt;code&gt;NReco.PdfGenerator&lt;/code&gt;, &lt;code&gt;Rotativa&lt;/code&gt;, &lt;code&gt;WkHtmlToX&lt;/code&gt;, &lt;code&gt;wkhtmltopdf&lt;/code&gt;. Every hit is a candidate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick the package for where you deploy&lt;/strong&gt; (see the top): SelectPdf.Universal for Linux, Docker and new projects; the classic library for an existing Windows or .NET Framework app; the Online API for anything else.&lt;/li&gt;
&lt;li&gt;Pick the one endpoint that renders user-influenced HTML. Highest-risk surface; start there.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dotnet add package SelectPdf.HtmlToPdf.Universal&lt;/code&gt; + &lt;code&gt;SelectPdf.Universal.Native.&amp;lt;rid&amp;gt;&lt;/code&gt; for each runtime you deploy to. Free, 5-page cap.&lt;/li&gt;
&lt;li&gt;Port the converter call and your header/footer flags using the mapping above; run your existing tests; eyeball the output diff.&lt;/li&gt;
&lt;li&gt;If layout shifts (it might — wkhtmltopdf has 2012-era quirks your CSS may lean on), the fix is almost always a CSS cleanup, not a converter tweak.&lt;/li&gt;
&lt;li&gt;Past 5 pages, get a license — but validate output on the free build first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If SelectPdf isn't right for your stack, &lt;strong&gt;still migrate.&lt;/strong&gt; Anything maintained beats an archived 9.8. And if you try it and hit a snag, drop me a line — I read the inbox.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-platform library (Windows, Linux, Docker, macOS on Apple Silicon): &lt;a href="https://selectpdf.com/pdf-library-cross-platform/" rel="noopener noreferrer"&gt;selectpdf.com/pdf-library-cross-platform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Free Community Edition: &lt;a href="https://selectpdf.com/community-edition/" rel="noopener noreferrer"&gt;selectpdf.com/community-edition&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo (paste any URL): &lt;a href="https://selectpdf.com/demo-universal/" rel="noopener noreferrer"&gt;selectpdf.com/demo-universal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Online API for any language: &lt;a href="https://selectpdf.com/html-to-pdf-api/" rel="noopener noreferrer"&gt;selectpdf.com/html-to-pdf-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs, including Docker and Azure deployment: &lt;a href="https://selectpdf.com/pdf-library/" rel="noopener noreferrer"&gt;selectpdf.com/pdf-library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NuGet: &lt;a href="https://www.nuget.org/packages/SelectPdf.HtmlToPdf.Universal/" rel="noopener noreferrer"&gt;&lt;code&gt;SelectPdf.HtmlToPdf.Universal&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this was useful, follow — the next post is &lt;strong&gt;"Top 10 HTML-to-PDF Libraries for C# / .NET (2026): A Practical Comparison,"&lt;/strong&gt; a fair head-to-head of the real options in this space, including where SelectPdf &lt;em&gt;isn't&lt;/em&gt; the right pick.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>azure</category>
      <category>pdf</category>
    </item>
    <item>
      <title>Stop your AI assistant from hallucinating PDF APIs: SelectPdf AI Tools</title>
      <dc:creator>Florentin Badea</dc:creator>
      <pubDate>Wed, 01 Jul 2026 14:38:44 +0000</pubDate>
      <link>https://dev.to/florentin_badea/stop-your-ai-assistant-from-hallucinating-pdf-apis-selectpdf-ai-tools-39nb</link>
      <guid>https://dev.to/florentin_badea/stop-your-ai-assistant-from-hallucinating-pdf-apis-selectpdf-ai-tools-39nb</guid>
      <description>&lt;p&gt;AI coding assistants are great at .NET boilerplate, but ask one to generate PDF code and it will happily invent a method that doesn't exist, or reach for a commercial feature while claiming it's in the free edition. The model is guessing from training data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/selectpdf/ai-tools" rel="noopener noreferrer"&gt;SelectPdf AI Tools&lt;/a&gt; fixes that. It's a free, open-source (MIT) set of &lt;strong&gt;Agent Skills&lt;/strong&gt; that give your assistant accurate, version-matched knowledge of the &lt;a href="https://selectpdf.com/pdf-library-for-net/" rel="noopener noreferrer"&gt;SelectPdf .NET library&lt;/a&gt; — real API signatures, working code, and the gotchas that matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's in it
&lt;/h3&gt;

&lt;p&gt;A single Claude Code plugin with two skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;selectpdf-lib-net&lt;/code&gt;&lt;/strong&gt; — the full commercial &lt;a href="https://selectpdf.com/pdf-library-for-net/" rel="noopener noreferrer"&gt;Select.Pdf library&lt;/a&gt;: HTML→PDF, building/editing PDFs, merge/split, AcroForms, digital signatures, encryption, PDF→text/image, and accessible tagged PDF/UA-1 and PDF/A-3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;selectpdf-lib-htmltopdf-net&lt;/code&gt;&lt;/strong&gt; — the free &lt;a href="https://selectpdf.com/community-edition/" rel="noopener noreferrer"&gt;Community Edition&lt;/a&gt; (HTML→PDF).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every sample is in &lt;strong&gt;C# and VB.NET&lt;/strong&gt;, tracking SelectPdf v26.3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install in Claude Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add selectpdf/ai-tools
/plugin install selectpdf@selectpdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right skill activates automatically when you ask it to work with PDFs in a .NET project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor, Copilot, Codex
&lt;/h3&gt;

&lt;p&gt;The same knowledge ships in each tool's native format — Cursor &lt;code&gt;.mdc&lt;/code&gt; rules, Copilot &lt;code&gt;.instructions.md&lt;/code&gt;, and a Codex &lt;code&gt;AGENTS.md&lt;/code&gt; guide. Drop the file into your repo and your assistant is up to speed.&lt;/p&gt;

&lt;p&gt;It's all open source, so you can read exactly what your assistant is being told — and send a PR if you spot something. 👉 &lt;a href="https://github.com/selectpdf/ai-tools" rel="noopener noreferrer"&gt;https://github.com/selectpdf/ai-tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to see the library itself? Browse the &lt;a href="https://selectpdf.com/pdf-library/" rel="noopener noreferrer"&gt;online documentation&lt;/a&gt; or try the &lt;a href="https://selectpdf.com/demo/" rel="noopener noreferrer"&gt;C# and VB.NET live demos&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>ai</category>
      <category>pdf</category>
    </item>
  </channel>
</rss>
