<?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: dotnet</title>
    <description>The latest articles tagged 'dotnet' on DEV Community.</description>
    <link>https://dev.to/t/dotnet</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/dotnet"/>
    <language>en</language>
    <item>
      <title>Global Exception Handling in ASP.NET Core — The Complete Guide</title>
      <dc:creator>Libin Tom Baby</dc:creator>
      <pubDate>Sat, 25 Apr 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/libintombaby/global-exception-handling-in-aspnet-core-the-complete-guide-3cpc</link>
      <guid>https://dev.to/libintombaby/global-exception-handling-in-aspnet-core-the-complete-guide-3cpc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;UseExceptionHandler, IExceptionHandler (.NET 8), ProblemDetails, middleware vs filters, logging errors&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unhandled exceptions are inevitable.&lt;/p&gt;

&lt;p&gt;The question is not whether they will happen — it's whether your API returns a clear, structured error response or exposes a raw stack trace to the client.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why You Need Global Exception Handling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Without it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unhandled exceptions return &lt;code&gt;500 Internal Server Error&lt;/code&gt; with no useful body&lt;/li&gt;
&lt;li&gt;Stack traces may be leaked to clients in non-production environments&lt;/li&gt;
&lt;li&gt;Logging is inconsistent — some exceptions get logged, others disappear&lt;/li&gt;
&lt;li&gt;Every controller has its own try-catch — duplicated and inconsistent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every unhandled exception is caught in one place&lt;/li&gt;
&lt;li&gt;Errors are logged consistently&lt;/li&gt;
&lt;li&gt;Clients receive a structured, predictable error response&lt;/li&gt;
&lt;li&gt;Controllers stay clean — no try-catch everywhere&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Approach 1: &lt;code&gt;UseExceptionHandler&lt;/code&gt; Middleware&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The simplest and most compatible approach. Works in all ASP.NET Core versions.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorApp&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;errorApp&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="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;context&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;error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IExceptionHandlerFeature&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;exception&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;NotFoundException&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status404NotFound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidationException&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;UnauthorizedException&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status401Unauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"application/problem+json"&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;problem&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;ProblemDetails&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problem&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;h2&gt;
  
  
  &lt;strong&gt;Approach 2: &lt;code&gt;IExceptionHandler&lt;/code&gt; (.NET 8+)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The modern approach — typed handlers that can be chained.&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidationExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IExceptionHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ValidationExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ValidationExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ValidationExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&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;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;TryHandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt; &lt;span class="n"&gt;validationEx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Pass to next handler&lt;/span&gt;

        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Validation failed: {Errors}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validationEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&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;problem&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;ValidationProblemDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validationEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToDictionary&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Validation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Handled — stop processing&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;Register handlers in order — first match wins.&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ValidationExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NotFoundExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GlobalExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// catch-all last&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProblemDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Approach 3: Exception Filter (MVC only)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For MVC controllers specifically — not minimal APIs.&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiExceptionFilter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IExceptionFilter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiExceptionFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ApiExceptionFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiExceptionFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&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;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unhandled exception"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;context&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;NotFoundException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotFoundObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ValidationException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BadRequestObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandled&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Register globally&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiExceptionFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;ProblemDetails — RFC 7807&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ProblemDetails&lt;/code&gt; is the standard structured error response format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://tools.ietf.org/html/rfc7807"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The 'Name' field is required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"traceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00-8a3e12b1c456d789-b23f4a12c3d45e67-00"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enabling globally:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProblemDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Custom Domain Exceptions&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DomainException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;DomainException&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotFoundException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DomainException&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;NotFoundException&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;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&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="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with key &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was not found."&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidationException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DomainException&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;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"One or more validation failures occurred."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnauthorizedException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DomainException&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UnauthorizedException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unauthorised."&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;Throw them anywhere in your application code — the global handler catches and maps them.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Logging Exceptions&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Unhandled exception for {Method} {Path} — TraceId: {TraceId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceIdentifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Interview-Ready Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UseExceptionHandler&lt;/code&gt; = catch-all middleware, compatible with all ASP.NET Core versions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IExceptionHandler&lt;/code&gt; (.NET 8+) = typed, chainable handlers — preferred for new projects&lt;/li&gt;
&lt;li&gt;Exception filters = MVC-specific, controller-level&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ProblemDetails&lt;/code&gt; = RFC 7807 standard for structured error responses&lt;/li&gt;
&lt;li&gt;Define a domain exception hierarchy to map business errors to HTTP status codes&lt;/li&gt;
&lt;li&gt;Log every exception with request context and a correlation/trace ID&lt;/li&gt;
&lt;li&gt;Never expose raw stack traces to clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A strong interview answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;"In ASP.NET Core, global exception handling is best done with UseExceptionHandler middleware or the IExceptionHandler interface introduced in .NET 8. Both catch all unhandled exceptions in one place, log them, and return a structured ProblemDetails response. IExceptionHandler allows typed, chainable handlers where each handles a specific exception type. The key is to keep controllers free of try-catch blocks and let the global handler decide the HTTP status code."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dotnet</category>
      <category>aspnetcore</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Built GPU-Accelerated Frosted Glass and Acrylic Blur for .NET MAUI (And Why Every Other Library Gets It Wrong)</title>
      <dc:creator>Plixroit</dc:creator>
      <pubDate>Fri, 24 Apr 2026 14:29:53 +0000</pubDate>
      <link>https://dev.to/plixroit/how-i-built-gpu-accelerated-blur-for-net-maui-and-why-every-other-library-was-doing-it-wrong-1o0j</link>
      <guid>https://dev.to/plixroit/how-i-built-gpu-accelerated-blur-for-net-maui-and-why-every-other-library-was-doing-it-wrong-1o0j</guid>
      <description>&lt;p&gt;Blur on Android in MAUI is kind of a mess. I needed a frosted-glass effect over a scrolling message list and everything I tried either stuttered or just looked bad when the content behind it moved.&lt;/p&gt;

&lt;p&gt;So I dug into how all the existing libraries actually work under the hood. Turns out they're all doing the same thing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Grab a &lt;code&gt;Bitmap&lt;/code&gt; of what's behind the view, on the &lt;strong&gt;CPU&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Run a blur pass in &lt;strong&gt;software&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Push it back up to the GPU&lt;/li&gt;
&lt;li&gt;Do it again next frame&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every. Single. Frame. No wonder it drops frames.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just stay on the GPU the whole time?
&lt;/h2&gt;

&lt;p&gt;Android 12 added &lt;code&gt;RenderEffect&lt;/code&gt; and &lt;code&gt;RenderNode&lt;/code&gt;. These let you hook into the GPU render pipeline directly and apply blur without ever touching the CPU. No bitmap snapshots, no software blur, no memory uploads between frames. The GPU just handles it natively.&lt;/p&gt;

&lt;p&gt;That's the whole idea behind &lt;strong&gt;VitrumMAUI&lt;/strong&gt;. Keep it on the GPU and get out of the way.&lt;/p&gt;

&lt;p&gt;On a mid-range device blurring over a fast scroll, the difference is obvious. No jank.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two views, that's it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;BlurHostView&lt;/code&gt; wraps whatever content sits in the background. &lt;code&gt;BlurConsumerView&lt;/code&gt; goes on top and shows the blurred result with an optional tint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;vitrum:BlurHostView&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/vitrum:BlurHostView&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;vitrum:BlurConsumerView&lt;/span&gt;
    &lt;span class="na"&gt;BlurRadius=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
    &lt;span class="na"&gt;TintColor=&lt;/span&gt;&lt;span class="s"&gt;"#33FFFFFF"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No bitmaps to manage. No invalidation loops. No hacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package VitrumMAUI &lt;span class="nt"&gt;--version&lt;/span&gt; 1.0.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in your csproj:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"VitrumMAUI"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's supported
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Android version&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;12+ (API 31+)&lt;/td&gt;
&lt;td&gt;Full GPU blur via RenderEffect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 to 11&lt;/td&gt;
&lt;td&gt;Tint-only fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;iOS and macOS are not included. Apple's own blur APIs are solid and already built into the platform, so there's no point reinventing that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I actually built this
&lt;/h2&gt;

&lt;p&gt;I was working on a chat UI in my own app. The bottom sheet sits over the message list and I wanted it blurred. Everything I tried was either too slow or required me to wire up manual invalidation which felt wrong. I wanted something that just works without me babysitting it.&lt;/p&gt;

&lt;p&gt;So I built it. It's one day old, rough around the edges, but the core works well.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/Plixroit/VitrumMAUI" rel="noopener noreferrer"&gt;VitrumMAUI&lt;/a&gt;&lt;br&gt;
NuGet: &lt;a href="https://www.nuget.org/packages/VitrumMAUI" rel="noopener noreferrer"&gt;VitrumMAUI 1.0.8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MIT licensed. If you run into issues or have ideas, open an issue.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>maui</category>
      <category>android</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Why .NET interviews feel harder than they should</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Fri, 24 Apr 2026 14:19:20 +0000</pubDate>
      <link>https://dev.to/a95yman/why-net-interviews-feel-harder-than-they-should-4hbc</link>
      <guid>https://dev.to/a95yman/why-net-interviews-feel-harder-than-they-should-4hbc</guid>
      <description>&lt;p&gt;Most &lt;strong&gt;.NET interview&lt;/strong&gt; prep goes into learning concepts.&lt;/p&gt;

&lt;p&gt;But in actual interviews, I’ve noticed something else matters more.&lt;/p&gt;

&lt;p&gt;It’s not just what you know, it’s how you explain it.&lt;/p&gt;

&lt;p&gt;A lot of people run into the same issues:&lt;/p&gt;

&lt;p&gt;They start explaining too much at once, or they lose clarity halfway through their answer.&lt;/p&gt;

&lt;p&gt;And even simple questions like &lt;strong&gt;“what’s the difference between IEnumerable and IQueryable?”&lt;/strong&gt; end up sounding more complicated than they are.&lt;/p&gt;

&lt;p&gt;I think the better approach is actually pretty simple:&lt;/p&gt;

&lt;p&gt;Start with a clear, short answer first. Then add detail only if needed.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;strong&gt;“IEnumerable works in memory, IQueryable works with the database.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then expand from there if the interviewer wants more.&lt;/p&gt;

&lt;p&gt;It sounds small, but it changes how your answers come across a lot.&lt;/p&gt;

&lt;p&gt;At the end of the day, interviews are less about how much you know, and more about how clearly you can communicate it.&lt;/p&gt;

&lt;p&gt;If you're preparing for .NET interviews, this is something worth paying attention to.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>Migrating from jsreport to IronPDF: no fuss, no fluff</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 11:32:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/migrating-from-jsreport-to-ironpdf-no-fuss-no-fluff-4om</link>
      <guid>https://dev.to/ironsoftware/migrating-from-jsreport-to-ironpdf-no-fuss-no-fluff-4om</guid>
      <description>&lt;p&gt;The deployment worked fine on-prem. Then the team containerized the application for Kubernetes, and the PDF generation service started failing intermittently. The error logs pointed to jsreport's child process spawning — Node.js inside a Docker container, with the reporting service trying to launch headless Chromium inside that Node.js process. The layered process management, the user permission model in the container, the memory limits — it all compounds. Getting jsreport running reliably in a container environment takes work that has nothing to do with generating PDFs.&lt;/p&gt;

&lt;p&gt;That's a common trigger for evaluating alternatives. This article is for .NET teams considering IronPDF as the replacement. If you're not switching, the architecture comparison section still maps out the tradeoffs worth understanding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture difference — this matters first
&lt;/h2&gt;

&lt;p&gt;jsreport is a reporting server. IronPDF is a .NET library. That difference has practical implications before you write a single line of migration code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;jsreport architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;External Node.js process (separate service, or embedded via jsreport.Local)&lt;/li&gt;
&lt;li&gt;Templates stored in jsreport's template engine (Handlebars, etc.)&lt;/li&gt;
&lt;li&gt;.NET communicates via HTTP API or local IPC&lt;/li&gt;
&lt;li&gt;Reports can be managed via a browser-based designer UI&lt;/li&gt;
&lt;li&gt;Supports multiple output formats: PDF, Excel, CSV, HTML, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;IronPDF architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In-process .NET library&lt;/li&gt;
&lt;li&gt;Templates are HTML strings or files (rendered via Chromium)&lt;/li&gt;
&lt;li&gt;No external service — runs in your process&lt;/li&gt;
&lt;li&gt;PDF output only (plus manipulation of existing PDFs)&lt;/li&gt;
&lt;li&gt;No built-in template management UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The migration decision depends on what you're actually using jsreport for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;jsreport usage&lt;/th&gt;
&lt;th&gt;Migration path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF generation only, HTML templates&lt;/td&gt;
&lt;td&gt;Direct replacement with IronPDF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple output formats (Excel, CSV, etc.)&lt;/td&gt;
&lt;td&gt;IronPDF covers PDF; other formats need separate handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template management via browser UI&lt;/td&gt;
&lt;td&gt;IronPDF has no equivalent — build template management separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex Handlebars/EJS logic&lt;/td&gt;
&lt;td&gt;Rewrite templates in HTML/Razor; logic moves to .NET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-premises, works fine&lt;/td&gt;
&lt;td&gt;No migration trigger — document this and move on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why migrate (without drama)
&lt;/h2&gt;

&lt;p&gt;Eight specific reasons teams trigger this migration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Container deployment complexity&lt;/strong&gt; — jsreport's Chromium-in-Node.js-in-Docker requires managing layered process permissions, seccomp profiles, and memory limits that don't apply to an in-process library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js dependency in a .NET shop&lt;/strong&gt; — maintaining a Node.js runtime, managing npm packages, and keeping jsreport updated adds operational overhead for teams without Node.js expertise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inter-process communication overhead&lt;/strong&gt; — HTTP or IPC round trips to a reporting server add latency per PDF. In-process rendering eliminates the network hop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service orchestration&lt;/strong&gt; — jsreport as a separate service needs health checks, restart policies, and separate monitoring. An in-process library reduces the service count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure App Service / serverless constraints&lt;/strong&gt; — some Azure tiers restrict background process spawning. An in-process renderer avoids this constraint. See &lt;a href="https://ironpdf.com/how-to/azure/" rel="noopener noreferrer"&gt;IronPDF Azure documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template management coupling&lt;/strong&gt; — jsreport templates stored in jsreport's data store create a dependency between your template design and the reporting service. Moving to HTML files in your codebase gives you version control integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output format scope&lt;/strong&gt; — if PDF is the only output you actually use, maintaining a full reporting platform is overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jsreport licensing tiers&lt;/strong&gt; — if you're hitting limits on the community edition for your use case, evaluating alternatives is reasonable before upgrading tiers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Comparison table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;jsreport&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;Focus&lt;/td&gt;
&lt;td&gt;Multi-format reporting platform&lt;/td&gt;
&lt;td&gt;PDF generation + manipulation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;Community (free) + commercial tiers&lt;/td&gt;
&lt;td&gt;Commercial — verify at ironsoftware.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Style&lt;/td&gt;
&lt;td&gt;HTTP REST API + .NET client&lt;/td&gt;
&lt;td&gt;In-process .NET library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Curve&lt;/td&gt;
&lt;td&gt;Medium (requires understanding reporting concepts)&lt;/td&gt;
&lt;td&gt;Medium (.NET API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML Rendering&lt;/td&gt;
&lt;td&gt;Chromium via Puppeteer&lt;/td&gt;
&lt;td&gt;Chromium-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Indexing&lt;/td&gt;
&lt;td&gt;N/A (output only)&lt;/td&gt;
&lt;td&gt;0-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Safety&lt;/td&gt;
&lt;td&gt;External service handles isolation&lt;/td&gt;
&lt;td&gt;Renderer instance reuse — see async docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Namespace&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;jsreport.Client&lt;/code&gt; (HTTP client)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IronPdf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Migration complexity assessment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Effort by feature
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;jsreport approach&lt;/th&gt;
&lt;th&gt;Effort to migrate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTML/Handlebars to PDF&lt;/td&gt;
&lt;td&gt;API call with template data&lt;/td&gt;
&lt;td&gt;Medium — migrate templates to HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL to PDF&lt;/td&gt;
&lt;td&gt;jsreport recipe&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headers and footers&lt;/td&gt;
&lt;td&gt;Template recipe options&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermark&lt;/td&gt;
&lt;td&gt;Custom CSS in template / post-processing&lt;/td&gt;
&lt;td&gt;Low (native in IronPDF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge PDFs&lt;/td&gt;
&lt;td&gt;Not a jsreport core feature&lt;/td&gt;
&lt;td&gt;Low (native in IronPDF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password protection&lt;/td&gt;
&lt;td&gt;Not a jsreport core feature&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excel output&lt;/td&gt;
&lt;td&gt;jsreport native feature&lt;/td&gt;
&lt;td&gt;N/A — IronPDF doesn't cover this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSV output&lt;/td&gt;
&lt;td&gt;jsreport native feature&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template management UI&lt;/td&gt;
&lt;td&gt;jsreport studio&lt;/td&gt;
&lt;td&gt;Not in IronPDF — build separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled reports&lt;/td&gt;
&lt;td&gt;jsreport scheduling&lt;/td&gt;
&lt;td&gt;Not in IronPDF — build separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container deployment&lt;/td&gt;
&lt;td&gt;Complex (see opening)&lt;/td&gt;
&lt;td&gt;Simpler — in-process&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Decision matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF only, container deployment issues&lt;/td&gt;
&lt;td&gt;IronPDF is a direct fit — evaluates well&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-format output (Excel, CSV) needed&lt;/td&gt;
&lt;td&gt;IronPDF covers PDF; retain jsreport for other formats, or add libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template management UI is business-critical&lt;/td&gt;
&lt;td&gt;jsreport's studio has no equivalent in IronPDF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js runtime is already in your stack&lt;/td&gt;
&lt;td&gt;jsreport overhead is lower — migration ROI is smaller&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;.NET 6+&lt;/li&gt;
&lt;li&gt;HTML template inventory from jsreport&lt;/li&gt;
&lt;li&gt;Access to jsreport template files for migration&lt;/li&gt;
&lt;li&gt;NuGet access&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Find jsreport references in your codebase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find jsreport client usage&lt;/span&gt;
rg &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s2"&gt;"jsreport&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;JsReport&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;IJsReportClient"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs &lt;span class="nt"&gt;-i&lt;/span&gt;

&lt;span class="c"&gt;# Find HTTP API calls to jsreport&lt;/span&gt;
rg &lt;span class="s2"&gt;"jsreport&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;localhost:5488&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;api/report"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs &lt;span class="nt"&gt;-n&lt;/span&gt;

&lt;span class="c"&gt;# Find template rendering calls&lt;/span&gt;
rg &lt;span class="s2"&gt;"ReportingService&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;RenderAsync"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs &lt;span class="nt"&gt;-n&lt;/span&gt;

&lt;span class="c"&gt;# Find NuGet package references&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"jsreport"&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.csproj &lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.csproj 2&amp;gt;/dev/null

&lt;span class="c"&gt;# Find jsreport config files&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"jsreport.config.json"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsrep"&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove jsreport client, install IronPDF
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove jsreport .NET client packages&lt;/span&gt;
dotnet remove package jsreport.Client
dotnet remove package jsreport.Local    &lt;span class="c"&gt;# if using embedded mode&lt;/span&gt;
dotnet remove package jsreport.AspNetCore  &lt;span class="c"&gt;# if using middleware&lt;/span&gt;

&lt;span class="c"&gt;# Install IronPDF&lt;/span&gt;
dotnet add package IronPdf
dotnet restore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separately: remove jsreport from Docker images, docker-compose files, and service orchestration config.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick start migration (3 steps)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: License configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport — no .NET license key; authenticated via jsreport server config):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// jsreport authentication is handled at the HTTP level&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Optional: rs.Username = "admin"; rs.Password = "password";&lt;/span&gt;
&lt;span class="c1"&gt;// No in-process license key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF):&lt;/strong&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;// One-time setup at application startup&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;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_IRONPDF_LICENSE_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Guide: https://ironpdf.com/how-to/license-keys/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Namespace imports
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&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;jsreport.Client&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;jsreport.Types&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Rendering&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Basic PDF generation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport API call):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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.IO&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuickStartExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&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;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&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;report&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;rs&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RenderRequest&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;"&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="n"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromePdf&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="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ms&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;MemoryStream&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;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&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="nf"&gt;WriteAllBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF):&lt;/strong&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="n"&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&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;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Guide: https://ironpdf.com/how-to/html-string-to-pdf/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  API mapping tables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Namespace mapping
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;jsreport&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jsreport.Client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IronPdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jsreport.Types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IronPdf.Rendering&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configuration types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP client pattern&lt;/td&gt;
&lt;td&gt;In-process — no HTTP&lt;/td&gt;
&lt;td&gt;Architectural change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Core class mapping
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;jsreport class&lt;/th&gt;
&lt;th&gt;IronPDF class&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReportingService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Entry point for PDF generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RenderRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderOptions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Template&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTML string or file&lt;/td&gt;
&lt;td&gt;Template definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Report.Content&lt;/code&gt; (stream)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PDF output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Document loading methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;jsreport&lt;/th&gt;
&lt;th&gt;IronPDF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTML string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Template.Content&lt;/code&gt; + &lt;code&gt;Engine.None&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renderer.RenderHtmlAsPdf(html)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;jsreport URL recipe&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renderer.RenderUrlAsPdf(url)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template file&lt;/td&gt;
&lt;td&gt;jsreport template store&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renderer.RenderHtmlFileAsPdf(path)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing PDF&lt;/td&gt;
&lt;td&gt;Not jsreport's scope&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.FromFile(path)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Page operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;jsreport&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;Page size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfOptions.PaperSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderOptions.PaperSize&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Margins&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ChromePdfOptions&lt;/code&gt; properties&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderOptions.Margin*&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orientation&lt;/td&gt;
&lt;td&gt;jsreport Chrome options&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderOptions.PaperOrientation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headers/footers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HeaderTemplate&lt;/code&gt; / &lt;code&gt;FooterTemplate&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HtmlHeaderFooter&lt;/code&gt; — verify IronPDF API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Merge/split operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;jsreport&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;Merge&lt;/td&gt;
&lt;td&gt;Not a core jsreport feature&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.Merge(pdf1, pdf2)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split&lt;/td&gt;
&lt;td&gt;Not a core jsreport feature&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.CopyPages(startIndex, endIndex)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Four complete before/after migrations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. HTML to PDF
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport RenderAsync with template data):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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&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.IO&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HtmlToPdfExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&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;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Template with Handlebars data binding&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
            &amp;lt;html&amp;gt;&amp;lt;body&amp;gt;
                &amp;lt;h1&amp;gt;Invoice #{{invoiceNumber}}&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;Customer: {{customerName}}&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Amount: ${{amount}}&amp;lt;/p&amp;gt;
            &amp;lt;/body&amp;gt;&amp;lt;/html&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;report&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;rs&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RenderRequest&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Engine&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="n"&gt;Handlebars&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromePdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Chrome&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;Chrome&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="s"&gt;"10mm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"10mm"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Data&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;invoiceNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;customerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ACME Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"500.00"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Stream to bytes&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;ms&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;MemoryStream&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;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&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="nf"&gt;WriteAllBytes&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;ms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&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;"Saved: invoice.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF — template logic moves to your .NET code):&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&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;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;// Template data rendering moves to .NET (Razor, string interpolation, etc.)&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;invoiceNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1234"&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;customerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ACME Corp"&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;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"500.00"&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="s"&gt;$@"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    &amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;        &amp;lt;h1&amp;gt;Invoice #&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;        &amp;lt;p&amp;gt;Customer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;        &amp;lt;p&amp;gt;Amount: $&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    &amp;lt;/body&amp;gt;&amp;lt;/html&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="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;10&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;MarginBottom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="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;h3&gt;
  
  
  2. Merge PDFs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport doesn't merge natively — multi-report generation then merge separately):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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;// secondary library&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PdfSharp.Pdf.IO&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.Collections.Generic&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.IO&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MergeReportsExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&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;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&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;tempPaths&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Generate each section as separate PDF&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;sectionHtml&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Section 1&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Section 2&amp;lt;/h1&amp;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;report&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;rs&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RenderRequest&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sectionHtml&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="n"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromePdf&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;tempPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTempFileName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fs&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tempPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;tempPaths&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;tempPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Merge with PdfSharp (secondary library)&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;outputDoc&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;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;path&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tempPaths&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="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="n"&gt;PdfReader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PdfDocumentOpenMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Import&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="n"&gt;PdfPage&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="k"&gt;in&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;Pages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;outputDoc&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="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;outputDoc&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;"merged.pdf"&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;path&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tempPaths&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="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// cleanup&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF native merge):&lt;/strong&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="n"&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;section1&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;Section 1&amp;lt;/h1&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;section2&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;Section 2&amp;lt;/h1&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;merged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;section2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;merged&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;"merged.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Guide: https://ironpdf.com/how-to/merge-or-split-pdfs/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Watermark
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport — watermark typically via CSS in template, or post-process with secondary library):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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;iTextSharp.text&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;iTextSharp.text.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;System.IO&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WatermarkExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&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;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Generate PDF&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;report&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;rs&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RenderRequest&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Confidential Report&amp;lt;/h1&amp;gt;"&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="n"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromePdf&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Write to temp file, apply watermark with secondary library&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTempFileName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;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;fs&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Apply watermark via iTextSharp (secondary library)&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;reader&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;PdfReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&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;outFs&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"watermarked.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;stamper&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;PdfStamper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outFs&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="n"&gt;BaseFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HELVETICA_BOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CP1252&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumberOfPages&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stamper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOverContent&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="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFontAndSize&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="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetColorFill&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;BaseColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShowTextAligned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALIGN_CENTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"CONFIDENTIAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndText&lt;/span&gt;&lt;span class="p"&gt;();&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="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF — no secondary library needed):&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Editing&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;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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Confidential Report&amp;lt;/h1&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;stamper&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;TextStamper&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;"CONFIDENTIAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FontColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IronSoftware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Drawing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LightGray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Opacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;VerticalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VerticalAlignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HorizontalAlignment&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApplyStamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stamper&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;"watermarked.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Guide: https://ironpdf.com/how-to/custom-watermark/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Password protection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (jsreport — not a native feature; secondary library pattern):&lt;/strong&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;jsreport.Client&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;jsreport.Types&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;iTextSharp.text.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;System.IO&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&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;rs&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;ReportingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5488"&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;report&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;rs&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RenderRequest&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Private Document&amp;lt;/h1&amp;gt;"&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="n"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromePdf&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Stream to bytes, then encrypt with secondary library&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdfBytes&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;ms&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;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;())&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;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;pdfBytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&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="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reader&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;PdfReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfBytes&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;fs&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"secured.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;stamper&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;PdfStamper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;stamper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetEncryption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASCII&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userpass"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASCII&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ownerpass"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;PdfWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALLOW_PRINTING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;PdfWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTION_AES_128&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF):&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Security&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;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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Private Document&amp;lt;/h1&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="n"&gt;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserPassword&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"userpass"&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OwnerPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ownerpass"&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllowUserPrinting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPrintSecurity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullPrintRights&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;"secured.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Guide: https://ironpdf.com/how-to/pdf-permissions-passwords/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Critical migration notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Template engine migration
&lt;/h3&gt;

&lt;p&gt;jsreport templates use Handlebars, EJS, or similar engines running in Node.js. After migration, that template logic needs a .NET home. Common patterns:&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;// Option 1: String interpolation (simple cases)&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="s"&gt;$"&amp;lt;h1&amp;gt;Invoice #&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invoice&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;&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Total: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&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;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Option 2: Razor in a separate class library&lt;/span&gt;
&lt;span class="c1"&gt;// Use RazorLight or a similar Razor rendering library to compile .cshtml → string&lt;/span&gt;
&lt;span class="c1"&gt;// Then pass string to ChromePdfRenderer&lt;/span&gt;

&lt;span class="c1"&gt;// Option 3: Handlebars.NET&lt;/span&gt;
&lt;span class="c1"&gt;// NuGet: Handlebars.Net — allows reusing Handlebars template syntax in .NET&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Handlebars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handlebarsTemplate&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="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  jsreport.Local (embedded mode)
&lt;/h3&gt;

&lt;p&gt;If you were using &lt;code&gt;jsreport.Local&lt;/code&gt; (which embeds Node.js directly in the .NET process), the migration is simpler — the HTTP layer is already internal. The code change is still significant, but the deployment change is minimal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service removal
&lt;/h3&gt;

&lt;p&gt;If jsreport runs as a separate Docker service in your compose or Kubernetes setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml — remove this service entirely:&lt;/span&gt;
&lt;span class="c1"&gt;# services:&lt;/span&gt;
&lt;span class="c1"&gt;#   jsreport:&lt;/span&gt;
&lt;span class="c1"&gt;#     image: jsreport/jsreport&lt;/span&gt;
&lt;span class="c1"&gt;#     ports:&lt;/span&gt;
&lt;span class="c1"&gt;#       - "5488:5488"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update any service dependencies that reference the jsreport container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page indexing
&lt;/h3&gt;

&lt;p&gt;IronPDF uses 0-based page indexing for all document operations. jsreport doesn't expose a page model (it renders and outputs), so this only matters if you added post-processing with page-aware operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Eliminate HTTP round trip
&lt;/h3&gt;

&lt;p&gt;Every jsreport PDF generation made a network round trip (or at least IPC). IronPDF renders in-process — no network latency:&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;// jsreport: HTTP call → jsreport Node process → Chromium → response&lt;/span&gt;
&lt;span class="c1"&gt;// Typical latency: 100-500ms+ depending on network and template complexity&lt;/span&gt;

&lt;span class="c1"&gt;// IronPDF: in-process → Chromium → return&lt;/span&gt;
&lt;span class="c1"&gt;// Same Chromium rendering, no network overhead&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Renderer reuse in ASP.NET
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register as singleton in ASP.NET Core for reuse&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&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;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;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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;renderer&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;h3&gt;
  
  
  Async rendering
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Async pattern for web handlers&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="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="c1"&gt;// Async guide: https://ironpdf.com/how-to/async/&lt;/span&gt;

&lt;span class="c1"&gt;// Return as file download&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;Stream&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Azure / cloud deployment
&lt;/h3&gt;

&lt;p&gt;IronPDF's in-process Chromium is more predictable in containerized environments than jsreport's Node.js + Chromium stack. Verify configuration for your specific Azure tier — see &lt;a href="https://ironpdf.com/how-to/azure/" rel="noopener noreferrer"&gt;IronPDF Azure documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge cases worth flagging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Handlebars template complexity&lt;/strong&gt; — if your jsreport templates have complex Handlebars helpers or partials, inventory them before migrating. Each helper needs a .NET equivalent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jsreport assets&lt;/strong&gt; — images, fonts, and stylesheets referenced as jsreport assets need to be moved to accessible static paths or embedded in the HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling and reporting workflows&lt;/strong&gt; — jsreport includes scheduling features. If your system uses them, build those workflows separately in .NET after migration.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Migration checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pre-migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Find all jsreport client code: &lt;code&gt;rg "jsreport\|ReportingService" --type cs -i&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Inventory all jsreport templates — export from jsreport studio if needed&lt;/li&gt;
&lt;li&gt;[ ] Identify non-PDF outputs (Excel, CSV) — plan separately&lt;/li&gt;
&lt;li&gt;[ ] Identify scheduled reports — plan .NET-based scheduling separately&lt;/li&gt;
&lt;li&gt;[ ] Map Handlebars helpers to .NET equivalents&lt;/li&gt;
&lt;li&gt;[ ] Identify jsreport assets (images, fonts, styles)&lt;/li&gt;
&lt;li&gt;[ ] Verify IronPDF .NET version compatibility&lt;/li&gt;
&lt;li&gt;[ ] Check Azure / cloud tier for Chromium subprocess permissions if relevant&lt;/li&gt;
&lt;li&gt;[ ] Set up IronPDF trial license in dev environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Remove &lt;code&gt;jsreport.Client&lt;/code&gt;, &lt;code&gt;jsreport.Local&lt;/code&gt;, &lt;code&gt;jsreport.AspNetCore&lt;/code&gt; NuGet packages&lt;/li&gt;
&lt;li&gt;[ ] Add &lt;code&gt;IronPdf&lt;/code&gt; NuGet package&lt;/li&gt;
&lt;li&gt;[ ] Replace &lt;code&gt;ReportingService&lt;/code&gt; client with &lt;code&gt;ChromePdfRenderer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Replace &lt;code&gt;RenderRequest&lt;/code&gt; + &lt;code&gt;Template&lt;/code&gt; with HTML string generation&lt;/li&gt;
&lt;li&gt;[ ] Migrate Handlebars templates to .NET template engine (Razor, Handlebars.NET, etc.)&lt;/li&gt;
&lt;li&gt;[ ] Replace merge operations (if any secondary library involved)&lt;/li&gt;
&lt;li&gt;[ ] Replace watermark operations&lt;/li&gt;
&lt;li&gt;[ ] Replace password protection&lt;/li&gt;
&lt;li&gt;[ ] Add IronPDF license key to config&lt;/li&gt;
&lt;li&gt;[ ] Register &lt;code&gt;ChromePdfRenderer&lt;/code&gt; in DI if using ASP.NET&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Render each template and compare output against jsreport reference PDFs&lt;/li&gt;
&lt;li&gt;[ ] Test with production-representative data (edge cases in data binding)&lt;/li&gt;
&lt;li&gt;[ ] Verify merge, watermark, security operations&lt;/li&gt;
&lt;li&gt;[ ] Test async rendering under concurrent load&lt;/li&gt;
&lt;li&gt;[ ] Test in target deployment environment (container, Azure, etc.)&lt;/li&gt;
&lt;li&gt;[ ] Verify no Chromium subprocess permission issues in container&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Post-migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Remove jsreport Docker service from compose / Kubernetes config&lt;/li&gt;
&lt;li&gt;[ ] Remove jsreport port from service discovery / firewall rules&lt;/li&gt;
&lt;li&gt;[ ] Remove Node.js runtime from Docker images if jsreport was the only consumer&lt;/li&gt;
&lt;li&gt;[ ] Update monitoring — replace jsreport health checks with in-process metrics&lt;/li&gt;
&lt;li&gt;[ ] Monitor memory baseline (Chromium in-process vs external service)&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The architectural shift from external reporting server to in-process library is the meaningful part of this migration. The API differences are straightforward once you've mapped the template engine and determined how non-PDF outputs will be handled.&lt;/p&gt;

&lt;p&gt;Teams that hit friction tend to be the ones with large Handlebars template libraries — that migration is mechanical but takes time. And teams who depended on jsreport Studio for template management need to decide where that workflow goes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you add to this migration checklist based on your own jsreport integration?&lt;/strong&gt; Particularly interested in teams who had complex Handlebars helpers or were running jsreport in Kubernetes — those setups tend to have extra migration steps not covered here.&lt;/p&gt;




</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>wkhtmltopdf Memory Leak and High Memory Usage (Issue Fixed)</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 11:27:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/wkhtmltopdf-memory-leak-and-high-memory-usage-issue-fixed-1b25</link>
      <guid>https://dev.to/ironsoftware/wkhtmltopdf-memory-leak-and-high-memory-usage-issue-fixed-1b25</guid>
      <description>&lt;p&gt;When wkhtmltopdf generates large documents, memory consumption escalates rapidly and often does not return to baseline after conversion completes. A 4,250-page document can require approximately 5GB of RAM. Tables with 400,000 records cause memory to climb at roughly 20MB per second. In containerized environments, this results in OOMKilled errors that terminate the process mid-conversion. The wkhtmltopdf project was archived in January 2023 with no further updates to address these memory management issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;wkhtmltopdf exhibits several memory-related behaviors that impact production deployments. Memory allocation grows proportionally with document complexity, but deallocation after conversion is incomplete. Successive conversions accumulate unreleased memory until the process is terminated.&lt;/p&gt;

&lt;p&gt;The Qt WebKit rendering engine at the core of wkhtmltopdf was designed for interactive browser sessions, not batch document processing. When rendering large HTML tables or complex CSS layouts, WebKit allocates memory for the entire document tree. Elements with JavaScript animations or dynamic content consume additional memory that persists after rendering completes.&lt;/p&gt;

&lt;p&gt;Container orchestration systems like Kubernetes enforce memory limits on pods. When wkhtmltopdf exceeds these limits, the Linux OOM killer terminates the container. This presents as sudden process death without meaningful error messages in application logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Messages and Symptoms
&lt;/h3&gt;

&lt;p&gt;Developers encounter these errors related to wkhtmltopdf memory consumption:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OOMKilled in Docker/Kubernetes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;State:          Terminated
Reason:         OOMKilled
Exit Code:      137
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Container killed due to memory limit exceeded
wkhtmltopdf process exited with code 137
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;System Memory Errors:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cannot allocate memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Memory limit too low
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Process Hangs or Crashes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exit with code 1 due to network error: ContentOperationNotPermittedError
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;



&lt;p&gt;The symptoms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory usage increasing steadily during conversion (approximately 20MB/second for large tables)&lt;/li&gt;
&lt;li&gt;Memory not returning to baseline after conversion completes&lt;/li&gt;
&lt;li&gt;Multiple sequential conversions exhausting available RAM&lt;/li&gt;
&lt;li&gt;Container restart loops in Kubernetes deployments&lt;/li&gt;
&lt;li&gt;Process freezing or hanging during large document generation&lt;/li&gt;
&lt;li&gt;Exit code 137 indicating OOM termination&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Is Affected
&lt;/h2&gt;

&lt;p&gt;This wkhtmltopdf memory issue impacts specific deployment scenarios:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operating Systems&lt;/strong&gt;: Linux servers, Docker containers (Debian, Ubuntu, Alpine), and cloud platform instances. Windows and macOS local development machines may not exhibit the issue due to higher default memory limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Container Platforms&lt;/strong&gt;: Docker with default memory limits, Kubernetes pods with resource constraints, AWS ECS tasks, Azure Container Instances, and Google Cloud Run instances with 512MB-2GB limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;: Large report generation (1000+ pages), data export to PDF with extensive tables (100,000+ rows), batch processing of multiple documents in sequence, long-running services performing repeated conversions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale Factors&lt;/strong&gt;: The issue becomes critical when documents exceed approximately 500 pages, when tables contain more than 50,000 rows, when generating multiple PDFs without process restart, or when container memory is limited below 4GB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frameworks&lt;/strong&gt;: Any .NET, Python, Ruby, PHP, or Node.js application using wkhtmltopdf through wrapper libraries (DinkToPdf, pdfkit, wicked_pdf, snappy, node-wkhtmltopdf).&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence from the Developer Community
&lt;/h2&gt;

&lt;p&gt;The wkhtmltopdf memory leak has been documented across multiple platforms over several years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2016-2017&lt;/td&gt;
&lt;td&gt;Memory issues reported with large documents&lt;/td&gt;
&lt;td&gt;GitHub Issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018-2019&lt;/td&gt;
&lt;td&gt;Container memory problems widely discussed&lt;/td&gt;
&lt;td&gt;Stack Overflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Recommendations emerge to limit container memory to 4GB+&lt;/td&gt;
&lt;td&gt;GitHub, Forums&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-12&lt;/td&gt;
&lt;td&gt;Final wkhtmltopdf release (0.12.6.1-3)&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-01&lt;/td&gt;
&lt;td&gt;Project archived with no memory fixes planned&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-2025&lt;/td&gt;
&lt;td&gt;Legacy deployments continue experiencing OOM issues&lt;/td&gt;
&lt;td&gt;Various platforms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Community Reports
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Generating a 4250-page PDF was using close to 5 gigs of memory."&lt;br&gt;
— Developer, Stack Overflow, 2018&lt;/p&gt;

&lt;p&gt;"Memory consumption is increasing around 20 MB per second during the build. My table records are 400k."&lt;br&gt;
— Developer, GitHub Issues, 2019&lt;/p&gt;

&lt;p&gt;"Complex CSS is causing memory to grow without bounds. We had to add a memory limit of 4GB to the container."&lt;br&gt;
— Developer, Reddit r/docker, 2021&lt;/p&gt;

&lt;p&gt;"Our wkhtmltopdf containers keep getting OOMKilled. We're seeing memory climb and never release between conversions."&lt;br&gt;
— Developer, Stack Overflow, 2022&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Multiple GitHub issues document the memory behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue #3052: "High memory usage with large tables"&lt;/li&gt;
&lt;li&gt;Issue #4120: "Memory not released after conversion"&lt;/li&gt;
&lt;li&gt;Issue #4521: "OOM in Docker containers"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Root Cause Analysis
&lt;/h2&gt;

&lt;p&gt;The wkhtmltopdf memory leak stems from several architectural factors:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Qt WebKit Memory Model&lt;/strong&gt;: The underlying Qt WebKit engine maintains DOM nodes and rendering context in memory. Large documents create extensive node trees that persist beyond their use. WebKit's garbage collection is designed for interactive browsing, not single-use document generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Process Architecture&lt;/strong&gt;: wkhtmltopdf runs as a single process that handles the entire conversion. Memory allocated during rendering phases is not released until the process terminates. Sequential conversions accumulate allocations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS and Layout Engine&lt;/strong&gt;: Complex CSS (especially flexible layouts, transforms, and nested elements) requires additional memory for layout calculations. Large tables trigger row-by-row rendering that holds all previous rows in memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript Execution&lt;/strong&gt;: When JavaScript is enabled, the V8 engine (or JavaScriptCore in older builds) allocates memory for script execution contexts. Memory associated with completed scripts may not be released.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image Handling&lt;/strong&gt;: Embedded or referenced images are decoded and cached in memory. Large images or numerous images multiply memory consumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Streaming Output&lt;/strong&gt;: wkhtmltopdf builds the entire document in memory before writing output. There is no streaming mode that would allow memory-efficient processing of large documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Archived Project&lt;/strong&gt;: With maintenance ended in January 2023, these memory management issues will not receive fixes. The underlying Qt WebKit has not been updated to modern memory management patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempted Workarounds
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Workaround 1: Disable JavaScript and Images
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Reduce memory by disabling features that consume additional resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wkhtmltopdf &lt;span class="nt"&gt;--disable-javascript&lt;/span&gt; &lt;span class="nt"&gt;--no-images&lt;/span&gt; &lt;span class="nt"&gt;--lowquality&lt;/span&gt; input.html output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command-line options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--disable-javascript&lt;/code&gt;: Prevents V8 memory allocation for script execution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--no-images&lt;/code&gt;: Skips image decoding and caching&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--lowquality&lt;/code&gt;: Reduces image quality and processing memory&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Removes functionality required by many documents&lt;/li&gt;
&lt;li&gt;JavaScript-dependent content will not render&lt;/li&gt;
&lt;li&gt;Images will be missing from output&lt;/li&gt;
&lt;li&gt;Not applicable when documents require these features&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 2: Increase Container Memory Limits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Allocate 4GB or more to the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Kubernetes deployment&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pdf-generator&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4Gi"&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2Gi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker run&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4g myapp-with-wkhtmltopdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Increases infrastructure costs&lt;/li&gt;
&lt;li&gt;May not be possible on constrained platforms (serverless, shared hosting)&lt;/li&gt;
&lt;li&gt;Does not fix the leak, only delays OOM&lt;/li&gt;
&lt;li&gt;4GB may still be insufficient for very large documents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 3: Process Isolation and Restart
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Run each conversion in a new process and terminate it after completion.&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="c1"&gt;# Python example: subprocess isolation
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert_with_isolation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Run wkhtmltopdf in isolated subprocess to contain memory leaks.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wkhtmltopdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wkhtmltopdf failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Process terminates here, releasing all memory
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdf_path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C# example: process-per-conversion&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IsolatedWkhtmltopdf&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConvertWithMemoryIsolation&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;htmlPath&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;pdfPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Each conversion spawns a new process&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;process&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;Process&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&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;ProcessStartInfo&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"wkhtmltopdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Arguments&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="n"&gt;htmlPath&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="n"&gt;pdfPath&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="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;RedirectStandardError&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;300000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5 minute timeout&lt;/span&gt;

            &lt;span class="c1"&gt;// Process disposal releases memory&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Process startup overhead for each conversion&lt;/li&gt;
&lt;li&gt;Does not help with single large document that exceeds memory&lt;/li&gt;
&lt;li&gt;Adds complexity to application code&lt;/li&gt;
&lt;li&gt;Kubernetes container restarts may still occur during conversion&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 4: Document Chunking
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Split large documents into smaller segments and merge PDFs.&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="c1"&gt;# Split large HTML table into chunks
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunk_table_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generate separate PDFs for chunks of data, then merge.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_html_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;convert_to_pdf&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="c1"&gt;# Merge PDFs using pdftk or similar
&lt;/span&gt;    &lt;span class="nf"&gt;merge_pdfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final_output.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Requires document restructuring&lt;/li&gt;
&lt;li&gt;Headers/footers may be inconsistent across chunks&lt;/li&gt;
&lt;li&gt;Page numbering becomes complicated&lt;/li&gt;
&lt;li&gt;Additional tooling required for PDF merge&lt;/li&gt;
&lt;li&gt;Not applicable for documents that cannot be segmented&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Different Approach: IronPDF
&lt;/h2&gt;

&lt;p&gt;For applications experiencing wkhtmltopdf memory issues, IronPDF offers an architecture designed for efficient memory usage during document generation. IronPDF uses an embedded Chromium rendering engine with memory management appropriate for server-side batch processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why IronPDF Has Different Memory Characteristics
&lt;/h3&gt;

&lt;p&gt;The architectural differences address the memory concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chromium's Memory Model&lt;/strong&gt;: Chromium includes garbage collection and memory pooling designed for long-running processes, unlike Qt WebKit's browser-session assumptions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Proper Resource Disposal&lt;/strong&gt;: IronPDF implements IDisposable patterns that release native memory when documents are disposed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming Capabilities&lt;/strong&gt;: Large documents can be processed with streaming patterns that reduce peak memory consumption&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Active Maintenance&lt;/strong&gt;: Memory issues can be addressed through updates, unlike the archived wkhtmltopdf&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Example
&lt;/h3&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;System&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.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Demonstrates memory-efficient PDF generation for large documents.&lt;/span&gt;
&lt;span class="c1"&gt;/// Addresses the wkhtmltopdf memory leak issue by using IronPDF's&lt;/span&gt;
&lt;span class="c1"&gt;/// Chromium-based rendering with proper resource management.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryEfficientPdfGenerator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GenerateLargeReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReportRow&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Configure for server environments&lt;/span&gt;
        &lt;span class="n"&gt;Installation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LinuxAndDockerDependenciesAutoConfig&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="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;// Configure rendering for large documents&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;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;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 5 minutes for large documents&lt;/span&gt;

        &lt;span class="c1"&gt;// Build HTML with large data table&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildLargeTableHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Using statement ensures proper memory cleanup after conversion&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="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;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/output/large-report.pdf"&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;$"Generated PDF: &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;PageCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; pages, &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="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Memory released when pdf is disposed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessMultipleDocumentsEfficiently&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;htmlDocuments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Single renderer instance can be reused without memory accumulation&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;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;html&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;htmlDocuments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Each document is properly disposed after use&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="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;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"/output/doc-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&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="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="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Memory from previous document is released before next iteration&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GenerateWithExplicitMemoryControl&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;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;// Configure rendering options that impact memory usage&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="c1"&gt;// For very large tables, consider pagination in HTML&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="s"&gt;@"
            &amp;lt;!DOCTYPE html&amp;gt;
            &amp;lt;html&amp;gt;
            &amp;lt;head&amp;gt;
                &amp;lt;style&amp;gt;
                    table { width: 100%; border-collapse: collapse; }
                    th, td { border: 1px solid #ccc; padding: 8px; }
                    tr { page-break-inside: avoid; }
                    thead { display: table-header-group; }
                &amp;lt;/style&amp;gt;
            &amp;lt;/head&amp;gt;
            &amp;lt;body&amp;gt;
                &amp;lt;h1&amp;gt;Large Data Report&amp;lt;/h1&amp;gt;
                &amp;lt;table&amp;gt;
                    &amp;lt;thead&amp;gt;
                        &amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Value&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;
                    &amp;lt;/thead&amp;gt;
                    &amp;lt;tbody&amp;gt;
                        &amp;lt;!-- Data rows would be generated here --&amp;gt;
                        "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;GenerateTableRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&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;
            &amp;lt;/body&amp;gt;
            &amp;lt;/html&amp;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;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;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/output/large-table.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;BuildLargeTableHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReportRow&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&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;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&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;&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;r&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="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="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$@"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;html&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;head&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                &amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    table &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;100&lt;/span&gt;&lt;span class="p"&gt;%;&lt;/span&gt; &lt;span class="n"&gt;border&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;collapse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collapse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    th, td &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt; &lt;span class="n"&gt;solid&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    th &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="n"&gt;CAF50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    tr:nth-child(even) &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;f2f2f2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                &amp;lt;/style&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;/head&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                &amp;lt;h1&amp;gt;Report with &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Records&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                &amp;lt;table&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Value&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                    &amp;lt;tbody&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                &amp;lt;/table&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;/body&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;            &amp;lt;/html&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateTableRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&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;sb&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;System&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="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendLine&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;i&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;Item &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="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;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;F2&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;2025-01-&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="m"&gt;28&lt;/span&gt;&lt;span class="p"&gt;)&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;D2&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="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sb&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportRow&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Docker configuration with appropriate memory:&lt;/strong&gt;&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-bookworm-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# IronPDF dependencies - memory-efficient compared to wkhtmltopdf stack&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    libc6 &lt;span class="se"&gt;\
&lt;/span&gt;    libgcc-s1 &lt;span class="se"&gt;\
&lt;/span&gt;    libgssapi-krb5-2 &lt;span class="se"&gt;\
&lt;/span&gt;    libicu72 &lt;span class="se"&gt;\
&lt;/span&gt;    libssl3 &lt;span class="se"&gt;\
&lt;/span&gt;    libstdc++6 &lt;span class="se"&gt;\
&lt;/span&gt;    zlib1g &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/publish .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "YourApp.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Kubernetes deployment - compare to wkhtmltopdf's 4GB requirement&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pdf-service&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2Gi"&lt;/span&gt;  &lt;span class="c1"&gt;# Typically sufficient vs 4GB+ for wkhtmltopdf&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1Gi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points about this code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;using&lt;/code&gt; statements ensure native memory is released after each document&lt;/li&gt;
&lt;li&gt;Single renderer instance can process multiple documents without memory accumulation&lt;/li&gt;
&lt;li&gt;Timeout configuration prevents indefinite hangs on complex documents&lt;/li&gt;
&lt;li&gt;Disposed resources are released back to the system, unlike wkhtmltopdf's retained allocations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Reference
&lt;/h3&gt;

&lt;p&gt;For more details on memory-efficient PDF generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderer.html" rel="noopener noreferrer"&gt;ChromePdfRenderer API Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/docker-linux/" rel="noopener noreferrer"&gt;Docker and Linux Deployment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/html-to-pdf/" rel="noopener noreferrer"&gt;Large Document Handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderOptions.html" rel="noopener noreferrer"&gt;Rendering Options&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Licensing
&lt;/h3&gt;

&lt;p&gt;IronPDF is commercial software with per-developer licensing. A free trial allows evaluation. wkhtmltopdf is open source under LGPLv3. The licensing cost should be evaluated against infrastructure costs (higher memory containers) and engineering time spent managing wkhtmltopdf memory issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Differences
&lt;/h3&gt;

&lt;p&gt;Migration from wkhtmltopdf involves adapting to the IronPDF API:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command-line flags to IronPDF properties:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;wkhtmltopdf Flag&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--disable-javascript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.EnableJavaScript = false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--no-images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.RenderImages = false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--lowquality&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.ImageQuality = 50&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--page-size A4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.PaperSize = PdfPaperSize.A4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--orientation Landscape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Memory-related differences:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;wkhtmltopdf&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;Memory after conversion&lt;/td&gt;
&lt;td&gt;Not fully released&lt;/td&gt;
&lt;td&gt;Released on dispose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sequential conversions&lt;/td&gt;
&lt;td&gt;Memory accumulates&lt;/td&gt;
&lt;td&gt;Memory stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended container memory&lt;/td&gt;
&lt;td&gt;4GB+&lt;/td&gt;
&lt;td&gt;2GB typical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process restart for memory&lt;/td&gt;
&lt;td&gt;Often required&lt;/td&gt;
&lt;td&gt;Not required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What You Gain
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Proper memory release after document generation&lt;/li&gt;
&lt;li&gt;Ability to process sequential documents without memory accumulation&lt;/li&gt;
&lt;li&gt;Lower container memory requirements&lt;/li&gt;
&lt;li&gt;No OOMKilled errors under normal operation&lt;/li&gt;
&lt;li&gt;Active maintenance and bug fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What to Consider
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Commercial licensing cost&lt;/li&gt;
&lt;li&gt;Different rendering engine may produce visual differences&lt;/li&gt;
&lt;li&gt;API migration effort from wrapper libraries&lt;/li&gt;
&lt;li&gt;Chromium runtime is larger than Qt WebKit binary&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;wkhtmltopdf's memory management behavior makes it unsuitable for generating large documents or processing multiple conversions in memory-constrained environments. The project's archived status means these issues will not be resolved. For applications experiencing OOMKilled errors, memory accumulation between conversions, or needing to process documents exceeding several hundred pages, migrating to a library with proper resource disposal addresses the root cause rather than working around it with increased memory limits.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by &lt;a href="https://ironsoftware.com/about-us/authors/jacobmellor/" rel="noopener noreferrer"&gt;Jacob Mellor&lt;/a&gt;, the original developer of IronPDF with 25+ years of commercial software experience.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf" rel="noopener noreferrer"&gt;wkhtmltopdf GitHub Repository - Archived&lt;/a&gt;{:rel="nofollow"} - Official repository, archived January 2023&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/tagged/wkhtmltopdf+memory" rel="noopener noreferrer"&gt;wkhtmltopdf Memory Issues on Stack Overflow&lt;/a&gt;{:rel="nofollow"} - Community questions about memory consumption&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3052" rel="noopener noreferrer"&gt;wkhtmltopdf Issue #3052: High Memory Usage&lt;/a&gt;{:rel="nofollow"} - GitHub issue documenting memory with large tables&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/" rel="noopener noreferrer"&gt;Kubernetes OOMKilled Documentation&lt;/a&gt;{:rel="nofollow"} - Understanding container memory limits&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wkhtmltopdf.org/status.html" rel="noopener noreferrer"&gt;wkhtmltopdf Known Issues&lt;/a&gt;{:rel="nofollow"} - Official status page listing limitations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;For IronPDF documentation and tutorials, visit &lt;a href="https://ironpdf.com" rel="noopener noreferrer"&gt;ironpdf.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Migrating from Ghostscript GPL to IronPDF: what actually changes in your code</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 10:33:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/migrating-from-ghostscript-gpl-to-ironpdf-what-actually-changes-in-your-code-4mek</link>
      <guid>https://dev.to/ironsoftware/migrating-from-ghostscript-gpl-to-ironpdf-what-actually-changes-in-your-code-4mek</guid>
      <description>&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ghostscript GPL maintenance status:&lt;/strong&gt; Actively maintained by Artifex Software. GPL version is open source; commercial license (Ghostscript AGPL → commercial) is available. Source: &lt;a href="https://www.ghostscript.com" rel="noopener noreferrer"&gt;https://www.ghostscript.com&lt;/a&gt; — verify current version (10.x as of 2025).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical approach + .NET versions:&lt;/strong&gt; Ghostscript is a native C library invoked via CLI subprocess, P/Invoke wrapper, or third-party .NET wrapper (e.g., &lt;code&gt;Ghostscript.NET&lt;/code&gt;). No first-party .NET SDK. Verify wrapper library versions and .NET compatibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install footprint:&lt;/strong&gt; Native Ghostscript binary installation required (&lt;code&gt;gs&lt;/code&gt; on Linux/macOS, &lt;code&gt;gswin64c.exe&lt;/code&gt; on Windows). .NET wrappers vary in package quality. Container images require explicit &lt;code&gt;apt-get install ghostscript&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IronPDF doc links:&lt;/strong&gt; &lt;a href="https://ironpdf.com/how-to/license-keys/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/license-keys/&lt;/a&gt; · &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/html-string-to-pdf/&lt;/a&gt; · &lt;a href="https://ironpdf.com/how-to/merge-or-split-pdfs/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/merge-or-split-pdfs/&lt;/a&gt; · &lt;a href="https://ironpdf.com/how-to/pdf-permissions-passwords/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/pdf-permissions-passwords/&lt;/a&gt; · &lt;a href="https://ironpdf.com/how-to/rendering-options/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/rendering-options/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Ghostscript is for:&lt;/strong&gt; PostScript/PDF interpretation, conversion, rendering, rasterization. Not an HTML-to-PDF tool. Primary use in .NET: PDF-to-image conversion, PDF manipulation via shell command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-guess rule applied:&lt;/strong&gt; All .NET wrapper API calls marked "verify in docs."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Checklist: What to Read Before You Start
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of replacement code, confirm these items.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Identify your Ghostscript invocation pattern&lt;/strong&gt; — subprocess call, &lt;code&gt;Ghostscript.NET&lt;/code&gt; wrapper, or direct P/Invoke?&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Audit GPL exposure&lt;/strong&gt; — Ghostscript GPL is AGPL-licensed; distributing software that links against it may require your source to be open or a commercial Ghostscript license. Verify with your legal team.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;List every &lt;code&gt;gs&lt;/code&gt; argument combination&lt;/strong&gt; in your codebase — each combination is a feature that needs a migration target.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Determine which operations you actually use&lt;/strong&gt; — Ghostscript does a lot; you probably use 10% of it.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Check IronPDF feature overlap&lt;/strong&gt; — IronPDF is not a PostScript interpreter; some Ghostscript use cases have no IronPDF equivalent.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Migrate (Without Drama)
&lt;/h2&gt;

&lt;p&gt;Teams using Ghostscript in .NET applications often encounter a familiar set of issues depending on their use case and deployment environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GPL/AGPL license obligations&lt;/strong&gt; — Ghostscript under GPL requires derivative works to also be GPL unless a commercial license is purchased; this drives migration for commercial software.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No first-party .NET library&lt;/strong&gt; — Ghostscript requires subprocess invocation or third-party wrappers; both add fragility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subprocess fragility&lt;/strong&gt; — path resolution, process management, and argument escaping are error-prone across environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native binary dependency&lt;/strong&gt; — Ghostscript must be installed on every machine, container, and CI runner; version mismatches cause subtle failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No HTML-to-PDF capability&lt;/strong&gt; — Ghostscript doesn't render HTML; if you've added a web-based report layer, you're already using a second tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt; — Ghostscript returns exit codes and stderr output; structured error handling requires parsing text output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread safety&lt;/strong&gt; — running multiple Ghostscript processes concurrently requires process management; a managed library is simpler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows vs. Linux binary differences&lt;/strong&gt; — &lt;code&gt;gswin64c.exe&lt;/code&gt; vs. &lt;code&gt;gs&lt;/code&gt; adds conditional logic to process invocations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade fragility&lt;/strong&gt; — Ghostscript version upgrades sometimes change argument behavior or output format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container overhead&lt;/strong&gt; — &lt;code&gt;apt-get install ghostscript&lt;/code&gt; in Dockerfiles pulls native dependencies; image sizes grow and dependency audits become more complex.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Ghostscript (via .NET wrapper/subprocess)&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;Focus&lt;/td&gt;
&lt;td&gt;PostScript/PDF interpretation, rasterization, conversion&lt;/td&gt;
&lt;td&gt;HTML-to-PDF rendering + document manipulation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;GPL (free with AGPL conditions) or commercial license&lt;/td&gt;
&lt;td&gt;Commercial license&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Style&lt;/td&gt;
&lt;td&gt;Subprocess args / P/Invoke / wrapper library&lt;/td&gt;
&lt;td&gt;Fluent .NET library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Curve&lt;/td&gt;
&lt;td&gt;High — PostScript model, arg-based configuration&lt;/td&gt;
&lt;td&gt;Medium — .NET-native API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML Rendering&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Primary capability (Chromium-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Indexing&lt;/td&gt;
&lt;td&gt;1-based in most CLI contexts&lt;/td&gt;
&lt;td&gt;0-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Safety&lt;/td&gt;
&lt;td&gt;Process-per-operation (subprocess) or verify wrapper&lt;/td&gt;
&lt;td&gt;Renderer-per-thread recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Namespace&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ghostscript.NET&lt;/code&gt; (third-party) or System.Diagnostics.Process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IronPdf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Migration Complexity Assessment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Effort by Feature
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTML to PDF&lt;/td&gt;
&lt;td&gt;Low (IronPDF)&lt;/td&gt;
&lt;td&gt;Ghostscript can't do this; IronPDF is new capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF to image (rasterize)&lt;/td&gt;
&lt;td&gt;Medium-High&lt;/td&gt;
&lt;td&gt;Ghostscript strength; verify IronPDF rasterization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF merge&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;IronPDF supports; Ghostscript merge via &lt;code&gt;-sDEVICE=pdfwrite&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF split&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Both support; different API shape&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF compression&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Ghostscript &lt;code&gt;-dPDFSETTINGS&lt;/code&gt; vs IronPDF compression options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostScript to PDF&lt;/td&gt;
&lt;td&gt;N/A for IronPDF&lt;/td&gt;
&lt;td&gt;Ghostscript-only; no IronPDF equivalent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Font embedding&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Handled differently in Chromium-based rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password protection&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;IronPDF supports natively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermarking&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;IronPDF supports natively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Subprocess batching → managed async pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Decision Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostScript processing required&lt;/td&gt;
&lt;td&gt;Retain Ghostscript; no IronPDF equivalent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML-to-PDF primary workload&lt;/td&gt;
&lt;td&gt;IronPDF migration is well-scoped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF-to-image rasterization required&lt;/td&gt;
&lt;td&gt;Verify IronPDF raster export; test quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPL license compliance concern&lt;/td&gt;
&lt;td&gt;Commercial Ghostscript license or migrate; verify with legal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Before You Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;.NET 6, 7, or 8 project&lt;/li&gt;
&lt;li&gt;Ghostscript invocation audit complete&lt;/li&gt;
&lt;li&gt;IronPDF license key (&lt;a href="https://ironpdf.com/how-to/license-keys/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/license-keys/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Find Ghostscript References in Your Codebase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find subprocess calls to gs/gswin64c&lt;/span&gt;
rg &lt;span class="s2"&gt;"gswin|ghostscript|&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;gs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs &lt;span class="nt"&gt;-i&lt;/span&gt;

&lt;span class="c"&gt;# Find Ghostscript.NET wrapper references&lt;/span&gt;
rg &lt;span class="s2"&gt;"Ghostscript&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;NET"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs

&lt;span class="c"&gt;# Find process start calls (broader scan)&lt;/span&gt;
rg &lt;span class="s2"&gt;"Process&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;Start"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; cs

&lt;span class="c"&gt;# Find NuGet package references&lt;/span&gt;
rg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"ghostscript"&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove Ghostscript Wrapper, Add IronPDF
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove Ghostscript.NET wrapper (if used — verify package name)&lt;/span&gt;
dotnet remove package Ghostscript.NET

&lt;span class="c"&gt;# Add IronPDF&lt;/span&gt;
dotnet add package IronPdf

dotnet restore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Ghostscript native binary installation (OS package) is separate from any NuGet wrapper. If you remove all Ghostscript usages, you can also remove the native binary from container images, Dockerfiles, and CI provisioning scripts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start Migration (3 Steps)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: License Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript — no .NET license initialization)&lt;/strong&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;// Ghostscript doesn't have a .NET license initialization step.&lt;/span&gt;
&lt;span class="c1"&gt;// Licensing is handled at the OS/binary level.&lt;/span&gt;
&lt;span class="c1"&gt;// GPL usage is governed by Ghostscript's AGPL license terms — verify legal compliance.&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Typical Ghostscript process setup:&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;psi&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;ProcessStartInfo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FileName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RuntimeInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsOSPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OSPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"gswin64c.exe"&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-dNOPAUSE -dBATCH ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// varies per operation&lt;/span&gt;
    &lt;span class="n"&gt;RedirectStandardOutput&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;RedirectStandardError&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;UseShellExecute&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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;// See: https://ironpdf.com/how-to/license-keys/&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;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_IRONPDF_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// One line. No binary path resolution. No platform branching.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Namespace Imports
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&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;System.Diagnostics&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.Runtime.InteropServices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// If using Ghostscript.NET wrapper:&lt;/span&gt;
&lt;span class="c1"&gt;// using Ghostscript.NET;&lt;/span&gt;
&lt;span class="c1"&gt;// using Ghostscript.NET.Processor;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Rendering&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.Editing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Basic Conversion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript PDF merge via subprocess)&lt;/strong&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;System.Diagnostics&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.Runtime.InteropServices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Ghostscript PDF merge — subprocess approach&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MergeWithGhostscript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;inputs&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;output&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;gs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RuntimeInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsOSPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OSPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"gswin64c.exe"&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Build input file args&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;inputArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&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="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;psi&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;ProcessStartInfo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FileName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;$"-sOutputFile=\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;output&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="n"&gt;inputArgs&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="n"&gt;RedirectStandardOutput&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;RedirectStandardError&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;UseShellExecute&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;var&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psi&lt;/span&gt;&lt;span class="p"&gt;)!;&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToEnd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Ghostscript exited &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&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="n"&gt;error&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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;// See: https://ironpdf.com/how-to/merge-or-split-pdfs/&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;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_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MergeWithIronPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;inputs&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;output&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;docs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&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;merged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;merged&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="n"&gt;output&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;d&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&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;h2&gt;
  
  
  API Mapping Tables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Namespace Mapping
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ghostscript Approach&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;System.Diagnostics.Process&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;IronPdf&lt;/code&gt; (no subprocess)&lt;/td&gt;
&lt;td&gt;Process management → managed API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ghostscript.NET.Processor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-sDEVICE=pdfwrite&lt;/code&gt; args&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfDocument&lt;/code&gt; operations&lt;/td&gt;
&lt;td&gt;PDF output device&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Core Class/Concept Mapping
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ghostscript Concept&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gs&lt;/code&gt; process&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ChromePdfRenderer&lt;/code&gt; / &lt;code&gt;PdfDocument&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Core processing unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-sOutputFile=&lt;/code&gt; arg&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.SaveAs(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exit code check&lt;/td&gt;
&lt;td&gt;&lt;code&gt;try/catch (PdfException)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stdin/file input&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.FromFile(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document loading&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Document Loading
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Ghostscript&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;Load PDF for processing&lt;/td&gt;
&lt;td&gt;Input file arg to process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.FromFile(path)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load from bytes&lt;/td&gt;
&lt;td&gt;Temp file + process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.FromBytes(bytes)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load from stream&lt;/td&gt;
&lt;td&gt;Temp file + process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.FromStream(stream)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML to PDF&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renderer.RenderHtmlAsPdf(html)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Page Operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Ghostscript CLI&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;Page count&lt;/td&gt;
&lt;td&gt;Parse output or use wrapper&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.PageCount&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract page range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-dFirstPage=N -dLastPage=M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.CopyPages(indices)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page size&lt;/td&gt;
&lt;td&gt;Parse pagesize output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.Pages[0].Width/Height&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rotate&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-c "&amp;lt;&amp;lt; /Rotate 90 &amp;gt;&amp;gt; setpagedevice"&lt;/code&gt; (complex)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;page.Rotation = 90&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Merge/Split Operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Ghostscript&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;Merge PDFs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-sDEVICE=pdfwrite&lt;/code&gt; + multiple input files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.Merge(docs)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split by page range&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-dFirstPage=N -dLastPage=M&lt;/code&gt; per range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pdf.CopyPages(indices)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Four Complete Before/After Migrations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. HTML to PDF
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript — not applicable)&lt;/strong&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;// Ghostscript does not render HTML to PDF.&lt;/span&gt;
&lt;span class="c1"&gt;// If you have an HTML-to-PDF requirement with Ghostscript in your stack,&lt;/span&gt;
&lt;span class="c1"&gt;// you're likely using a second tool (wkhtmltopdf, headless Chrome, etc.)&lt;/span&gt;
&lt;span class="c1"&gt;// or converting HTML→PostScript→PDF via an intermediate step.&lt;/span&gt;

&lt;span class="c1"&gt;// Ghostscript can convert PostScript to PDF:&lt;/span&gt;
&lt;span class="c1"&gt;// gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf in.ps&lt;/span&gt;
&lt;span class="c1"&gt;// But this is PostScript, not HTML.&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;"Ghostscript cannot render HTML; this is new capability in IronPDF"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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;// See: https://ironpdf.com/how-to/html-string-to-pdf/&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;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_KEY"&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;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="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="s"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Report&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Generated via IronPDF.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&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="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;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;$"Created &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;PageCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; page(s)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Merge PDFs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript subprocess)&lt;/strong&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;System.Diagnostics&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.Runtime.InteropServices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MergeExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;inputFiles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"file1.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file2.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file3.pdf"&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;outputFile&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"merged.pdf"&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;gs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RuntimeInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsOSPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OSPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"gswin64c.exe"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Escape and join input paths&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&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="n"&gt;inputFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;psi&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;ProcessStartInfo&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FileName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;$"-dCompatibilityLevel=1.7 "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;$"-sOutputFile=\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;outputFile&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="n"&gt;inputs&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="n"&gt;RedirectStandardOutput&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;RedirectStandardError&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;UseShellExecute&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;var&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psi&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;stderr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToEnd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Ghostscript merge failed (exit &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&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="n"&gt;stderr&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="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;$"Merged to &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;outputFile&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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;// See: https://ironpdf.com/how-to/merge-or-split-pdfs/&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;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_KEY"&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;docs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"file1.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file2.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file3.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;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&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;merged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;merged&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;"merged.pdf"&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;d&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&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;$"Merged: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; pages"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Watermark
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript — PostScript-based, complex)&lt;/strong&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;System.Diagnostics&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.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WatermarkExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ghostscript watermarking requires injecting PostScript content&lt;/span&gt;
        &lt;span class="c1"&gt;// via a stamp/overlay file — a non-trivial approach&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 1: Create a PostScript watermark file&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;watermarkPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTempFileName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".ps"&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="nf"&gt;WriteAllText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;watermarkPs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;@"
            /watermark {
                gsave
                0.3 setgray
                /Helvetica-Bold 40 selectfont
                200 400 moveto
                45 rotate
                (CONFIDENTIAL) show
                grestore
            } def
        "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 2: Apply via Ghostscript — exact args vary&lt;/span&gt;
        &lt;span class="c1"&gt;// This is a simplified illustration; real implementation is more involved&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InteropServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RuntimeInformation&lt;/span&gt;
                         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsOSPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InteropServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OSPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                         &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"gswin64c.exe"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gs"&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;psi&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;ProcessStartInfo&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FileName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;$"-sOutputFile=\"watermarked.pdf\" "&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="n"&gt;watermarkPs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\" \"input.pdf\""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;UseShellExecute&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;var&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psi&lt;/span&gt;&lt;span class="p"&gt;)!;&lt;/span&gt;
        &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&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="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;watermarkPs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Editing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// See: https://ironpdf.com/how-to/custom-watermark/&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;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_KEY"&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;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&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;stamp&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;TextStamper&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;"CONFIDENTIAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FontSize&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Opacity&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Rotation&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;VerticalAlignment&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VerticalAlignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HorizontalAlignment&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApplyStamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stamp&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;"watermarked.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Password Protection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (Ghostscript)&lt;/strong&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;System.Diagnostics&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.Runtime.InteropServices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityExample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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;gs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RuntimeInformation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsOSPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OSPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"gswin64c.exe"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Ghostscript password protection via -sOwnerPassword / -sUserPassword&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;psi&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;ProcessStartInfo&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FileName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;"-dEncryptionR=3 -dKeyLength=128 "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;"-sOwnerPassword=owner456 "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;"-sUserPassword=user123 "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                        &lt;span class="s"&gt;"-dPermissions=-3904 "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;      &lt;span class="c1"&gt;// permission bitmask — verify values&lt;/span&gt;
                        &lt;span class="s"&gt;"-sOutputFile=\"protected.pdf\" \"input.pdf\""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;RedirectStandardError&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;UseShellExecute&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;var&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psi&lt;/span&gt;&lt;span class="p"&gt;)!;&lt;/span&gt;
        &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Ghostscript encryption failed: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToEnd&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="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;"Protected PDF created"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (IronPDF)&lt;/strong&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IronPdf.Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// See: https://ironpdf.com/how-to/pdf-permissions-passwords/&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;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_KEY"&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;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserPassword&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user123"&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OwnerPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"owner456"&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllowUserPrinting&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPrintSecurity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullPrintRights&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;SecuritySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllowUserCopyPasteContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Critical Migration Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Page Indexing
&lt;/h3&gt;

&lt;p&gt;Ghostscript CLI uses &lt;strong&gt;1-based&lt;/strong&gt; page numbering (&lt;code&gt;-dFirstPage=1&lt;/code&gt;). IronPDF uses &lt;strong&gt;0-based&lt;/strong&gt; indexing. Adjust any page number calculations.&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;// Ghostscript: -dFirstPage=1 -dLastPage=3 extracts pages 1, 2, 3&lt;/span&gt;
&lt;span class="c1"&gt;// IronPDF equivalent:&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pageIndices&lt;/span&gt; &lt;span class="p"&gt;=&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// 0-based: first three pages&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;extracted&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;CopyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageIndices&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error Handling: Exit Codes vs Exceptions
&lt;/h3&gt;

&lt;p&gt;Ghostscript returns exit codes; IronPDF raises exceptions. Remove all &lt;code&gt;ExitCode != 0&lt;/code&gt; checks and replace with try/catch.&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;// Replace exit code checks:&lt;/span&gt;
&lt;span class="c1"&gt;// if (proc.ExitCode != 0) throw new Exception(...)&lt;/span&gt;

&lt;span class="c1"&gt;// With exception handling:&lt;/span&gt;
&lt;span class="k"&gt;try&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;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// operations&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&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;Exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Structured exception with message, no stderr parsing&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&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;h3&gt;
  
  
  No PostScript Support
&lt;/h3&gt;

&lt;p&gt;IronPDF cannot interpret PostScript (&lt;code&gt;.ps&lt;/code&gt;) files. If your pipeline processes &lt;code&gt;.ps&lt;/code&gt; input, you have two options: convert PostScript to PDF first (retain Ghostscript for that step only), or restructure the input pipeline to start from HTML/PDF.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Subprocess Overhead Elimination
&lt;/h3&gt;

&lt;p&gt;Ghostscript subprocess invocations carry process creation overhead per operation. IronPDF operates in-process. For high-throughput pipelines, this overhead reduction is measurable — but benchmark against your actual workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel Processing
&lt;/h3&gt;

&lt;p&gt;Ghostscript parallel processing requires managing multiple processes. IronPDF parallel processing uses &lt;code&gt;Task&lt;/code&gt;/&lt;code&gt;Parallel&lt;/code&gt; with one &lt;code&gt;ChromePdfRenderer&lt;/code&gt; per task:&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;// See: https://ironpdf.com/examples/parallel/&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;results&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;htmlDocuments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;html&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;r&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;r&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="k"&gt;return&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Container Image Size
&lt;/h3&gt;

&lt;p&gt;Removing Ghostscript from Docker images reduces the native dependency footprint. IronPDF adds Chromium binaries. Net effect on image size depends on your Ghostscript version and IronPDF version — measure before and after.&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="c"&gt;# Before: Ghostscript in Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# RUN apt-get update &amp;amp;&amp;amp; apt-get install -y ghostscript&lt;/span&gt;

&lt;span class="c"&gt;# After: remove above line; IronPDF handles Chromium deps automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Migration Checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pre-Migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Audit all Ghostscript subprocess invocations — list every argument pattern&lt;/li&gt;
&lt;li&gt;[ ] Identify PostScript processing (&lt;code&gt;.ps&lt;/code&gt; inputs) — these cannot be migrated to IronPDF&lt;/li&gt;
&lt;li&gt;[ ] Confirm GPL/AGPL license compliance intent with legal team&lt;/li&gt;
&lt;li&gt;[ ] Verify IronPDF covers all required operations (especially rasterization if needed)&lt;/li&gt;
&lt;li&gt;[ ] Obtain IronPDF license key; confirm activation&lt;/li&gt;
&lt;li&gt;[ ] Review &lt;a href="https://ironpdf.com/how-to/license-keys/" rel="noopener noreferrer"&gt;https://ironpdf.com/how-to/license-keys/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Create migration branch&lt;/li&gt;
&lt;li&gt;[ ] Document current Ghostscript version in all environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code Migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Remove &lt;code&gt;Ghostscript.NET&lt;/code&gt; NuGet package (if used)&lt;/li&gt;
&lt;li&gt;[ ] Add &lt;code&gt;IronPdf&lt;/code&gt; NuGet package&lt;/li&gt;
&lt;li&gt;[ ] Replace all &lt;code&gt;Process.Start("gs*")&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;[ ] Replace &lt;code&gt;Ghostscript.NET&lt;/code&gt; wrapper calls&lt;/li&gt;
&lt;li&gt;[ ] Implement &lt;code&gt;IronPdf.License.LicenseKey&lt;/code&gt; initialization at startup&lt;/li&gt;
&lt;li&gt;[ ] Replace merge subprocess calls with &lt;code&gt;PdfDocument.Merge(...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Replace security subprocess calls with &lt;code&gt;pdf.SecuritySettings&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Replace watermark subprocess/PS calls with &lt;code&gt;TextStamper&lt;/code&gt; / &lt;code&gt;ImageStamper&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Replace page index logic: 1-based → 0-based&lt;/li&gt;
&lt;li&gt;[ ] Replace exit code checks with try/catch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Visual comparison of merged PDFs (page order, content)&lt;/li&gt;
&lt;li&gt;[ ] Test password protection roundtrip&lt;/li&gt;
&lt;li&gt;[ ] Test watermark rendering on 3+ page types&lt;/li&gt;
&lt;li&gt;[ ] Verify page extraction by index (check off-by-one)&lt;/li&gt;
&lt;li&gt;[ ] Test concurrent operations under load&lt;/li&gt;
&lt;li&gt;[ ] Compare file sizes for compressed outputs&lt;/li&gt;
&lt;li&gt;[ ] Verify no &lt;code&gt;gs&lt;/code&gt; or &lt;code&gt;gswin64c&lt;/code&gt; references remain in code or scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Post-Migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Remove Ghostscript from all Dockerfiles and provisioning scripts&lt;/li&gt;
&lt;li&gt;[ ] Remove Ghostscript from CI/CD runner setup&lt;/li&gt;
&lt;li&gt;[ ] Remove Ghostscript license keys or binaries from deployment packages&lt;/li&gt;
&lt;li&gt;[ ] Update deployment docs: remove Ghostscript installation steps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where to Go From Here
&lt;/h2&gt;

&lt;p&gt;The Ghostscript migration is often the most impactful from an architecture perspective, because Ghostscript isn't a .NET library — it's a native binary invoked via subprocess. Replacing it with a managed NuGet package removes an entire category of deployment complexity: binary path resolution, platform-specific process names, stderr parsing for error detection, and native package management in containers.&lt;/p&gt;

&lt;p&gt;The capability gaps are real: PostScript interpretation and certain rasterization workflows don't have direct IronPDF equivalents. For those, retaining a minimal Ghostscript deployment or restructuring the pipeline is the honest answer.&lt;/p&gt;

&lt;p&gt;A practical question for the comments: &lt;strong&gt;when you've audited your Ghostscript subprocess calls, how many distinct argument patterns did you find, and which ones proved hardest to map to a managed library?&lt;/strong&gt; The range of Ghostscript usage in production .NET applications is wider than most expect.&lt;/p&gt;




</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Nutrient.io vs IronPDF: side by side for .NET teams in 2026</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 10:30:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/nutrientio-vs-ironpdf-side-by-side-for-net-teams-in-2026-463p</link>
      <guid>https://dev.to/ironsoftware/nutrientio-vs-ironpdf-side-by-side-for-net-teams-in-2026-463p</guid>
      <description>&lt;p&gt;A supply chain platform needed to generate shipping manifests from HTML templates and let warehouse managers annotate them on tablets. The architect chose Nutrient (then PSPDFKit) because the marketing materials showed both PDF generation and annotation in one package. Two sprints into development, the team realized they'd purchased a Ferrari when they needed a pickup truck.&lt;/p&gt;

&lt;p&gt;Nutrient excels at building interactive PDF viewers—think Dropbox's document preview pane or DocuSign's signing interface. Its annotation tools, form-filling UI, and real-time collaboration features power enterprise document workflows at IBM, Disney, and government agencies. What it doesn't do well: generate PDFs from HTML templates at scale without building custom UI.&lt;/p&gt;

&lt;p&gt;The supply chain team ended up with two problems: their HTML-to-PDF pipeline required converting content to Nutrient's document model (not a direct HTML string→PDF path), and they were paying for annotation and viewer capabilities their backend batch process would never use. Six months later, they'd factored out the HTML generation into a separate library and kept Nutrient only for the tablet annotation interface.&lt;/p&gt;

&lt;p&gt;This isn't a complaint about Nutrient's quality—it's extremely well-built for its intended use case. It's a scope mismatch story. Teams search for ".NET PDF library" expecting HTML-to-PDF tools and land on feature-complete SDKs designed for interactive document experiences. This guide helps you diagnose whether that scope mismatch applies to your requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding IronPDF
&lt;/h2&gt;

&lt;p&gt;IronPDF focuses on one workflow: converting HTML (strings, files, URLs) to PDF documents using an embedded Chromium engine. The API surfaces HTML-to-PDF rendering methods first, with PDF manipulation capabilities (merge, split, watermark, encrypt, extract text) integrated alongside. The design priority is backend document generation—invoice rendering, report exports, automated document assembly—not building PDF viewer interfaces.&lt;/p&gt;

&lt;p&gt;No UI components ship with IronPDF. No annotation toolbars. No form-filling widgets. If your requirement is "generate PDF from HTML template and serve it as download," IronPDF matches directly. If your requirement is "embed a PDF viewer where users can highlight text and add comments," you need a viewer SDK like Nutrient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Limitations of Nutrient.io for HTML-to-PDF Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Product Status
&lt;/h3&gt;

&lt;p&gt;Actively maintained (monthly releases) but positioned as PDF viewer/editor SDK, not HTML-to-PDF generator. The .NET offering includes HTML conversion via Processor microservice or client SDK, but this is not the primary documented workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing Capabilities (for HTML-to-PDF use cases)
&lt;/h3&gt;

&lt;p&gt;No direct &lt;code&gt;RenderHtmlStringAsPdf(string html)&lt;/code&gt; method—conversion typically requires Processor service deployment or document model construction. HTML-to-PDF conversion documented primarily for their cloud Processor API, not the .NET SDK's core workflow. Licensing tailored for interactive viewer applications (per-device/per-user), not batch generation servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Issues
&lt;/h3&gt;

&lt;p&gt;Large download footprint (100MB+ for full SDK) when you only need generation. OCR, barcode, imaging, TWAIN scanning bundled even if unused. Steep learning curve for teams expecting simple HTML→PDF API. HTML conversion capabilities exist but require understanding Nutrient's document abstraction layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Status
&lt;/h3&gt;

&lt;p&gt;Excellent support for viewer/editor scenarios. Support resources heavily skewed toward UI integration (React components, MAUI embedding, iOS/Android views) rather than backend generation workflows. Teams using Nutrient purely for HTML-to-PDF generation often report confusion navigating docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Problems
&lt;/h3&gt;

&lt;p&gt;Modular SDK architecture means picking correct packages for HTML generation isn't obvious. The Processor microservice offers HTML-to-PDF but requires separate Docker deployment. Client SDK can generate PDFs programmatically but API differs from HTML-first libraries. Licensing model doesn't fit "generate 100k invoice PDFs per month" usage pattern—designed for "deploy PDF viewer to 500 end-user devices."&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Comparison Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Nutrient.io .NET SDK&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;&lt;strong&gt;Current Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active (monthly releases, rebranded from PSPDFKit 2024)&lt;/td&gt;
&lt;td&gt;Active development, monthly releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTML Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Via Processor API or SDK conversion&lt;/td&gt;
&lt;td&gt;Direct Chromium-based HTML rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering Quality&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PDFium-based viewer, HTML conversion quality varies&lt;/td&gt;
&lt;td&gt;Pixel-perfect HTML5/CSS3/JS rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100MB+ modular SDK, many features&lt;/td&gt;
&lt;td&gt;Focused NuGet package, HTML-to-PDF core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Excellent for viewer/editor scenarios&lt;/td&gt;
&lt;td&gt;24/5 for generation scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Future Viability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Evolving toward comprehensive document platform&lt;/td&gt;
&lt;td&gt;Focused HTML-to-PDF product&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Troubleshooting: HTML String Fails to Render Correctly
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: Nutrient — HTML Content Doesn't Match Template
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You pass an HTML invoice template to Nutrient's conversion API expecting a simple render, but CSS Grid layouts collapse, images don't load, or the result doesn't match your browser preview.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Nutrient's HTML-to-PDF path is not its primary feature. If using the Processor API, HTML is rendered server-side in that service's environment—CSS/JS execution context differs from your development browser. If using the .NET SDK to construct PDFs, you're working with a document object model, not direct HTML rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify which Nutrient component you're using (Processor API vs .NET SDK programmatic generation)&lt;/li&gt;
&lt;li&gt;Check Processor API documentation for HTML conversion limitations&lt;/li&gt;
&lt;li&gt;If using SDK, confirm you're not expected to build the document structure from HTML yourself&lt;/li&gt;
&lt;li&gt;Test with minimal HTML first: &lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Test&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt; before adding CSS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Workaround (if confirmed limitation):&lt;/strong&gt;&lt;br&gt;
Convert HTML to Nutrient's document structure using their provided APIs. This often means parsing your HTML and reconstructing layout using Nutrient's drawing/text placement methods—essentially a rewrite of your template logic.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: IronPDF — Direct HTML Rendering
&lt;/h3&gt;

&lt;p&gt;For comprehensive rendering documentation, see &lt;a href="https://ironpdf.com/how-to/pixel-perfect-html-to-pdf/" rel="noopener noreferrer"&gt;Pixel-Perfect HTML to PDF&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;// Direct HTML to PDF conversion&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;// CSS Grid and Flexbox work exactly as in Chrome&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;htmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
    &amp;lt;style&amp;gt;
        .invoice {
            display: grid;
            grid-template-columns: 2fr 1fr;
            gap: 20px;
        }
    &amp;lt;/style&amp;gt;
    &amp;lt;div class='invoice'&amp;gt;
        &amp;lt;div&amp;gt;Left column content&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;Right column content&amp;lt;/div&amp;gt;
    &amp;lt;/div&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;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;htmlContent&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;&lt;strong&gt;Why this works:&lt;/strong&gt; IronPDF uses Chromium Blink engine—the same renderer as Chrome browser. If your HTML displays correctly in Chrome print preview, IronPDF will match it exactly. No separate conversion layer or document model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting: PDF Generation Requires Complex Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: Nutrient — Deployment Requires Multiple Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Documentation suggests deploying Nutrient Processor as a separate microservice for HTML-to-PDF conversion. Your infrastructure team questions why a PDF library needs Docker deployments and service-to-service calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Nutrient's architecture separates concerns: client SDKs handle PDF manipulation and viewing; Processor handles conversions (HTML→PDF, Office→PDF, OCR). This makes sense for interactive document applications but adds complexity for simple generation workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm whether your Nutrient plan includes Processor or only client SDK&lt;/li&gt;
&lt;li&gt;Check if .NET SDK offers HTML-to-PDF without Processor&lt;/li&gt;
&lt;li&gt;Estimate operational overhead: Docker images, service monitoring, network latency&lt;/li&gt;
&lt;li&gt;Calculate licensing costs for Processor instances vs. client SDK usage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Workaround (if Processor required):&lt;/strong&gt;&lt;br&gt;
Deploy Processor as containerized service. Add health checks, monitoring, and retry logic to your application's PDF generation code. Accept 200-500ms additional latency from service round-trips.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: IronPDF — Single Package, No External Services
&lt;/h3&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;// Installation: dotnet add package IronPdf&lt;/span&gt;
&lt;span class="c1"&gt;// No external services, no microservices, no containers required&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;// Entire conversion happens in-process&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="s"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Content&amp;lt;/body&amp;gt;&amp;lt;/html&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;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// No network calls, no service dependencies&lt;/span&gt;
&lt;span class="c1"&gt;// Latency: 50-200ms for typical documents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; IronPDF embeds Chromium engine in the library. All rendering happens in your application's process space. No separate deployments. No service mesh complexity. For complete API reference, see &lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderer.html" rel="noopener noreferrer"&gt;ChromePdfRenderer class documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting: High Memory Usage During Batch Processing
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Problem: Nutrient — SDK Memory Footprint Impacts Batch Jobs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You're batch-generating 500 PDFs from HTML templates. Memory usage climbs steadily, and the process eventually crashes with OutOfMemoryException after ~200 documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Nutrient SDK is comprehensive, loading OCR engines, imaging libraries, barcode readers, TWAIN interfaces even if your code only uses PDF generation. The full feature set has memory overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Profile memory usage: which Nutrient assemblies are loaded?&lt;/li&gt;
&lt;li&gt;Check if you're disposing of PDF objects after each generation&lt;/li&gt;
&lt;li&gt;Verify whether you need any Nutrient features beyond HTML→PDF&lt;/li&gt;
&lt;li&gt;Test if selective package installation reduces footprint (if supported)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt;&lt;br&gt;
Process batches in smaller chunks (50-100 documents), recycling the app domain between chunks. Add aggressive garbage collection. Consider horizontal scaling rather than single-process batching.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: IronPDF — Designed for Batch Processing
&lt;/h3&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;System.IO&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.Linq&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Parallel processing with memory-efficient design&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;htmlFiles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"templates/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.html"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;htmlFile&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&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;RenderHtmlFileAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;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="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ChangeExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// Explicit disposal releases Chromium resources immediately&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; IronPDF is designed for batch scenarios. Chromium engine resources are pooled and reused across conversions. Proper disposal patterns prevent memory accumulation. Thread-safe architecture supports parallel execution without overhead multiplication.&lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting: Licensing Costs Don't Match Usage Pattern
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Problem: Nutrient — Viewer-Focused Licensing for Backend Generation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You need to generate 50,000 PDFs per month from a batch job that runs on 3 backend servers. Nutrient's licensing is per-device or per-user, designed for interactive viewer deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Nutrient's business model targets applications where end users interact with PDFs through viewer interfaces. Pricing reflects "50 users with annotation capabilities" not "50,000 documents generated." For pure backend generation, the licensing math often doesn't favor this SDK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request Nutrient quote specifically for backend batch generation usage&lt;/li&gt;
&lt;li&gt;Compare cost per document vs. alternative libraries&lt;/li&gt;
&lt;li&gt;Evaluate whether you need Nutrient's viewer/editor features at all&lt;/li&gt;
&lt;li&gt;Check if Processor API licensing differs from SDK licensing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt;&lt;br&gt;
Negotiate custom licensing terms for backend generation. Some vendors offer "document transaction" licensing for these scenarios, but verify whether Nutrient supports this model.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: IronPDF — Generation-Focused Licensing
&lt;/h3&gt;

&lt;p&gt;IronPDF licensing is based on developers and deployment servers, not documents generated or end-user counts. A license covers unlimited PDF generation volume. Pricing structure: &lt;a href="https://ironpdf.com/licensing/" rel="noopener noreferrer"&gt;IronPDF Licensing Options&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;// Set license once at application startup&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;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;// Generate unlimited PDFs with this license&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;50000&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="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="s"&gt;$"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Invoice &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="s"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&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_&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="s"&gt;.pdf"&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;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// No per-document costs, no usage tracking, no throttling&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; IronPDF's licensing aligns with backend generation workloads. You pay for developers writing code and servers running it, not for volume generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting: CSS/JavaScript Compatibility Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: Nutrient — Modern CSS Features Don't Render
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Your HTML templates use CSS Grid for layout and custom CSS properties (variables). In browser they look perfect. Nutrient-generated PDFs have collapsed layouts or missing styles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause (requires verification):&lt;/strong&gt; Nutrient's HTML-to-PDF path may use a different rendering engine than PDFium (their viewer engine). HTML conversion quality depends on whether you're using Processor API (which engine?) or SDK methods (which rendering path?). Official docs needed to confirm current capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify current Nutrient docs for HTML-to-PDF rendering engine specifications&lt;/li&gt;
&lt;li&gt;Test with minimal HTML containing &lt;code&gt;display: grid;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check Processor API release notes for rendering engine updates&lt;/li&gt;
&lt;li&gt;Contact Nutrient support with specific CSS/JS compatibility questions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt;&lt;br&gt;
Rewrite templates using table-based layouts instead of CSS Grid/Flexbox. Avoid CSS variables. Test against whichever rendering engine Nutrient actually uses for your configuration.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: IronPDF — Full Modern Web Standards Support
&lt;/h3&gt;

&lt;p&gt;Complete rendering options reference: &lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderOptions.html" rel="noopener noreferrer"&gt;ChromePdfRenderOptions documentation&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="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;// Modern CSS just works - Chromium Blink engine&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;modernHtml&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
    &amp;lt;style&amp;gt;
        :root {
            --primary-color: #007bff;
            --spacing: 20px;
        }
        .container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: var(--spacing);
        }
        .card {
            background: var(--primary-color);
            padding: var(--spacing);
        }
    &amp;lt;/style&amp;gt;
    &amp;lt;div class='container'&amp;gt;
        &amp;lt;div class='card'&amp;gt;Card 1&amp;lt;/div&amp;gt;
        &amp;lt;div class='card'&amp;gt;Card 2&amp;lt;/div&amp;gt;
        &amp;lt;div class='card'&amp;gt;Card 3&amp;lt;/div&amp;gt;
    &amp;lt;/div&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;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;modernHtml&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;"modern_layout.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; Chromium Blink renderer in IronPDF supports CSS Grid, Flexbox, CSS Variables, Subgrid, Container Queries, and other modern features. If it works in Chrome 120+, it works in IronPDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Mapping Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nutrient.io Concept&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Processor API HTML conversion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChromePdfRenderer.RenderHtmlAsPdf()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document object model construction&lt;/td&gt;
&lt;td&gt;Direct HTML rendering (no intermediate model)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDFium viewer rendering&lt;/td&gt;
&lt;td&gt;Chromium Blink rendering (for PDF creation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Annotation UI components&lt;/td&gt;
&lt;td&gt;Not applicable (no UI in IronPDF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form filling widget&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.CreatePdfFormsFromHtml = true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital signature UI&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfDocument.Sign()&lt;/code&gt; method (programmatic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text extraction API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.ExtractText()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page manipulation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PdfDocument.CopyPage()&lt;/code&gt;, &lt;code&gt;PdfDocument.RemovePage()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCR capabilities&lt;/td&gt;
&lt;td&gt;Not included (IronOCR available separately)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Barcode reading&lt;/td&gt;
&lt;td&gt;Not included (IronBarcode available separately)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document collaboration&lt;/td&gt;
&lt;td&gt;Not applicable (no real-time features)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redaction tools&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.RedactTextByRegex()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermarking&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PdfDocument.ApplyWatermark()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Comprehensive Feature Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Nutrient.io&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;&lt;strong&gt;Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Last Update&lt;/td&gt;
&lt;td&gt;Monthly releases&lt;/td&gt;
&lt;td&gt;Monthly releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Maintenance Mode&lt;/td&gt;
&lt;td&gt;Active development&lt;/td&gt;
&lt;td&gt;Active development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Primary Focus&lt;/td&gt;
&lt;td&gt;Viewer/editor SDKs&lt;/td&gt;
&lt;td&gt;HTML-to-PDF generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Documentation for HTML→PDF&lt;/td&gt;
&lt;td&gt;Limited (viewer-focused docs)&lt;/td&gt;
&lt;td&gt;Comprehensive generation docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Technical Support&lt;/td&gt;
&lt;td&gt;Excellent for viewer scenarios&lt;/td&gt;
&lt;td&gt;24/5 for generation scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;SLA Available&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (Enterprise)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content Creation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTML to PDF&lt;/td&gt;
&lt;td&gt;Via Processor or SDK&lt;/td&gt;
&lt;td&gt;Direct Chromium rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;URL to PDF&lt;/td&gt;
&lt;td&gt;Via Processor&lt;/td&gt;
&lt;td&gt;Yes with async&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;CSS3 Support&lt;/td&gt;
&lt;td&gt;Varies by component&lt;/td&gt;
&lt;td&gt;Full modern CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;JavaScript Support&lt;/td&gt;
&lt;td&gt;Varies by component&lt;/td&gt;
&lt;td&gt;Full ES2020+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Custom Fonts&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;All formats including WOFF2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Headers/Footers&lt;/td&gt;
&lt;td&gt;Verify current API&lt;/td&gt;
&lt;td&gt;Full HTML/CSS headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Watermarks&lt;/td&gt;
&lt;td&gt;Programmatic API&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Page Numbering&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Flexible placeholders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PDF Operations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Merge PDFs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Split PDFs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Extract Text&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Extract Images&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Fill Forms&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Create Forms from HTML&lt;/td&gt;
&lt;td&gt;Yes (via UI or programmatic)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Digital Signatures&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Password Protection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interactive Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Annotation UI&lt;/td&gt;
&lt;td&gt;Core feature&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Form-Filling UI&lt;/td&gt;
&lt;td&gt;Core feature&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Collaboration&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Viewer Component&lt;/td&gt;
&lt;td&gt;Core feature&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Specialized Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OCR&lt;/td&gt;
&lt;td&gt;Yes (built-in)&lt;/td&gt;
&lt;td&gt;Separate library (IronOCR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Barcode Reading&lt;/td&gt;
&lt;td&gt;Yes (built-in)&lt;/td&gt;
&lt;td&gt;Separate library (IronBarcode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;TWAIN Scanning&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Image Processing&lt;/td&gt;
&lt;td&gt;Extensive&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Async/Await Support&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Full async support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Thread Safety&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fully thread-safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Parallel Processing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Designed for it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Docker Support&lt;/td&gt;
&lt;td&gt;Processor requires container&lt;/td&gt;
&lt;td&gt;Works in containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Cross-Platform&lt;/td&gt;
&lt;td&gt;Full cross-platform&lt;/td&gt;
&lt;td&gt;Full cross-platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;.NET Version Support&lt;/td&gt;
&lt;td&gt;.NET Framework, Core, 6+&lt;/td&gt;
&lt;td&gt;.NET Framework, 6-10+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Installation Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nutrient.io Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Installation varies by feature requirements&lt;/span&gt;
&lt;span class="c"&gt;# Check current documentation for correct packages&lt;/span&gt;
dotnet add package Nutrient.NET.SDK
&lt;span class="c"&gt;# Or for specific features:&lt;/span&gt;
&lt;span class="c"&gt;# dotnet add package GdPicture.NET.14&lt;/span&gt;
&lt;span class="c"&gt;# (Package names may vary - verify official docs)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;GdPicture14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or Nutrient namespace - verify docs&lt;/span&gt;

&lt;span class="c1"&gt;// SDK initialization and licensing&lt;/span&gt;
&lt;span class="c1"&gt;// (Specific API calls require current documentation)&lt;/span&gt;
&lt;span class="n"&gt;GdPictureDocumentConverter&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;GdPictureDocumentConverter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Further usage depends on whether using SDK directly&lt;/span&gt;
&lt;span class="c1"&gt;// or calling Processor API&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nutrient requires reviewing documentation to select appropriate packages for your use case. Full SDK is 100MB+. Processor API requires separate service deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  IronPDF Installation
&lt;/h3&gt;

&lt;p&gt;Step-by-step guide: &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;HTML String to PDF Tutorial&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package IronPdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="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;// Works immediately, no additional setup&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;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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromePdfRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderHtmlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Content&amp;lt;/body&amp;gt;&amp;lt;/html&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;"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;IronPDF installation is single NuGet package. No external services. No microservice deployments. No complexity beyond standard .NET library usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Nutrient, When to Use IronPDF
&lt;/h2&gt;

&lt;p&gt;Use Nutrient when you're building interactive PDF experiences: document viewers with annotation toolbars, form-filling interfaces, real-time collaboration features, or apps where users need to markup and sign PDFs within your UI. Nutrient excels at these scenarios and provides cross-platform viewer SDKs (Web, iOS, Android, desktop) with consistent APIs.&lt;/p&gt;

&lt;p&gt;Use IronPDF when your requirement is backend document generation: rendering invoices from HTML templates, exporting reports to PDF, batch-converting content for archival, or automated document assembly. IronPDF's direct HTML-to-PDF workflow and generation-focused licensing align with these use cases.&lt;/p&gt;

&lt;p&gt;The red flag scenario: you're using Nutrient purely for HTML-to-PDF generation without leveraging its viewer/editor capabilities. In these cases, you're paying for features you don't use and working with an SDK optimized for different problems.&lt;/p&gt;

&lt;p&gt;The green flag scenario: you need both generation and interactive viewing. Evaluate whether Nutrient's comprehensive approach makes sense or whether combining IronPDF (generation) + a simpler viewer library (PDF.js, browser native) costs less and integrates easier.&lt;/p&gt;

&lt;p&gt;For teams generating thousands of PDFs per day from HTML templates with no interactive viewing requirements, IronPDF's focused API and licensing structure typically prove more cost-effective. For teams building document-centric applications where users annotate, sign, and collaborate on PDFs, Nutrient's comprehensive SDK justifies its scope.&lt;/p&gt;

&lt;p&gt;Have you hit any of these Nutrient integration pain points? How did you handle the decision between comprehensive PDF SDKs versus focused HTML-to-PDF libraries?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/html-file-to-pdf/" rel="noopener noreferrer"&gt;IronPDF HTML File to PDF Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/ironpdf-2021-chrome-rendering-engine-eap/" rel="noopener noreferrer"&gt;Chrome Rendering Engine Technical Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>ComPDFKit vs IronPDF: a technical breakdown for 2026</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 10:29:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/compdfkit-vs-ironpdf-a-technical-breakdown-for-2026-2gn9</link>
      <guid>https://dev.to/ironsoftware/compdfkit-vs-ironpdf-a-technical-breakdown-for-2026-2gn9</guid>
      <description>&lt;p&gt;A software team chose ComPDFKit to build a document management system. The vendor pitch emphasized "comprehensive PDF features" and showed a polished demo with annotations, form filling, and digital signatures working smoothly. Three months into development, marketing requested invoice generation from HTML email templates. The team discovered ComPDFKit's core SDK doesn't include HTML-to-PDF conversion—that requires purchasing the separate HtmlConverter product with its own license. The "comprehensive" toolkit turned out to be a multi-product suite where each capability required evaluation of which component to license.&lt;/p&gt;

&lt;p&gt;ComPDFKit is a mature PDF SDK with strong viewer and editor features for building PDF applications. IronPDF is a unified library focused on HTML-to-PDF conversion and programmatic PDF manipulation. This comparison helps teams understand when UI-focused SDKs serve document viewer requirements versus when server-side HTML rendering drives the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding IronPDF
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ironpdf.com" rel="noopener noreferrer"&gt;IronPDF&lt;/a&gt; is a .NET library for HTML-to-PDF conversion and PDF manipulation. Install via &lt;code&gt;Install-Package IronPdf&lt;/code&gt;—no separate products for HTML rendering versus PDF editing. The &lt;code&gt;ChromePdfRenderer&lt;/code&gt; converts HTML using embedded Chromium, while &lt;code&gt;PdfDocument&lt;/code&gt; handles merging, splitting, forms, and security. One library, one license, no UI components.&lt;/p&gt;

&lt;p&gt;For server-side teams, this means generating PDFs from web content, API responses, or data templates without building viewer interfaces. HTML-to-PDF and PDF manipulation work together in the same namespace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Limitations of ComPDFKit
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Product Status
&lt;/h3&gt;

&lt;p&gt;ComPDFKit is actively maintained by PDF Technologies Inc. as a commercial multi-platform SDK. Multiple .NET packages: ComPDFKit v3.0.0 (June 2025), ComPDFKit.NetCore v2.0.0 (May 2024), ComPDFKit.NetFramework v2.2.0 (December 2024). The SDK targets PDF viewer/editor applications with desktop UI focus (WPF SDK for Windows, UWP SDK). Also supports iOS, Android, Web, React Native, Flutter.&lt;/p&gt;

&lt;p&gt;Product suite includes: PDF SDK (viewing/editing), Conversion SDK (Office to PDF), HtmlConverter SDK (HTML to PDF—separate product). Each product requires separate licensing and integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing Capabilities
&lt;/h3&gt;

&lt;p&gt;ComPDFKit's core PDF SDK is designed for &lt;strong&gt;building PDF viewer/editor UI applications&lt;/strong&gt;. It excels at displaying PDFs, adding annotations, form filling, and digital signatures in desktop apps. However, &lt;strong&gt;HTML-to-PDF conversion requires the separate HtmlConverter product&lt;/strong&gt; with additional licensing.&lt;/p&gt;

&lt;p&gt;The architectural focus on UI components means teams building server-side PDF generation (invoices from HTML, reports from templates) face integration complexity. HtmlConverter SDK is a separate NuGet package (ceTe.DynamicPDF.HtmlConverter.NET) that must be licensed and integrated alongside the core SDK.&lt;/p&gt;

&lt;p&gt;.NET 9/10 support: verify with vendor. NuGet packages list .NET 5-8 compatibility. Native library dependencies (ComPDFKitNative.so on Linux) require deployment alongside .NET assemblies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Multi-product complexity&lt;/strong&gt;: Teams needing both PDF manipulation and HTML rendering must license and integrate multiple ComPDFKit products. Core SDK + HtmlConverter = two separate purchases, two license keys, two sets of documentation. This mirrors patterns seen in other multi-product PDF suites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI-first architecture&lt;/strong&gt;: ComPDFKit SDK includes viewer controls (CPDFViewer) and editor components designed for WPF/UWP desktop applications. Server-side API applications don't need UI controls but must still integrate the SDK's UI-oriented architecture. Teams report (in community forums) navigating around viewer components when only programmatic PDF operations are needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License key management&lt;/strong&gt;: Every ComPDFKit product requires license key verification. Keys are project-specific. Production deployments require managing license files. Trial licenses available but must be obtained through vendor contact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Status
&lt;/h3&gt;

&lt;p&gt;Commercial support from PDF Technologies Inc. Documentation available per product (PDF SDK docs, Conversion SDK docs, HtmlConverter docs separately). Community license offered for startups/individual developers—verify eligibility and limitations with vendor.&lt;/p&gt;

&lt;p&gt;Support includes 1v1 technical assistance (per marketing materials). Response times and SLA terms vary by license tier. GitHub repository provides code samples but core SDK is closed-source commercial product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Problems
&lt;/h3&gt;

&lt;p&gt;The multi-product strategy creates decision overhead: which SDK for which task, how products integrate, separate licensing per component, version compatibility between products. For teams needing server-side HTML-to-PDF plus PDF manipulation, this means licensing and configuring at least two products (HtmlConverter + PDF SDK) versus a single integrated library.&lt;/p&gt;

&lt;p&gt;Desktop UI focus creates deployment surface area for server applications. Viewer controls, annotation toolbars, and UI resources ship with SDK even when building headless API services. Teams must configure which components to load and which to ignore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Comparison Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;ComPDFKit (SDK + HtmlConverter)&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;&lt;strong&gt;Current Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active (v3.0.0, Jun 2025)&lt;/td&gt;
&lt;td&gt;Active (regular updates)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTML Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HtmlConverter (separate product)&lt;/td&gt;
&lt;td&gt;Built-in Chromium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering Quality&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verify with vendor&lt;/td&gt;
&lt;td&gt;Chromium (pixel-perfect)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-package + license keys&lt;/td&gt;
&lt;td&gt;Single NuGet package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial (PDF Technologies)&lt;/td&gt;
&lt;td&gt;Commercial (Iron Software)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Future Viability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active (multi-platform focus)&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  PDF Annotation and Form Filling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ComPDFKit — Viewer-Oriented SDK
&lt;/h3&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;ComPDFKit.PDFDocument&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;ComPDFKit.PDFAnnotation&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&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.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComPdfKitAnnotator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;AddAnnotationsAndFillForm&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;pdfPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ComPDFKit SDK emphasizes viewer/editor UI workflows&lt;/span&gt;
        &lt;span class="c1"&gt;// API designed for desktop applications with UI controls&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 1: Initialize license (required for all operations)&lt;/span&gt;
        &lt;span class="c1"&gt;// License verification necessary before SDK usage&lt;/span&gt;
        &lt;span class="c1"&gt;// Verify exact API in ComPDFKit documentation&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 2: Load PDF document&lt;/span&gt;
        &lt;span class="c1"&gt;// CPDFDocument class represents PDF file&lt;/span&gt;
        &lt;span class="n"&gt;CPDFDocument&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;CPDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InitWithFilePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to load PDF document"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Step 3: Add text annotation (viewer-style API)&lt;/span&gt;
            &lt;span class="c1"&gt;// Annotations designed for interactive viewing&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;PageAtIndex&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;// Create annotation (API patterns from SDK samples)&lt;/span&gt;
            &lt;span class="c1"&gt;// Exact method names verify in ComPDFKit documentation&lt;/span&gt;
            &lt;span class="c1"&gt;// Typical pattern: create annotation, set properties, add to page&lt;/span&gt;

            &lt;span class="c1"&gt;// Step 4: Fill form fields&lt;/span&gt;
            &lt;span class="c1"&gt;// Form API requires iterating widgets&lt;/span&gt;
            &lt;span class="c1"&gt;// Field identification by name&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;fieldName&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Field filling pattern (verify API in docs)&lt;/span&gt;
                &lt;span class="c1"&gt;// ComPDFKit uses widget-based form model&lt;/span&gt;
                &lt;span class="c1"&gt;// Must locate field by name, get widget, set value&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Step 5: Save modified document&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTempFileName&lt;/span&gt;&lt;span class="p"&gt;()&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="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;saved&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;WriteToFilePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to save PDF"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Step 6: Read file bytes for return&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdfBytes&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="nf"&gt;ReadAllBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&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="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdfBytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Step 7: Release document resources&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;Release&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="c1"&gt;// Usage requires license key configuration&lt;/span&gt;
&lt;span class="c1"&gt;// Contact ComPDFKit for license: https://www.compdf.com/contact-us&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;annotator&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;ComPdfKitAnnotator&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;formData&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;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"CustomerName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Acme Corp"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"InvoiceNumber"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"INV-2026-001"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;annotator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAnnotationsAndFillForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"template.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Technical limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;License key required&lt;/strong&gt;: All SDK operations require valid license verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File-based operations&lt;/strong&gt;: Primary APIs work with file paths, not streams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI-oriented architecture&lt;/strong&gt;: Designed for viewer applications, not headless servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual resource management&lt;/strong&gt;: Explicit Release() calls required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-product for complete workflow&lt;/strong&gt;: HTML conversion requires separate HtmlConverter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop SDK deployment&lt;/strong&gt;: Viewer controls ship with SDK even for server use&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  IronPDF — Server-Optimized API
&lt;/h3&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;System.Collections.Generic&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.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IronPdfAnnotator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AddAnnotationsAndFillFormAsync&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;pdfPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;formData&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 existing 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;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Fill form fields&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;form&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;Form&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;field&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&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="n"&gt;field&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="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Add text annotation (programmatic, not UI-based)&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;AddTextAnnotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Review required"&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="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage - no license key in code&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;annotator&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;IronPdfAnnotator&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;formData&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;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"CustomerName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Acme Corp"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"InvoiceNumber"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"INV-2026-001"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;annotator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAnnotationsAndFillFormAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"template.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IronPDF uses server-focused APIs: stream-based operations, automatic disposal, no UI controls. See &lt;a href="https://ironpdf.com/examples/pdf-generation-settings/" rel="noopener noreferrer"&gt;PDF generation settings&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  HTML to PDF Conversion Requires Separate Product
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ComPDFKit — HtmlConverter SDK Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// NOTE: HTML-to-PDF requires ComPDFKit HtmlConverter&lt;/span&gt;
&lt;span class="c1"&gt;// This is a SEPARATE PRODUCT from the core ComPDFKit SDK&lt;/span&gt;
&lt;span class="c1"&gt;// Requires separate licensing and NuGet package&lt;/span&gt;
&lt;span class="c1"&gt;// Package: ceTe.DynamicPDF.HtmlConverter.NET (verify name with vendor)&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&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.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComPdfKitHtmlConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;ConvertHtmlToPdf&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// HtmlConverter is separate ComPDFKit product&lt;/span&gt;
        &lt;span class="c1"&gt;// Separate purchase, separate license key&lt;/span&gt;
        &lt;span class="c1"&gt;// Installation steps:&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Contact ComPDFKit for HtmlConverter license&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. Install HtmlConverter NuGet package&lt;/span&gt;
        &lt;span class="c1"&gt;// 3. Configure license key for HtmlConverter&lt;/span&gt;
        &lt;span class="c1"&gt;// 4. Integrate HtmlConverter API (different from PDF SDK API)&lt;/span&gt;

        &lt;span class="c1"&gt;// Exact API pattern: verify in HtmlConverter documentation&lt;/span&gt;
        &lt;span class="c1"&gt;// Typical workflow (conceptual):&lt;/span&gt;
        &lt;span class="c1"&gt;// - Initialize HtmlConverter with license&lt;/span&gt;
        &lt;span class="c1"&gt;// - Configure conversion options&lt;/span&gt;
        &lt;span class="c1"&gt;// - Execute conversion&lt;/span&gt;
        &lt;span class="c1"&gt;// - Retrieve PDF bytes&lt;/span&gt;

        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"ComPDFKit HtmlConverter is a separate product. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="s"&gt;"Requires separate license and installation. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="s"&gt;"Contact ComPDFKit for HtmlConverter licensing: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="s"&gt;"https://www.compdf.com/contact-us"&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="c1"&gt;// MULTI-PRODUCT ARCHITECTURE IMPLICATIONS:&lt;/span&gt;
&lt;span class="c1"&gt;// - Core ComPDFKit SDK: PDF viewing, editing, annotations → License A&lt;/span&gt;
&lt;span class="c1"&gt;// - ComPDFKit Conversion SDK: Office to PDF → License B&lt;/span&gt;
&lt;span class="c1"&gt;// - ComPDFKit HtmlConverter: HTML to PDF → License C&lt;/span&gt;
&lt;span class="c1"&gt;// Total licensing: negotiate with vendor for multi-product needs&lt;/span&gt;
&lt;span class="c1"&gt;// Integration: configure each product separately&lt;/span&gt;
&lt;span class="c1"&gt;// Deployment: multiple NuGet packages, multiple license keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multi-product challenges:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Separate HtmlConverter purchase&lt;/strong&gt;: Not included in base ComPDFKit SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Additional license negotiation&lt;/strong&gt;: HtmlConverter licensed separately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple API integrations&lt;/strong&gt;: Learn PDF SDK API + HtmlConverter API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version coordination&lt;/strong&gt;: Ensure SDK and HtmlConverter versions compatible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procurement overhead&lt;/strong&gt;: Multiple vendor discussions for complete workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment complexity&lt;/strong&gt;: Manage multiple products in production&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  IronPDF — Integrated HTML Rendering
&lt;/h3&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;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IronPdfHtmlConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ConvertHtmlToPdfAsync&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="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="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="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="k"&gt;return&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IronPdfHtmlConverter&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="s"&gt;@"
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
        body { font-family: Arial; margin: 20px; }
        .invoice { border: 2px solid #333; padding: 20px; }
        table { width: 100%; border-collapse: collapse; margin: 15px 0; }
        th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
        th { background: #f0f0f0; }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class='invoice'&amp;gt;
        &amp;lt;h1&amp;gt;Invoice #2026-001&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Customer:&amp;lt;/strong&amp;gt; Acme Corporation&amp;lt;/p&amp;gt;
        &amp;lt;table&amp;gt;
            &amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;
            &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Services&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$1,500.00&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
            &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;License&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$2,000.00&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
        &amp;lt;/table&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Total:&amp;lt;/strong&amp;gt; $3,500.00&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConvertHtmlToPdfAsync&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="c1"&gt;// HTML-to-PDF included in base IronPDF package&lt;/span&gt;
&lt;span class="c1"&gt;// No separate HtmlConverter product needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IronPDF includes Chromium-based HTML rendering in base package. No additional products to license. See &lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;HTML string to PDF guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  PDF Viewer Controls vs. Programmatic APIs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ComPDFKit — Desktop UI Components
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ComPDFKit SDK architecture focuses on UI viewer controls&lt;/span&gt;
&lt;span class="c1"&gt;// Designed for WPF, UWP desktop applications&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ComPDFKit.Controls.PDFControl&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.Windows.Controls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComPdfKitViewerIntegration&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IntegrateViewerInDesktopApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Grid&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ComPDFKit provides CPDFViewer control for WPF apps&lt;/span&gt;
        &lt;span class="c1"&gt;// Rich UI features: toolbar, annotation tools, form filling&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdfViewer&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;CPDFViewer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Configure viewer properties&lt;/span&gt;
        &lt;span class="c1"&gt;// UI customization: show/hide toolbars, annotation palette&lt;/span&gt;
        &lt;span class="c1"&gt;// Event handlers for user interactions&lt;/span&gt;

        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Children&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;pdfViewer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Load PDF into viewer&lt;/span&gt;
        &lt;span class="c1"&gt;// Viewer displays PDF with interactive controls&lt;/span&gt;

        &lt;span class="c1"&gt;// SERVER-SIDE CONSIDERATION:&lt;/span&gt;
        &lt;span class="c1"&gt;// API services don't need viewer controls&lt;/span&gt;
        &lt;span class="c1"&gt;// But SDK includes UI components in deployment&lt;/span&gt;
        &lt;span class="c1"&gt;// Teams must work around viewer-focused architecture&lt;/span&gt;
        &lt;span class="c1"&gt;// when building headless PDF processing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ARCHITECTURAL MISMATCH FOR SERVER SCENARIOS:&lt;/span&gt;
&lt;span class="c1"&gt;// - Server APIs generate PDFs programmatically (no UI)&lt;/span&gt;
&lt;span class="c1"&gt;// - ComPDFKit SDK ships with viewer controls and UI resources&lt;/span&gt;
&lt;span class="c1"&gt;// - WPF/UWP dependencies in SDK even for console apps&lt;/span&gt;
&lt;span class="c1"&gt;// - Need to isolate programmatic APIs from UI components&lt;/span&gt;
&lt;span class="c1"&gt;// - Deployment includes viewer assets unused in server context&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UI architecture considerations:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Viewer controls included&lt;/strong&gt;: SDK ships with CPDFViewer for desktop apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WPF/UWP dependencies&lt;/strong&gt;: Desktop UI framework requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server deployment overhead&lt;/strong&gt;: UI components deployed to servers unnecessarily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API isolation needed&lt;/strong&gt;: Separate viewer features from programmatic APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop licensing model&lt;/strong&gt;: SDK priced for desktop app integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation emphasis&lt;/strong&gt;: Heavy focus on viewer customization vs. server APIs&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  IronPDF — Headless Processing Architecture
&lt;/h3&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;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IronPdfProgrammaticApi&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GenerateInvoiceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InvoiceData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// No UI components - pure programmatic API&lt;/span&gt;
        &lt;span class="c1"&gt;// Designed for server-side PDF generation&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildInvoiceHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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="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="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="c1"&gt;// Add watermark programmatically&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;ApplyWatermark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h2 style='color:red'&amp;gt;DRAFT&amp;lt;/h2&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;BuildInvoiceHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InvoiceData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$@"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;head&amp;gt;&amp;lt;style&amp;gt;/* CSS */&amp;lt;/style&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    &amp;lt;h1&amp;gt;Invoice #&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvoiceNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    &amp;lt;p&amp;gt;Total: $&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/html&amp;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="c1"&gt;// SERVER-OPTIMIZED:&lt;/span&gt;
&lt;span class="c1"&gt;// - No viewer controls&lt;/span&gt;
&lt;span class="c1"&gt;// - No UI framework dependencies&lt;/span&gt;
&lt;span class="c1"&gt;// - Headless Chromium rendering&lt;/span&gt;
&lt;span class="c1"&gt;// - Stream-based operations&lt;/span&gt;
&lt;span class="c1"&gt;// - Async/await throughout&lt;/span&gt;
&lt;span class="c1"&gt;// - Standard .NET deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IronPDF has no UI components. Headless processing for APIs and background jobs. See &lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderer.html/" rel="noopener noreferrer"&gt;ChromePdfRenderer API&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Mapping Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ComPDFKit Concept&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPDFDocument class&lt;/td&gt;
&lt;td&gt;PdfDocument class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitWithFilePath()&lt;/td&gt;
&lt;td&gt;PdfDocument.FromFile()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PageAtIndex()&lt;/td&gt;
&lt;td&gt;pdf.Pages[index]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WriteToFilePath()&lt;/td&gt;
&lt;td&gt;pdf.SaveAs() or pdf.BinaryData&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release()&lt;/td&gt;
&lt;td&gt;Dispose() (automatic with using)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License key verification&lt;/td&gt;
&lt;td&gt;License in code or config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPDFViewer control&lt;/td&gt;
&lt;td&gt;N/A (no UI components)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HtmlConverter (separate)&lt;/td&gt;
&lt;td&gt;ChromePdfRenderer (built-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion SDK (separate)&lt;/td&gt;
&lt;td&gt;Built-in conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-product suite&lt;/td&gt;
&lt;td&gt;Single library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WPF/UWP focus&lt;/td&gt;
&lt;td&gt;Server-first architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop licensing model&lt;/td&gt;
&lt;td&gt;Server/desktop licensing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Comprehensive Feature Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Category&lt;/th&gt;
&lt;th&gt;ComPDFKit&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;&lt;strong&gt;Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;Active (v3.0.0, Jun 2025)&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Multi-product suite&lt;/td&gt;
&lt;td&gt;Unified library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Focus&lt;/td&gt;
&lt;td&gt;PDF viewer/editor UI&lt;/td&gt;
&lt;td&gt;Server-side PDF generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commercial Support&lt;/td&gt;
&lt;td&gt;PDF Technologies Inc.&lt;/td&gt;
&lt;td&gt;Iron Software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;Per-product docs&lt;/td&gt;
&lt;td&gt;Unified documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community License&lt;/td&gt;
&lt;td&gt;Startups/individuals&lt;/td&gt;
&lt;td&gt;Trial available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content Creation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML to PDF&lt;/td&gt;
&lt;td&gt;HtmlConverter (separate)&lt;/td&gt;
&lt;td&gt;Built-in Chromium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF Viewer UI&lt;/td&gt;
&lt;td&gt;CPDFViewer control (WPF/UWP)&lt;/td&gt;
&lt;td&gt;N/A (headless only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Annotations&lt;/td&gt;
&lt;td&gt;UI-focused annotation tools&lt;/td&gt;
&lt;td&gt;Programmatic annotations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Filling&lt;/td&gt;
&lt;td&gt;Interactive + programmatic&lt;/td&gt;
&lt;td&gt;Programmatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PDF Operations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create PDFs&lt;/td&gt;
&lt;td&gt;SDK + Viewer&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge PDFs&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split PDFs&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract Text&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital Signatures&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermarks&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET Framework&lt;/td&gt;
&lt;td&gt;.NET Framework&lt;/td&gt;
&lt;td&gt;4.6.2+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET Core / .NET 5+&lt;/td&gt;
&lt;td&gt;.NET Core 2.1+, .NET 5-8&lt;/td&gt;
&lt;td&gt;3.1+ / 5-10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installation&lt;/td&gt;
&lt;td&gt;Multiple NuGet packages&lt;/td&gt;
&lt;td&gt;Single NuGet package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License Keys&lt;/td&gt;
&lt;td&gt;Required per product&lt;/td&gt;
&lt;td&gt;License in code/config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Style&lt;/td&gt;
&lt;td&gt;UI-oriented + programmatic&lt;/td&gt;
&lt;td&gt;Server-optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Components&lt;/td&gt;
&lt;td&gt;Included (WPF/UWP)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Installation Comparison
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ComPDFKit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Core PDF SDK&lt;/span&gt;
Install-Package ComPDFKit.NetCore
&lt;span class="c"&gt;# or for .NET Framework:&lt;/span&gt;
Install-Package ComPDFKit.NetFramework

&lt;span class="c"&gt;# For HTML-to-PDF, also need HtmlConverter:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Contact ComPDFKit for HtmlConverter license&lt;/span&gt;
&lt;span class="c"&gt;# 2. Install HtmlConverter package (verify package name with vendor)&lt;/span&gt;
&lt;span class="c"&gt;# 3. Configure license keys for each product&lt;/span&gt;

&lt;span class="c"&gt;# For Office conversion:&lt;/span&gt;
&lt;span class="c"&gt;# Install-Package ComPDFKit.Conversion.SDK (verify with vendor)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;span class="c1"&gt;// License verification required&lt;/span&gt;
&lt;span class="c1"&gt;// Contact: https://www.compdf.com/contact-us&lt;/span&gt;

&lt;span class="n"&gt;CPDFDocument&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;CPDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InitWithFilePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... operations ...&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;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IronPDF:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Install-Package IronPdf
&lt;span class="c"&gt;# All features included: HTML-to-PDF, manipulation, forms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;






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

&lt;p&gt;ComPDFKit is a mature multi-platform SDK with 20+ years backing (ceTe Software/PDF Technologies Inc.) serving enterprises that build PDF viewer and editor applications. The SDK excels at desktop PDF applications with rich UI controls, annotation toolbars, and interactive form filling in WPF and UWP environments. For teams building PDF readers, document editors, or annotation tools into Windows desktop apps, ComPDFKit provides comprehensive viewer components.&lt;/p&gt;

&lt;p&gt;However, the multi-product architecture creates integration complexity for server-side scenarios: HTML-to-PDF requires the separate HtmlConverter product with additional licensing, Office conversion requires the Conversion SDK, and complete workflows span multiple products with separate license keys and documentation. The UI-focused architecture means server applications deploy viewer controls and desktop dependencies they don't use.&lt;/p&gt;

&lt;p&gt;The story that opened this comparison—discovering HTML conversion requires a separate product after licensing the core SDK—reflects a common pattern with multi-product PDF suites. Teams building server-side PDF generation face procurement overhead coordinating multiple licenses and integration complexity managing multiple SDKs.&lt;/p&gt;

&lt;p&gt;Migration from ComPDFKit to IronPDF makes sense when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary need is server-side HTML-to-PDF generation (not desktop PDF viewing)&lt;/li&gt;
&lt;li&gt;Team prefers unified library over multi-product suite&lt;/li&gt;
&lt;li&gt;HtmlConverter licensing adds complexity or cost to ComPDFKit deployment&lt;/li&gt;
&lt;li&gt;Desktop UI components unnecessary for headless API services&lt;/li&gt;
&lt;li&gt;Simplified procurement (single vendor, single license) reduces administrative overhead&lt;/li&gt;
&lt;li&gt;Chromium-based rendering preferred over vendor-specific HTML engine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ComPDFKit serves teams building PDF viewer applications into desktop software. IronPDF serves teams generating PDFs from HTML in server applications. Evaluate based on whether you're building a PDF editor UI or processing PDFs headlessly—the architectures optimize for different use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're using ComPDFKit, did you need the HtmlConverter or Conversion SDK?&lt;/strong&gt; How did multi-product integration work for your team?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/html-string-to-pdf/" rel="noopener noreferrer"&gt;IronPDF HTML Conversion Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ironpdf.com/how-to/pixel-perfect-html-to-pdf/" rel="noopener noreferrer"&gt;Pixel-Perfect PDF Rendering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to prevent Foreign Key crashes with Stripe Webhooks in .NET 10.</title>
      <dc:creator>Francisco Simões</dc:creator>
      <pubDate>Fri, 24 Apr 2026 09:53:52 +0000</pubDate>
      <link>https://dev.to/s1moes/how-to-prevent-foreign-key-crashes-with-stripe-webhooks-in-net-10-1abo</link>
      <guid>https://dev.to/s1moes/how-to-prevent-foreign-key-crashes-with-stripe-webhooks-in-net-10-1abo</guid>
      <description>&lt;p&gt;When integrating Stripe Subscriptions in a .NET application using Entity Framework Core, one of the most common issues developers face is dealing with the checkout.session.completed webhook.&lt;/p&gt;

&lt;p&gt;If you have a relational database, you probably have a SubscriptionPlans table and a UserSubscriptions table.&lt;/p&gt;

&lt;p&gt;A frequent mistake is forgetting to map the Stripe Price ID back to your internal Database Plan ID when the webhook fires. If you just try to insert the subscription, EF Core will default your Foreign Key to 0, resulting in a nasty Foreign Key constraint violation (Error 500).&lt;/p&gt;

&lt;p&gt;Here is the clean way to handle the webhook and map the Stripe Price ID to your internal database ID:&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;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleCheckoutSessionCompleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&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;userId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientReferenceId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// We passed this during checkout&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriptionService&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;SubscriptionService&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;stripeSubscription&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;subscriptionService&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Extract the Stripe Price ID (price_...) from the invoice items&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stripePriceId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripeSubscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="n"&gt;Price&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="c1"&gt;// 2. Look up the internal Plan ID in your database using the Stripe Price ID&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;plan&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionPlans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StripePriceId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;stripePriceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Fallback to ID 1 to prevent Foreign Key constraint errors if sync is missing&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;planId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;plan&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="c1"&gt;// Now you can safely insert or update your user's subscription record&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userSubscription&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserSubscriptions&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userSubscription&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;userSubscription&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;UserSubscription&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;StripeSubscriptionId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripeSubscription&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;StripeCustomerId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripeSubscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;SubscriptionPlanId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;planId&lt;/span&gt; &lt;span class="c1"&gt;// Safe to insert!&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserSubscriptions&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;userSubscription&lt;/span&gt;&lt;span class="p"&gt;);&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&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;Why this works:&lt;br&gt;
Failsafe mapping: By querying your database for the StripePriceId, you ensure your relational data stays intact.&lt;/p&gt;

&lt;p&gt;Fallback: If you forgot to add the plan to your local DB but created it on Stripe, it gracefully falls back to a default plan instead of crashing the API.&lt;/p&gt;

&lt;p&gt;Setting up Stripe Webhooks, Identity, and Clean Architecture from scratch usually takes weeks of trial and error.&lt;/p&gt;

&lt;p&gt;If you want to skip this headache, I actually packaged my entire setup into a Premium .NET SaaS Boilerplate. It has Authentication, Stripe Billing, the Customer Portal, and EF Core completely wired up and ready for production.&lt;/p&gt;

&lt;p&gt;You can grab the code and start building your product today here:&lt;br&gt;
👉 &lt;a href="https://s1moes.gumroad.com/l/pdjrrt" rel="noopener noreferrer"&gt;https://s1moes.gumroad.com/l/pdjrrt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>saas</category>
      <category>sql</category>
      <category>aspdotnet</category>
    </item>
    <item>
      <title>wkhtmltopdf Generates Blank or Empty Output: Causes and Solutions</title>
      <dc:creator>IronSoftware</dc:creator>
      <pubDate>Fri, 24 Apr 2026 08:26:00 +0000</pubDate>
      <link>https://dev.to/ironsoftware/wkhtmltopdf-generates-blank-or-empty-output-causes-and-solutions-23b0</link>
      <guid>https://dev.to/ironsoftware/wkhtmltopdf-generates-blank-or-empty-output-causes-and-solutions-23b0</guid>
      <description>&lt;p&gt;Developers using wkhtmltopdf frequently encounter a frustrating issue: the tool completes without error but produces a blank or nearly empty PDF file. The converted document may show only a white page, contain a 2KB file with no content, or display partial rendering with missing sections. This problem has generated hundreds of GitHub issues and Stack Overflow questions, yet many cases remain unresolved because wkhtmltopdf was archived in January 2023 and will never receive fixes for these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;wkhtmltopdf's blank output problem manifests in several ways. Some users report completely empty PDFs where the file size indicates zero or minimal content. Others see PDFs that render correctly in browsers but produce blank pages when converted. The most frustrating variation involves intermittent failures where the same HTML sometimes works and sometimes produces empty output.&lt;/p&gt;

&lt;p&gt;The root cause typically involves timing and rendering synchronization. wkhtmltopdf uses an outdated Qt WebKit rendering engine that predates modern JavaScript execution patterns. When HTML pages load content dynamically through JavaScript or AJAX, wkhtmltopdf often captures the page before that content has finished loading. The tool starts rendering immediately after the initial DOM is constructed, without waiting for asynchronous operations to complete.&lt;/p&gt;

&lt;p&gt;This timing issue is compounded by wkhtmltopdf's architecture. Unlike modern browser-based solutions that wait for page load events and network idle states, wkhtmltopdf relies on fixed delays and manual synchronization. The &lt;code&gt;--javascript-delay&lt;/code&gt; option exists specifically because the tool cannot intelligently detect when a page is "ready" for conversion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Messages and Symptoms
&lt;/h3&gt;

&lt;p&gt;Blank output rarely produces clear error messages. Common symptoms include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exit with code 0 (appears successful, but PDF is empty)
Warning: SSL error ignored
Loading page (1/2)
Printing pages (2/2)
Done (file is blank)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases where debugging is enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: SyntaxError: Parse error
(PDF generates but contains no visible content)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When JavaScript console output is enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wkhtmltopdf &lt;span class="nt"&gt;--debug-javascript&lt;/span&gt; http://example.com output.pdf
&lt;span class="go"&gt;Loading page (1/2)
JavaScript console message: Uncaught ReferenceError: ... is not defined
Printing pages (2/2)
Done
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Who Is Affected
&lt;/h2&gt;

&lt;p&gt;The blank PDF issue affects developers across multiple scenarios:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Web Applications&lt;/strong&gt;: Single-page applications built with React, Vue, Angular, or similar frameworks that render content client-side after the initial page load. These applications may show loading spinners or skeleton screens in the PDF instead of actual content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AJAX-Heavy Pages&lt;/strong&gt;: Applications that load data through API calls and inject it into the DOM after the initial render. Dashboard applications, reporting systems, and data-driven pages are particularly vulnerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages with External Resources&lt;/strong&gt;: HTML that references external stylesheets, fonts, or scripts from CDNs. SSL certificate issues or CORS restrictions can cause silent failures where resources fail to load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication-Protected Content&lt;/strong&gt;: Pages behind login walls that redirect unauthenticated requests. wkhtmltopdf may capture the login page instead of the intended content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modern CSS Layouts&lt;/strong&gt;: Pages using Flexbox, CSS Grid, or other modern layout techniques that the outdated WebKit engine cannot render correctly, resulting in elements positioned outside the visible area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence from the Developer Community
&lt;/h2&gt;

&lt;p&gt;The wkhtmltopdf GitHub repository accumulated thousands of issues related to blank output before being archived. These issues span multiple years and remain unresolved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2016-05-14&lt;/td&gt;
&lt;td&gt;"outputing empty pdf" reported&lt;/td&gt;
&lt;td&gt;GitHub Issue #2177&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017-01-28&lt;/td&gt;
&lt;td&gt;"empty pdf" issue filed with workaround requests&lt;/td&gt;
&lt;td&gt;GitHub Issue #2540&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017-07-15&lt;/td&gt;
&lt;td&gt;"Empty page at end of PDF" documented&lt;/td&gt;
&lt;td&gt;GitHub Issue #3088&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018-01-10&lt;/td&gt;
&lt;td&gt;"Empty pdf when saving a website to pdf" reported&lt;/td&gt;
&lt;td&gt;GitHub Issue #3086&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018-09-04&lt;/td&gt;
&lt;td&gt;"I got an empty PDF document but in browser is normal"&lt;/td&gt;
&lt;td&gt;GitHub Issue #3273&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020-06-09&lt;/td&gt;
&lt;td&gt;Version 0.12.6 released (final release)&lt;/td&gt;
&lt;td&gt;GitHub Releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-01-02&lt;/td&gt;
&lt;td&gt;Repository archived, no further fixes&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Community Reports
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"If I try to render an html page served by localhost everything is ok. But if I try to render the same pdf directly from file I obtain an empty pdf."&lt;br&gt;
— Developer, wkhtmltopdf-general mailing list&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The distinction between localhost and file-based rendering reveals wkhtmltopdf's inconsistent handling of content origins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"wkhtmltopdf 0.12.2.4 (with patched qt) on Windows XP can return an empty pdf when converting certain URLs, even though printing from Chrome works fine."&lt;br&gt;
— Issue #2540, GitHub&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This demonstrates the rendering gap between wkhtmltopdf's outdated engine and modern browsers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Having put in the debug, I can see there's a warning for a parse error in the page's javascript... Blank pages even with a long javascript-delay."&lt;br&gt;
— wkhtmltopdf-general mailing list&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JavaScript parsing errors can silently cause blank output without clear indication of the failure cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Cause Analysis
&lt;/h2&gt;

&lt;p&gt;Blank PDF output from wkhtmltopdf stems from several technical limitations:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. JavaScript Timing Issues
&lt;/h3&gt;

&lt;p&gt;wkhtmltopdf cannot intelligently wait for JavaScript execution to complete. The &lt;code&gt;--javascript-delay&lt;/code&gt; option provides a fixed wait time, but this approach has fundamental problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too short a delay captures the page before content loads&lt;/li&gt;
&lt;li&gt;Too long a delay wastes time on fast pages&lt;/li&gt;
&lt;li&gt;Network latency variations make any fixed value unreliable&lt;/li&gt;
&lt;li&gt;The delay does not respond to actual page readiness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;--window-status&lt;/code&gt; option offers an alternative, allowing JavaScript to signal readiness by setting &lt;code&gt;window.status&lt;/code&gt;. However, this requires modifying the HTML being converted, which is not always possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AJAX Content Not Loading
&lt;/h3&gt;

&lt;p&gt;Modern web applications load data asynchronously. A typical pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initial HTML loads with loading indicators&lt;/li&gt;
&lt;li&gt;JavaScript executes and makes API calls&lt;/li&gt;
&lt;li&gt;API responses arrive and content is injected into DOM&lt;/li&gt;
&lt;li&gt;Page becomes "ready" for viewing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;wkhtmltopdf may capture at step 1 or 2, producing a PDF of loading spinners rather than actual content.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. SSL Certificate Failures
&lt;/h3&gt;

&lt;p&gt;wkhtmltopdf may silently skip HTTPS resources when SSL certificate validation fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: SSL error ignored
Failed to load https://cdn.example.com/styles.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without stylesheets, the page may render with content positioned off-screen or invisible.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Authentication and Redirects
&lt;/h3&gt;

&lt;p&gt;When wkhtmltopdf requests a protected page, the server may redirect to a login page. The resulting PDF contains the login form instead of the expected content. Cookie-based session management is complex to configure correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. CORS Restrictions
&lt;/h3&gt;

&lt;p&gt;When HTML makes cross-origin requests, wkhtmltopdf may fail to load those resources due to CORS policies that do not account for the tool's unusual request patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Missing DOCTYPE Declaration
&lt;/h3&gt;

&lt;p&gt;The absence of &lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt; can cause wkhtmltopdf to enter quirks mode, resulting in unpredictable rendering including blank output.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Content Size Zero
&lt;/h3&gt;

&lt;p&gt;If the HTML body contains only absolutely positioned elements or content that flows outside the document bounds, the PDF may appear blank despite technically containing elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempted Workarounds
&lt;/h2&gt;

&lt;p&gt;The developer community has tried numerous approaches to fix blank output, with varying success.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workaround 1: Increasing JavaScript Delay
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Use &lt;code&gt;--javascript-delay&lt;/code&gt; to wait longer for content to load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wkhtmltopdf &lt;span class="nt"&gt;--javascript-delay&lt;/span&gt; 5000 http://example.com output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Arbitrary delay values are unreliable across different pages&lt;/li&gt;
&lt;li&gt;Network latency can exceed any fixed delay&lt;/li&gt;
&lt;li&gt;Slows conversion significantly for pages that load quickly&lt;/li&gt;
&lt;li&gt;Does not address fundamental timing architecture issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 2: Window Status Signaling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Use &lt;code&gt;--window-status&lt;/code&gt; combined with JavaScript that signals when the page is ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wkhtmltopdf &lt;span class="nt"&gt;--window-status&lt;/span&gt; ready http://example.com output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With corresponding JavaScript in the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Requires modifying the HTML being converted&lt;/li&gt;
&lt;li&gt;Not applicable when converting external websites&lt;/li&gt;
&lt;li&gt;Interaction with &lt;code&gt;--javascript-delay&lt;/code&gt; is confusing and poorly documented&lt;/li&gt;
&lt;li&gt;Some users report the combination hangs indefinitely&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 3: Using run-script for Timing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Inject JavaScript to delay rendering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wkhtmltopdf &lt;span class="nt"&gt;--run-script&lt;/span&gt; &lt;span class="s1"&gt;'window.setTimeout(function(){window.status="FOOBAR";},2000);'&lt;/span&gt; &lt;span class="nt"&gt;--window-status&lt;/span&gt; &lt;span class="s1"&gt;'FOOBAR'&lt;/span&gt; http://example.com output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Complex command-line syntax prone to quoting issues&lt;/li&gt;
&lt;li&gt;Fixed timeout still unreliable&lt;/li&gt;
&lt;li&gt;Does not wait for actual content loading&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 4: SSL Certificate Bypass
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Ignore SSL errors to load HTTPS resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wkhtmltopdf &lt;span class="nt"&gt;--ssl-protocol&lt;/span&gt; any &lt;span class="nt"&gt;--ignore-ssl-errors&lt;/span&gt; http://example.com output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Security risk in production environments&lt;/li&gt;
&lt;li&gt;May not resolve all SSL-related loading issues&lt;/li&gt;
&lt;li&gt;Does not address underlying resource loading problems&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workaround 5: Using Xvfb on Headless Servers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach&lt;/strong&gt;: Run wkhtmltopdf with a virtual framebuffer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xvfb-run wkhtmltopdf http://example.com output.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Adds complexity and dependencies&lt;/li&gt;
&lt;li&gt;Does not address JavaScript timing issues&lt;/li&gt;
&lt;li&gt;Can cause additional memory and process management problems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Different Approach: IronPDF
&lt;/h2&gt;

&lt;p&gt;For developers who need consistent PDF output from dynamic web content, IronPDF provides an architectural solution to the blank output problem. Rather than wrapping an external command-line tool with fixed timing mechanisms, IronPDF embeds a Chromium rendering engine that uses the same intelligent page loading detection as modern browsers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why IronPDF Avoids Blank Output Issues
&lt;/h3&gt;

&lt;p&gt;The fundamental difference is how IronPDF determines when a page is ready for conversion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network Idle Detection&lt;/strong&gt;: IronPDF waits until network activity settles, ensuring AJAX calls complete before rendering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modern Event Handling&lt;/strong&gt;: The Chromium engine understands &lt;code&gt;DOMContentLoaded&lt;/code&gt;, &lt;code&gt;load&lt;/code&gt;, and other standard events that signal page readiness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intelligent Resource Loading&lt;/strong&gt;: External stylesheets, fonts, and scripts load through Chromium's standard resource loading pipeline with proper error handling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JavaScript Compatibility&lt;/strong&gt;: ES6+, modern frameworks, and asynchronous patterns execute correctly because IronPDF uses a current rendering engine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configurable Wait Conditions&lt;/strong&gt;: Developers can specify wait-for-element conditions or custom JavaScript readiness checks when needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Example: Handling Dynamic Content
&lt;/h3&gt;

&lt;p&gt;The following example demonstrates converting content that loads dynamically via JavaScript:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamicContentConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;ConvertDynamicPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;// Enable JavaScript execution&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;EnableJavaScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Wait for network to become idle (handles AJAX automatically)&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;WaitFor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NetworkIdle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Alternative: wait for specific element to appear&lt;/span&gt;
        &lt;span class="c1"&gt;// renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");&lt;/span&gt;

        &lt;span class="c1"&gt;// Render the page - Chromium handles timing automatically&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="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderUrlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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="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;This code handles the exact scenarios that cause wkhtmltopdf to produce blank output. The Chromium engine waits for network activity to complete before capturing the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Example: Converting AJAX-Heavy Dashboard
&lt;/h3&gt;

&lt;p&gt;For applications that load data through multiple API calls:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConvertDashboard&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;dashboardUrl&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;outputPath&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;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;// JavaScript must be enabled for dynamic content&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;EnableJavaScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Wait for network idle with extended timeout for multiple API calls&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;WaitFor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NetworkIdle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Set viewport to capture full dashboard&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;ViewPortWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1920&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;ViewPortHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Render with appropriate paper size&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;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;PaperOrientation&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;PdfPaperOrientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Landscape&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="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="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderUrlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dashboardUrl&lt;/span&gt;&lt;span class="p"&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="n"&gt;outputPath&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;h3&gt;
  
  
  Code Example: Handling Authenticated Pages
&lt;/h3&gt;

&lt;p&gt;For pages requiring authentication, IronPDF supports cookie injection:&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;System.Net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthenticatedPageConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;ConvertProtectedPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;sessionCookie&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;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;EnableJavaScript&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;WaitFor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NetworkIdle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Set authentication cookie&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;CustomCookies&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;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"session_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sessionCookie&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Alternatively, use HTTP authentication&lt;/span&gt;
        &lt;span class="c1"&gt;// renderer.RenderingOptions.HttpLoginCredentials = new ChromeHttpLoginCredentials&lt;/span&gt;
        &lt;span class="c1"&gt;// {&lt;/span&gt;
        &lt;span class="c1"&gt;//     NetworkUsername = "username",&lt;/span&gt;
        &lt;span class="c1"&gt;//     NetworkPassword = "password"&lt;/span&gt;
        &lt;span class="c1"&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="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RenderUrlAsPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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="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;h3&gt;
  
  
  Code Example: Converting Local HTML with External Resources
&lt;/h3&gt;

&lt;p&gt;For HTML files that reference external stylesheets and scripts:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalHtmlConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConvertLocalHtml&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;htmlFilePath&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;outputPath&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;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;EnableJavaScript&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;WaitFor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NetworkIdle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Set base URL for resolving relative resource paths&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;baseUrl&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;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFilePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetLeftPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UriPartial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&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;baseUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&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="c1"&gt;// Read HTML content&lt;/span&gt;
        &lt;span class="kt"&gt;var&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;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IO&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="nf"&gt;ReadAllText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Render with base URL context&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="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;htmlContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&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="n"&gt;outputPath&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;h3&gt;
  
  
  API Reference
&lt;/h3&gt;

&lt;p&gt;For more details on handling dynamic content and avoiding blank output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ironpdf.com/object-reference/api/IronPdf.ChromePdfRenderer.html" rel="noopener noreferrer"&gt;ChromePdfRenderer&lt;/a&gt; - Main rendering class&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ironpdf.com/object-reference/api/IronPdf.Rendering.WaitFor.html" rel="noopener noreferrer"&gt;WaitFor Options&lt;/a&gt; - Network idle and element detection&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ironpdf.com/tutorials/html-to-pdf/" rel="noopener noreferrer"&gt;HTML to PDF Tutorial&lt;/a&gt; - Getting started with conversions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ironpdf.com/how-to/javascript/" rel="noopener noreferrer"&gt;Handling JavaScript Content&lt;/a&gt; - JavaScript execution configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Licensing
&lt;/h3&gt;

&lt;p&gt;IronPDF is commercial software with per-developer licensing. A free trial allows evaluation. Teams should consider the cost of continued debugging and workarounds with wkhtmltopdf against the cost of a maintained solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Differences
&lt;/h3&gt;

&lt;p&gt;Migration from wkhtmltopdf command-line or wrapper libraries requires code changes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;wkhtmltopdf&lt;/th&gt;
&lt;th&gt;IronPDF Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--javascript-delay 5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.WaitFor.NetworkIdle()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--window-status ready&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.WaitFor.JavaScript("window.ready")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--enable-javascript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.EnableJavaScript = true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--cookie name value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.CustomCookies["name"] = "value"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--username --password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RenderingOptions.HttpLoginCredentials&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External binary required&lt;/td&gt;
&lt;td&gt;Embedded engine (no external dependencies)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What You Gain
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intelligent page load detection eliminates blank output from timing issues&lt;/li&gt;
&lt;li&gt;Modern CSS and JavaScript support renders content correctly&lt;/li&gt;
&lt;li&gt;Active development means bugs get fixed&lt;/li&gt;
&lt;li&gt;Consistent behavior across platforms without native library management&lt;/li&gt;
&lt;li&gt;No need to install and configure external binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What to Consider
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Commercial licensing cost&lt;/li&gt;
&lt;li&gt;Different API patterns require code changes&lt;/li&gt;
&lt;li&gt;Larger deployment size due to embedded Chromium&lt;/li&gt;
&lt;li&gt;Different rendering engine may produce slightly different visual output&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;wkhtmltopdf's blank output problem stems from fundamental architectural limitations that cannot be fixed through command-line flags or workarounds. The tool's fixed-delay timing mechanism cannot reliably handle modern dynamic web content, and the project's archival in January 2023 means these issues will never be resolved. For teams experiencing blank PDF output, migration to a solution with intelligent page load detection is the path forward.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/jacob-mellor" rel="noopener noreferrer"&gt;Jacob Mellor&lt;/a&gt; has spent 25+ years building developer tools, including IronPDF as CTO at Iron Software.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2177" rel="noopener noreferrer"&gt;outputing empty pdf - Issue #2177&lt;/a&gt;{:rel="nofollow"} - Early report of empty PDF output&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2540" rel="noopener noreferrer"&gt;empty pdf - Issue #2540&lt;/a&gt;{:rel="nofollow"} - Discussion of blank output causes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3088" rel="noopener noreferrer"&gt;Empty page at end of PDF - Issue #3088&lt;/a&gt;{:rel="nofollow"} - Trailing blank page problems&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3086" rel="noopener noreferrer"&gt;Empty pdf when saving a website to pdf - Issue #3086&lt;/a&gt;{:rel="nofollow"} - Website conversion failures&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3273" rel="noopener noreferrer"&gt;I got an empty PDF document but in browser is normal - Issue #3273&lt;/a&gt;{:rel="nofollow"} - Browser vs wkhtmltopdf rendering differences&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/4190" rel="noopener noreferrer"&gt;Blank page at the end of pdf document - Issue #4190&lt;/a&gt;{:rel="nofollow"} - Landscape orientation blank page issues&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2616" rel="noopener noreferrer"&gt;javascript-delay versus window-status - Issue #2616&lt;/a&gt;{:rel="nofollow"} - Documentation of timing options interaction&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2490" rel="noopener noreferrer"&gt;Using --window-status and --javascript-delay never returns - Issue #2490&lt;/a&gt;{:rel="nofollow"} - Infinite wait issues&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2721" rel="noopener noreferrer"&gt;wkhtmltopdf does not work using --javascript-delay with plotly.js - Issue #2721&lt;/a&gt;{:rel="nofollow"} - JavaScript framework compatibility&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/KnpLabs/KnpSnappyBundle/issues/120" rel="noopener noreferrer"&gt;PDF Generated is login screen not the route passed - KnpSnappyBundle Issue #120&lt;/a&gt;{:rel="nofollow"} - Authentication redirect issues&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2346" rel="noopener noreferrer"&gt;Redirection Errors? No page or blank page - Issue #2346&lt;/a&gt;{:rel="nofollow"} - Redirect handling problems&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.doppio.sh/article/wkhtmltopdf-is-now-abandonware" rel="noopener noreferrer"&gt;wkhtmltopdf is now an abandonware - Doppio Documentation&lt;/a&gt;{:rel="nofollow"} - Project status assessment&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf" rel="noopener noreferrer"&gt;wkhtmltopdf GitHub Repository&lt;/a&gt;{:rel="nofollow"} - Archived January 2, 2023&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;For IronPDF documentation and tutorials, visit &lt;a href="https://ironpdf.com" rel="noopener noreferrer"&gt;ironpdf.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Post-Quantum Cryptography</title>
      <dc:creator>Christian Alt-Wibbing</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:58:50 +0000</pubDate>
      <link>https://dev.to/christian_altwibbing_985/post-quantum-cryptography-151b</link>
      <guid>https://dev.to/christian_altwibbing_985/post-quantum-cryptography-151b</guid>
      <description>&lt;h2&gt;
  
  
  Why the Future of Encryption Starts Today
&lt;/h2&gt;

&lt;p&gt;I have been developing software for over 20 years but what is happening right now is something interesting. Something that genuinely makes me sit up and pay attention.&lt;/p&gt;

&lt;p&gt;Quantum computers are no longer science fiction. And with &lt;strong&gt;.NET 10&lt;/strong&gt;, Microsoft has built the answer directly into the platform: &lt;strong&gt;Post-Quantum Cryptography (PQC)&lt;/strong&gt;. Quantum-safe encryption that holds up even when quantum computers become a reality.&lt;/p&gt;

&lt;p&gt;In this article, I will walk you through what I understand what that means step by step even if you are just starting out in software development. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Why Is This Even a Topic?
&lt;/h3&gt;

&lt;p&gt;Imagine you send a encrypted data today. Someone copy the data without decrypt it and waits. In ten years, they have a tool that can decrypt it. Then they read your data.&lt;/p&gt;

&lt;p&gt;That is exactly the scenario keeping security experts around the world up at night. It even has a name: “&lt;strong&gt;Harvest Now, Decrypt Later.&lt;/strong&gt;” Attackers collect encrypted data today and decrypt it later, once quantum computers are powerful enough.&lt;/p&gt;

&lt;p&gt;The data most at risk is anything that needs to &lt;strong&gt;stay secret for a long time&lt;/strong&gt;: financial information, health records, long-term contracts. If you work in finance, this is not an abstract problem — it is your reality.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What Exactly Are Qubits?
&lt;/h3&gt;

&lt;p&gt;Before we go further, let me explain what we are dealing with. I am not an expert in that topic but here is what I understood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classical Computers: Bits&lt;/strong&gt;&lt;br&gt;
A classical computer knows only two states: 0 or 1. That is called a bit. Everything a computer does — writing text, playing videos, encrypting data. Everything is built on billions of these tiny 0-or-1 decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quantum Computers: Qubits&lt;/strong&gt;&lt;br&gt;
A quantum computer works with qubits. Thanks to a quantum phenomenon called &lt;em&gt;superposition&lt;/em&gt;, qubits can be 0 and 1 at the same time as long as you do not measure them. That sounds strange (and I’m not scientific enough to explain that :) ), but the effect is enormous.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Analogy&lt;/strong&gt;: Think of a maze. A classical computer tries every path one by one. A quantum computer tries all paths simultaneously. For certain problems, that is exponentially faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The result: for certain mathematical problems, exactly those that uses RSA, ECDSA, a quantum computer, is exponentially faster than a classical one. Our current encryption would be broken.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The New Standards — International and Official
&lt;/h3&gt;

&lt;p&gt;The good news: The American NIST (National Institute of Standards and Technology) finalised the first three post-quantum standards in 2024. And this was not an American solo project. 82 algorithms from 25 countries were submitted and reviewed by cryptographers worldwide.&lt;/p&gt;

&lt;p&gt;In parallel, ISO/IEC and ETSI (the European Telecommunications Standards Institute) are working on their own frameworks. The IETF is currently integrating PQC into core protocols like &lt;strong&gt;TLS&lt;/strong&gt;. This is a global process .&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;FIPS Standard&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Replaces&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ML-KEM&lt;/td&gt;
&lt;td&gt;FIPS 203&lt;/td&gt;
&lt;td&gt;Key Exchange (Key Encapsulation)&lt;/td&gt;
&lt;td&gt;ECDH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-DSA&lt;/td&gt;
&lt;td&gt;FIPS 204&lt;/td&gt;
&lt;td&gt;Digital Signatures&lt;/td&gt;
&lt;td&gt;RSA / ECDSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLH-DSA&lt;/td&gt;
&lt;td&gt;FIPS 205&lt;/td&gt;
&lt;td&gt;Digital Signatures (hash-based)&lt;/td&gt;
&lt;td&gt;RSA / ECDSA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. What Does .NET 10 Do with This?
&lt;/h3&gt;

&lt;p&gt;.NET 10 released in November 2025 as a Long-Term Support (LTS) release — brings these algorithms directly into the familiar &lt;code&gt;System.Security.Cryptography&lt;/code&gt; namespace. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No external NuGet package, no third-party library. Just there, ready to use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a .NET developer, you now find these new classes:&lt;br&gt;
• MLKem&lt;br&gt;
• MLDsa&lt;br&gt;
• SlhDsa&lt;/p&gt;

&lt;p&gt;Even &lt;code&gt;X509Certificate2&lt;/code&gt;has been extended: certificates can now contain PQC keys. This is important for &lt;strong&gt;TLS&lt;/strong&gt; and &lt;strong&gt;PKI&lt;/strong&gt; infrastructures.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The Hybrid Approach — The Best of Both Worlds
&lt;/h3&gt;

&lt;p&gt;I would advise against switching everything over to PQC overnight. In my experience, new developments need time to prove themselves first. The PQC algorithms are mathematically well-founded and approved by NIST – but they have not yet proven themselves to the same level as RSA, which has been in use for decades.&lt;/p&gt;

&lt;p&gt;The solution: &lt;strong&gt;the hybrid approach&lt;/strong&gt;. You combine a classical algorithm (e.g. ECDH) plus ML-KEM simultaneously. If one algorithm is broken, the other still protects you. You are secured against both classical and quantum attacks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hybrid = classical algorithm + PQC in parallel. Secure against both attack types.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;.NET 10 also supports *&lt;em&gt;Composite ML-DSA *&lt;/em&gt; which is a combination of a classical and a post-quantum algorithm in a single signature. This is the recommended transition path.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. What Does This Mean for Your Project?
&lt;/h3&gt;

&lt;p&gt;Let me be honest: if you have services today that &lt;strong&gt;do not encrypt data at all&lt;/strong&gt;, that is more urgent than PQC. Post-quantum only protects encrypted communication. If data is flowing in plain text, the best quantum algorithm in the world will not help.&lt;br&gt;
My recommended order of priority:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Enable TLS for all services, Kafka, RabbitMQ&lt;/td&gt;
&lt;td&gt;Today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;New services directly with ML-KEM + AES (Hybrid)&lt;/td&gt;
&lt;td&gt;During .NET 10 migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Migrate internal services to PQC&lt;/td&gt;
&lt;td&gt;When both sides are under your control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Switch certificates to PQC&lt;/td&gt;
&lt;td&gt;When CA + infrastructure are ready&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  7. What About the IdentityServer?
&lt;/h3&gt;

&lt;p&gt;Many projects use &lt;strong&gt;Duende IdentityServer&lt;/strong&gt; for authentication — with JWT tokens signed using RS256 (RSA + SHA-256). That is exactly the algorithm a quantum computer could break.&lt;br&gt;
The goal: add &lt;strong&gt;ML-DSA as an additional signing algorithm&lt;/strong&gt; — alongside RS256, not as a replacement. Why alongside? Because older clients expect RS256 and do not yet understand ML-DSA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Important: &lt;br&gt;
Duende IdentityServer must explicitly support ML-DSA. This is a dependency outside your control. Keep an eye on the Duende roadmap!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  8. My Personal Conclusion
&lt;/h3&gt;

&lt;p&gt;After years of software development, I have learned: the biggest security problems do not arise from missing technology but they come from acting too late. PQC is not a “someday” topic. It is a “prepare now” topic.&lt;/p&gt;

&lt;p&gt;.NET 10 makes it easy for us: the algorithms are there, they are standardised, they are internationally recognised. The entry point is manageable. And anyone currently migrating to .NET 10 has the perfect opportunity to incorporate PQC at the same time, no extra overhead.&lt;/p&gt;

&lt;p&gt;Start with what matters most: encrypt first. Then think about PQC. And then you will be ready for what is coming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources &amp;amp; Further Reading
&lt;/h3&gt;

&lt;p&gt;• &lt;a href="https://devblogs.microsoft.com/dotnet/post-quantum-cryptography-in-dotnet/" rel="noopener noreferrer"&gt;Post-Quantum Cryptography in .NET&lt;/a&gt; — .NET Blog (Microsoft)&lt;br&gt;
• &lt;a href="https://anthonygiretti.com/2026/01/19/net-10-post-quantum-cryptography-comes-to-net/" rel="noopener noreferrer"&gt;.NET 10: Post-Quantum Cryptography Comes to .NET&lt;/a&gt; — Anthony Giretti&lt;br&gt;
• &lt;a href="https://www.nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards" rel="noopener noreferrer"&gt;NIST Releases First 3 Finalized Post-Quantum Encryption Standards&lt;/a&gt;&lt;br&gt;
• &lt;a href="https://www.strathweb.com/2025/02/ml-kem-and-ml-dsa-post-quantum-cryptography-in-net/" rel="noopener noreferrer"&gt;ML-KEM and ML-DSA Post-Quantum Cryptography in .NET&lt;/a&gt; — Strathweb&lt;br&gt;
• &lt;a href="https://www.nist.gov/pqc" rel="noopener noreferrer"&gt;Post-Quantum Cryptography&lt;/a&gt; — NIST (international adoption)&lt;br&gt;
• &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview#net-libraries" rel="noopener noreferrer"&gt;What’s new in .NET 10 &lt;/a&gt;— Microsoft Learn&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>encryption</category>
      <category>programming</category>
      <category>security</category>
    </item>
    <item>
      <title>What 2 Years as a Developer Actually Feels Like (Not the TikTok Version)</title>
      <dc:creator>Emma Hall</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:22:04 +0000</pubDate>
      <link>https://dev.to/emma_hall/what-2-years-as-a-developer-actually-feels-like-not-the-tiktok-version-1pij</link>
      <guid>https://dev.to/emma_hall/what-2-years-as-a-developer-actually-feels-like-not-the-tiktok-version-1pij</guid>
      <description>&lt;p&gt;I've been a software developer for 2 years now and going by the "day in the life" videos on TikTok, you'd think I should feel confident by now.&lt;/p&gt;

&lt;p&gt;I don't. Yet.&lt;/p&gt;

&lt;p&gt;Most days still involve second guessing my technical decisions, worrying I'm not progressing fast enough, and quietly hoping nobody notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reality
&lt;/h2&gt;

&lt;p&gt;It's easy to spiral into overthinking and let the imposter syndrome take over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this actually a good way to code this?&lt;/li&gt;
&lt;li&gt;Will the next person think &lt;em&gt;“what idiot wrote this”&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Am I too slow? Other developers seem to finish tasks faster than me.&lt;/li&gt;
&lt;li&gt;Should I know more than I do at this point?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That constant background noise can get loud. Sometimes it's hard to think about anything other than mistakes I've made.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Are people keeping track of how many bugs I've released to production? Do they think I'm bad at my job?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The paranoid part of my brain says yes.&lt;/p&gt;

&lt;p&gt;The pragmatic part knows that every developer creates bugs. Being “good” at this job isn’t about never getting things wrong. It’s about how you deal with it when you do.&lt;/p&gt;

&lt;p&gt;I'm still learning, and most of that learning has come from messing things up and figuring out how to fix it. Every bug or mistake is something to look back on and ask &lt;em&gt;what would I do differently next time?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What I'm starting to realise is confidence doesn’t just show up when you hit a certain number of years or tick off a specific goal. It’s something you have to build on purpose.&lt;/p&gt;

&lt;p&gt;I'm not going to wake up one random Tuesday and suddenly feel confident in my abilities. If I want that, I have to actually practice it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm trying
&lt;/h2&gt;

&lt;p&gt;I'm trying to be more vocal.&lt;/p&gt;

&lt;p&gt;In meetings, if I have an answer or an idea, I say it - even if I’m not 100% sure, even if my instinct is to stay quiet and wait for someone else to speak first. Half the time, someone ends up saying what I was thinking anyway, so I may as well be the one to say it.&lt;/p&gt;

&lt;p&gt;I've started tracking a “Win of the Week”.&lt;/p&gt;

&lt;p&gt;I've made a Trello board with a list dedicated to this. Some weeks the wins might be big, like doing well in a performance review. Other weeks might be something smaller, like speaking up in a meeting or suggesting an idea for a feature.&lt;/p&gt;

&lt;p&gt;Normally I’d brush those off, thinking they are insignificant and don't deserve recognition. But writing them down makes it harder to ignore or dismiss them. It’s proof that I am making progress, even if it doesn’t always feel like it. And even if they are small steps forward - a small step forward is still closer to the end destination than if I had stayed still the whole time.&lt;/p&gt;

&lt;p&gt;I'm also trying to be more intentional with my goals. Breaking things down into smaller milestones that feel more manageable, means I have a roadmap of my journey to the goal that I can tick off on my way. This helps me with staying motivated, and, honestly it feels good to see things moving forward instead of feeling stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I need to stop using other people as my benchmark. Someone being better than me at something doesn’t automatically mean I’m bad at it. It usually just means they’ve had more time or experience with it. Instead of letting that spiral, I’m trying to see it as a chance to learn from them.&lt;/li&gt;
&lt;li&gt;If nothing changes, nothing changes. I’ve spent a lot of time waiting and hoping to feel more confident, like it would just come with time. And while I am learning more as I go, I wouldn't say that I feel anymore confident than when I knew a fraction of what I do now. Something needs to change, which is what I'm doing now.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, no, my experience isn't like the polished, aesthetic “day in the life” version you see online. It’s messier, more internal, and a lot more about getting out of your own head than I expected.&lt;/p&gt;

&lt;p&gt;But I’m starting to realise that’s probably a normal part of it. And perhaps I'm not the only one having these kinds of realisations. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>discuss</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
