<?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: IronSoftware</title>
    <description>The latest articles on DEV Community by IronSoftware (@ironsoftware).</description>
    <link>https://dev.to/ironsoftware</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%2F714737%2F30e70529-1628-476f-9a14-3b157c0f5c82.png</url>
      <title>DEV Community: IronSoftware</title>
      <link>https://dev.to/ironsoftware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ironsoftware"/>
    <language>en</language>
    <item>
      <title>QuestPDF vs IronPDF: The Revenue Line the License Now Draws</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Tue, 04 Aug 2026 20:25:51 +0000</pubDate>
      <link>https://dev.to/ironsoftware/questpdf-vs-ironpdf-the-revenue-line-the-license-now-draws-l0p</link>
      <guid>https://dev.to/ironsoftware/questpdf-vs-ironpdf-the-revenue-line-the-license-now-draws-l0p</guid>
      <description>&lt;p&gt;QuestPDF earned its reputation honestly. It arrived when most .NET PDF libraries still made a team think in coordinates and canvases, and offered a fluent, code-first way to compose documents instead, containers and spacing expressed as C# and checked by the compiler. For a document generated straight from data that is often the whole job, and it stays the whole job until the revenue line in the new license moves, the document has to start as HTML, or a signature is required. The question an architecture review should ask is who absorbs those three gaps once the app ships, which is where &lt;a href="https://ironpdf.com/competitors/questpdf-vs-ironpdf/" rel="noopener noreferrer"&gt;a side-by-side read&lt;/a&gt; starts to price the choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full disclosure. We build &lt;a href="https://ironpdf.com/" rel="noopener noreferrer"&gt;IronPDF&lt;/a&gt; at Iron Software, and this read looks at where QuestPDF is strong and where its limits show for a .NET team.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A C# Document Tree
&lt;/h2&gt;

&lt;p&gt;QuestPDF's model is a document tree built entirely in C#. A page is described as nested containers, and the layout engine handles pagination, spacing, and overflow, so the same tree behaves consistently whether the invoice is one page or twenty, a pattern &lt;a href="https://ironpdf.com/examples/using-html-to-create-a-pdf/" rel="noopener noreferrer"&gt;a code-first generation path&lt;/a&gt; recognizes.&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;QuestPDF.Fluent&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;QuestPDF.Helpers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// compose a page as a container tree, no coordinate math&lt;/span&gt;
&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Page&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;=&amp;gt;&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;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PageSizes&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Margin&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;Unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Centimetre&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;Header&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoice #1042"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;SemiBold&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;FontSize&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total due: $250.00"&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;span class="nf"&gt;GeneratePdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its standards coverage reaches further than most code-first libraries, with PDF/A archival output, PDF/UA-1 accessibility tagging, and EN 16931 e-invoicing in the box, and the Companion app gives a live preview that refreshes on every save, which is where &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;a repeatable document workflow&lt;/a&gt; benefits. That composition model is the strength to weigh against the licensing and capability limits the next sections walk through, the gaps a &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;single rendering package&lt;/a&gt; is built to absorb.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Revenue Line the License Now Draws
&lt;/h2&gt;

&lt;p&gt;The limit that decides most QuestPDF evaluations is not a feature, it is the license, and it changed on 6 July 2026. Community License v3.0 stops applying once a company crosses $1M in annual gross revenue, and publicly traded companies and public-sector bodies are excluded regardless of what they earn, so a team that goes public or crosses that threshold gets a 90-day window to buy a commercial license before continued use is a breach, a compliance deadline landing on legal's desk rather than the deployment-based terms &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;a commercially licensed renderer&lt;/a&gt; sets out up front.&lt;/p&gt;

&lt;p&gt;The category of the license adds its own review. Community License v3.0 is source-available rather than an OSI-approved open-source license, so a procurement or open-source-compliance team signs off on a different class of terms than a permissive license would ask for, and eligibility runs on good-faith self-certification, which means the burden of tracking whether a company still qualifies sits with the company rather than any tooling that would catch a missed threshold, unlike the flat, deployment-scoped terms &lt;a href="https://ironpdf.com/competitors/questpdf-vs-ironpdf/" rel="noopener noreferrer"&gt;a published pricing page&lt;/a&gt; states outright.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Ships, and Under Which License
&lt;/h2&gt;

&lt;p&gt;What separates the two for a .NET project is the license terms and the layers QuestPDF does not ship, not the composition model it does, and the table draws from QuestPDF's own license guide and Document Operations documentation rather than an older post, read against &lt;a href="https://ironpdf.com/blog/compare-to-other-components/" rel="noopener noreferrer"&gt;what a single renderer covers&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;QuestPDF&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;License&lt;/td&gt;
&lt;td&gt;Community License v3.0, source-available&lt;/td&gt;
&lt;td&gt;Commercial, with support and updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revenue eligibility&lt;/td&gt;
&lt;td&gt;Community tier stops at $1M annual revenue&lt;/td&gt;
&lt;td&gt;No revenue-based restriction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public or public-sector use&lt;/td&gt;
&lt;td&gt;Excluded from the community tier&lt;/td&gt;
&lt;td&gt;No such exclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation model&lt;/td&gt;
&lt;td&gt;Code-first fluent composition&lt;/td&gt;
&lt;td&gt;Chromium &lt;code&gt;ChromePdfRenderer&lt;/code&gt;, plus a direct API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML and CSS to PDF&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Chromium &lt;code&gt;ChromePdfRenderer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load, merge, encrypt existing PDFs&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text extraction&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ExtractAllText&lt;/code&gt; at word level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital signatures&lt;/td&gt;
&lt;td&gt;Not supported natively&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfSignature&lt;/code&gt; with X.509 and timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fillable AcroForms&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/A and PDF/UA&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported and validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release cadence&lt;/td&gt;
&lt;td&gt;Four releases, June to July 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. QuestPDF and IronPDF across the &lt;a href="https://ironpdf.com/blog/compare-to-other-components/csharp-pdf-library-comparison/" rel="noopener noreferrer"&gt;licensing and capability questions&lt;/a&gt; a .NET review tends to price.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Document Has to Start as Code
&lt;/h2&gt;

&lt;p&gt;QuestPDF was built for code-composed documents, so the first gap is HTML, in both directions. There is no HTML or CSS engine, so an existing web page, email template, or Razor view has to be re-implemented inside the container model by hand, field by field, rather than pointing a renderer at markup that already exists, which is the whole job &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;a Chromium renderer&lt;/a&gt; is built to do.&lt;/p&gt;

&lt;p&gt;Signing and form-filling close the set. Digital signatures and fillable AcroForms are not part of QuestPDF, so signing a generated PDF means handing it to a second library like BouncyCastle for the certificate work and interactive forms are not supported at all, an extra dependency and extra certificate-handling code to keep patched next to &lt;a href="https://ironpdf.com/examples/digitally-sign-a-pdf/" rel="noopener noreferrer"&gt;a built-in signing call&lt;/a&gt;. Text extraction sits in the same place, since the Document Operations API can load, merge, and encrypt an existing PDF as a whole document but has no facility for reading text back out of one, so QuestPDF stays a generation library rather than the reading layer &lt;a href="https://ironpdf.com/how-to/extract-text-and-images/" rel="noopener noreferrer"&gt;a word-level extraction call&lt;/a&gt; provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens If the Maintainer Steps Away?
&lt;/h2&gt;

&lt;p&gt;QuestPDF has no CVEs of its own on record in the GitHub Advisory Database, NVD, or Snyk, the same clean read &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;a maintained release line&lt;/a&gt; would give it. The read that matters for a long-run dependency is concentration, since QuestPDF is maintained by one developer operating as a sole proprietorship rather than a company with a bench of engineers, a single point of continuity for a library a production system depends on.&lt;/p&gt;

&lt;p&gt;The release record runs the other way, with four releases in the two months before this was written and features like the ARM64 port and encryption support landing on a real schedule, so this is an active project rather than a stale one, the difference a team weighs against &lt;a href="https://ironpdf.com/competitors/questpdf-vs-ironpdf/" rel="noopener noreferrer"&gt;a vendor-backed line&lt;/a&gt; that carries a support channel behind it. The open feature requests for signing and AcroForm support have sat in the tracker for years apiece, so a team blocked on either should not plan around it landing on a predictable date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering HTML and Signing in One Package
&lt;/h2&gt;

&lt;p&gt;The seam that sends QuestPDF teams looking is usually a signature on a document already generated, or an existing web page that has to become a PDF, with no second library bolted on for either. IronPDF renders the markup through Chromium and signs in the same package, in &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;one call&lt;/a&gt; each.&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;IronPdf&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;IronPdf.Signing&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;System.Security.Cryptography.X509Certificates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// render existing HTML through the bundled Chromium engine&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&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="s"&gt;"&amp;lt;h1&amp;gt;Invoice #1042&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total due: $250.00&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// sign it in the same library, no BouncyCastle plumbing&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;PdfSignature&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;X509Certificate2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cert.pfx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"password"&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;Sign&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;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;"invoice.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 renderer is a full Chromium build, so an existing web page or Razor view becomes a PDF without its layout rebuilt by hand, and the signing, AcroForm, merge, and word-level-extraction work QuestPDF sends to second libraries all sits in this one package, which is where &lt;a href="https://ironpdf.com/examples/cshtml-to-pdf-razor/" rel="noopener noreferrer"&gt;a Razor-to-PDF path&lt;/a&gt; and &lt;a href="https://ironpdf.com/examples/merge-pdfs/" rel="noopener noreferrer"&gt;a merge call&lt;/a&gt; stay under a single license. Those are the gaps QuestPDF leaves to the team, and a project crossing the revenue line meets the license bill and the missing layers at the same moment, both resolved inside one deployment-priced dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Flips the Decision to IronPDF
&lt;/h2&gt;

&lt;p&gt;A small team composing invoices from C# data, comfortably under the revenue line with no signing or HTML need, is the one case QuestPDF answers cleanly. Every other direction a review runs, once the company crosses $1M or goes public, the document starts as HTML, or a signature or readable text is needed, a single-package renderer with deployment-based pricing closes those gaps in one dependency rather than a revenue clock, a hand-ported layout, and a second signing library, which &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;IronPDF's documentation&lt;/a&gt; lays out. So where does your project actually sit, comfortably under QuestPDF's revenue line, or already into one of the gaps above? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QuestPDF belongs to its maintainer, and Iron Software is not affiliated with the project. Everything above comes from QuestPDF's own license pages, documentation, and release notes, alongside the public CVE record, accurate at the time of writing. Spot a figure that has changed since? Flag it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PaddleSharp vs IronOCR: The Native-Dependency Tree</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Tue, 04 Aug 2026 16:39:56 +0000</pubDate>
      <link>https://dev.to/ironsoftware/paddlesharp-vs-ironocr-the-native-dependency-tree-o4f</link>
      <guid>https://dev.to/ironsoftware/paddlesharp-vs-ironocr-the-native-dependency-tree-o4f</guid>
      <description>&lt;p&gt;Search for PaddleOCR in C# and PaddleSharp is the first answer, the .NET binding that lets a team call Baidu's PaddleOCR models without leaving the language. The part those recommendations skip is what sits between &lt;code&gt;dotnet add package Sdcb.PaddleOCR&lt;/code&gt; and a working OCR call, and it is not recognition accuracy, it is a native-dependency decision tree, operating system, CPU instruction set, and, for GPU work, an exact CUDA version. The question an architecture review should ask is who owns that package and runtime matrix in two years, which is where &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/compare-paddlesharp-ocr-vs-ironocr/" rel="noopener noreferrer"&gt;a side-by-side read&lt;/a&gt; starts to price the choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One disclosure up front. We are the team behind &lt;a href="https://ironsoftware.com/csharp/ocr/" rel="noopener noreferrer"&gt;IronOCR&lt;/a&gt; at Iron Software, and this read maps where PaddleSharp holds up and where a .NET pipeline outgrows it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardware Reach Behind the Binding
&lt;/h2&gt;

&lt;p&gt;Once the packages are in place, the call itself is compact, point at a model set, hand it an image, read the text back, the shape any project already managing native binaries would expect from &lt;a href="https://ironsoftware.com/csharp/ocr/examples/simple-csharp-ocr-tesseract/" rel="noopener noreferrer"&gt;a first OCR call&lt;/a&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;OpenCvSharp&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;Sdcb.PaddleOCR&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;Sdcb.PaddleOCR.Models.Local&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// build the pipeline with rotation handling in the same call&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PaddleOcrAll&lt;/span&gt; &lt;span class="n"&gt;ocr&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="n"&gt;LocalFullModels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChineseV5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaddleDevice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Mkldnn&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AllowRotateDetection&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;Enable180Classification&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Mat&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ImRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;PaddleOcrResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocr&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;span class="n"&gt;image&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its one clear strength is hardware reach, since once the right package is selected PaddleSharp runs across Windows x64, Linux x64 and ARM64, and macOS on Intel and Apple Silicon, with CPU inference through MKL or OpenBLAS and optional GPU inference, a spread a &lt;a href="https://ironsoftware.com/csharp/ocr/examples/intl-languages/" rel="noopener noreferrer"&gt;broad language pipeline&lt;/a&gt; can draw on. That reach is the strength to weigh against the install matrix the next section walks through, the setup a &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;single-package engine&lt;/a&gt; is built to remove.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Native-Dependency Tree a Team Signs For
&lt;/h2&gt;

&lt;p&gt;Behind that one call sits most of what adopting PaddleSharp actually commits a team to, and the first cost is a package decision tree with no default answer, run before any OCR call. Getting from &lt;code&gt;dotnet add package&lt;/code&gt; to a working &lt;code&gt;PaddleOcrAll&lt;/code&gt; means choosing a combination of operating system, CPU backend, and, for GPU work, a CUDA-version-matched package out of three separate lines, and the wrong pick surfaces as a &lt;code&gt;DllNotFoundException&lt;/code&gt; at first run rather than the compile error &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-ocr-input-for-iron-tesseract/" rel="noopener noreferrer"&gt;a clean install path&lt;/a&gt; would raise.&lt;/p&gt;

&lt;p&gt;GPU acceleration deepens that ownership rather than adding a package. PaddleSharp's own README says CUDA, cuDNN, and TensorRT have to be installed by hand, with &lt;code&gt;PATH&lt;/code&gt; or &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; set on every machine that runs the app, version-matching infrastructure a team keeps in sync across dev, CI, and every production node for the life of the deployment, not the single reference &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;a managed engine&lt;/a&gt; resolves at build time. The failures that hurt land at runtime, since a CPU without AVX needs the separate no-AVX package plus a code change and a missing Visual C++ Redistributable surfaces as a native-loader exception, both on a box nobody thought to check rather than in local testing where a self-contained package never raises the question. The quickstart also adds &lt;code&gt;OpenCvSharp4&lt;/code&gt; purely to decode the image before PaddleSharp sees it, and unless a project switches to the local models package the models download on first run, a non-issue on a workstation and a real one in a locked-down container that expects &lt;a href="https://ironsoftware.com/csharp/ocr/examples/ocr-image-filters-for-net-tesseract/" rel="noopener noreferrer"&gt;everything already in the image&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Install Surface, Row by Row
&lt;/h2&gt;

&lt;p&gt;The rows that decide a C# integration sit next to the rows that decide an engine benchmark, and the table draws from PaddleSharp's own README, NuGet listings, and repository rather than an older post, with release dates checked on NuGet and read against &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/csharp-ocr-library/" rel="noopener noreferrer"&gt;a single-package install&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;PaddleSharp&lt;/th&gt;
&lt;th&gt;IronOCR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache License 2.0&lt;/td&gt;
&lt;td&gt;Commercial, with support and updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packages to install&lt;/td&gt;
&lt;td&gt;Core, inference, runtime, and models&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;IronOcr&lt;/code&gt; package per OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU instruction set&lt;/td&gt;
&lt;td&gt;AVX assumed, no-AVX package separate&lt;/td&gt;
&lt;td&gt;No instruction-set choice required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU acceleration&lt;/td&gt;
&lt;td&gt;Matches one of three CUDA lines&lt;/td&gt;
&lt;td&gt;No CUDA path required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image decoding&lt;/td&gt;
&lt;td&gt;Adds &lt;code&gt;OpenCvSharp4&lt;/code&gt; to load images&lt;/td&gt;
&lt;td&gt;Built in via &lt;code&gt;OcrInput.LoadImage&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows runtime&lt;/td&gt;
&lt;td&gt;Visual C++ Redistributable required&lt;/td&gt;
&lt;td&gt;No redistributable required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model delivery&lt;/td&gt;
&lt;td&gt;Downloads on first run, or bundled&lt;/td&gt;
&lt;td&gt;Language packs as NuGet packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recent release&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Sdcb.PaddleOCR&lt;/code&gt; 3.3.1, May 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. PaddleSharp and IronOCR across the &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/" rel="noopener noreferrer"&gt;deployment requirements&lt;/a&gt; that stand between a package install and a first OCR call.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Whose Bandwidth Backs a Breaking Change?
&lt;/h2&gt;

&lt;p&gt;PaddleSharp, and the &lt;code&gt;Sdcb.PaddleOCR&lt;/code&gt; and &lt;code&gt;Sdcb.PaddleInference&lt;/code&gt; packages it is built from, carry no CVEs of their own in the GitHub Advisory Database, NVD, or Snyk, the same read &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/best-ocr-engine/" rel="noopener noreferrer"&gt;an OCR engine survey&lt;/a&gt; would give it. The exposure a review should read sits one layer down in PaddlePaddle, the Python training framework whose CVEs live in training and model-loading utilities the native inference path never touches, so attributing them to this binding misreads which layer they belong to.&lt;/p&gt;

&lt;p&gt;The read that decides a long-run dependency is concentration. Of roughly 768 commits in the project's history, 752, better than 98%, come from a single contributor, with the next account at seven, so a production pipeline that leans on PaddleSharp leans on one person's bandwidth for a fast response to a breaking issue, the risk a team prices against &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/compare-paddleocr-vs-ironocr/" rel="noopener noreferrer"&gt;a vendor-backed release line&lt;/a&gt; before it commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Package, Whatever Server It Lands On
&lt;/h2&gt;

&lt;p&gt;Teams tend to hit the deployment-matrix wall first, an OCR feature that has to run on whatever hardware a customer's server happens to have, without a small infrastructure project attached just to get text out of an image. IronOCR installs as one package and reads the same image in &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-ocr-input-for-iron-tesseract/" rel="noopener noreferrer"&gt;one call&lt;/a&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;IronOcr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// no CPU-backend, CUDA, or redistributable matrix to pick from&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ocr&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;IronTesseract&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;var&lt;/span&gt; &lt;span class="n"&gt;input&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;OcrInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;OcrResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no CPU-backend choice, no CUDA line to match, and no separate decoder, since image loading is built into &lt;code&gt;OcrInput&lt;/code&gt; and each language ships as its own NuGet package resolved at build time rather than downloaded on first run, which is what makes a &lt;a href="https://ironsoftware.com/csharp/ocr/examples/read-scanned-document/" rel="noopener noreferrer"&gt;locked-down container&lt;/a&gt; behave the same as a laptop. The native engine ships bundled inside each OS package, so the same read runs unchanged across a worker pool without the version-matching that shapes how the binding scales in &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-tesseract-multithreading-for-speed/" rel="noopener noreferrer"&gt;a batch pipeline&lt;/a&gt;, and the package matrix PaddleSharp leaves to the team sits closed inside one dependency with published pricing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing the Binding to the Deployment
&lt;/h2&gt;

&lt;p&gt;Owning the CUDA and native stack pays off in one narrow case, a team already on that tooling and after GPU throughput on models it has benchmarked. Everything else a .NET OCR feature usually is falls outside it, and for Latin-script or &lt;a href="https://ironsoftware.com/csharp/ocr/examples/intl-languages/" rel="noopener noreferrer"&gt;well-supported languages&lt;/a&gt; read reliably on whatever server it lands on, a single-package library removes the CPU-backend, CUDA, decoder, and redistributable decisions at once and reads an image or PDF in one call, which &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;IronOCR's documentation&lt;/a&gt; lays out. So what is running your OCR in production right now, a hand-maintained native stack or a package that just installs? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PaddleSharp and PaddleOCR belong to their respective owners, and Iron Software has no affiliation with either. The specifics above come from the project's own README, NuGet listings, and repository, together with the public CVE record, accurate as of writing. If something reads wrong now, correct us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>ocr</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PDFsharp vs IronPDF: The Layer It Was Never Built to Add</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Mon, 03 Aug 2026 23:17:22 +0000</pubDate>
      <link>https://dev.to/ironsoftware/pdfsharp-vs-ironpdf-the-layer-it-was-never-built-to-add-2mol</link>
      <guid>https://dev.to/ironsoftware/pdfsharp-vs-ironpdf-the-layer-it-was-never-built-to-add-2mol</guid>
      <description>&lt;p&gt;PDFsharp shows up in a lot of .NET codebases quietly, with no sales call and no license negotiation, just a NuGet install and a coordinate system for drawing onto a page. That is often exactly enough for a code-generated invoice, and it stays enough until a project needs something PDFsharp was never built for, an HTML email template turned into a PDF, readable paragraphs pulled back out of a report, or a PDF/A file that holds up for a compliance team. The question an architecture review should ask is who builds and maintains the layer PDFsharp leaves out, which is where &lt;a href="https://ironpdf.com/blog/compare-to-other-components/csharp-pdf-library-comparison/" rel="noopener noreferrer"&gt;a side-by-side read&lt;/a&gt; starts to price the choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure. Our team at Iron Software develops IronPDF, and we highlight where PDFsharp is better suited.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PDFsharp Stops
&lt;/h2&gt;

&lt;p&gt;The constraint that ends most PDFsharp evaluations is rendering. PDFsharp can build a page from scratch, but it cannot open an existing PDF and render it, so there is no preview, no thumbnail, and no page-to-image export, and a document list that needs a thumbnail of page one or a QA step that needs an image diff between two versions requires a second library bolted on for that step alone, the step &lt;a href="https://ironpdf.com/examples/rasterize-a-pdf-to-images/" rel="noopener noreferrer"&gt;a native rasterizer&lt;/a&gt; covers in one call.&lt;/p&gt;

&lt;p&gt;Text extraction stops at raw characters in the same way. PDFsharp hands back character positions in drawing order and nothing else, with no word, line, or paragraph reconstruction, so a project that needs readable text out of a PDF is writing that reconstruction logic itself rather than reaching for &lt;a href="https://ironpdf.com/how-to/extract-text-and-images/" rel="noopener noreferrer"&gt;a word-level extraction call&lt;/a&gt; that returns the paragraphs already assembled.&lt;/p&gt;

&lt;p&gt;The largest gap is HTML, in both directions. There is no HTML or CSS engine anywhere in PDFsharp, so an existing template, email design, or web page has to be hand-ported into &lt;code&gt;XGraphics&lt;/code&gt; and MigraDoc calls line by line, and the maintainers have said outright they do not intend to build a PDF-to-Word or PDF-to-HTML converter, so the reverse is closed too, which is the whole job &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;a Chromium renderer&lt;/a&gt; is built to do. Font coverage is narrower as well, limited to TrueType and OpenType-with-TrueType-outlines, and PDF/A creation shipped early with no way yet to target a specific conformance level, so a compliance requirement that needs a defined level today runs straight into that gap where &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;a validated PDF/A path&lt;/a&gt; would meet it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing by Coordinate
&lt;/h2&gt;

&lt;p&gt;Set against those gaps, PDFsharp keeps one strength. Its model is a blank page and a coordinate system, a different design point than a browser engine, and for a fixed-layout invoice that never touches HTML, that can mean less code than rendering a template, a pattern &lt;a href="https://ironpdf.com/examples/using-html-to-create-a-pdf/" rel="noopener noreferrer"&gt;a code-first generation path&lt;/a&gt; recognizes.&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;PdfSharp.Drawing&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;PdfSharp.Pdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// draw text at a fixed position on a blank page&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&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;PdfDocument&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;page&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="nf"&gt;AddPage&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;gfx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;XGraphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromPdfPage&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;font&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;XFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Verdana"&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;gfx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DrawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoice #1042"&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;XBrushes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Black&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;XRect&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;0&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="n"&gt;Width&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;XStringFormats&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;document&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;"invoice.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 MIT terms are the other point in its favor, unrestricted by empira with no seat or revenue threshold, which stands out next to source-available licenses that stop applying past a revenue line, and the project is not stagnant either, with &lt;code&gt;6.2.4&lt;/code&gt; in January 2026 adding digital signatures, AES-256 encryption, and PDF/UA output on a real cadence that &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;an actively released library&lt;/a&gt; can be weighed against.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Capability and License Split
&lt;/h2&gt;

&lt;p&gt;The differences that decide a .NET project sit in the layers PDFsharp does not ship, not in the drawing model it does, and the table draws from PDFsharp's own release notes and FAQ rather than an older post, read against &lt;a href="https://ironpdf.com/blog/compare-to-other-components/" rel="noopener noreferrer"&gt;what a single renderer covers&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;PDFsharp&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;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Commercial, with support and updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost at scale&lt;/td&gt;
&lt;td&gt;Engineering time for the missing layers&lt;/td&gt;
&lt;td&gt;One license, those layers built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML and CSS to PDF&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Chromium &lt;code&gt;ChromePdfRenderer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordinate-based drawing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;XGraphics&lt;/code&gt; canvas&lt;/td&gt;
&lt;td&gt;Available, alongside HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Higher-level assembly&lt;/td&gt;
&lt;td&gt;Through MigraDoc&lt;/td&gt;
&lt;td&gt;Built in, plus HTML and CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read and modify existing PDFs&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text extraction&lt;/td&gt;
&lt;td&gt;Raw character positions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ExtractAllText&lt;/code&gt; at word level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rasterize pages to images&lt;/td&gt;
&lt;td&gt;No native support&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RasterizeToImageFiles&lt;/code&gt;, built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/A creation&lt;/td&gt;
&lt;td&gt;Early-stage, no level target&lt;/td&gt;
&lt;td&gt;Supported and validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/UA tagging&lt;/td&gt;
&lt;td&gt;Supported since &lt;code&gt;6.2.x&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported and validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital signatures&lt;/td&gt;
&lt;td&gt;Supported since &lt;code&gt;6.2.0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfSignature&lt;/code&gt; with timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;AES-256, PDF 2.0&lt;/td&gt;
&lt;td&gt;AES-256, PDF 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release cadence&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;6.2.4&lt;/code&gt;, January 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. PDFsharp and IronPDF across the &lt;a href="https://ironpdf.com/blog/compare-to-other-components/csharp-pdf-library-comparison/" rel="noopener noreferrer"&gt;capability and licensing questions&lt;/a&gt; a .NET review tends to price.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the Maintenance Record
&lt;/h2&gt;

&lt;p&gt;PDFsharp's security record is clean, with no CVEs of its own in the GitHub Advisory Database, NVD, or Snyk, and the one item a search surfaces is a NuGet restore warning for &lt;code&gt;System.Formats.Asn1&lt;/code&gt;, which is &lt;code&gt;CVE-2024-38095&lt;/code&gt; in the .NET runtime's X.509 parsing rather than in PDFsharp's own code, resolved by bumping the dependency in a &lt;code&gt;6.2.0&lt;/code&gt; preview, the same read &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;a maintained release line&lt;/a&gt; would give it.&lt;/p&gt;

&lt;p&gt;That pattern holds for the project. Each release corresponds to a commit on the master branch, &lt;code&gt;6.2.4&lt;/code&gt; landed in January 2026 keeping pace with .NET 8, 9, and 10, and the open question for a review is scope, the rendering and extraction layers PDFsharp does not intend to add and a team therefore builds and carries itself against &lt;a href="https://ironpdf.com/examples/using-html-to-create-a-pdf/" rel="noopener noreferrer"&gt;a single package that ships them&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the Gaps in One Library
&lt;/h2&gt;

&lt;p&gt;Teams tend to hit the same wall, an existing web page or Razor view that has to become a PDF without re-implementing the layout by hand. IronPDF renders that markup through Chromium in &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;one call&lt;/a&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;IronPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// render existing HTML through the bundled Chromium engine&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&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="s"&gt;"&amp;lt;h1&amp;gt;Invoice #1042&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total due: $250.00&amp;lt;/p&amp;gt;"&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;"invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the engine is a real Chromium build, HTML, CSS, and script-driven content come out the way a browser shows them, and PDF/A, word-level extraction, and page-to-image rasterization are built in rather than gaps to design around, which is where &lt;a href="https://ironpdf.com/examples/cshtml-to-pdf-razor/" rel="noopener noreferrer"&gt;a Razor-to-PDF path&lt;/a&gt; and &lt;a href="https://ironpdf.com/examples/rasterize-a-pdf-to-images/" rel="noopener noreferrer"&gt;a rasterizer call&lt;/a&gt; stay inside one package. That is the layer PDFsharp leaves to the team, closed inside a single commercial package with published pricing rather than a set of second dependencies to bolt on and maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing by Where the Document Starts
&lt;/h2&gt;

&lt;p&gt;A fixed-layout invoice that never touches HTML is the one case PDFsharp plus MigraDoc answers. Every other direction a review runs, once the document starts as HTML, or the project needs level-specific PDF/A, readable text back out, or a page rendered to an image, a Chromium-based renderer closes those gaps inside one package rather than turning each into a second dependency to bolt on and maintain, which &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;IronPDF's documentation&lt;/a&gt; walks through. Which of PDFsharp's gaps hit your project first, the missing rasterizer, the raw-character extraction, or the absent HTML engine? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PDFsharp is MIT-licensed by empira Software GmbH, and the PDFsharp name is the property of its owner, with which we have no affiliation. The details above are drawn from its public documentation, release notes, and the public CVE record as they stood at the time of writing. If a point came out wrong, tell us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PaddleOCR vs IronOCR: The Python Runtime a .NET Team Owns</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Mon, 03 Aug 2026 23:09:37 +0000</pubDate>
      <link>https://dev.to/ironsoftware/paddleocr-vs-ironocr-the-python-runtime-a-net-team-owns-4nfl</link>
      <guid>https://dev.to/ironsoftware/paddleocr-vs-ironocr-the-python-runtime-a-net-team-owns-4nfl</guid>
      <description>&lt;p&gt;The confusion most .NET teams hit with PaddleOCR is not the engine, it is what installing it turns into once C# has to reach it. PaddleOCR is a Python-first project, so adding the package becomes standing up a Python runtime, pulling model weights from a hub on first run, and carrying a stack of platform-specific computer-vision dependencies a .NET build server never expected to host. The question an architecture review should ask is who owns that runtime and model plumbing in two years, which is where &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/compare-paddleocr-vs-ironocr/" rel="noopener noreferrer"&gt;a side-by-side read&lt;/a&gt; starts to price the choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure. Our team at Iron Software builds &lt;a href="https://ironsoftware.com/csharp/ocr/" rel="noopener noreferrer"&gt;IronOCR&lt;/a&gt;, and we point out below where PaddleOCR is the right call.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What PaddleOCR Gets Right
&lt;/h2&gt;

&lt;p&gt;PaddleOCR documents its accuracy with benchmarks PaddlePaddle publishes itself rather than numbers taken on faith, and its one clear strength is recognition on messy input, with PP-OCRv6 improving detection and recognition over the prior generation and scaling across tiny, small, and medium tiers so a team can trade accuracy against speed, the kind of result &lt;a href="https://ironsoftware.com/csharp/ocr/examples/read-scanned-document/" rel="noopener noreferrer"&gt;a demanding OCR pipeline&lt;/a&gt; is built around.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;paddleocr&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PaddleOCR&lt;/span&gt;

&lt;span class="n"&gt;ocr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaddleOCR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# detection, recognition, orientation in one call
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoice.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Language coverage runs to 50 languages in a unified PP-OCRv6 checkpoint and 109 in the newer PaddleOCR-VL pipeline, which reads mixed-script documents without swapping a language pack per file, a range &lt;a href="https://ironsoftware.com/csharp/ocr/examples/intl-languages/" rel="noopener noreferrer"&gt;wide language support&lt;/a&gt; also has to answer. That accuracy and coverage is the strength here, and what the Apache License asks of a .NET team in return is the runtime and model work the next section walks through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the .NET Path Gets Expensive
&lt;/h2&gt;

&lt;p&gt;The single-image demo hides most of what adopting PaddleOCR commits a .NET team to. The gaps below are not edge cases, they are the reason a proof of concept that ran in an afternoon becomes a subsystem someone maintains for years.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No first-party path into .NET-&lt;/strong&gt; PaddleOCR's own release notes list official SDKs for Python, Go, and TypeScript, and C# is not among them, so a .NET team calling this engine directly is working through the underlying Paddle inference library or a community binding, not anything PaddlePaddle ships or supports, unlike &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-ocr-input-for-iron-tesseract/" rel="noopener noreferrer"&gt;a first-party C# API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model management becomes the team's job-&lt;/strong&gt;  nothing about the package arrives as a self-contained .NET artifact, models live on Hugging Face and ModelScope and resolve through Python tooling on first run, so a C# pipeline builds and maintains its own download, caching, and versioning step before a single image is processed, work &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;a package with bundled models&lt;/a&gt; does not ask for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footprint depends on the pipeline-&lt;/strong&gt; the classic detection-and-recognition pair is a few megabytes, but the newer PaddleOCR-VL pipeline is a 0.9-billion-parameter vision-language model needing on the order of 1 to 1.5 GB of weights and considerably more memory at inference, so two teams that both claim to run PaddleOCR can mean very different deployment footprints, a variance a fixed-footprint engine does not carry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a version-to-version cost on top of that, because the long-standing &lt;code&gt;ocr()&lt;/code&gt; method no longer accepts the &lt;code&gt;det&lt;/code&gt; and &lt;code&gt;rec&lt;/code&gt; parameters older tutorials rely on, replaced in the 3.x line by a &lt;code&gt;predict()&lt;/code&gt; interface, so code written against a 2.x guide needs real rework. The dependency stack adds its own friction before any of that, since installing the package pulls in the PaddlePaddle inference engine plus OpenCV, shapely, scikit-image, and others, and needs Python 3.8 or newer, a meaningfully different install surface than the single reference &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/csharp-ocr-library/" rel="noopener noreferrer"&gt;a managed .NET library&lt;/a&gt; resolves at build time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration and Deployment at a Glance
&lt;/h2&gt;

&lt;p&gt;The rows that decide a C# integration sit next to the rows that decide an engine benchmark, and the table draws from PaddleOCR's own release notes and installation docs rather than an older post, read against &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/" rel="noopener noreferrer"&gt;a single-package install&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;PaddleOCR&lt;/th&gt;
&lt;th&gt;IronOCR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache License 2.0&lt;/td&gt;
&lt;td&gt;Commercial, with support and updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language runtime&lt;/td&gt;
&lt;td&gt;Python-native, Paddle or ONNX&lt;/td&gt;
&lt;td&gt;Native .NET, one &lt;code&gt;IronOcr&lt;/code&gt; package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First-party bindings&lt;/td&gt;
&lt;td&gt;Python, Go, TypeScript SDKs&lt;/td&gt;
&lt;td&gt;C# and .NET, shipped in &lt;code&gt;2026.7.2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model distribution&lt;/td&gt;
&lt;td&gt;Downloaded from Hugging Face&lt;/td&gt;
&lt;td&gt;Bundled inside the package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multilingual OCR&lt;/td&gt;
&lt;td&gt;50 to 109 languages&lt;/td&gt;
&lt;td&gt;125+ languages in one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input types&lt;/td&gt;
&lt;td&gt;Images, PDF via a separate step&lt;/td&gt;
&lt;td&gt;Images and PDFs through &lt;code&gt;OcrInput&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured table extraction&lt;/td&gt;
&lt;td&gt;The separate PaddleOCR-VL pipeline&lt;/td&gt;
&lt;td&gt;Structured table and form output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-premises inference&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release cadence&lt;/td&gt;
&lt;td&gt;Four releases, April to June 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. PaddleOCR and IronOCR across the &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/csharp-ocr-library/" rel="noopener noreferrer"&gt;integration questions&lt;/a&gt; a .NET review tends to price.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the Security Risk Sit One Layer Down?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;PaddleOCR&lt;/code&gt; toolkit itself has no advisories on record in GitHub's database, NVD, or Snyk, and the exposure a .NET team should read sits one layer down in PaddlePaddle, the deep-learning framework an install pulls in, which carries a real CVE history the same read &lt;a href="https://ironsoftware.com/csharp/ocr/blog/compare-to-other-components/best-ocr-engine/" rel="noopener noreferrer"&gt;an OCR engine survey&lt;/a&gt; would flag. It includes a critical command-injection flaw rated &lt;code&gt;9.6&lt;/code&gt; (&lt;code&gt;CVE-2023-52314&lt;/code&gt;), a &lt;code&gt;9.8&lt;/code&gt; remote-code-execution issue in the &lt;code&gt;2.6.0&lt;/code&gt; line (&lt;code&gt;CVE-2024-0917&lt;/code&gt;), a code-injection flaw calling &lt;code&gt;eval()&lt;/code&gt; on user input (&lt;code&gt;CVE-2022-46742&lt;/code&gt;), and a path-traversal issue enabling arbitrary file overwrite (&lt;code&gt;CVE-2024-0818&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Each of those sits in training-time, debugging, or data-conversion code, so a deployment that also trains or fine-tunes models on the same PaddlePaddle installation inherits that exposure directly, and the framework's own advisories are worth reading before a deployment decision, a review a bundled-engine path avoids because there is no framework underneath to audit against &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;a maintained release line&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Single-Package Path
&lt;/h2&gt;

&lt;p&gt;For a .NET team, the practical question is not the engine's recognition quality but whether a Python toolchain, a separate model step, and a community binding are worth carrying to reach it from C#. IronOCR installs as one package and reads an image or PDF in &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-ocr-input-for-iron-tesseract/" rel="noopener noreferrer"&gt;one call&lt;/a&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;IronOcr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// create the engine, no Python runtime anywhere in the build&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ocr&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;IronTesseract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// one input object takes both an image and a PDF&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;input&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;OcrInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadPdf&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;OcrResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IronOCR ships its models inside the package rather than as a separate download, exposes OCR through a plain C# API rather than a bridge into a Python process, and reads PDFs alongside images through the same &lt;code&gt;OcrInput&lt;/code&gt;, so the whole category of runtime and model setup never comes up, which is where &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-pdf-ocr/" rel="noopener noreferrer"&gt;native PDF OCR&lt;/a&gt; and &lt;a href="https://ironsoftware.com/csharp/ocr/examples/ocr-image-filters-for-net-tesseract/" rel="noopener noreferrer"&gt;a built-in filter pipeline&lt;/a&gt; stay inside one dependency. That is the plumbing PaddleOCR leaves to the team, closed inside a single package with published pricing rather than a Python stack to assemble and maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Stack Is Yours?
&lt;/h2&gt;

&lt;p&gt;PaddleOCR answers one narrow case, a team already on Python tooling and willing to own the runtime and model management. For the pipeline that lives in .NET and mostly needs Latin-script or well-supported languages read reliably on whatever server it lands on, a single-package library removes the Python runtime, the model step, and the framework audit at once and reads an image or PDF in one call, which &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;IronOCR's documentation&lt;/a&gt; lays out. So what is reading your documents in production right now, a Python stack you maintain or a package that just installs? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PaddleOCR and PaddlePaddle are trademarks of Baidu, with which we have no affiliation. The details above are drawn from their public documentation and the public CVE record as they stood at the time of writing. If a point came out wrong, tell us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>ocr</category>
      <category>opensource</category>
    </item>
    <item>
      <title>iText vs. IronPDF: Comparing .NET PDF Libraries, Licensing, and HTML-to-PDF Rendering</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Mon, 03 Aug 2026 23:04:45 +0000</pubDate>
      <link>https://dev.to/ironsoftware/itext-vs-ironpdf-comparing-net-pdf-libraries-licensing-and-html-to-pdf-rendering-76j</link>
      <guid>https://dev.to/ironsoftware/itext-vs-ironpdf-comparing-net-pdf-libraries-licensing-and-html-to-pdf-rendering-76j</guid>
      <description>&lt;p&gt;Open a .NET PDF thread about signing, merging, or reading PDFs in C#, and iText comes up in the first few answers, and has for a decade. It is not the plucky upstart in this space, it is closer to the standard everything else gets measured against, and for a whole class of documents that reputation is earned. The figure that reputation leaves off is the one an architecture review exists to surface, which is what the AGPL terms and the .NET-specific packaging cost a team once the app ships, and &lt;a href="https://ironpdf.com/competitors/itext-vs-ironpdf/" rel="noopener noreferrer"&gt;a side-by-side read&lt;/a&gt; is a reasonable place to price it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure. Our team at Iron Software builds &lt;a href="https://ironpdf.com/" rel="noopener noreferrer"&gt;IronPDF&lt;/a&gt;, and we point out below where iText is the right call.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What iText Handles Well
&lt;/h2&gt;

&lt;p&gt;iText's breadth is real. It reads, edits, extracts, assembles, and signs PDFs, generates PDF/A-1 through PDF/A-4, and tags PDF/UA accessibility across one architecture spanning Java and .NET, and a basic document takes a handful of lines much like &lt;a href="https://ironpdf.com/examples/using-html-to-create-a-pdf/" rel="noopener noreferrer"&gt;any generation path&lt;/a&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;iText.Kernel.Pdf&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;iText.Layout&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;iText.Layout.Element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// open a writer and add two lines of content&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;document&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;Document&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;PdfDocument&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;PdfWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice.pdf"&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="nf"&gt;Add&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;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoice #1042"&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="nf"&gt;Add&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;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total due: $250.00"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its one dimension that leads is digital signatures, with PAdES B/T/LT/LTA support, long-term validation, and FIPS-140-2-capable cryptography that reaches further than most .NET PDF libraries, and for provably compliant signing that depth is the tool for the job, a place &lt;a href="https://ironpdf.com/examples/digitally-sign-a-pdf/" rel="noopener noreferrer"&gt;a broader signing feature&lt;/a&gt; meets rather than exceeds. That is the one strength to price against the seams the next sections cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licensing and Packaging, Line by Line
&lt;/h2&gt;

&lt;p&gt;The differences that decide a .NET project sit in licensing and in where each feature lives, not in raw document capability, and the table draws from iText's own license pages and release notes rather than an older writeup, read against &lt;a href="https://ironpdf.com/blog/compare-to-other-components/itext7-csharp-html-to-pdf-alternatives/" rel="noopener noreferrer"&gt;what a single package covers&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;iText 7 (Core)&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;License&lt;/td&gt;
&lt;td&gt;AGPLv3 or commercial subscription&lt;/td&gt;
&lt;td&gt;Commercial, tiered by deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commercial pricing&lt;/td&gt;
&lt;td&gt;Quote-only, metered by volume&lt;/td&gt;
&lt;td&gt;Published tiers, licensed by deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML and CSS to PDF&lt;/td&gt;
&lt;td&gt;Separate &lt;code&gt;pdfHTML&lt;/code&gt;, no JavaScript&lt;/td&gt;
&lt;td&gt;Chromium &lt;code&gt;ChromePdfRenderer&lt;/code&gt;, built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read and edit existing PDFs&lt;/td&gt;
&lt;td&gt;Supported natively&lt;/td&gt;
&lt;td&gt;Supported natively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text extraction&lt;/td&gt;
&lt;td&gt;Location-aware, low-level&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ExtractAllText&lt;/code&gt; at word level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rasterize pages to images&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pdfRender&lt;/code&gt;, CLI-only on .NET&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RasterizeToImageFiles&lt;/code&gt; in managed .NET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/A creation&lt;/td&gt;
&lt;td&gt;PDF/A-1 through PDF/A-4&lt;/td&gt;
&lt;td&gt;Supported and validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF/UA tagging&lt;/td&gt;
&lt;td&gt;PDF/UA-1 and PDF/UA-2&lt;/td&gt;
&lt;td&gt;Supported and validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital signatures&lt;/td&gt;
&lt;td&gt;PAdES B/T/LT/LTA&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfSignature&lt;/code&gt; with X.509 and timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;AES, FIPS-140-2-capable&lt;/td&gt;
&lt;td&gt;AES-256, PDF 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supported version line&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;9.7.x&lt;/code&gt;, 7.1 and 7.2 past EOL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. iText 7 and IronPDF across the &lt;a href="https://ironpdf.com/blog/compare-to-other-components/" rel="noopener noreferrer"&gt;licensing and packaging questions&lt;/a&gt; a .NET architecture review tends to price.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the .NET Seams Show
&lt;/h2&gt;

&lt;p&gt;The gaps below are not stray missing features. They follow from how iText is packaged and licensed, and each one lands on a .NET team specifically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rasterizing a page to an image means shelling out to a CLI, not calling a method:&lt;/strong&gt; &lt;code&gt;pdfRender&lt;/code&gt;, iText's add-on for turning a page into a PNG or TIFF, ships as native code for Java, and on .NET there is no managed API, only a command-line executable invoked from C# with its output parsed back, a different integration than &lt;a href="https://ironpdf.com/examples/rasterize-a-pdf-to-images/" rel="noopener noreferrer"&gt;a native rasterizer&lt;/a&gt; offers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML rendering lives outside the core, under its own license&lt;/strong&gt; - &lt;code&gt;pdfHTML&lt;/code&gt; converts HTML and CSS to PDF, but it is a package apart from iText Core, carries its own AGPL or commercial license on top of the core's, and executes no JavaScript, so a dashboard or invoice template that renders on the client has to be pre-rendered before &lt;a href="https://ironpdf.com/examples/execute-custom-javascript/" rel="noopener noreferrer"&gt;a Chromium engine&lt;/a&gt; would have run it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AGPL tier bills in source disclosure, not paperwork:&lt;/strong&gt; AGPLv3 network copyleft obliges a team to publish its own application's full source whenever iText is served over a network without a commercial license, and the license separately requires keeping iText's notice in the PDF producer metadata, which is a legal-review conversation before shipping rather than a decision a team makes on its own published tiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commercial pricing adds its own friction, with no published list and billing by document volume rather than a flat license, and migrating an existing 7.x codebase to the supported &lt;code&gt;9.x&lt;/code&gt; line crosses two rounds of breaking changes, cryptography moving into its own &lt;code&gt;itext.bouncy-castle-adapter&lt;/code&gt; package at &lt;code&gt;8.0&lt;/code&gt;, which is real work a team schedules against &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;a single maintained line&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does the Security Record Actually Say?
&lt;/h2&gt;

&lt;p&gt;iText is the one library in this space with a real, current CVE history, and the number that should move a decision is which branch a team is on rather than the count itself, a read &lt;a href="https://ironpdf.com/blog/compare-to-other-components/csharp-html-to-pdf-libraries-compared/" rel="noopener noreferrer"&gt;an HTML-to-PDF comparison&lt;/a&gt; frames the same way. &lt;code&gt;CVE-2021-43113&lt;/code&gt;, a &lt;code&gt;9.8&lt;/code&gt; critical command injection through a filename reaching a Ghostscript call, was fixed in &lt;code&gt;7.1.17&lt;/code&gt;, and a resource-allocation issue and a buffer overflow in &lt;code&gt;ByteBuffer.append&lt;/code&gt; were fixed in &lt;code&gt;7.1.18&lt;/code&gt; and &lt;code&gt;7.2.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The active risk is version currency, not an open flaw. The &lt;code&gt;9.x&lt;/code&gt; line shipped &lt;code&gt;9.7.0&lt;/code&gt; in July 2026 with patches landing within days, but &lt;code&gt;7.1&lt;/code&gt; has been unpatched since April 2025 and &lt;code&gt;7.2&lt;/code&gt; since October 2025, so anything still labeled iText 7 in production is likely past its security-update window, which is the calculation the &lt;a href="https://ironpdf.com/competitors/itext-vs-ironpdf/" rel="noopener noreferrer"&gt;migration path&lt;/a&gt; is really about rather than the CVE list on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Single Dependency Replaces
&lt;/h2&gt;

&lt;p&gt;The seam that sends teams looking is almost always the same, an HTML template that has to become a PDF without a second license to track, or a thumbnail feature that should not need a command-line tool. IronPDF renders through Chromium in one package and rasterizes in managed .NET, so both cross &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;one dependency&lt;/a&gt; rather than three.&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;IronPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// render existing HTML through the bundled Chromium engine&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&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="s"&gt;"&amp;lt;h1&amp;gt;Invoice #1042&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total due: $250.00&amp;lt;/p&amp;gt;"&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;"invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// rasterize in managed .NET, no CLI and no separate add-on&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;RasterizeToImageFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice_page_*.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because IronPDF is a real Chromium build, web fonts, Flexbox and Grid, and script-driven content land the way a browser paints them, and signing, form-filling, merging, and word-level extraction sit in the same package with published pricing rather than a quote, which is where &lt;a href="https://ironpdf.com/how-to/extract-text-and-images/" rel="noopener noreferrer"&gt;a text-extraction call&lt;/a&gt; and &lt;a href="https://ironpdf.com/examples/merge-pdfs/" rel="noopener noreferrer"&gt;a merge call&lt;/a&gt; stay in one license instead of stacking add-ons. That is the seam the AGPL terms and the CLI rasterizer sharpen, and where a single-package renderer removes the second and third dependencies a .NET team would otherwise track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matching the Library to the Job
&lt;/h2&gt;

&lt;p&gt;A project that needs iText's signing depth, is comfortable with AGPL, and generates documents from code is the one narrow case it answers. Everything else a .NET review runs into sits on the other side, and once the document starts as HTML, or a thumbnail is needed without a CLI, or the AGPL disclosure clause reaches legal, a single-package renderer with published pricing closes those seams in one dependency instead of a core package, a rendering add-on, and a CLI rasterizer to license and track, which &lt;a href="https://ironpdf.com/docs" rel="noopener noreferrer"&gt;IronPDF's documentation&lt;/a&gt; lays out. Has an AGPL clause or a CLI-only feature ever forced a licensing conversation you did not plan for? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;iText is a registered trademark of Apryse, with which we have no affiliation. The details above are drawn from iText's public license pages, release notes, and the public CVE record as they stood at the time of writing. If a point came out wrong, tell us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>opensource</category>
    </item>
    <item>
      <title>wkhtmltopdf Is Archived: A .NET Migration Decision</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Sun, 02 Aug 2026 16:34:06 +0000</pubDate>
      <link>https://dev.to/ironsoftware/wkhtmltopdf-is-archived-a-net-migration-decision-1k97</link>
      <guid>https://dev.to/ironsoftware/wkhtmltopdf-is-archived-a-net-migration-decision-1k97</guid>
      <description>&lt;p&gt;A dependency that stopped shipping releases in 2020 and had its repository archived in 2023 is still parsing HTML in a lot of .NET stacks. It usually shows up as &lt;a href="https://wkhtmltopdf.org/status.html" rel="noopener noreferrer"&gt;wkhtmltopdf&lt;/a&gt;, or through DinkToPdf, the C# wrapper built around it, and we still meet it in migration reviews more often than you would expect. It was useful and simple in its day, which is why so much of it is still deployed. What changed is the calendar, not that early usefulness, and an archived wkhtmltopdf binary carrying open CVEs is a risk the person who owns the deployment now has to accept on purpose rather than inherit by default.&lt;/p&gt;

&lt;p&gt;This is a decision-layer question more than a code question. A clean demo on a developer laptop is what keeps wkhtmltopdf in place, and it hides the one thing that matters, which is who patches the engine when the next HTML parser bug lands. For an archived project, the answer is nobody, and that gap does not show up until something in production depends on it being closed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure. Our team at Iron Software builds &lt;a href="https://ironpdf.com/" rel="noopener noreferrer"&gt;IronPDF&lt;/a&gt;, and we point out below where the open-source alternatives are the better call.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happened to the Project
&lt;/h2&gt;

&lt;p&gt;The timeline sits in the project's own history, and none of it is in dispute. The last stable release was &lt;code&gt;0.12.6&lt;/code&gt;, back in June 2020. The last commit to the main branch landed in June 2022. In January 2023, the maintainer archived the repository himself. In July 2024, GitHub archived the entire wkhtmltopdf organization, all four repositories, read-only, including one named &lt;code&gt;obsolete-downloads&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The maintainer's own &lt;a href="https://wkhtmltopdf.org/status.html" rel="noopener noreferrer"&gt;status page&lt;/a&gt; does not soften it. The project runs on Qt 4, unsupported since 2015, on top of a WebKit engine frozen since 2012. That is the code parsing production HTML and CSS today, whether the team that shipped it knew that or not. The maintainer points anyone starting fresh at WeasyPrint or Prince for static documents, and Puppeteer where JavaScript rendering is needed. When the author of a tool is directing new users elsewhere, that is the clearest signal an architect gets about where the project is going.&lt;/p&gt;

&lt;p&gt;The frozen wkhtmltopdf engine is a rendering problem as much as a security one. WebKit from 2012 predates most of the CSS a current template takes for granted, so grid layouts, flexbox behavior, and newer font handling drift or fail outright, and there is no wkhtmltopdf release coming to close the gap. Every rendering bug found from 2020 onward is permanent by definition. For a team maintaining document output over several years, that is a component whose behavior can only diverge further from the HTML the rest of the stack is written against, and the divergence is one-directional because only the frozen side has stopped moving.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CVEs Still on the Record
&lt;/h2&gt;

&lt;p&gt;Two vulnerabilities are logged against the binary, and neither has a fix on the way, because there is no longer anyone to ship one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CVE-2020-21365&lt;/code&gt;-&lt;/strong&gt; directory traversal, where crafted HTML can read local files under the default configuration in versions through 0.12.5, rated High at CVSS 7.5 in the &lt;a href="https://github.com/advisories/GHSA-36cg-hvm7-mhwp" rel="noopener noreferrer"&gt;GitHub Advisory Database&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CVE-2022-35583&lt;/code&gt;-&lt;/strong&gt; server-side request forgery through an injected iframe pointed at internal addresses, present in 0.12.6, rated Critical at CVSS 9.8 in the &lt;a href="https://github.com/advisories/GHSA-v2fj-q75c-65mr" rel="noopener noreferrer"&gt;GitHub Advisory Database&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are wkhtmltopdf-specific, and both stay open. The moment any part of the HTML being rendered originates from a user, a customer-supplied field on an invoice template, a print-this-ticket button, a comment embedded in a generated report, these stop being historical footnotes and become live attack surface on an engine that will never be patched again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where DinkToPdf Inherits the Same Risk
&lt;/h2&gt;

&lt;p&gt;DinkToPdf, the most common .NET wrapper, does not reimplement HTML rendering. It marshals into the same wkhtmltopdf binary underneath and layers a C# API on top. Same engine, same frozen WebKit, same two CVEs, same warning from the maintainer about untrusted HTML. A project that references &lt;code&gt;DinkToPdf&lt;/code&gt; carries everything above even if its own code never names &lt;code&gt;wkhtmltopdf&lt;/code&gt; once, which is exactly what makes this easy to miss in a dependency audit.&lt;/p&gt;

&lt;p&gt;There is a deployment cost hiding in that arrangement as well. The native wkhtmltopdf library has to be present and correct on every target, which means the archived binary gets baked into container images and build artifacts and then travels with the application for as long as it lives. An unmaintained native dependency is not a line in a lockfile that quietly resolves itself. It is a payload someone has to keep shipping on purpose, and each new deployment surface, a fresh base image or a different Linux distribution, is another place it has to be reconciled by hand.&lt;/p&gt;

&lt;p&gt;It is worth being precise about the shape of the problem, because rendering untrusted HTML into a PDF is a category-wide attack surface rather than a single-tool defect. Feed almost any HTML-to-PDF renderer a reference to an internal address or a local file and the question is what comes back embedded in the output. The wkhtmltopdf binary is the renderer most exposed to it right now, because it is the one that stopped getting patched and cannot receive a fix. Whatever a team lands on next, the same question about untrusted input has to be asked of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where IronPDF Fits
&lt;/h2&gt;

&lt;p&gt;The migration target that removes the frozen-engine problem is a renderer that still ships. Here is the same task, rendering HTML to a PDF, on a Chromium engine instead.&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;IronPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// render an HTML string through the bundled Chromium engine&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&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="s"&gt;"&amp;lt;h1&amp;gt;Invoice #1042&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total due: $250.00&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// write the finished PDF to disk&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;"invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That writes &lt;code&gt;invoice.pdf&lt;/code&gt; to disk through the same Chromium engine that ships in Chrome, and the engine is patched on Chromium's release cadence instead of sitting at 2012-era WebKit. IronPDF ships that engine inside a single &lt;code&gt;IronPdf&lt;/code&gt; package that handles its own native binaries per platform, and it targets .NET 10 down through .NET Framework 4.6.2, so moving onto it rarely means touching the target framework first.&lt;/p&gt;

&lt;p&gt;The choice comes down to who patches the engine, and the difference reads clearly side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;wkhtmltopdf&lt;/code&gt; and &lt;code&gt;DinkToPdf&lt;/code&gt;
&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;Rendering engine&lt;/td&gt;
&lt;td&gt;Qt WebKit frozen since 2012, last release &lt;code&gt;0.12.6&lt;/code&gt; in June 2020&lt;/td&gt;
&lt;td&gt;Chromium through &lt;code&gt;ChromePdfRenderer&lt;/code&gt;, &lt;code&gt;IronPdf&lt;/code&gt; &lt;code&gt;2026.7.2&lt;/code&gt; shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance status&lt;/td&gt;
&lt;td&gt;Repository archived January 2023, GitHub organization archived July 2024, no patches&lt;/td&gt;
&lt;td&gt;Actively released on one line, &lt;code&gt;2026.7.2&lt;/code&gt; shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Untrusted HTML&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CVE-2020-21365&lt;/code&gt; and &lt;code&gt;CVE-2022-35583&lt;/code&gt; open with no fix coming&lt;/td&gt;
&lt;td&gt;Chromium sandbox patched on Chromium's cadence, one &lt;code&gt;IronPdf&lt;/code&gt; package on &lt;code&gt;2026.7.2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is an honest counterweight worth stating. When budget is the hard constraint and commercial support is not required, the maintainer's own suggestions, WeasyPrint for static documents and Puppeteer where JavaScript matters, are worth evaluating before landing on any paid renderer. The decision that needs making is not open-source versus paid. It is a frozen, unpatched engine versus one that still receives security fixes, and there is more than one supported way off the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Migration Actually Costs
&lt;/h2&gt;

&lt;p&gt;The reason this decision gets deferred is that nothing is on fire today yet, and a working PDF path is easy to leave alone. The cost of leaving it alone stays invisible until an auditor flags the CVEs, or a customer sends HTML the frozen engine mishandles, or the one person who understood the DinkToPdf setup moves on and takes the tribal knowledge with them. Priced over a few years, an unmaintained renderer is technical debt that accrues quietly and then comes due all at once, usually on someone else's schedule.&lt;/p&gt;

&lt;p&gt;The migration itself is bounded work rather than a rewrite. Both tools take an HTML string and hand back a PDF, so the surface that changes is the call site, not the document templates, which is where most of the real effort in any PDF pipeline lives. The templates carry over, the rendering call swaps, and the engine underneath moves from frozen to maintained. That is a small, testable change with a clear before and after, which is exactly the kind of migration worth doing while it is a planned line item rather than after an incident makes it urgent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The short version for a review meeting looks like this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wkhtmltopdf is archived, with its last release &lt;code&gt;0.12.6&lt;/code&gt; in June 2020 and its GitHub organization archived in July 2024.&lt;/li&gt;
&lt;li&gt;Two wkhtmltopdf-specific CVEs stay open with no patch coming, &lt;code&gt;CVE-2020-21365&lt;/code&gt; and &lt;code&gt;CVE-2022-35583&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;DinkToPdf wraps the same binary, so it inherits the same engine and the same CVEs.&lt;/li&gt;
&lt;li&gt;Rendering untrusted HTML is a category-wide attack surface, and an unpatched engine is the worst place to meet it.&lt;/li&gt;
&lt;li&gt;Moving to a Chromium renderer like IronPDF puts the render on a codebase that still ships patches, and open-source alternatives fit where budget is the constraint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what is keeping wkhtmltopdf or DinkToPdf in your stack right now, the migration effort, the license cost of a supported renderer, or the fact that nothing has visibly broken yet? That last one is the hardest to argue with a manager, and we are genuinely curious which of the three carries the most weight for your team.&lt;/p&gt;

&lt;p&gt;If you want to map the API before committing to anything, the &lt;a href="https://ironpdf.com/blog/migration-guides/migrate-from-wkhtmltopdf-to-ironpdf/" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; covers the move from wkhtmltopdf and DinkToPdf, and the status page and both advisories above are public if you would rather verify the risk for yourself first. Has an archived renderer with open CVEs already come up in one of your reviews, and did it change the plan or get waved through? Tell us how that went in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;wkhtmltopdf, DinkToPdf, WeasyPrint, and Puppeteer are the property of their respective owners, and we have no affiliation with any of them. The dates and CVE details above are drawn from the projects' own archives and the GitHub Advisory Database, and if one has moved since, correct us in the responses.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>pdf</category>
      <category>security</category>
    </item>
    <item>
      <title>Tesseract C# Wrapper vs IronOCR: The Deployment Cost</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Sat, 01 Aug 2026 06:21:58 +0000</pubDate>
      <link>https://dev.to/ironsoftware/tesseract-c-wrapper-vs-ironocr-the-deployment-cost-500m</link>
      <guid>https://dev.to/ironsoftware/tesseract-c-wrapper-vs-ironocr-the-deployment-cost-500m</guid>
      <description>&lt;p&gt;Every OCR decision in a .NET shop reaches the same crossroads. There is an open-source engine everyone already knows, reached through a NuGet wrapper, and there is a managed .NET library that ships the engine in one package. On paper the open-source path wins before anyone opens an editor. Search for a Tesseract 5 C# wrapper and the recommendation comes back the same way every time, install the &lt;code&gt;Tesseract&lt;/code&gt; package, point it at a folder of &lt;code&gt;.traineddata&lt;/code&gt; files, and get offline OCR backed by an engine with nearly ten million downloads behind it. When our team weighs that against the engine documented in the &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;IronOCR reference docs&lt;/a&gt;, the deciding factor is rarely the license line. It is what that wrapper quietly asks of your deployment for the next several years.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure. Our team at Iron Software builds &lt;a href="https://ironsoftware.com/csharp/ocr/" rel="noopener noreferrer"&gt;IronOCR&lt;/a&gt;. We point out where the &lt;code&gt;charlesw/tesseract&lt;/code&gt; wrapper is the right call.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Approach Does Well
&lt;/h2&gt;

&lt;p&gt;The wrapper's own usage pattern is the place to start, because it states outright what the engine expects, an image, a language, and a couple of &lt;code&gt;using&lt;/code&gt; blocks.&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;// load the engine, pointing at a folder of tessdata files&lt;/span&gt;
&lt;span class="k"&gt;using&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;engine&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;TesseractEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"./tessdata"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"eng"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EngineMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// load an image, run OCR, and read the text and confidence&lt;/span&gt;
    &lt;span class="k"&gt;using&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;img&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"./images/invoice.png"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&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;page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&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;text&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;GetText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;confidence&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;GetMeanConfidence&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="n"&gt;text&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;That is the whole shape of it. Load a trained-data set, load an image into Leptonica's &lt;code&gt;Pix&lt;/code&gt; format, process it, and read the text and a confidence score back out. On a clean scan it prints the recognized block followed by a &lt;code&gt;GetMeanConfidence&lt;/code&gt; value somewhere around 87 percent, and the design is deliberately low-level.&lt;/p&gt;

&lt;p&gt;Two things here stand out. The license is Apache 2.0 across both the engine and the &lt;code&gt;charlesw/tesseract&lt;/code&gt; wrapper, and the language coverage runs well past 100 scripts. The engine's own cadence backs that reputation up, with &lt;code&gt;5.5.3&lt;/code&gt; shipping in July 2026 after &lt;code&gt;5.5.0&lt;/code&gt; through &lt;code&gt;5.5.2&lt;/code&gt; over the prior two years. For occasional OCR on clean images, with someone on the team keeping the native binaries current, the wrapper does the job for that narrow case. The cost it carries sits off the license line, in the native &lt;code&gt;tesseract&lt;/code&gt; and &lt;code&gt;leptonica&lt;/code&gt; binaries someone now owns for the life of the project. IronOCR removes that ownership by shipping the engine inside a single managed &lt;code&gt;IronOcr&lt;/code&gt; NuGet package, which is where the rest of this comparison lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability and Maintenance Comparison
&lt;/h2&gt;

&lt;p&gt;The table below sets the wrapper against IronOCR on the dimensions an architect prices before adoption, not on raw accuracy, which for both tools rests on the same underlying engine.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;charlesw/tesseract&lt;/code&gt; NuGet wrapper&lt;/th&gt;
&lt;th&gt;IronOCR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Commercial, with vendor support and a maintained release line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engine version wrapped&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5.2.0&lt;/code&gt;, per the wrapper's own README&lt;/td&gt;
&lt;td&gt;Tesseract &lt;code&gt;5.x&lt;/code&gt;, updated by Iron independent of the wrapper cycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last NuGet package release&lt;/td&gt;
&lt;td&gt;November 2022&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;IronOcr&lt;/code&gt; &lt;code&gt;2026.7.2&lt;/code&gt;, shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native setup&lt;/td&gt;
&lt;td&gt;Manual, VC++ runtime and platform-specific binaries&lt;/td&gt;
&lt;td&gt;None, a pure managed NuGet install&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language packs&lt;/td&gt;
&lt;td&gt;100+ languages, manual &lt;code&gt;.traineddata&lt;/code&gt; downloads&lt;/td&gt;
&lt;td&gt;125+ languages, bundled through NuGet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF input&lt;/td&gt;
&lt;td&gt;Not supported directly, images only&lt;/td&gt;
&lt;td&gt;Native through &lt;code&gt;OcrPdfInput&lt;/code&gt;, including password-protected PDFs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Barcode reading&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preprocessing (deskew, denoise, contrast)&lt;/td&gt;
&lt;td&gt;Not built in&lt;/td&gt;
&lt;td&gt;Built-in filter pipeline via &lt;code&gt;OcrInput&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region-of-interest cropping&lt;/td&gt;
&lt;td&gt;Broken since 2019 (Issue #489, still open)&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrent OCR calls&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;TesseractEngine&lt;/code&gt; instance per thread&lt;/td&gt;
&lt;td&gt;Single &lt;code&gt;IronTesseract&lt;/code&gt; instance handles concurrent calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-platform deployment&lt;/td&gt;
&lt;td&gt;Native binaries per OS, assembled by hand&lt;/td&gt;
&lt;td&gt;Windows, Linux, macOS, and Docker from one &lt;code&gt;IronOcr&lt;/code&gt; package&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. Built from the wrapper's README, changelog, and GitHub issue tracker, with the gaps that still cost teams real time today.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Approach Falls Short
&lt;/h2&gt;

&lt;p&gt;The single-image demo hides most of what adopting this wrapper commits a team to. The three gaps below are not edge cases, they are the reason a proof of concept that ran in an afternoon turns into a subsystem somebody maintains for years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most-downloaded wrapper is frozen at Tesseract &lt;code&gt;5.2.0&lt;/code&gt;:&lt;/strong&gt; The &lt;code&gt;Tesseract&lt;/code&gt; package, the one almost every C# OCR tutorial points to, with 9.6 million total downloads, has not shipped a new version since November 2022. Its own README still describes itself as a .NET wrapper for tesseract-ocr &lt;code&gt;5.2.0&lt;/code&gt;. In the years since, the engine has released &lt;code&gt;5.3&lt;/code&gt;, &lt;code&gt;5.4&lt;/code&gt;, and now &lt;code&gt;5.5.3&lt;/code&gt;, each carrying real accuracy and bug-fix work. None of that reaches a project on this wrapper unless someone manually swaps the native &lt;code&gt;tesseract&lt;/code&gt; and &lt;code&gt;leptonica&lt;/code&gt; binaries and then confirms the P/Invoke signatures still line up, which the maintainers have not done in a shipped release for over three years. Price that accurately and it carries a real cost, it is a fork the team has silently agreed to own. The question an architect has to answer is who performs that binary swap in year two, who validates it, and what happens to the OCR path when the one developer who understood the P/Invoke layer moves on. That is bus factor and version drift landing on the same line item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A scanned PDF never reaches the engine as a PDF:&lt;/strong&gt; The wrapper's image loader, &lt;code&gt;Pix.LoadFromFile&lt;/code&gt;, reads image formats through Leptonica and has no concept of a document. A pipeline that starts with a scanned invoice or an ID upload has to render every page to an image with a separate library before Tesseract ever sees it. That means a second native dependency, a second set of platform-specific binaries, and a second component that can break on a runtime the team did not test, all of it standing between the input the business hands you and the shape the engine will accept. Every one of those pieces is another thing to patch, another thing to certify on a new OS image, and another reason the deployment surface grows faster than the feature it supports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Region-of-interest cropping has been broken since 2019:&lt;/strong&gt; Issue #489 in the wrapper's own tracker, which reports that setting a region of interest does not work, was filed against the &lt;code&gt;4.1&lt;/code&gt; release and is still open, still reproducible, and still listed as a known issue in the changelog for the current &lt;code&gt;5.0&lt;/code&gt; wrapper release. A workflow that only needs one field from a scanned form, an account number or a line-item total, cannot lean on the engine's cropping API to isolate it. The image has to be cropped in a separate step before it reaches Tesseract, which is one more preprocessing stage the team writes, tests, and carries. A bug that has sat open across two major wrapper versions will not be fixed on your timeline, so any long-term plan has to design around it as a permanent shape of the tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preprocessing is a layer the team builds and then maintains:&lt;/strong&gt; Deskew, denoise, and contrast correction are not part of the wrapper, so a real document pipeline grows its own image-cleanup stage in front of the engine. On more than one project that glue has quietly grown into its own subsystem, with its own dependencies and its own failures, none of which shows up when the demo runs a pristine PNG. The accuracy the engine is famous for assumes an input that has already been cleaned, and on the open-source path the cleaning is code you own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The native setup never appears in the demo either:&lt;/strong&gt; Getting to that first snippet takes more than a NuGet reference. The native binaries need the Visual Studio 2019 runtime, &lt;code&gt;System.Drawing&lt;/code&gt; interop needs a separate package on .NET Core, and every language beyond English is a manual &lt;code&gt;.traineddata&lt;/code&gt; download that then has to stay in sync across developer machines, CI, and every server the code lands on. Each of those is fine on one workstation and a standing liability across an estate, because environment parity is now something the team enforces by hand rather than something the package guarantees. Over a three to five year horizon this is the part of the total cost of ownership that does not show on any invoice and shows up in every incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Maintenance
&lt;/h2&gt;

&lt;p&gt;The engine and the wrapper keep separate records, and conflating them leads to the wrong conclusion either way. The Tesseract engine itself has two CVEs on file, and both are effectively historical. &lt;code&gt;CVE-2021-36081&lt;/code&gt; is a use-after-free that affected only a 2020 alpha build, &lt;code&gt;5.0.0-alpha-20201231&lt;/code&gt;, not a stable release. &lt;code&gt;CVE-2011-1136&lt;/code&gt; dates to the &lt;code&gt;2.x&lt;/code&gt; line from over a decade ago. Both predate the &lt;code&gt;5.x&lt;/code&gt; branch teams actually ship today, and the engine has kept releasing on a regular cadence since, with &lt;code&gt;5.5.3&lt;/code&gt; landing in July 2026.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;charlesw/tesseract&lt;/code&gt; .NET wrapper has no CVEs of its own on record in the GitHub Advisory Database, the NVD, or Snyk. The maintenance picture is about pace rather than safety. The repository is not archived and still takes the occasional documentation pull request, but its last tagged NuGet release was November 2022 and 231 issues sit open in its tracker. A more actively maintained binding does exist. &lt;code&gt;TesseractOCR&lt;/code&gt;, maintained by Kees van Spelde, was forked from the same original codebase and currently tracks the engine's &lt;code&gt;5.5.x&lt;/code&gt; line at version &lt;code&gt;5.5.2&lt;/code&gt;, far more closely than the popular package has managed since 2022. A team set on staying open-source should evaluate that fork before defaulting to the stalled package everyone links to.&lt;/p&gt;

&lt;p&gt;The security review that matters here is not a scan for a live bug on either record, because neither has one. It is the standing question of who applies the next advisory, whenever it lands, on a wrapper whose native binaries no one has shipped in over three years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where IronOCR Fits Instead
&lt;/h2&gt;

&lt;p&gt;The teams that reach out to Iron Software tend to hit the PDF-input gap first, a document pipeline built around scanned invoices or ID uploads that does not want a second native library just to turn pages into images before OCR can begin. IronOCR reads the PDF directly.&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;IronOcr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// create the OCR engine&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ocrEngine&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;IronTesseract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// read a PDF directly, no rasterization step&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;pdfInput&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;OcrPdfInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scanned-invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;OcrResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocrEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfInput&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a scanned PDF to extracted text in a handful of lines, with no rasterization step, no native binaries, and no manually managed &lt;code&gt;tessdata&lt;/code&gt; folder. It prints the recognized document text straight to the console. Images load the same way through &lt;code&gt;OcrInput&lt;/code&gt;, and &lt;code&gt;OcrPdfInput&lt;/code&gt; exposes &lt;code&gt;Deskew()&lt;/code&gt; and &lt;code&gt;DeNoise()&lt;/code&gt; directly, so the preprocessing that was a hand-built layer on the open-source path is a method call here. A single &lt;code&gt;IronTesseract&lt;/code&gt; instance also handles concurrent calls, without the one-engine-per-thread limit that shapes how the wrapper scales across a worker pool.&lt;/p&gt;

&lt;p&gt;The architectural point is not the line count. It is that this snippet compiles and runs the same way inside a &lt;a href="https://ironsoftware.com/csharp/ocr/get-started/docker/" rel="noopener noreferrer"&gt;Docker container&lt;/a&gt; or on a &lt;a href="https://ironsoftware.com/csharp/ocr/get-started/linux/" rel="noopener noreferrer"&gt;Linux host&lt;/a&gt; as it does on a Windows desktop, from one &lt;code&gt;IronOcr&lt;/code&gt; package resolved at build time, with no native runtime to install and no version to swap by hand. The deployment surface that the wrapper grows over years, native binaries, a rasterizer, a preprocessing stage, per-machine language files, collapses into one dependency the build system owns. IronOCR's cost is published up front, so it can be weighed against the upkeep it removes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The Tesseract engine has earned its reputation. It is Apache 2.0 licensed, broad in language support, and still shipping real releases this month. What does not hold up is the assumption that the popular way to reach it from C# has kept pace. The &lt;code&gt;Tesseract&lt;/code&gt; package most tutorials point to has been frozen at &lt;code&gt;5.2.0&lt;/code&gt; since November 2022, PDF input was never part of its job, and a region-of-interest bug has sat open since 2019. For an occasional read of clean images, with someone owning the native binaries, that wrapper is still the right call, and it is worth recommending on those terms. For a team set on open source with real documents, the &lt;code&gt;TesseractOCR&lt;/code&gt; fork is worth evaluating before the stalled package.&lt;/p&gt;

&lt;p&gt;So here is the architecture question worth putting to the room. If your OCR code has to run three years from now as a headless service inside a Linux container, is the &lt;code&gt;charlesw/tesseract&lt;/code&gt; wrapper still the cheap option once you count the native-binary upkeep and the version drift, or has that ownership quietly become the most expensive line in the build? We would genuinely like to hear how that math landed in your own projects, and whether you stayed on the popular package, moved to the fork, or priced out a commercial engine instead.&lt;/p&gt;

&lt;p&gt;If the version lag, the native-binary maintenance, or the missing PDF input are the parts costing you time, which one would move you off the wrapper first? &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-pdf-ocr/" rel="noopener noreferrer"&gt;Run a scanned PDF through IronOCR&lt;/a&gt;, try the same document on the wrapper, and tell us in the comments where each one held up or fell over.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tesseract is a trademark of its respective owners, and we have no affiliation with the project or its maintainers. The facts above are drawn from the wrapper's public README, changelog, and issue tracker, and from published CVE records. If a detail has moved since, correct us in the responses.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>opensource</category>
      <category>ocr</category>
    </item>
    <item>
      <title>ClosedXML vs IronXL: The Hidden Cost of Open-Source .NET Excel Libraries</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Sat, 01 Aug 2026 05:20:33 +0000</pubDate>
      <link>https://dev.to/ironsoftware/closedxml-vs-ironxl-the-hidden-cost-of-open-source-net-excel-libraries-3n91</link>
      <guid>https://dev.to/ironsoftware/closedxml-vs-ironxl-the-hidden-cost-of-open-source-net-excel-libraries-3n91</guid>
      <description>&lt;p&gt;n open-source library rarely arrives as a decision. Someone needs a spreadsheet report on a Friday, &lt;code&gt;EPPlus&lt;/code&gt; licensing looks like a conversation nobody wants to have, the Open XML SDK reads like plumbing, and &lt;a href="https://github.com/ClosedXML/ClosedXML" rel="noopener noreferrer"&gt;&lt;code&gt;ClosedXML&lt;/code&gt;&lt;/a&gt; with its &lt;code&gt;XLWorkbook&lt;/code&gt; API gets it done. Sixteen years after its first commit it is still one of the most-installed Excel libraries in .NET, and that is exactly why it is worth looking at the way we look at any dependency before it turns load-bearing, which is to ask what it commits a team to over the next two years rather than what it does in the first afternoon. This piece walks ClosedXML the way we would walk it in an architecture review, and where it helps we contrast how &lt;a href="https://ironsoftware.com/csharp/excel/" rel="noopener noreferrer"&gt;IronXL&lt;/a&gt; handles the same work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick disclosure before we go further. Our team at Iron Software builds IronXL, and we will point out where ClosedXML is the better buy.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Library Does Well
&lt;/h2&gt;

&lt;p&gt;ClosedXML wraps the Open XML SDK in an object model built from workbooks, worksheets, cells, and ranges instead of raw XML parts and relationship IDs. A small report is short.&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;ClosedXML.Excel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// create a workbook and add a worksheet&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;workbook&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;XLWorkbook&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;worksheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&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;"Sales"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// set the header and data cells, plus a SUM formula&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B1"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Revenue"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A2"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Widgets"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B2"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;42500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B3"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Total"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C3"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FormulaA1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"=SUM(B2:B2)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// save the file&lt;/span&gt;
&lt;span class="n"&gt;workbook&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;"sales-report.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That writes a two-column sheet to &lt;code&gt;sales-report.xlsx&lt;/code&gt; with no relationship IDs and no manual part management, only cells and values set directly.&lt;/p&gt;

&lt;p&gt;That pattern is why it shows up in so many codebases. The license is MIT, and version &lt;code&gt;0.105.1&lt;/code&gt; shipped July 25, 2026. ClosedXML calculates built-in Excel functions itself rather than shelling out to Excel. For an internal report that lives entirely in &lt;code&gt;.xlsx&lt;/code&gt; and stays small, ClosedXML does that job, and on that narrow ground it is the better buy. What the core does not cover is the formats and the memory behavior most pipelines reach for next, which is where IronXL opens the file ClosedXML cannot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability and Licensing Comparison
&lt;/h2&gt;

&lt;p&gt;The differences that matter are easier to see side by side, so here is capability and licensing in one view.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;ClosedXML&lt;/th&gt;
&lt;th&gt;IronXL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Commercial, with vendor support and a maintained release line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost at scale&lt;/td&gt;
&lt;td&gt;Engineering time to work around the in-memory model at volume&lt;/td&gt;
&lt;td&gt;One license, predictable large-file handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File formats&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.xlsx&lt;/code&gt; and &lt;code&gt;.xlsm&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.xls&lt;/code&gt;, &lt;code&gt;.xlsx&lt;/code&gt;, &lt;code&gt;.xlsm&lt;/code&gt;, &lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.tsv&lt;/code&gt;, JSON, and XML from one API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy &lt;code&gt;.xls&lt;/code&gt; (binary) support&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Supported, &lt;code&gt;WorkBook.Load&lt;/code&gt; opens binary &lt;code&gt;.xls&lt;/code&gt; directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires Excel or Interop installed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No, &lt;code&gt;IronXL&lt;/code&gt; runs with no Excel installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-platform (Linux, Docker, Azure)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes, one build across Windows, Linux, macOS, and containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formula calculation&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;WorkBook.EvaluateAll&lt;/code&gt;, 150+ functions calculated in process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large-workbook memory model&lt;/td&gt;
&lt;td&gt;Full in-memory DOM, no built-in streaming reader&lt;/td&gt;
&lt;td&gt;In-memory &lt;code&gt;WorkBook&lt;/code&gt; model with predictable large-file handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public API stability&lt;/td&gt;
&lt;td&gt;Pre-1.0, own docs say the API is still not stable&lt;/td&gt;
&lt;td&gt;Versioned commercial release line, &lt;code&gt;2026.7.2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support channel&lt;/td&gt;
&lt;td&gt;Community and GitHub issues&lt;/td&gt;
&lt;td&gt;Commercial support included with &lt;code&gt;IronXL&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release cadence&lt;/td&gt;
&lt;td&gt;Active, &lt;code&gt;0.105.1&lt;/code&gt; shipped July 2026&lt;/td&gt;
&lt;td&gt;Active, &lt;code&gt;2026.7.2&lt;/code&gt; shipped June 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. Capability and licensing, built from ClosedXML's README, docs site, and NuGet metadata against IronXL's published API.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Library Falls Short
&lt;/h2&gt;

&lt;p&gt;What follows is scope rather than defect, and scope is exactly what an architecture review is for, because the cost of an open-source library is rarely the license. It is the developer time spent building and maintaining what the library does not do, and the question of who owns that work two years from now when the person who chose it has moved to another team. Four gaps carry most of that cost.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No path to legacy &lt;code&gt;.xls&lt;/code&gt;&lt;/strong&gt; - ClosedXML reads and writes the Open XML formats only, &lt;code&gt;.xlsx&lt;/code&gt; and &lt;code&gt;.xlsm&lt;/code&gt;. A binary &lt;code&gt;.xls&lt;/code&gt; file from an older ERP export, a decade-old template, or a partner feed that never left the legacy format will not open, and there is no fallback inside the library. The fix is a second dependency or a conversion step, and that glue is now something the team owns, tests, and carries through every upgrade. It is a small piece of code on the day it ships and a piece of tribal knowledge by the time someone new has to touch it. It also becomes part of the exit cost, because the day the team wants to change Excel libraries, the conversion shim is a second thing to unwind rather than one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The whole workbook loads into memory at once&lt;/strong&gt; - ClosedXML builds a full in-memory object model of the spreadsheet, with no streaming or SAX-style reader in the box. The project's own README publishes the numbers. Loading a million-row, ten-column text workbook takes roughly 800 MB of memory, and generating one from scratch runs close to 1.9 GB. A long-open GitHub issue, #86, tracks exactly this cost. On a memory-constrained container, or a batch job that processes several large exports at once, that is an out-of-memory risk to design around rather than a theoretical edge case. The architect version of the question is not how much memory one workbook needs, it is how many concurrent workbooks a container holds before it starts paging, because that number sets the throughput ceiling and it decides whether the answer to more volume is more replicas or a bigger box every month. Designing around it means either paying for that box or funding a rewrite that streams, and both are costs the MIT license never mentioned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sixteen years in, and still pre-1.0&lt;/strong&gt; - the first commit dates to May 2010 and the current release is &lt;code&gt;0.105.1&lt;/code&gt;. The documentation still tells you to read the release notes and the migration guide before every upgrade, because the public API, in its own words, is still not stable. For a team that does not want to re-test its formatting and formula code on every minor bump, that is a standing tax on maintenance, and it compounds. Multiply one careful upgrade a couple of times a year across a three-year horizon and the open-source library has quietly consumed a real slice of someone's calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not thread-safe, and says so&lt;/strong&gt; - ClosedXML is explicit that it is not thread-safe, and neither is the Open XML SDK underneath it. That is fine for one report at a time in a console app. It matters the moment the same code runs inside a web API or an Azure Function serving concurrent requests, where each request needs its own workbook instance and the team needs to know that rule before the first race condition shows up in production rather than after.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these is survivable on its own. Added together they describe a library whose real price is paid in the code a team writes around it, and that price does not appear on any invoice. The way to make that price visible is to put a number on it. A conversion shim for one legacy format, a streaming path for the large workbooks, a documented per-request instancing rule for concurrency, and a re-test pass on every minor upgrade are not exotic asks, and none is more than a few days of work. Spread across a three-year horizon and a team that turns over even once, though, they add up to a quiet subsystem that someone has to understand well enough to change, and the person who understands it best is usually the one who has already left. That is the bus factor an architecture review is supposed to catch before it becomes a production incident, and it is the part of the total cost that a zero-dollar license line item hides most effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Maintenance
&lt;/h2&gt;

&lt;p&gt;ClosedXML carries no CVEs of its own in the GitHub Advisory Database, NVD, or Snyk. What a security review has to weigh instead is the dependency surface, because a library inherits the advisories of everything it pulls in. ClosedXML's issue tracker shows the pattern. Issue #2491 references CVE-2024-43483 and CVE-2024-43484, the .NET runtime advisories covering a hash-flooding denial-of-service in &lt;code&gt;System.IO.Packaging&lt;/code&gt;, &lt;code&gt;System.Security.Cryptography.Cose&lt;/code&gt;, and &lt;code&gt;Microsoft.Extensions.Caching.Memory&lt;/code&gt;. ClosedXML depends on &lt;code&gt;System.IO.Packaging&lt;/code&gt;, so clearing that advisory meant waiting for a ClosedXML release that bumped the dependency. Issue #2099 shows the same shape earlier, flagging CVE-2018-8292 in &lt;code&gt;System.Net.Http&lt;/code&gt;. The exposure is real while it is open, and the timeline for closing it runs on the maintainers' release cadence, not yours.&lt;/p&gt;

&lt;p&gt;The maintenance picture is mixed. Releases do land, with &lt;code&gt;0.105.1&lt;/code&gt; shipping July 25, 2026 and a push to the repository a day earlier. The issue tracker also sits at 468 open issues, a meaningful backlog for a community project with no dedicated support team behind it. Releases and a large open-issue count coexist, and both belong in the same review, because the question a security review answers is how fast a fix lands when a deadline is riding on it, not merely whether one exists, and behind a community backlog that timing is out of your hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where IronXL Fits Instead
&lt;/h2&gt;

&lt;p&gt;The teams that hit this in practice are the ones that meet the &lt;code&gt;.xls&lt;/code&gt; wall, a legacy export ClosedXML will not open, or that need predictable memory on a large batch without redesigning around a full in-memory DOM. A library that covers the format range and calculates formulas in one pass is solving a different problem than ClosedXML was scoped for, and the code reads about the same.&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;IronXL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Legacy .xls files that ClosedXML cannot open load the same way as .xlsx&lt;/span&gt;
&lt;span class="n"&gt;WorkBook&lt;/span&gt; &lt;span class="n"&gt;workBook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WorkBook&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;"quarterly-export.xls"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;WorkSheet&lt;/span&gt; &lt;span class="n"&gt;workSheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultWorkSheet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// set a formula and calculate it in process, no Excel required&lt;/span&gt;
&lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"C2"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Formula&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"=SUM(B2:B50)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;workBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EvaluateAll&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;$"Quarterly total: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"C2"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;FormattedCellValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// save the result as xlsx&lt;/span&gt;
&lt;span class="n"&gt;workBook&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;"quarterly-export.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That opens a binary &lt;code&gt;.xls&lt;/code&gt;, sets a formula, calculates it in process with &lt;code&gt;WorkBook.EvaluateAll&lt;/code&gt;, and writes the result out as &lt;code&gt;.xlsx&lt;/code&gt;, printing something like &lt;code&gt;Quarterly total: 48250&lt;/code&gt; on the way.&lt;/p&gt;

&lt;p&gt;The trade is straightforward. &lt;a href="https://ironsoftware.com/csharp/excel/" rel="noopener noreferrer"&gt;&lt;code&gt;IronXL&lt;/code&gt;&lt;/a&gt; opens and writes the older binary &lt;code&gt;.xls&lt;/code&gt; alongside &lt;code&gt;.xlsx&lt;/code&gt;, &lt;code&gt;.xlsm&lt;/code&gt;, &lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.tsv&lt;/code&gt;, JSON, and XML from one API, so there is no second library bolted on for a single file type, and it calculates formulas built in through &lt;code&gt;WorkBook.EvaluateAll&lt;/code&gt; on the same &lt;code&gt;WorkBook&lt;/code&gt; and &lt;code&gt;WorkSheet&lt;/code&gt; model rather than leaving that to Excel. Commercial support comes with it, which is a real difference when a formula edge case is the thing standing between a team and a release. IronXL is licensed per deployment against ClosedXML's MIT terms, and that is the line the decision turns on.&lt;/p&gt;

&lt;p&gt;There is a deployment shape behind this too, and it is the part the architect cares about most. Where a spreadsheet job belongs, a synchronous request, a background worker, or a scheduled batch, is driven partly by how the library behaves under load, and a full in-memory model with no streaming pushes the large jobs off the request thread and into a worker pool whose size is capped by memory rather than CPU. Covering the awkward formats and the calculation in one dependency does not remove that constraint on its own, but it removes the second and third libraries that would otherwise each need their own instancing rule, their own upgrade cadence, and their own line in the exit plan. Fewer moving parts is not a slogan here, it is one less thing to keep aligned across a build matrix and one less place for a new hire to guess wrong.&lt;/p&gt;

&lt;p&gt;For a small internal report generator that only ever touches &lt;code&gt;.xlsx&lt;/code&gt;, ClosedXML does the job. When the pipeline has to ingest whatever format a partner sends, or needs a support line instead of a GitHub queue when production breaks at 2am, the broader-format library is the more direct fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;ClosedXML in 2026 is MIT-licensed and still shipping, and that is what the repository shows rather than anything we are spinning. What has not moved in sixteen years is the shape of it. Open XML formats only, a full in-memory model with no streaming path, an API its own maintainers still call not stable, and a fix timeline that runs on a community backlog. Stay where you are if the pipeline lives entirely in &lt;code&gt;.xlsx&lt;/code&gt;, the workbooks stay in the tens of thousands of rows, and one report at a time is all the concurrency you need, and if you are willing to bet the next three years that none of those three ever changes, because ClosedXML will not move to meet you when one of them does. Move to &lt;a href="https://ironsoftware.com/csharp/excel/" rel="noopener noreferrer"&gt;IronXL&lt;/a&gt; if you have to open a legacy &lt;code&gt;.xls&lt;/code&gt;, if predictable memory on large files is a requirement rather than a hope, or if you need a support channel behind the library when a release is on the line. The &lt;a href="https://ironsoftware.com/csharp/excel/blog/compare-to-other-components/closedxml-read-excel-csharp-alternatives/" rel="noopener noreferrer"&gt;longer format-by-format comparison&lt;/a&gt; walks the same ground in more depth.&lt;/p&gt;

&lt;p&gt;So where does your project land right now, comfortably inside &lt;code&gt;.xlsx&lt;/code&gt; and small-to-mid workbooks, or already fighting a legacy format or a memory ceiling in production?&lt;/p&gt;

&lt;p&gt;If you are weighing the move, the quickest test is to run one of your own awkward files through &lt;a href="https://ironsoftware.com/csharp/excel/" rel="noopener noreferrer"&gt;IronXL&lt;/a&gt; and watch what it does. Which wall did you hit first, the format or the memory ceiling? Tell us in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ClosedXML is the property of its respective owners, and our team has no affiliation with the project. The facts above are drawn from its public README, documentation, and CVE records at the time of writing. If a detail has moved since, correct us in the responses.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>opensource</category>
      <category>xml</category>
    </item>
    <item>
      <title>C# PDF Libraries: Evaluating Syncfusion’s License Cliff and Azure Limitations</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 31 Jul 2026 01:23:39 +0000</pubDate>
      <link>https://dev.to/ironsoftware/c-pdf-libraries-evaluating-syncfusions-license-cliff-and-azure-limitations-295g</link>
      <guid>https://dev.to/ironsoftware/c-pdf-libraries-evaluating-syncfusions-license-cliff-and-azure-limitations-295g</guid>
      <description>&lt;p&gt;When a team already ships Syncfusion grids, charts, and schedulers, adding its PDF library the moment a project needs invoices or reports feels like the safe call. One vendor, one license dashboard, one support contract. We have sat in enough architecture reviews to know that the safe call is exactly the one that deserves a second look, because the cost of a same-vendor default shows up months later in deployment and headcount, not on day one. This piece walks the Syncfusion PDF story the way we would walk it before signing off, and where it makes sense, we contrast it with how &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;IronPDF&lt;/a&gt; handles the same work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick note on our involvement is that our team at Iron Software works to develop IronPDF. We will highlight where Syncfusion is the better option as it clearly is in some areas.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The commitment nobody reads until later
&lt;/h2&gt;

&lt;p&gt;Build-versus-buy is rarely the real question with a PDF library. The real question is what you commit to when you buy, and Syncfusion asks for three commitments that stay quiet during evaluation and get loud in production. The first is licensing. The second is a separate rendering package with its own native binaries. The third is a hosting environment where the primary rendering engine will not start. None of these are secrets. All of them are easy to miss when the demo runs clean on a developer laptop.&lt;/p&gt;

&lt;p&gt;We want to set the frame before we get into the friction. The core Syncfusion &lt;code&gt;PdfDocument&lt;/code&gt; API is solid engineering. Conformance, signatures, and encryption are genuinely deep, and the release cadence is fast. If your work is programmatic document assembly with a heavy compliance angle, Syncfusion is a serious tool and we will say so again later with specifics. The friction is narrow and it is real, and it lives in two places, HTML rendering from markup and the license as your team grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Community License cliff
&lt;/h2&gt;

&lt;p&gt;Syncfusion offers a free Community License, and it is genuinely free, which is why so many prototypes start on it. The catch is that eligibility is not a single threshold you clear once. It is four conditions that must all hold at the same time, and the moment any one of them breaks, the whole thing lapses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Annual gross revenue under one million US dollars.&lt;/li&gt;
&lt;li&gt; Five or fewer developers.&lt;/li&gt;
&lt;li&gt; Ten or fewer total employees.&lt;/li&gt;
&lt;li&gt; Three million US dollars or less in lifetime raised capital from outside sources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read those as an architect planning two years out rather than as a founder shipping this week. A startup that raises a modest seed round can cross the capital line without adding a single engineer. A team that hires its sixth developer trips the developer count while every other number stays comfortable. Cross any one line and you are buying paid seats, and you are doing it on the vendor's timeline rather than your own, usually right when the company is busy with the growth that broke the condition in the first place. The pricing is fine. The problem is that the trigger is coupled to hiring and fundraising, which are the two things a healthy company does on purpose, so the license risk is highest exactly when you can least afford a surprise procurement cycle.&lt;/p&gt;

&lt;p&gt;This is not a knock on Syncfusion charging money. Everyone charges money, and IronPDF has no free tier at all, which we get to below. The architecture point is narrower. A free tier whose eligibility is tied to four moving business metrics is a dependency on your own growth, and that belongs in the risk column of the review, not the savings column.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML to PDF is a different package with its own binaries
&lt;/h2&gt;

&lt;p&gt;Most teams that reach for a PDF library want to render HTML. It is the fastest way to turn an existing invoice template or a report layout into a document, because the design already exists as markup and CSS. Here is where the Syncfusion architecture asks for the second commitment.&lt;/p&gt;

&lt;p&gt;HTML conversion is not in the core PDF package. It lives in a separate &lt;code&gt;Syncfusion.HtmlToPdfConverter.*&lt;/code&gt; family, and that converter drives a browser engine, either a modern Blink engine or a legacy WebKit one. Blink needs native binaries, and those binaries are shipped per operating system as distinct packages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;Syncfusion.HtmlToPdfConverter.Net.Windows&lt;/code&gt; pulling &lt;code&gt;BlinkBinaries&lt;/code&gt; for Windows.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;BlinkBinariesLinux&lt;/code&gt; for Linux hosts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BlinkBinariesMac&lt;/code&gt; for macOS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BlinkBinariesAws&lt;/code&gt; for AWS Lambda style targets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build on Windows and deploy to a Linux container, and plenty of shops now do exactly that, you have to get the right binary package onto the right target and keep it aligned across your build matrix. This is solvable. Teams solve it every day. But it is real configuration surface that has to be reviewed, documented, and maintained, and it is the kind of surface that passes CI on one machine and fails on the deployment host because the native payload for that platform was never referenced. The core &lt;code&gt;PdfDocument&lt;/code&gt; work does not carry this weight. The HTML path does, and the HTML path is the one most product teams actually want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blink will not run on Azure App Service for Windows
&lt;/h2&gt;

&lt;p&gt;This is the point that turns a configuration chore into a hosting decision, and it is the one we would flag hardest in a review.&lt;/p&gt;

&lt;p&gt;Blink does not run on Azure App Service on Windows. The App Service sandbox restricts the GDI calls the Chromium engine needs, so the engine does not start in that environment. Syncfusion documents this, and the documented workaround is to move HTML rendering off App Service entirely, into a Linux Azure Function or a container, and call it over HTTP from your main application. Read that again as an architecture change rather than a config flag. You are introducing a second deployment unit, a network hop, its own scaling and failure characteristics, and its own monitoring, purely to render HTML to PDF. For a team that chose Azure App Service on Windows precisely because it is simple, that is a meaningful tax.&lt;/p&gt;

&lt;p&gt;The escape hatch is the legacy WebKit engine, which sidesteps the Azure block, and it comes with its own bundle of problems that make it a hard sell in 2026.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; WebKit does not execute JavaScript, so anything rendered by a client side framework or a charting script never appears.&lt;/li&gt;
&lt;li&gt; It mishandles modern CSS3, so current layouts drift.&lt;/li&gt;
&lt;li&gt; Its NuGet packages were pulled, so you are building on distribution that is no longer served.&lt;/li&gt;
&lt;li&gt; Its bundled Qt binaries carry an unpatched CVE-2019-7317 in the zlib and libpng stack, which is the kind of finding a security review flags on sight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Syncfusion's own guidance is to move from WebKit to Blink. So the two engines box you in. Blink is the recommended path but it is blocked on the exact Azure tier many .NET shops default to, and WebKit clears Azure but is deprecated, insecure, and cannot run JavaScript. Neither answer is clean for the common case of a Windows-hosted .NET app that needs HTML rendering.&lt;/p&gt;

&lt;p&gt;For contrast, IronPDF ships a single Chromium engine inside the one package and handles its own binaries per platform without you selecting them by hand. On Azure the constraint is different in kind. The free, shared, and consumption tiers block the GDI+ calls, so you need Basic B1 or higher, which is a tier requirement rather than a platform block. There is no separate Linux Function to stand up and no HTTP hop to render a document. The &lt;a href="https://ironpdf.com/how-to/azure/" rel="noopener noreferrer"&gt;Azure deployment notes&lt;/a&gt; spell out the tier, and if you are weighing a move the &lt;a href="https://ironpdf.com/blog/migration-guides/migrate-from-syncfusion-pdf-to-ironpdf/" rel="noopener noreferrer"&gt;Syncfusion to IronPDF migration guide&lt;/a&gt; covers the API mapping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clearing up the CVEs, because misattribution is its own risk
&lt;/h2&gt;

&lt;p&gt;If you search for Syncfusion CVEs you will find several, and it would be dishonest to let them sit unexplained, because none of them belong to the PDF component. We want to be precise here, since scaring a team off with a mislabeled vulnerability is as bad as hiding a real one.&lt;/p&gt;

&lt;p&gt;The Syncfusion PDF component itself has no CVEs. The widely cited ones belong to other Syncfusion products. CVE-2024-55969 is in DocIO, the Word library. CVE-2024-55970 is in File Manager. CVE-2023-26563, CVE-2023-26564, and CVE-2023-26565 are in file provider components. Do not carry those into a PDF risk assessment, because they are not PDF findings. The one real PDF-adjacent security issue is CVE-2019-7317 in the legacy WebKit Qt bundle we described above, and that is a reason to avoid legacy WebKit specifically, not the core library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Syncfusion is the stronger choice
&lt;/h2&gt;

&lt;p&gt;We promised specifics, so here they are without hedging. Syncfusion's signature and encryption stack is deep, and if regulated document workflows are your center of gravity it may well be the better fit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Signatures cover PAdES, CAdES, and CMS, with SHA-256 and SHA-512, PFX files, HSM support, OCSP and CRL revocation checking, and RFC 3161 timestamps.&lt;/li&gt;
&lt;li&gt; Encryption spans RC4, AES-128 and AES-256, and PDF 2.0 AES-GCM 256.&lt;/li&gt;
&lt;li&gt; The release cadence is fast, with quarterly volume releases plus weekly hotfix NuGet packages, and version 34.1.29 landed on July 14 2026.&lt;/li&gt;
&lt;li&gt; Suite consolidation is real value. One vendor and one license dashboard across grids, charts, schedulers, and documents is a legitimate operational simplification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the whole reason a same-vendor default is tempting, and it is not wrong. If your team already lives inside the Syncfusion suite and your PDF needs are programmatic assembly with heavy signing, staying put is a defensible architecture decision. IronPDF covers &lt;a href="https://ironpdf.com/how-to/signing/" rel="noopener noreferrer"&gt;digital signing&lt;/a&gt; and &lt;a href="https://ironpdf.com/how-to/pdfa/" rel="noopener noreferrer"&gt;PDF/A conformance&lt;/a&gt; too, so it is not a gap on our side, but we are not going to pretend a single library replaces a whole controls suite for a team that uses the whole suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same job in code
&lt;/h2&gt;

&lt;p&gt;The clearest way to see the split is the code for one small task, rendering an invoice. Syncfusion's core path builds the document programmatically, drawing text at coordinates.&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;// build a new PDF document and add a page&lt;/span&gt;
&lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;document&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;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PdfPage&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;document&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="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PdfGraphics&lt;/span&gt; &lt;span class="n"&gt;graphics&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="n"&gt;Graphics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// create a standard font at 20 point&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PdfStandardFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PdfFontFamily&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;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// draw the text at a fixed point on the page&lt;/span&gt;
&lt;span class="n"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DrawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoice #1042"&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;PdfBrushes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Black&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;PointF&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;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// save and close the document&lt;/span&gt;
&lt;span class="n"&gt;document&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;"invoice.pdf"&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="nf"&gt;Close&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is clean for fixed layouts. It becomes tedious the moment the invoice has a table, wrapping text, or a design that a non-developer owns, because every element becomes a coordinate you compute. The IronPDF path renders the markup you already have.&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;IronPdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// create the Chromium based renderer&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="c1"&gt;// render an HTML string straight to a PDF&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;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="s"&gt;"&amp;lt;h1&amp;gt;Invoice #1042&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total due: $250.00&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// write the file to disk&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;"invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same output, different center of gravity. When the source of truth is HTML and CSS, rendering markup keeps the design where designers can touch it. When the source of truth is structured data and coordinates, the drawing API is fine. The architecture question is which one your document pipeline actually starts from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The side-by-side
&lt;/h2&gt;

&lt;p&gt;Here is the honest comparison in one view, wins and losses on both sides.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Syncfusion PDF&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;Free tier&lt;/td&gt;
&lt;td&gt;Community License, but four conditions must all hold&lt;/td&gt;
&lt;td&gt;No free tier, commercial and tiered by deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML rendering&lt;/td&gt;
&lt;td&gt;Separate converter package, Blink or legacy WebKit&lt;/td&gt;
&lt;td&gt;Chromium built into the one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native binaries&lt;/td&gt;
&lt;td&gt;Per-OS Blink packages you select by hand&lt;/td&gt;
&lt;td&gt;Handled per platform for you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure App Service on Windows&lt;/td&gt;
&lt;td&gt;Blink blocked, needs a Linux Function or container over HTTP&lt;/td&gt;
&lt;td&gt;Runs on Basic B1 or higher, a tier requirement not a block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy engine risk&lt;/td&gt;
&lt;td&gt;WebKit is deprecated, no JavaScript, carries CVE-2019-7317&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signatures and encryption&lt;/td&gt;
&lt;td&gt;Very deep, PAdES CAdES CMS HSM OCSP CRL, AES-GCM 256&lt;/td&gt;
&lt;td&gt;Solid coverage of signing and conformance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suite consolidation&lt;/td&gt;
&lt;td&gt;Strong, one vendor across many controls&lt;/td&gt;
&lt;td&gt;PDF focused, not a controls suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF component CVEs&lt;/td&gt;
&lt;td&gt;None, cited CVEs belong to other products&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For completeness, IronPDF also handles &lt;a href="https://ironpdf.com/how-to/rasterize-pdf-to-images/" rel="noopener noreferrer"&gt;rasterizing pages to images&lt;/a&gt;, and its &lt;a href="https://ironpdf.com/licensing/" rel="noopener noreferrer"&gt;licensing&lt;/a&gt; is deployment tiered rather than headcount gated, which is the direct counterpoint to the Community License cliff.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we would actually decide
&lt;/h2&gt;

&lt;p&gt;Strip it down and the decision is not about which library draws a rectangle better. Both are competent. The decision is about the two commitments that survive past the demo. If your PDF work is programmatic assembly with serious signing, and your company is stable in size or already paying for the Syncfusion suite, its depth and consolidation are worth real money and we would not talk you out of it. If your documents start life as HTML, or you deploy to Azure App Service on Windows, or your headcount and funding are on a growth curve that will trip the Community License, then the separate converter package, the per-OS binaries, and the Blink block on Azure are recurring costs that a single-package Chromium engine avoids. Name the shape of your pipeline first, and the library mostly picks itself.&lt;/p&gt;

&lt;p&gt;So here is the question we would put to your team in the review. Look at your next two years of hiring and funding, and at where you actually deploy. Does a free tier gated on four business metrics you plan to outgrow belong in the savings column, or in the risk column? Tell us in the comments how you would score that trade, because the answer changes a lot depending on your stage.&lt;/p&gt;

&lt;p&gt;If you are weighing this right now, the &lt;a href="https://ironpdf.com/blog/migration-guides/migrate-from-syncfusion-pdf-to-ironpdf/" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; is the fastest way to see the API mapping for yourself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Syncfusion and its component suite are trademarks of Syncfusion, Inc. We are not affiliated with them, and the details above draw on Syncfusion's public license terms, documentation, and CVE records as they stood at the time of writing. If a figure or a date has moved since, correct us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>pdf</category>
      <category>architecture</category>
    </item>
    <item>
      <title>C# Excel Generation: The Real Cost of Using the Free Open XML SDK</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 31 Jul 2026 01:20:30 +0000</pubDate>
      <link>https://dev.to/ironsoftware/c-excel-generation-the-real-cost-of-using-the-free-open-xml-sdk-jg9</link>
      <guid>https://dev.to/ironsoftware/c-excel-generation-the-real-cost-of-using-the-free-open-xml-sdk-jg9</guid>
      <description>&lt;p&gt;Every team that generates spreadsheets in .NET eventually runs the same evaluation. There is a free, Microsoft-backed option sitting right there, and there is a commercial one that costs money per deployment. On a spreadsheet of first-order costs, the free option wins before the meeting even starts. The problem is that the interesting costs are second-order, and they do not show up until you are three sprints into &lt;a href="https://ironsoftware.com/csharp/excel/blog/using-ironxl/csharp-create-excel-report-tutorial/" rel="noopener noreferrer"&gt;building a reporting service&lt;/a&gt;. This is a build-versus-buy story, and the fair version of it starts by admitting that Open XML SDK is genuinely excellent at the thing it was designed to do.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At Iron Software, we develop IronXL. We will point out cases where the Open XML SDK is more suitable, as it performs best for specific tasks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Open XML SDK Actually Is
&lt;/h2&gt;

&lt;p&gt;DocumentFormat.OpenXml is a low-level schema API. It is shipped by Microsoft under the .NET Foundation, it is MIT-licensed, and the current release is 3.5.1 from March 2026 with more than 1,300 commits behind it. It tracks the OOXML schema that Office itself uses, which means the objects you work with are strongly typed and generated directly from the ECMA-376 and ISO/IEC 29500 standards. When the standard changes, the object model follows.&lt;/p&gt;

&lt;p&gt;That design has real, concrete advantages, and we want to name them before we get anywhere near the gaps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The strongly typed object model means the compiler catches malformed structure. You are manipulating the same schema Word and Excel serialize, not a friendly approximation of it.&lt;/li&gt;
&lt;li&gt;✅ There is a built-in OpenXmlValidator that checks byte-level structural fidelity, which is the kind of guarantee you want when a downstream system is strict about the format.&lt;/li&gt;
&lt;li&gt;✅ The MIT license carries zero revenue, seat, or SaaS restrictions. You can ship it in a product sold to a million customers and owe nothing and ask no one.&lt;/li&gt;
&lt;li&gt;✅ The Open Packaging Conventions give you random access to individual document parts. You can strip the comments out of a docx or move a single slide inside a deck without parsing the rest of the file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your work is document surgery, schema validation, or high-volume structural transformation where licensing risk at scale is the thing keeping you up at night, this is the correct tool and nothing we sell changes that. The README is honest about the contract. It expects you to bring detailed knowledge of the relevant formats and standards. It hands you the schema and, deliberately, nothing more.&lt;/p&gt;

&lt;p&gt;The architecture question is what "nothing more" costs to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Gap Is the Expensive One. There Is No Calculation Engine.
&lt;/h2&gt;

&lt;p&gt;This is the gap that surprises people, so it is worth being precise about the mechanics. In OOXML, a cell that contains a formula stores two separate things. &lt;code&gt;CellFormula&lt;/code&gt; holds the formula text, something like &lt;code&gt;SUM(B1:B10)&lt;/code&gt;. &lt;code&gt;CellValue&lt;/code&gt; holds a cached result, and that cached number was computed by Excel, not by the SDK. Open XML SDK reads and writes both fields faithfully. What it never does is calculate.&lt;/p&gt;

&lt;p&gt;Play that forward. You build a workbook in a headless service, you write the formula text into every total cell, and you save the file. Because your service never computed a result, there is no cached value to write, so those cells are blank or stale on disk. They stay blank until a human opens the file in Excel and Excel recalculates on load. On a server that generates an invoice and emails it straight to a customer, no human ever opens it first. The customer receives a document where the total is empty.&lt;/p&gt;

&lt;p&gt;The workaround people reach for is to compute every value in their own C# and write it into &lt;code&gt;CellValue&lt;/code&gt; themselves, keeping the formula text alongside only for show. That works, but read what it actually is. You have written a spreadsheet calculation engine. You own SUM and AVERAGE and the operator precedence and the cell-reference resolution and the edge cases around blanks and text-in-numeric-cells, forever, in your application code. That is a large surface to build and a larger one to keep correct.&lt;/p&gt;

&lt;p&gt;This is not an oversight that a patch is about to close. The request for a calculation feature has been open since 2018. A formal API proposal was still open in late 2025. This is a matter of roadmap scope rather than neglect, and the safe reading is that you should not architect around it landing. If your workload needs computed results, plan to supply that layer yourself or pick a library that already has one. For teams that live in formulas, our own &lt;a href="https://ironsoftware.com/csharp/excel/blog/excel-tools/how-to-copy-a-formula-in-excel/" rel="noopener noreferrer"&gt;guide to copying and working with formulas&lt;/a&gt; shows the behavior we mean when we say the result should exist the moment the file is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Second Gap Is Legacy Formats. It Is OOXML or Nothing.
&lt;/h2&gt;

&lt;p&gt;Open XML SDK reads and writes the modern XML-based formats and only those. There is no support for the old binary formats, no .xls, no .doc, no .ppt, in either direction. If your pipeline receives files from the outside world, this matters more than it looks.&lt;/p&gt;

&lt;p&gt;Consider the common case. A partner or an internal legacy system uploads an .xls file, the binary format that was standard for years and still turns up constantly in finance, logistics, and government data. Hand that file to Open XML SDK and it cannot open it, because it is not OOXML. Your pipeline now needs a separate conversion step in front of the SDK, some other component that turns .xls into .xlsx before the SDK is allowed to see it. That is another dependency, another failure point, and another thing to test against the messy real-world files that never quite match the spec.&lt;/p&gt;

&lt;p&gt;None of this is a bug. It is the scope Microsoft chose, and it is a reasonable scope for a tool named after the Open XML standard. It just means the format-conversion layer is, once again, yours to build or source. We wrote up &lt;a href="https://ironsoftware.com/csharp/excel/blog/using-ironxl/csharp-convert-xls-to-xlsx-file-tutorial/" rel="noopener noreferrer"&gt;the .xls to .xlsx conversion problem&lt;/a&gt; precisely because so many teams hit it the week after they commit to an OOXML-only stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Third Gap Is Memory. The Default API Buffers Everything.
&lt;/h2&gt;

&lt;p&gt;The friendly, discoverable way to use Open XML SDK is the DOM API, the one in every tutorial where you build up &lt;code&gt;Row&lt;/code&gt; and &lt;code&gt;Cell&lt;/code&gt; objects and append them to &lt;code&gt;SheetData&lt;/code&gt;. It is readable and it is the approach the sample below uses. It also loads the entire document part into memory as an object tree.&lt;/p&gt;

&lt;p&gt;On a normal report this is fine. On a workbook with hundreds of thousands of rows it is an OutOfMemoryException, and Microsoft's own position is that this is expected behavior for a DOM. A DOM materializes the whole tree by definition, so a big enough tree exhausts the heap. There is nothing broken here, only a boundary you have to know about before you cross it in production.&lt;/p&gt;

&lt;p&gt;The supported fix is to drop from the DOM down to the SAX-style &lt;code&gt;OpenXmlReader&lt;/code&gt; and &lt;code&gt;OpenXmlWriter&lt;/code&gt;. These stream the document element by element and keep memory flat, and they work correctly. The cost is that the streaming API is materially more verbose and lower level than the DOM. Your clean object-building code becomes a state machine of read-element and write-element calls, and the thing you were trying to express gets buried under the mechanics of the stream. You end up maintaining two mental models, the pretty one for small files and the painful one for large files, and choosing between them per feature. When we handle &lt;a href="https://ironsoftware.com/csharp/excel/blog/using-ironxl/how-to-export-huge-data-from-datatable-to-excel-csharp/" rel="noopener noreferrer"&gt;large exports from a DataTable&lt;/a&gt; we want one path that does not fall over at scale, not a fork in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Code
&lt;/h2&gt;

&lt;p&gt;Here is the SDK writing a single labeled cell into a new workbook. It is verbose by design, and we have kept it that way rather than trim it, because the verbosity is the point. Every part of the package is something you name and wire up by hand.&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;DocumentFormat.OpenXml&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;DocumentFormat.OpenXml.Packaging&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;DocumentFormat.OpenXml.Spreadsheet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create the package and the workbook part&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;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SpreadsheetDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.xlsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SpreadsheetDocumentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workbook&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;workbookPart&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="nf"&gt;AddWorkbookPart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;workbookPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workbook&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;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add a worksheet part and its sheet data container&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;worksheetPart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbookPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddNewPart&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WorksheetPart&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sheetData&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;SheetData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;worksheetPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheet&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;Worksheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheetData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register the sheet in the workbook's sheet list&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sheets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbookPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&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;Sheets&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;sheets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&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;Sheet&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="n"&gt;workbookPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetIdOfPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worksheetPart&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;SheetId&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;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Report"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Build one row with one string cell&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;row&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;Row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;RowIndex&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;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&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;Cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;CellReference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CellValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CellValue&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;CellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total Due"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;sheetData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;workbookPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workbook&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the same neighborhood of work in IronXL, including a formula that is actually computed and read back as a number before the file is ever saved.&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;IronXL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Load an existing workbook, including legacy .xls if that is what arrives&lt;/span&gt;
&lt;span class="n"&gt;WorkBook&lt;/span&gt; &lt;span class="n"&gt;workBook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WorkBook&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;"invoice.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;WorkSheet&lt;/span&gt; &lt;span class="n"&gt;workSheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultWorkSheet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Write a label, a value, and a formula&lt;/span&gt;
&lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Total Due"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"B1"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;250.00m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"B2"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Formula&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"=SUM(B1:B1)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Calculate every formula in the book without Excel installed&lt;/span&gt;
&lt;span class="n"&gt;workBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EvaluateAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workSheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"B2"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;DecimalValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;workBook&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;"invoice-updated.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference that matters is the one line &lt;code&gt;EvaluateAll()&lt;/code&gt;. IronXL computes formulas itself with no Excel on the machine, so &lt;code&gt;total&lt;/code&gt; is a real number the instant that call returns. &lt;code&gt;WorkBook.Load&lt;/code&gt; opens .xls in the same breath as .xlsx, so the conversion step disappears. And the large-file memory management happens inside the library rather than in a streaming state machine you maintain. The &lt;a href="https://ironsoftware.com/csharp/excel/blog/using-ironxl/best-way-read-excel-csharp/" rel="noopener noreferrer"&gt;reading walkthrough&lt;/a&gt; shows the same shape across more of the surface, and the full &lt;a href="https://ironsoftware.com/csharp/excel/docs/" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt; covers the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comparison, Both Sides
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Open XML SDK&lt;/th&gt;
&lt;th&gt;IronXL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT, no seat or revenue limits at any scale&lt;/td&gt;
&lt;td&gt;Commercial, tiered by deployment, no free tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formula calculation&lt;/td&gt;
&lt;td&gt;None. You compute values and cache them yourself&lt;/td&gt;
&lt;td&gt;Built in via &lt;code&gt;EvaluateAll()&lt;/code&gt;, no Excel needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy .xls read and write&lt;/td&gt;
&lt;td&gt;Not supported, OOXML only&lt;/td&gt;
&lt;td&gt;Loads .xls alongside .xlsx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large files&lt;/td&gt;
&lt;td&gt;DOM buffers in memory, drop to SAX to stream&lt;/td&gt;
&lt;td&gt;Managed internally on the default path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema and structural fidelity&lt;/td&gt;
&lt;td&gt;Strongly typed, built-in validator&lt;/td&gt;
&lt;td&gt;Higher-level model over the same formats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API level&lt;/td&gt;
&lt;td&gt;Low level, expects format knowledge&lt;/td&gt;
&lt;td&gt;Task-level, fewer moving parts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backing&lt;/td&gt;
&lt;td&gt;Microsoft, .NET Foundation, active&lt;/td&gt;
&lt;td&gt;Iron Software, commercial support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A quick word on things that get miscounted against the SDK. It has no CVEs of its own. The CVE-2023-21538 that turns up in an issue thread is a denial-of-service in the .NET runtime's System.Private.DataContractSerialization, not in the SDK's code. And the discontinued "Microsoft Open XML File Format Converter", an old Mac Office tool, is a naming coincidence with no relationship to this library. The maintenance story is genuinely healthy and corporate-backed. The formula gap staying open since 2018 is a scope decision, not a sign of an abandoned project.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Should You Actually Pick
&lt;/h2&gt;

&lt;p&gt;Reframe it as a total-cost-of-ownership question and the answer stops being universal. If you need schema-level control, structural validation, part-by-part surgery on documents, or zero licensing exposure across an unbounded number of installs, Open XML SDK is the stronger choice and the price is unbeatable. You are trading engineering time for license fees, and for that class of work the trade is sound.&lt;/p&gt;

&lt;p&gt;The trade inverts when your service needs computed totals, ingests legacy binaries, or streams very large workbooks under memory pressure. There you are not choosing between free and paid. You are choosing between building and maintaining a calculation engine, a format-conversion layer, and a memory strategy, versus buying those three layers already built and tested. IronXL is commercial and there is no free tier, so it is a real budget line, and you can read exactly how the tiers work on the &lt;a href="https://ironsoftware.com/csharp/excel/licensing/" rel="noopener noreferrer"&gt;licensing page&lt;/a&gt;. The comparison worth making is that cost against the salaried months your own team would spend rebuilding the layers the SDK deliberately leaves out.&lt;/p&gt;

&lt;p&gt;The mistake we watch teams make is pricing only the license and never the layer. When you have shipped one calculation engine of your own, which of the three gaps would cost your team the most to close, and did that surprise you when you first hit it in production? We would genuinely like to read your war stories in the comments.&lt;/p&gt;

&lt;p&gt;If you are weighing this decision right now, try the two approaches on your own worst file, the ugliest legacy .xls with the deepest formulas you have, and tell us in the comments how each one held up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open XML SDK is maintained by Microsoft under the .NET Foundation, and the names referenced are the property of their owners. We are not affiliated with Microsoft, and the details above draw on the SDK's public documentation, repository, and issue tracker as they stood at the time of writing. If a version or a date has moved since, correct us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>opensource</category>
      <category>xml</category>
    </item>
    <item>
      <title>C# OCR Comparison: Windows.Media.Ocr Limitations vs. IronOCR</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 31 Jul 2026 01:15:58 +0000</pubDate>
      <link>https://dev.to/ironsoftware/c-ocr-comparison-windowsmediaocr-limitations-vs-ironocr-2c32</link>
      <guid>https://dev.to/ironsoftware/c-ocr-comparison-windowsmediaocr-limitations-vs-ironocr-2c32</guid>
      <description>&lt;p&gt;Every OCR decision on Windows eventually reaches the same fork. There is a free text recognition engine already built into the operating system, and there is a commercial library you can pull from NuGet. On paper, the free option wins before the discussion even starts. Windows.Media.Ocr ships inside Windows 10 and 11, costs nothing, and runs fully on the device. When our team weighs that against a paid engine documented in the &lt;a href="https://ironsoftware.com/csharp/ocr/docs/" rel="noopener noreferrer"&gt;IronOCR reference docs&lt;/a&gt;, the deciding factor is rarely the license fee. It is what the built-in engine quietly asks of your deployment in return.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick note on where we stand. Our team at Iron Software builds &lt;a href="https://ironsoftware.com/csharp/ocr/" rel="noopener noreferrer"&gt;IronOCR&lt;/a&gt;, so we have a stake here. We will point out where Windows.Media.Ocr is the right call, because for one kind of app it clearly is.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Windows.Media.Ocr gets right
&lt;/h2&gt;

&lt;p&gt;Let us be fair before we get critical, because the engine has real strengths that explain why so many teams reach for it first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ It is free and needs no NuGet reference of any kind.&lt;/li&gt;
&lt;li&gt;✅ Every byte stays on the machine, which suits regulated or offline work where nothing may leave the box.&lt;/li&gt;
&lt;li&gt;✅ It has been stable since the first Windows 10 release and still appears in the current SDK.&lt;/li&gt;
&lt;li&gt;✅ The output is structured. You get &lt;code&gt;OcrResult.Text&lt;/code&gt; plus a collection of lines and words, and each word carries a bounding box.&lt;/li&gt;
&lt;li&gt;✅ The API describes itself through &lt;code&gt;AvailableRecognizerLanguages&lt;/code&gt; and &lt;code&gt;IsLanguageSupported&lt;/code&gt;, so you can probe capability at runtime instead of guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a certain kind of app, this really is enough, and we will name that app clearly before the end. The trouble starts when your deployment shape does not match the one narrow shape this engine assumes you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MSIX trap that blocks services and console apps
&lt;/h2&gt;

&lt;p&gt;Here is the constraint that catches most teams off guard. &lt;code&gt;Windows.Media.Ocr.OcrEngine&lt;/code&gt; appears on Microsoft's own published list of WinRT APIs that require package identity. Put in practical terms, the calling process has to run with an MSIX package identity or the call fails outright.&lt;/p&gt;

&lt;p&gt;Think about what that rules out. A plain console app has no package identity. A Windows service running under a system account has none. A traditional WPF or WinForms app shipped as a folder of files behind an installer has none either. None of those three can call the engine as written, and those three cover an enormous share of real production .NET software.&lt;/p&gt;

&lt;p&gt;The only supported route is to wrap the whole application in an MSIX package for the sole purpose of satisfying this one API. That is a packaging model, a signing story, and an install channel adopted not because your app needs any of it, but because a single OCR call demands it. For a headless service that reads invoices on a schedule, repackaging as MSIX is a heavy architectural tax to pay for text recognition.&lt;/p&gt;

&lt;p&gt;The request to relax this restriction has been raised with Microsoft and remains open and unresolved. Our advice is to plan around it holding rather than betting a release date on it lifting. If MSIX is not already how you ship, this one dependency reshapes your deployment before you have read a single character.&lt;/p&gt;

&lt;h2&gt;
  
  
  The silent null on a missing language pack
&lt;/h2&gt;

&lt;p&gt;The second problem is quieter and, for that reason, more dangerous. When you ask for a language the engine cannot serve, &lt;code&gt;TryCreateFromLanguage&lt;/code&gt; returns null. No exception is thrown, no message is written, and no reason is given. You get a null reference and nothing to explain it.&lt;/p&gt;

&lt;p&gt;This matters because language packs are a separate per-device install, not part of the OCR component itself. On a developer workstation you may have added the pack by hand through Windows Settings months ago and forgotten it. On Windows Server the packs are absent by default and are pulled in with &lt;code&gt;DISM /Add-Capability&lt;/code&gt; for &lt;code&gt;Language.OCR&lt;/code&gt;, which often needs a Features on Demand source that a locked-down server does not have configured.&lt;/p&gt;

&lt;p&gt;The failure mode writes itself. The engine works perfectly in development on your hand-tuned machine, sails through every test, then throws a null reference on the very first real document in production because the server was never given the pack. The code that broke did nothing wrong. The environment lacked a dependency the API refuses to describe out loud. If you go this route, our &lt;a href="https://ironsoftware.com/csharp/ocr/blog/migration-guides/migrate-from-windows-media-ocr-to-ironocr/" rel="noopener noreferrer"&gt;migration notes on Windows.Media.Ocr&lt;/a&gt; walk through exactly where these environment gaps bite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The successor that only runs on an NPU
&lt;/h2&gt;

&lt;p&gt;Now the strategic risk, which outweighs both of the above for anyone planning past the current release. Microsoft's Windows AI documentation frames the newer Windows App SDK text recognition API as the forward path for OCR on the platform, and it states that the new API runs exclusively on devices with an NPU.&lt;/p&gt;

&lt;p&gt;Read that carefully. The replacement does not run on the large installed base of PCs that lack a neural processing unit. Copilot+ hardware is still a small slice of machines in the field, and the servers most of us deploy to have no NPU at all. So the modern successor cannot target the fleet you actually ship to, while the API you would adopt today is the one Microsoft is steering away from.&lt;/p&gt;

&lt;p&gt;That leaves Windows.Media.Ocr in a difficult spot. It is the current engine, it works, and it is also the engine with a documented replacement that most hardware cannot run. Building new architecture on it in 2026 means committing to something in maintenance mode whose only sanctioned future needs silicon your users mostly do not own. For a codebase meant to live five years, that direction is the risk worth pricing in, well ahead of the license cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Images and documents are yours to prepare
&lt;/h2&gt;

&lt;p&gt;Two more practical limits deserve a mention because they turn into code you have to write.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OcrEngine.MaxImageDimension&lt;/code&gt; caps how large an image the engine will accept, and there is no built-in resize or tiling. A high resolution scan above that ceiling has to be downscaled by your own code before the engine will look at it, and downscaling a dense document is exactly where recognition accuracy tends to slip.&lt;/p&gt;

&lt;p&gt;Input is &lt;code&gt;SoftwareBitmap&lt;/code&gt; only. The engine has no awareness of document formats, so a PDF page or a multi-page TIFF has to be decoded into that bitmap shape first, one frame at a time, before any text comes back. By contrast, &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-pdf-ocr/" rel="noopener noreferrer"&gt;IronOCR reads a scanned PDF directly&lt;/a&gt;, and its input handling and &lt;a href="https://ironsoftware.com/csharp/ocr/examples/ocr-image-filters-for-net-tesseract/" rel="noopener noreferrer"&gt;image filters for cleanup&lt;/a&gt; live inside the library rather than in glue code you maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fair word on security
&lt;/h2&gt;

&lt;p&gt;We want to be careful and honest here rather than score a point. A search for CVEs tied specifically to the Windows.Media.Ocr namespace comes back clean. That is worth stating, and it is equally worth stating why. Windows vulnerabilities are catalogued against Windows and its broad components, not against individual WinRT namespaces, so a quiet search result reflects how the database is organized as much as any actual track record. We are not implying hidden risk, and we are not claiming proven safety. The real exposure with this engine is not a bug waiting to surface. It is platform direction, which we covered above.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two code paths, side by side
&lt;/h2&gt;

&lt;p&gt;The runtime story shows up in the code. Windows.Media.Ocr is asynchronous throughout and expects you to arrive already holding a &lt;code&gt;SoftwareBitmap&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="c1"&gt;// Windows.Media.Ocr, async, and the input must already be a SoftwareBitmap&lt;/span&gt;
&lt;span class="n"&gt;OcrEngine&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OcrEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryCreateFromUserProfileLanguages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;StorageFile&lt;/span&gt; &lt;span class="n"&gt;file&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;StorageFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileFromPathAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\scans\invoice.jpg"&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;var&lt;/span&gt; &lt;span class="n"&gt;stream&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileAccessMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;BitmapDecoder&lt;/span&gt; &lt;span class="n"&gt;decoder&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;BitmapDecoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;SoftwareBitmap&lt;/span&gt; &lt;span class="n"&gt;bitmap&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;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSoftwareBitmapAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;OcrResult&lt;/span&gt; &lt;span class="n"&gt;result&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;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RecognizeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&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;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IronOCR path takes the file and returns the text, with no package identity and no bitmap decoding to arrange first.&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;// IronOCR, no MSIX, no SoftwareBitmap, runs unpackaged on every target&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronOcr&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;ocr&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;IronTesseract&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;var&lt;/span&gt; &lt;span class="n"&gt;input&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;OcrInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scanned-invoice.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;OcrResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference that matters is not line count. It is that the second snippet compiles and runs the same way inside a &lt;a href="https://ironsoftware.com/csharp/ocr/get-started/docker/" rel="noopener noreferrer"&gt;Docker container&lt;/a&gt; or on a &lt;a href="https://ironsoftware.com/csharp/ocr/get-started/linux/" rel="noopener noreferrer"&gt;Linux host&lt;/a&gt; as it does on a Windows desktop, with no packaging ceremony in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comparison at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Windows.Media.Ocr&lt;/th&gt;
&lt;th&gt;IronOCR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Free, no NuGet&lt;/td&gt;
&lt;td&gt;Commercial, tiered by deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package identity&lt;/td&gt;
&lt;td&gt;MSIX required for the API&lt;/td&gt;
&lt;td&gt;None required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Console app or Windows service&lt;/td&gt;
&lt;td&gt;Not supported unless repackaged&lt;/td&gt;
&lt;td&gt;Runs directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language packs&lt;/td&gt;
&lt;td&gt;Per-device OS install, null on miss&lt;/td&gt;
&lt;td&gt;NuGet package resolved at build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platforms&lt;/td&gt;
&lt;td&gt;Windows only&lt;/td&gt;
&lt;td&gt;Windows, Linux, macOS, Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input formats&lt;/td&gt;
&lt;td&gt;SoftwareBitmap only&lt;/td&gt;
&lt;td&gt;Image, scanned PDF, multi-page TIFF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large images&lt;/td&gt;
&lt;td&gt;Manual downscale under MaxImageDimension&lt;/td&gt;
&lt;td&gt;Handled inside the library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forward path&lt;/td&gt;
&lt;td&gt;Successor needs an NPU&lt;/td&gt;
&lt;td&gt;Cross-platform, actively shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When Windows.Media.Ocr is the right call
&lt;/h2&gt;

&lt;p&gt;We promised an honest trade-off, so here it is without hedging. If you are building an already-packaged, Windows-only WinUI or UWP app, and it reads languages that are already installed on the device, then Windows.Media.Ocr costs nothing, ships in the box, and is the correct choice. In that world the MSIX requirement is already met, the language pack is already present, and the single-OS limit is not a limit because you were never leaving Windows. Paying for a library there would buy you nothing.&lt;/p&gt;

&lt;p&gt;Everything IronOCR adds is aimed at the world outside that box. Each language becomes its own NuGet dependency such as &lt;code&gt;IronOcr.Languages.French&lt;/code&gt; or &lt;code&gt;IronOcr.Languages.Japanese&lt;/code&gt;, &lt;a href="https://ironsoftware.com/csharp/ocr/examples/intl-languages/" rel="noopener noreferrer"&gt;resolved at build time&lt;/a&gt; rather than provisioned per machine, so the server that runs your code is guaranteed to have what the code asked for. That is a commercial product with &lt;a href="https://ironsoftware.com/csharp/ocr/licensing/" rel="noopener noreferrer"&gt;tiered licensing&lt;/a&gt;, and it earns its cost precisely when your deployment is a service, a container, or anything other than a packaged Windows desktop app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we land
&lt;/h2&gt;

&lt;p&gt;The built-in engine is free, and for the packaged Windows-only app it is the right free. For a service, a container, or a cross-platform build, the price of the free engine is paid in MSIX packaging, per-device language provisioning, hand-written image preparation, and a bet on an API whose only documented successor needs an NPU most machines do not have. Those costs do not show on the license line, which is exactly why they are worth naming before you commit.&lt;/p&gt;

&lt;p&gt;So we will put the real architecture question to you. If your OCR code has to run tomorrow as a headless service or inside a Linux container, does a zero-dollar engine that forces MSIX and single-OS deployment still read as free, or has the platform commitment quietly become the most expensive part? We would genuinely like to hear how that math worked out in your own builds.&lt;/p&gt;

&lt;p&gt;If you are weighing this decision now, &lt;a href="https://ironsoftware.com/csharp/ocr/examples/csharp-pdf-ocr/" rel="noopener noreferrer"&gt;run a scanned PDF through IronOCR&lt;/a&gt;, try the same document on the built-in engine, and tell us in the comments where each one held up or fell over.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Windows, Windows.Media.Ocr, and the Windows App SDK are trademarks of Microsoft Corporation. We are not affiliated with Microsoft, and the details above draw on Microsoft's public API reference and documentation as they stood at the time of writing. If a detail has moved since, correct us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>ocr</category>
      <category>architecture</category>
    </item>
    <item>
      <title>List &amp; Label vs a Single .NET Library</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:53:05 +0000</pubDate>
      <link>https://dev.to/ironsoftware/list-label-vs-a-single-net-library-327d</link>
      <guid>https://dev.to/ironsoftware/list-label-vs-a-single-net-library-327d</guid>
      <description>&lt;p&gt;List &amp;amp; Label is not one product a team licenses. It is a Designer, an optional report server, a Web Report Designer, and a stack of connectors, priced per developer per year. A team that needs little more than data in and a PDF out ends up renting the whole ecosystem to use one corner of it.&lt;/p&gt;

&lt;p&gt;List &amp;amp; Label has been shipping since 1992, so plenty of the .NET teams running it today never actually chose it. They inherited it, along with a Designer surface nobody on the current team was hired to learn. The useful way to weigh it against a &lt;a href="https://ironpdf.com/tutorials/html-to-pdf/" rel="noopener noreferrer"&gt;code-first library like IronPDF&lt;/a&gt; is not feature by feature. It is by what each choice puts into the deployment, what it bills every year to keep, and what it costs to walk away from once the author has moved on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For transparency, our team at Iron Software develops IronPDF. We evaluate architecture and long-term costs, highlighting when List &amp;amp; Label might be the better option.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The smallest thing worth looking at first is how little it ships when IronPDF turns data into a document. There is no Designer to install and no project file to inherit, so a first report is one NuGet package and a handful of lines.&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;// dotnet add package 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="n"&gt;License&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LicenseKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR-LICENSE-KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a 30-day trial key works here&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="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;notice&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="s"&gt;"&amp;lt;h1&amp;gt;Renewal Notice&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;The layout lives in HTML the team already reads, not a designer file one person owns.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;notice&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;"renewal-notice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing there is a &lt;code&gt;.lst&lt;/code&gt; or &lt;code&gt;.lsr&lt;/code&gt; file a new hire has to open in a separate application. &lt;code&gt;ChromePdfRenderer&lt;/code&gt; hands the markup to an embedded Chromium build and returns a PDF, so the layout a team maintains as HTML is the layout that ships. Whether that reads as a lean deployment or a missing safety net depends on who authors the reports, which is what the rest of this turns on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ecosystem You Deploy, and the Package You Deploy
&lt;/h2&gt;

&lt;p&gt;The deployment surface is the part teams underestimate, because it stays invisible until an audit or a container rebuild forces someone to list it.&lt;/p&gt;

&lt;p&gt;A List &amp;amp; Label deployment carries more than a rendering call. It carries the report definitions, the &lt;code&gt;.lst&lt;/code&gt; and &lt;code&gt;.lsr&lt;/code&gt; and &lt;code&gt;.lld&lt;/code&gt; files that only combit tooling opens. It carries the Report Designer somewhere in the organisation, because a format nobody can edit is a liability rather than an asset. When reports show inside the app, it carries a viewer component across whichever targets a team supports, WinForms, WPF, or the browser-based Web Report Viewer. Each of those is a real capability, and each is one more thing to version, patch, and keep compatible across .NET upgrades for years.&lt;/p&gt;

&lt;p&gt;An IronPDF deployment carries the library and a Chromium engine it manages for the team. The report definitions are HTML and CSS files that live in the same repository as the rest of the code, readable in any pull request and blamed in git like anything else. Turning an &lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;HTML file into a PDF&lt;/a&gt; on the server is the same call whether the markup is a string or a file on disk. The honest trade is that Chromium is memory-hungry by design, so the real capacity question is not whether a page renders correctly but how many concurrent renders a container takes before a team pays for a bigger box. List &amp;amp; Label's report engine is lighter per render. Neither answer is free, and the right one depends on throughput rather than a benchmark headline.&lt;/p&gt;

&lt;h2&gt;
  
  
  List &amp;amp; Label and IronPDF, Feature by Feature
&lt;/h2&gt;

&lt;p&gt;Here is the head-to-head, kept accurate rather than flattering, with the rows that commit a team the longest sitting near the bottom. The row count leans toward List &amp;amp; Label on purpose, because a fair comparison names what the incumbent does that a library does not.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;List &amp;amp; Label (combit)&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;Authoring model&lt;/td&gt;
&lt;td&gt;WYSIWYG Report Designer plus Web Report Designer&lt;/td&gt;
&lt;td&gt;HTML, CSS, JavaScript, plus Razor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Report definition&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.lst&lt;/code&gt; and &lt;code&gt;.lsr&lt;/code&gt; binaries, combit tooling only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.html&lt;/code&gt; or &lt;code&gt;.cshtml&lt;/code&gt;, plain text and diffable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rendering engine&lt;/td&gt;
&lt;td&gt;Proprietary report-band engine&lt;/td&gt;
&lt;td&gt;Chromium, the engine behind Chrome&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End-user self-service authoring&lt;/td&gt;
&lt;td&gt;Yes, desktop and browser designers&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data-provider breadth&lt;/td&gt;
&lt;td&gt;40 plus built-in providers, plus a custom interface&lt;/td&gt;
&lt;td&gt;You bind data in your own code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive viewer&lt;/td&gt;
&lt;td&gt;Yes, Web Report Viewer and Ad-hoc Designer&lt;/td&gt;
&lt;td&gt;No, generation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling and distribution&lt;/td&gt;
&lt;td&gt;Yes, combit Report Server&lt;/td&gt;
&lt;td&gt;Your own job runner calls the renderer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF depth (merge, sign, PDF/A, PDF/UA)&lt;/td&gt;
&lt;td&gt;Export-focused&lt;/td&gt;
&lt;td&gt;Extensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment surface&lt;/td&gt;
&lt;td&gt;Designer, optional Report Server, viewer components&lt;/td&gt;
&lt;td&gt;Library plus a managed Chromium engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-platform (Linux, Docker, macOS)&lt;/td&gt;
&lt;td&gt;Cross-platform edition, Enterprise only&lt;/td&gt;
&lt;td&gt;Full, included in every tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Licensing model&lt;/td&gt;
&lt;td&gt;Per developer, per year (EUR)&lt;/td&gt;
&lt;td&gt;Perpetual, per developer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 1. A capability comparison rather than a scoreboard, where List &amp;amp; Label leads on authoring, data breadth, and distribution while IronPDF leads on rendering fidelity, manipulation depth, and deployment simplicity.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-Developer, Per-Year, and the Three-Year Math
&lt;/h2&gt;

&lt;p&gt;Cost is where the two models diverge most, and the shape of the bill matters more than any current figure, because figures move and the shape is what a team lives inside.&lt;/p&gt;

&lt;p&gt;List &amp;amp; Label is sold per developer, per year, across three editions, priced in euros on the &lt;a href="https://www.combit.com/" rel="noopener noreferrer"&gt;combit pricing pages&lt;/a&gt;. Standard lands around EUR 740 per developer for the first year and roughly half that on renewal, Professional near EUR 1,390 first year adds the bilingual designer and 2D barcodes, and Enterprise near EUR 1,990 first year adds cross-platform support, the Web Report Designer, and the Report Server. Every developer who touches the codebase needs a seat, so the tool bills for people who will never open the Designer on purpose, and volume discounts start at three or more developers.&lt;/p&gt;

&lt;p&gt;IronPDF uses perpetual per-developer licensing instead, where a version is bought once and owned, with a full-function trial to evaluate first and no renewal required to keep the version bought. Because exact numbers age, the live tiers sit on the &lt;a href="https://ironsoftware.com/csharp/pdf/licensing/" rel="noopener noreferrer"&gt;IronPDF licensing page&lt;/a&gt; rather than in a snapshot here. The trade cuts both ways. An annual subscription keeps a team on combit's newest Designer release automatically and never strands anyone on an unsupported version, and it also never stops charging, so a five-person team pays five seats every year whether or not the reporting changed. A perpetual license is a larger one-time commitment that then belongs to the team, and a version that ages unless a later purchase refreshes it. Where the crossover lands is a function of team stability, and a small, steady team that stopped changing its reports tends to come out ahead on perpetual across a three-to-five-year horizon.&lt;/p&gt;

&lt;h2&gt;
  
  
  When List &amp;amp; Label Is the Right Call
&lt;/h2&gt;

&lt;p&gt;IronPDF does not win every scenario, and stating the cases where List &amp;amp; Label is the better buy matters more than winning the comparison, because a migration that fights the actual requirement costs more than the license it was meant to save.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Non-developers design the reports.&lt;/strong&gt; The desktop WYSIWYG Designer, shipping since the 1990s, hands layout to business analysts and customers through a code-free workflow, and a developer-written HTML template does not give that authorship back.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;A scheduled distribution server is a hard requirement.&lt;/strong&gt; The combit Report Server schedules jobs and delivers output over email, FTP or SFTP, Amazon S3, OneDrive, Google Drive, Dropbox, or webhooks from a browser console, which a rendering library does not pretend to offer.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;You want a modern in-browser design surface.&lt;/strong&gt; The Web Report Designer is a web-component designer that drops into React, Angular, or Vue front ends and redistributes to end users royalty-free.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Deep, pre-built connectors save real work.&lt;/strong&gt; More than forty providers cover SQL Server, PostgreSQL, MySQL, Oracle, DB2, and MongoDB, plus a custom &lt;code&gt;IDataProvider&lt;/code&gt; for anything left over, and recent versions add an &lt;code&gt;AI$()&lt;/code&gt; formula built on Microsoft's &lt;a href="https://learn.microsoft.com/en-us/semantic-kernel/" rel="noopener noreferrer"&gt;Semantic Kernel&lt;/a&gt; for translation or summarization inside the layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When two or more of those describe the project, staying on List &amp;amp; Label is the cheaper decision over any horizon, and the recurring subscription is buying something the team genuinely uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porting a .lst Report to HTML
&lt;/h2&gt;

&lt;p&gt;A large share of production reporting is not interactive at all. It is a scheduled job or an API endpoint turning data into a PDF nobody opens in a designer. When that is the real workload, the concept mapping off the proprietary format is small, and the mapping doubles as the exit cost of leaving it behind.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What leaves the deployment&lt;/th&gt;
&lt;th&gt;List &amp;amp; Label&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;Report definition&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.lst&lt;/code&gt; or &lt;code&gt;.lsr&lt;/code&gt; binary&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.html&lt;/code&gt; or &lt;code&gt;.cshtml&lt;/code&gt; in source control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load and render&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ll.Design.LoadFromFile(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build the HTML string or file directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export to PDF&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ll.Export(ExportTarget.Pdf, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renderer.RenderHtmlAsPdf(html).SaveAs(path)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export to bytes&lt;/td&gt;
&lt;td&gt;Export to a stream target&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderHtmlAsPdf(html).BinaryData&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async rendering&lt;/td&gt;
&lt;td&gt;Not the primary workflow&lt;/td&gt;
&lt;td&gt;&lt;code&gt;await renderer.RenderHtmlAsPdfAsync(html)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page header and footer&lt;/td&gt;
&lt;td&gt;Report bands&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RenderingOptions.TextHeader&lt;/code&gt; or &lt;code&gt;HtmlHeader&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page numbers&lt;/td&gt;
&lt;td&gt;Field bound to page info&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{page}&lt;/code&gt; and &lt;code&gt;{total-pages}&lt;/code&gt; placeholders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data binding&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;IDataProvider&lt;/code&gt; in the Designer&lt;/td&gt;
&lt;td&gt;Your own template, Razor or loops&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table 2. What actually leaves the deployment on the way out of the proprietary format, direct where the models line up and candid where re-authoring is the real cost.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first question a migrating team asks is whether footers and page numbers still work, so the sample below wires both in before anything else. The rows would come from EF Core, Dapper, or any data layer already in the app.&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;// CustomerStatement.cs (.NET 10)&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&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;lineItems&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="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;"Consulting, October"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;Hours&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;185.00m&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="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Consulting, November"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hours&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;185.00m&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="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"On-site workshop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="n"&gt;Hours&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;Rate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;225.00m&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Build the report body as ordinary HTML and reuse existing site CSS if you like.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&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;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Customer Statement&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;table&amp;gt;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Hours&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Rate&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Total&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&amp;lt;tbody&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&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;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lineItems&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;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&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;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hours&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hours&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;"&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;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;/tbody&amp;gt;&amp;lt;/table&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;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="c1"&gt;// A running footer with real page numbers, the report-band equivalent.&lt;/span&gt;
&lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextFooter&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;TextHeaderFooter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LeftText&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Confidential, Contoso Consulting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RightText&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Page {page} of {total-pages}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DrawDividerLine&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingOptions&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingOptions&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;IronPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rendering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPaperSize&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;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;statement&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;statement&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;"customer-statement.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;code&gt;TextHeaderFooter&lt;/code&gt; supplies the running band, and the &lt;code&gt;{page}&lt;/code&gt; and &lt;code&gt;{total-pages}&lt;/code&gt; placeholders resolve per page at render time, so pagination is handled without extra work. Richer layouts swap &lt;code&gt;TextFooter&lt;/code&gt; for an &lt;code&gt;HtmlHeaderFooter&lt;/code&gt; and use full markup, and the &lt;a href="https://ironpdf.com/how-to/headers-and-footers/" rel="noopener noreferrer"&gt;headers and footers how-to&lt;/a&gt; goes deeper on both. When the original reports were already close to HTML in spirit, &lt;a href="https://ironpdf.com/how-to/cshtml-to-pdf-razor" rel="noopener noreferrer"&gt;Razor views render straight to PDF&lt;/a&gt; with the same shape.&lt;/p&gt;

&lt;p&gt;For a batch job, the rendering is &lt;a href="https://ironpdf.com/how-to/async/" rel="noopener noreferrer"&gt;async&lt;/a&gt; so a worker can stream a run of statements without blocking, and the loop owns nothing but code the team can read.&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;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="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RenderingOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextFooter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RightText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Page {page} of {total-pages}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;foreach&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;account&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetActiveAccountsAsync&lt;/span&gt;&lt;span class="p"&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="n"&gt;StatementTemplate&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="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Razor or plain string composition&lt;/span&gt;
    &lt;span class="n"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;pdf&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdfAsync&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;$"statement-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For most tabular output, invoices, statements, audit summaries, translating a layout into HTML is a small task once the data is reachable in code. Reports with heavy grouping or Designer-built subreports carry more of that one-time cost, because those concepts have no one-line HTML equivalent and have to be rebuilt rather than mapped. Two pieces have no automatic equivalent either, end-user self-service authoring and scheduled distribution, so the plan is either a lightweight admin screen for report tweaks or a scheduler the team already runs. IronPDF is also a deeper PDF toolkit past rendering, with &lt;a href="https://ironpdf.com/how-to/pdfua/" rel="noopener noreferrer"&gt;PDF/UA accessibility&lt;/a&gt; and &lt;a href="https://ironpdf.com/how-to/pdfa/" rel="noopener noreferrer"&gt;PDF/A archival&lt;/a&gt; compliance for long-term storage. Recent releases also added signing over a hardware token and fine-grained compression, the kind of manipulation a reporting SDK's export pipeline rarely reaches for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform or Package, and When Each Wins
&lt;/h2&gt;

&lt;p&gt;After the deployment, the renewal, and the exit are all on the table, the decision usually resolves into one of three paths.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Stay with List &amp;amp; Label&lt;/strong&gt; when non-developers design the reports, when a browser-based interactive viewer with an ad-hoc designer is part of the spec, when a scheduled Report Server delivering to email, FTP, or S3 is a hard requirement, or when one engine already spans Delphi, Java, and .NET. The subscription is buying something used.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Move to IronPDF&lt;/strong&gt; when reports are code-driven and HTML-based, when existing web templates can be reused, when the deployment targets Linux or containers, or when a perpetual license beats renewing seats that keep charging after the reports stopped changing.&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Run both&lt;/strong&gt;, which is more common than teams expect. Some keep List &amp;amp; Label for interactive, business-authored reports and add IronPDF for high-volume, server-side jobs that never touch a designer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thread through all three is that the word reporting hides two different commitments, one where a person owns the layout in a design surface and one where code owns it as text, and the commitment a team actually has decides which one is cheaper to carry for years.&lt;/p&gt;

&lt;p&gt;What does your production reporting look like once you strip away the demo, are the &lt;code&gt;.lst&lt;/code&gt; files still opened and edited by a person each quarter, or have they quietly become a scheduled job writing PDFs that nobody views in a designer? Tell us in the comments, because that answer decides the whole thing.&lt;/p&gt;

&lt;p&gt;If it is the second, we think a short prototype is worth running before the next renewal decides for you. Start a &lt;a href="https://ironsoftware.com/csharp/pdf/licensing/" rel="noopener noreferrer"&gt;trial&lt;/a&gt;, point &lt;code&gt;ChromePdfRenderer&lt;/code&gt; at one existing report template on current &lt;a href="https://dotnet.microsoft.com/platform/support/policy/dotnet-core" rel="noopener noreferrer"&gt;.NET 10&lt;/a&gt;, and see how close the first render lands, because that one experiment answers the ownership question better than any table here, including ours.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;List &amp;amp; Label and combit are trademarks of combit GmbH. We have no affiliation with combit, and the details above rest on their public documentation. If we have a detail wrong, tell us in the comments and we will correct it.&lt;/em&gt;&lt;/p&gt;

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