<?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: W Wolt</title>
    <description>The latest articles on DEV Community by W Wolt (@woltw_dev).</description>
    <link>https://dev.to/woltw_dev</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3869069%2Fff4f481d-5fcc-4c94-9ebb-9bc4820775de.png</url>
      <title>DEV Community: W Wolt</title>
      <link>https://dev.to/woltw_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/woltw_dev"/>
    <language>en</language>
    <item>
      <title>The State of OCR in .NET (2026): From Text Extraction to Real Pipelines</title>
      <dc:creator>W Wolt</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:57:39 +0000</pubDate>
      <link>https://dev.to/woltw_dev/the-state-of-ocr-in-net-2026-from-text-extraction-to-real-pipelines-25cb</link>
      <guid>https://dev.to/woltw_dev/the-state-of-ocr-in-net-2026-from-text-extraction-to-real-pipelines-25cb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I’ve integrated OCR into enough systems to know where it actually breaks.&lt;/p&gt;

&lt;p&gt;Not in the demo.&lt;br&gt;
Not in the first API call.&lt;/p&gt;

&lt;p&gt;It breaks when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;documents are inconsistent&lt;/li&gt;
&lt;li&gt;traffic increases&lt;/li&gt;
&lt;li&gt;edge cases pile up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re building anything in fintech, operations, or compliance-heavy workflows, OCR stops being a feature very quickly. It becomes part of your backend pipeline.&lt;/p&gt;

&lt;p&gt;In 2026, the question is not how to extract text in C#. The question is whether your OCR setup can survive real input, real scale, and real business logic.&lt;/p&gt;

&lt;p&gt;This article is based on that reality.&lt;/p&gt;
&lt;h2&gt;
  
  
  What OCR Looks Like in a Real System
&lt;/h2&gt;

&lt;p&gt;In isolation, OCR looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;text&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="s"&gt;"document.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, it looks more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;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;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileId&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;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Preprocess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&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;rawText&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;image&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;structured&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawText&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;validated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structured&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;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OCR is one step in a chain. If you treat it as a standalone feature, you will end up rewriting everything around it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Things Actually Break
&lt;/h2&gt;

&lt;p&gt;After working with document pipelines in .NET services, the same problems show up every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy is tied to input quality, not the engine
&lt;/h3&gt;

&lt;p&gt;Developers often compare OCR engines like they are interchangeable.&lt;/p&gt;

&lt;p&gt;They are not.&lt;/p&gt;

&lt;p&gt;Take this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;text&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="s"&gt;"invoice.jpg"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that image is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slightly rotated&lt;/li&gt;
&lt;li&gt;low contrast&lt;/li&gt;
&lt;li&gt;compressed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;your results degrade fast.&lt;/p&gt;

&lt;p&gt;You don’t fix this by switching libraries. You fix it with preprocessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raw text is rarely useful
&lt;/h3&gt;

&lt;p&gt;OCR gives you this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice Number: INV-2026-001
Total Amount: $1,245.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your system needs this:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```json id="kk4k3c"&lt;br&gt;
{&lt;br&gt;
  "invoice_number": "INV-2026-001",&lt;br&gt;
  "total": 1245.00&lt;br&gt;
}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


That gap is where most of the engineering effort goes.

Parsing, validation, error handling. OCR is just the input layer.


### Throughput becomes a problem faster than expected

In a microservice setup, you might start with something like:



```csharp
foreach (var file in batch)
{
    var text = ocr.Read(file);
    Process(text);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now scale that across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple pods&lt;/li&gt;
&lt;li&gt;message queues&lt;/li&gt;
&lt;li&gt;concurrent requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU saturation&lt;/li&gt;
&lt;li&gt;memory pressure&lt;/li&gt;
&lt;li&gt;queue delays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OCR is expensive. Treat it like a heavy compute workload, not a simple utility.&lt;/p&gt;
&lt;h3&gt;
  
  
  Document variability kills assumptions
&lt;/h3&gt;

&lt;p&gt;Even within the same domain, documents are inconsistent.&lt;/p&gt;

&lt;p&gt;Two invoices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different layouts&lt;/li&gt;
&lt;li&gt;different labels&lt;/li&gt;
&lt;li&gt;different formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your OCR pipeline must handle variation, not just extraction.&lt;/p&gt;

&lt;p&gt;Hardcoding rules will work for a week. Then it breaks.&lt;/p&gt;
&lt;h2&gt;
  
  
  The OCR Options Most .NET Developers Use
&lt;/h2&gt;

&lt;p&gt;If you’ve been around .NET long enough, these are the usual paths.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tesseract OCR
&lt;/h3&gt;

&lt;p&gt;Still the default open source choice.&lt;/p&gt;

&lt;p&gt;I’ve used it in multiple systems where cost and control mattered.&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;Tesseract&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;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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&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;"document.png"&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;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="kt"&gt;var&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;full control&lt;/li&gt;
&lt;li&gt;no API dependency&lt;/li&gt;
&lt;li&gt;predictable cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tuning&lt;/li&gt;
&lt;li&gt;preprocessing&lt;/li&gt;
&lt;li&gt;inconsistent accuracy out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works, but you need to put effort into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure AI Vision OCR
&lt;/h3&gt;

&lt;p&gt;If you want something that works fast with minimal setup, this is usually where teams go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsync&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="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;line&lt;/span&gt; &lt;span class="k"&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;Lines&lt;/span&gt;&lt;span class="p"&gt;)&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;line&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strong accuracy&lt;/li&gt;
&lt;li&gt;layout awareness&lt;/li&gt;
&lt;li&gt;less setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you accept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API latency&lt;/li&gt;
&lt;li&gt;ongoing cost&lt;/li&gt;
&lt;li&gt;data leaving your system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is often the fastest way to production, but not always the best long-term fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hybrid approach
&lt;/h3&gt;

&lt;p&gt;This is what I see more teams doing now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;localOcr&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;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;IsLowConfidence&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="n"&gt;text&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;cloudOcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&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;You keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cost under control&lt;/li&gt;
&lt;li&gt;latency manageable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And still handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;edge cases with higher accuracy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern scales better in real systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Matters When Choosing OCR
&lt;/h2&gt;

&lt;p&gt;Forget feature lists. These are the decisions that matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy in your specific context
&lt;/h3&gt;

&lt;p&gt;OCR accuracy is not universal.&lt;/p&gt;

&lt;p&gt;Test with your documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scanned PDFs&lt;/li&gt;
&lt;li&gt;mobile photos&lt;/li&gt;
&lt;li&gt;compressed files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What works in a demo may fail in your pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration into your architecture
&lt;/h3&gt;

&lt;p&gt;If you are running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET APIs&lt;/li&gt;
&lt;li&gt;background workers&lt;/li&gt;
&lt;li&gt;message queues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then your OCR needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handle concurrency&lt;/li&gt;
&lt;li&gt;avoid blocking threads&lt;/li&gt;
&lt;li&gt;fit into async workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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;await&lt;/span&gt; &lt;span class="n"&gt;Task&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="p"&gt;=&amp;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;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even this can become a bottleneck if not managed properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment constraints
&lt;/h3&gt;

&lt;p&gt;In containerized environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:8.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU limits&lt;/li&gt;
&lt;li&gt;memory limits&lt;/li&gt;
&lt;li&gt;scaling behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some OCR engines are not friendly in containers without tuning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data privacy requirements
&lt;/h3&gt;

&lt;p&gt;If you are dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal identity documents&lt;/li&gt;
&lt;li&gt;financial records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sending data to external APIs may not be acceptable.&lt;/p&gt;

&lt;p&gt;This alone can eliminate certain options.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Has Changed in 2026
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OCR is now part of a broader document pipeline
&lt;/h3&gt;

&lt;p&gt;The flow is no longer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;text&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;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Preprocess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&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;raw&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;image&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;structured&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&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;enriched&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;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structured&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enriched&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OCR feeds into systems. It is not the end result.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI is handling what used to be manual parsing
&lt;/h3&gt;

&lt;p&gt;Instead of writing complex rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Match&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="s"&gt;@"Total:\s+\$(\d+)"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Groups&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;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;structured&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;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Extract&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;This reduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;brittle parsing logic&lt;/li&gt;
&lt;li&gt;maintenance overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependency on model behavior&lt;/li&gt;
&lt;li&gt;need for validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Preprocessing is no longer optional
&lt;/h3&gt;

&lt;p&gt;You will get better results doing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processed&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="nf"&gt;ToGrayscale&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IncreaseContrast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Deskew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Than switching OCR engines.&lt;/p&gt;

&lt;p&gt;This is one of the most overlooked parts of OCR pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling OCR is now an architecture problem
&lt;/h3&gt;

&lt;p&gt;You do not scale OCR by writing better code.&lt;/p&gt;

&lt;p&gt;You scale it by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;queueing workloads&lt;/li&gt;
&lt;li&gt;distributing processing&lt;/li&gt;
&lt;li&gt;controlling concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typical pattern:&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;await&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;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;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Consume&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;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;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where microservices and background processing matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Approach OCR in .NET Projects
&lt;/h2&gt;

&lt;p&gt;After enough iterations, this is the approach that holds up.&lt;/p&gt;

&lt;p&gt;Start with real documents, not samples.&lt;br&gt;
Build preprocessing early.&lt;br&gt;
Treat OCR as a compute-heavy service.&lt;br&gt;
Separate extraction from interpretation.&lt;br&gt;
Add validation layers.&lt;/p&gt;

&lt;p&gt;And most importantly, expect edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Fits in the Bigger Picture
&lt;/h2&gt;

&lt;p&gt;OCR is not the end of the pipeline.&lt;/p&gt;

&lt;p&gt;It sits at the start.&lt;/p&gt;

&lt;p&gt;Typical flow in modern systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCR extracts data from documents&lt;/li&gt;
&lt;li&gt;services process and validate it&lt;/li&gt;
&lt;li&gt;PDFs present final outputs&lt;/li&gt;
&lt;li&gt;Excel and Word handle structured workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you get OCR wrong, everything downstream becomes harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;OCR in .NET has matured, but the challenges have not disappeared.&lt;/p&gt;

&lt;p&gt;You can extract text in minutes.&lt;br&gt;
You will spend weeks making it reliable.&lt;/p&gt;

&lt;p&gt;If you are choosing a .NET OCR approach in 2026, optimize for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how it behaves with your real data&lt;/li&gt;
&lt;li&gt;how it scales in your architecture&lt;/li&gt;
&lt;li&gt;how it integrates with the rest of your pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else is secondary.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>ocr</category>
      <category>dotnet</category>
      <category>backend</category>
    </item>
    <item>
      <title>Best C# PDF Library in 2026? A Real-World .NET Comparison</title>
      <dc:creator>W Wolt</dc:creator>
      <pubDate>Thu, 09 Apr 2026 06:28:48 +0000</pubDate>
      <link>https://dev.to/woltw_dev/best-c-pdf-library-in-2026-a-real-world-net-comparison-3agi</link>
      <guid>https://dev.to/woltw_dev/best-c-pdf-library-in-2026-a-real-world-net-comparison-3agi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you’ve been building in .NET long enough, you already know this pattern.&lt;/p&gt;

&lt;p&gt;At some point, every system needs to generate a PDF.&lt;/p&gt;

&lt;p&gt;Invoices. Reports. Contracts. Audit trails. Export features that started as “nice to have” become critical workflows.&lt;/p&gt;

&lt;p&gt;And then someone searches “C# PDF library” or “.NET PDF library” and assumes the problem is solved.&lt;/p&gt;

&lt;p&gt;It isn’t.&lt;/p&gt;

&lt;p&gt;Generating a PDF in C# is easy. Getting that PDF to render correctly across environments, scale under load, and remain consistent over time is where things start to break.&lt;/p&gt;

&lt;p&gt;After shipping multiple systems across ASP.NET APIs, containerized workloads, and document-heavy pipelines, the decision in 2026 is no longer about features. It’s about behavior in production.&lt;/p&gt;

&lt;p&gt;This piece breaks down how PDF libraries actually perform in real systems, what trade-offs matter, and how to choose something that won’t slow you down later.&lt;/p&gt;

&lt;p&gt;This also sets up the broader picture. PDF is just one part of the document stack. OCR, Excel, and Word pipelines sit right next to it, and they all connect.&lt;/p&gt;

&lt;p&gt;We’ll get there in the next articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problems You Only See in Production
&lt;/h2&gt;

&lt;p&gt;Most blog posts stop at “create PDF from HTML” examples.&lt;/p&gt;

&lt;p&gt;That’s not where systems fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout breaks across environments
&lt;/h3&gt;

&lt;p&gt;You render a PDF locally. Looks perfect.&lt;/p&gt;

&lt;p&gt;Deploy to Linux containers. Suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fonts are missing&lt;/li&gt;
&lt;li&gt;spacing shifts&lt;/li&gt;
&lt;li&gt;page breaks cut content in half&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example scenario. You reuse a Razor view and convert it to PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_viewRenderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderToStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"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;Looks clean. Works locally.&lt;/p&gt;

&lt;p&gt;Then production hits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different font fallback&lt;/li&gt;
&lt;li&gt;different DPI handling&lt;/li&gt;
&lt;li&gt;subtle CSS differences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your invoice layout is off by just enough to cause support tickets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance collapses under load
&lt;/h3&gt;

&lt;p&gt;Rendering one PDF is trivial.&lt;/p&gt;

&lt;p&gt;Rendering thousands per hour in a microservice is not.&lt;/p&gt;

&lt;p&gt;Typical pattern in a Web API:&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"generate-report"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GenerateReport&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromBody&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;ReportRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_reportService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pdfRenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BinaryData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"report.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;This works fine until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concurrent requests increase&lt;/li&gt;
&lt;li&gt;memory spikes&lt;/li&gt;
&lt;li&gt;CPU saturates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your PDF service becomes the bottleneck in your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Output inconsistency becomes a real issue
&lt;/h3&gt;

&lt;p&gt;You deploy a new version of your service.&lt;/p&gt;

&lt;p&gt;Same input. Different output.&lt;/p&gt;

&lt;p&gt;Even small changes matter in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;financial reports&lt;/li&gt;
&lt;li&gt;legal documents&lt;/li&gt;
&lt;li&gt;audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your PDFs are not reproducible, you lose trust in your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Libraries Everyone Evaluates
&lt;/h2&gt;

&lt;p&gt;If you search “best C# PDF library”, you’ll see the same set of tools.&lt;/p&gt;

&lt;p&gt;Each one solves a different version of the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  iText 7
&lt;/h3&gt;

&lt;p&gt;This is the enterprise baseline.&lt;/p&gt;

&lt;p&gt;It gives you deep control over PDF structure. You can manipulate content at a very low level.&lt;/p&gt;

&lt;p&gt;Example:&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="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;writer&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;"output.pdf"&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;pdf&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;writer&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;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="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;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;"Hello 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;Where it fits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;complex PDF manipulation&lt;/li&gt;
&lt;li&gt;stamping, merging, signing workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;licensing is not trivial&lt;/li&gt;
&lt;li&gt;more verbose API&lt;/li&gt;
&lt;li&gt;steeper learning curve&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PDFsharp
&lt;/h3&gt;

&lt;p&gt;One of the most common open source options.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;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="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;"Hello PDF"&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;"Arial"&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;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;XPoint&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="m"&gt;50&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;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it fits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple document generation&lt;/li&gt;
&lt;li&gt;low complexity use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited modern capabilities&lt;/li&gt;
&lt;li&gt;not suitable for HTML-to-PDF&lt;/li&gt;
&lt;li&gt;struggles with scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  QuestPDF
&lt;/h3&gt;

&lt;p&gt;This is a different approach. You define layout in code.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;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;Content&lt;/span&gt;&lt;span class="p"&gt;()&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;"Hello PDF"&lt;/span&gt;&lt;span class="p"&gt;)&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="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;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it fits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured documents&lt;/li&gt;
&lt;li&gt;predictable layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not HTML-based&lt;/li&gt;
&lt;li&gt;requires writing layout logic in code&lt;/li&gt;
&lt;li&gt;less flexible for dynamic content reuse&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  wkhtmltopdf (via wrappers)
&lt;/h3&gt;

&lt;p&gt;Still widely used for HTML-to-PDF.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BasicConverter&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;PdfTools&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlToPdfDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Objects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ObjectSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it fits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quick HTML-to-PDF setups&lt;/li&gt;
&lt;li&gt;legacy systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;outdated rendering engine&lt;/li&gt;
&lt;li&gt;limited CSS support&lt;/li&gt;
&lt;li&gt;inconsistent behavior in modern environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Trade-Offs That Actually Matter
&lt;/h2&gt;

&lt;p&gt;After working across multiple systems, the decision usually comes down to these.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML vs programmatic layout
&lt;/h3&gt;

&lt;p&gt;If your content already exists as HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reports from Razor&lt;/li&gt;
&lt;li&gt;UI templates&lt;/li&gt;
&lt;li&gt;email templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then HTML-to-PDF makes sense.&lt;/p&gt;

&lt;p&gt;If your content is structured data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tables&lt;/li&gt;
&lt;li&gt;calculated fields&lt;/li&gt;
&lt;li&gt;fixed layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then programmatic libraries are more predictable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Engineering time vs licensing
&lt;/h3&gt;

&lt;p&gt;Open source looks cheaper.&lt;/p&gt;

&lt;p&gt;Until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you spend time fixing edge cases&lt;/li&gt;
&lt;li&gt;debugging rendering issues&lt;/li&gt;
&lt;li&gt;building workarounds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paid libraries shift that cost into licensing.&lt;/p&gt;

&lt;p&gt;In most real systems, engineering time is the bigger cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput vs rendering accuracy
&lt;/h3&gt;

&lt;p&gt;High fidelity rendering engines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consume more memory&lt;/li&gt;
&lt;li&gt;take longer to process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faster engines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simplify layout&lt;/li&gt;
&lt;li&gt;sacrifice CSS support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You need to align this with your workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed Around 2026
&lt;/h2&gt;

&lt;p&gt;This is where older advice starts to break.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything runs in containers now
&lt;/h3&gt;

&lt;p&gt;Most .NET apps are deployed via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your PDF library requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual OS setup&lt;/li&gt;
&lt;li&gt;system-level dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;it introduces friction immediately.&lt;/p&gt;

&lt;p&gt;You want something that behaves the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;locally&lt;/li&gt;
&lt;li&gt;in staging&lt;/li&gt;
&lt;li&gt;in production&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HTML reuse is the default
&lt;/h3&gt;

&lt;p&gt;Teams are not building document layouts twice.&lt;/p&gt;

&lt;p&gt;They reuse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Razor views&lt;/li&gt;
&lt;li&gt;frontend templates&lt;/li&gt;
&lt;li&gt;shared design systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&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;_razorRenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReportTemplate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pdfService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces duplication but increases pressure on the rendering engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI is increasing document volume
&lt;/h3&gt;

&lt;p&gt;LLMs are generating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;summaries&lt;/li&gt;
&lt;li&gt;reports&lt;/li&gt;
&lt;li&gt;structured outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PDF becomes the final delivery format.&lt;/p&gt;

&lt;p&gt;This increases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;volume of generation&lt;/li&gt;
&lt;li&gt;importance of consistency&lt;/li&gt;
&lt;li&gt;need for automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Evaluate a PDF Library Now
&lt;/h2&gt;

&lt;p&gt;After enough iterations, the evaluation becomes straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Can it survive my deployment model?
&lt;/h3&gt;

&lt;p&gt;Test in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux containers&lt;/li&gt;
&lt;li&gt;production-like environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If it behaves differently, it’s a red flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Does it handle concurrency?
&lt;/h3&gt;

&lt;p&gt;Simulate load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;For&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;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GenerateHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;CPU spikes&lt;/li&gt;
&lt;li&gt;failures under pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Is the output consistent?
&lt;/h3&gt;

&lt;p&gt;Generate the same document:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;across environments&lt;/li&gt;
&lt;li&gt;across deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare results.&lt;/p&gt;

&lt;p&gt;If they differ, it will become a problem later.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. How much workaround code do I need?
&lt;/h3&gt;

&lt;p&gt;The more patches you write:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the harder it becomes to maintain&lt;/li&gt;
&lt;li&gt;the more fragile your system gets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where This Is Going
&lt;/h2&gt;

&lt;p&gt;PDF is no longer a utility feature.&lt;/p&gt;

&lt;p&gt;It sits in the middle of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user-facing workflows&lt;/li&gt;
&lt;li&gt;compliance requirements&lt;/li&gt;
&lt;li&gt;backend automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it doesn’t exist alone.&lt;/p&gt;

&lt;p&gt;In most real systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCR feeds data into PDFs&lt;/li&gt;
&lt;li&gt;Excel exports complement reports&lt;/li&gt;
&lt;li&gt;Word documents handle editable workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is becoming a document pipeline, not isolated features.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We’ll Cover Next
&lt;/h2&gt;

&lt;p&gt;This article focused on PDF generation.&lt;/p&gt;

&lt;p&gt;Next in the series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCR in .NET and how extraction pipelines actually work&lt;/li&gt;
&lt;li&gt;Excel libraries and large dataset handling&lt;/li&gt;
&lt;li&gt;Word generation and template-driven workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These pieces connect more than most teams expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;After 15 years working with C#, APIs, and distributed systems, this is the pattern I keep seeing.&lt;/p&gt;

&lt;p&gt;The problem is never generating the document.&lt;/p&gt;

&lt;p&gt;The problem is everything around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consistency&lt;/li&gt;
&lt;li&gt;scale&lt;/li&gt;
&lt;li&gt;reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re choosing a C# PDF library in 2026, optimize for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;predictable behavior in production&lt;/li&gt;
&lt;li&gt;performance under load&lt;/li&gt;
&lt;li&gt;minimal operational friction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because once this sits in your pipeline, replacing it later is not trivial.&lt;/p&gt;

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