Skip to footer content
MIGRATION GUIDES

How to Migrate from jsreport to IronPDF in C#

Migrating from jsreport to IronPDF transforms your .NET PDF workflow from a Node.js-dependent system with external binary management and separate server processes to a pure .NET library that runs entirely in-process. This guide provides a comprehensive, step-by-step migration path that eliminates infrastructure complexity and JavaScript templating requirements for professional .NET developers.

Why Migrate from jsreport to IronPDF

The jsreport Challenges

jsreport introduces complexity that doesn't belong in a pure .NET environment:

  1. Node.js Dependency: Requires Node.js runtime and binaries, adding infrastructure complexity to what should be a straightforward .NET application.

  2. External Binary Management: Must download and manage platform-specific binaries for Windows, Linux, and OSX through separate NuGet packages (jsreport.Binary, jsreport.Binary.Linux, jsreport.Binary.OSX).

  3. Separate Server Process: Runs as either a utility or web server—additional process management is required with StartAsync() and KillAsync() lifecycle methods.

  4. JavaScript Templating: Forces learning Handlebars, JsRender, or other JavaScript templating systems instead of using native C# capabilities.

  5. Complex Request Structure: Requires verbose RenderRequest objects with nested Template configurations for even simple PDF generation.

  6. Licensing Limitations: Free tier limits template count; scaling requires commercial license.

  7. Stream-Based Output: Returns streams requiring manual file operations and memory stream management.

jsreport vs IronPDF Comparison

FeaturejsreportIronPDF
RuntimeNode.js + .NETPure .NET
Binary ManagementManual (jsreport.Binary packages)Automatic
Server ProcessRequired (utility or web server)In-process
TemplatingJavaScript (Handlebars, etc.)C# (Razor, string interpolation)
API StyleVerbose request objectsClean fluent methods
OutputStreamPdfDocument object
PDF ManipulationLimitedExtensive (merge, split, edit)
Async SupportAsync-onlyBoth sync and async

For teams planning .NET 10 and C# 14 adoption through 2025 and 2026, IronPDF provides a future-proof foundation as a native .NET library without external runtime dependencies.


Migration Complexity Assessment

Estimated Effort by Feature

FeatureMigration ComplexityNotes
HTML to PDFVery LowDirect method replacement
URL to PDFVery LowDirect method, no workarounds
Headers/FootersLowPlaceholder syntax change
Page SettingsLowProperty mapping
Server LifecycleLowDelete entirely
Binary ManagementLowDelete entirely

Paradigm Shift

The fundamental shift in this jsreport migration is from verbose request objects with server management to simple in-process method calls:

jsreport:  LocalReporting().UseBinary().AsUtility().Create() → RenderAsync(RenderRequest) → Stream → File
IronPDF:   ChromePdfRenderer → RenderHtmlAsPdf(html) → SaveAs()

Before You Start

Prerequisites

  1. .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5/6/7/8/9+
  2. NuGet Access: Ability to install NuGet packages
  3. IronPDF License: Obtain your license key from ironpdf.com

NuGet Package Changes

# Remove jsreport packages
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
dotnet remove package jsreport.Local
dotnet remove package jsreport.Types
dotnet remove package jsreport.Client

# Install IronPDF
dotnet add package IronPdf
# Remove jsreport packages
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
dotnet remove package jsreport.Local
dotnet remove package jsreport.Types
dotnet remove package jsreport.Client

# Install IronPDF
dotnet add package IronPdf
SHELL

License Configuration

// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

Identify jsreport Usage

# Find all jsreport references
grep -r "using jsreport\|LocalReporting\|RenderRequest\|RenderAsync" --include="*.cs" .
grep -r "JsReportBinary\|Template\|Recipe\|Engine\." --include="*.cs" .
# Find all jsreport references
grep -r "using jsreport\|LocalReporting\|RenderRequest\|RenderAsync" --include="*.cs" .
grep -r "JsReportBinary\|Template\|Recipe\|Engine\." --include="*.cs" .
SHELL

Complete API Reference

Class Mappings

jsreport ClassIronPDF EquivalentNotes
LocalReportingChromePdfRendererMain renderer
ReportingServiceChromePdfRendererSame class
RenderRequestMethod parametersNo wrapper needed
TemplateMethod parametersNo wrapper needed
ChromeRenderingOptionsChrome options
ReportPdfDocumentResult object
Engine(not needed)C# for templating

Method Mappings

jsreport MethodIronPDF EquivalentNotes
LocalReporting().UseBinary().AsUtility().Create()new ChromePdfRenderer()One-liner
rs.RenderAsync(request)renderer.RenderHtmlAsPdf(html)Direct call
rs.StartAsync()(not needed)In-process
rs.KillAsync()(not needed)Auto-cleanup
report.Content.CopyTo(stream)pdf.SaveAs(path) or pdf.BinaryDataDirect access

RenderRequest Property Mappings

jsreport Template PropertyIronPDF EquivalentNotes
Template.ContentFirst parameter to RenderHtmlAsPdf()Direct HTML string
Template.Recipe = Recipe.ChromePdf(not needed)Always ChromePdf
Template.Engine = Engine.None(not needed)Use C# templating
Chrome.HeaderTemplateRenderingOptions.TextHeader or HtmlHeaderHTML headers
Chrome.FooterTemplateRenderingOptions.TextFooter or HtmlFooterHTML footers
Chrome.DisplayHeaderFooter(automatic)Headers auto-enabled
Chrome.MarginTopRenderingOptions.MarginTopIn millimeters

Placeholder Mappings (Headers/Footers)

jsreport PlaceholderIronPDF PlaceholderNotes
<span class='pageNumber'></span>{page}Current page
<span class='totalPages'></span>{total-pages}Total pages
{#pageNum}{page}Current page
{#numPages}{total-pages}Total pages
{#timestamp}{date}Current date

Code Migration Examples

Example 1: Basic HTML to PDF

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Hello from jsreport</h1><p>This is a PDF document.</p>"
            }
        });

        using (var fileStream = File.Create("output.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF created successfully!");
    }
}
// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Hello from jsreport</h1><p>This is a PDF document.</p>"
            }
        });

        using (var fileStream = File.Create("output.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF created successfully!");
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello from IronPDF</h1><p>This is a PDF document.</p>");
        pdf.SaveAs("output.pdf");
        Console.WriteLine("PDF created successfully!");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello from IronPDF</h1><p>This is a PDF document.</p>");
        pdf.SaveAs("output.pdf");
        Console.WriteLine("PDF created successfully!");
    }
}
$vbLabelText   $csharpLabel

The jsreport approach requires three NuGet packages (jsreport.Binary, jsreport.Local, jsreport.Types), three namespace imports, async-only execution, a fluent builder chain (LocalReporting().UseBinary().AsUtility().Create()), a verbose RenderRequest with nested Template object specifying Recipe and Engine, and manual stream-to-file copying with a using block.

IronPDF reduces this to one NuGet package, one namespace, three lines of code, and synchronous execution. The ChromePdfRenderer.RenderHtmlAsPdf() method accepts HTML directly and returns a PdfDocument with a simple SaveAs() method. See the HTML to PDF documentation for additional rendering options.

Example 2: URL to PDF

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<html><body><script>window.location='https://example.com';</script></body></html>"
            }
        });

        using (var fileStream = File.Create("webpage.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("Webpage PDF created successfully!");
    }
}
// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<html><body><script>window.location='https://example.com';</script></body></html>"
            }
        });

        using (var fileStream = File.Create("webpage.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("Webpage PDF created successfully!");
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://example.com");
        pdf.SaveAs("webpage.pdf");
        Console.WriteLine("Webpage PDF created successfully!");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://example.com");
        pdf.SaveAs("webpage.pdf");
        Console.WriteLine("Webpage PDF created successfully!");
    }
}
$vbLabelText   $csharpLabel

This example highlights a significant limitation in jsreport: there's no direct URL-to-PDF method. The jsreport code must use a JavaScript redirect workaround (window.location='https://example.com') embedded in HTML content to capture a webpage. This indirect approach can fail with certain websites and adds unnecessary complexity.

IronPDF provides a dedicated RenderUrlAsPdf() method that directly renders any URL with full JavaScript execution and modern CSS support. No workarounds, no embedded scripts—just pass the URL. Learn more about URL to PDF conversion.

Example 3: PDF with Headers and Footers

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Document with Header and Footer</h1><p>Main content goes here.</p>",
                Chrome = new Chrome()
                {
                    DisplayHeaderFooter = true,
                    HeaderTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Custom Header</div>",
                    FooterTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
                }
            }
        });

        using (var fileStream = File.Create("document_with_headers.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}
// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Document with Header and Footer</h1><p>Main content goes here.</p>",
                Chrome = new Chrome()
                {
                    DisplayHeaderFooter = true,
                    HeaderTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Custom Header</div>",
                    FooterTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
                }
            }
        });

        using (var fileStream = File.Create("document_with_headers.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}
$vbLabelText   $csharpLabel

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
        {
            CenterText = "Custom Header",
            FontSize = 10
        };
        renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
        {
            CenterText = "Page {page} of {total-pages}",
            FontSize = 10
        };

        var pdf = renderer.RenderHtmlAsPdf("<h1>Document with Header and Footer</h1><p>Main content goes here.</p>");
        pdf.SaveAs("document_with_headers.pdf");
        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
        {
            CenterText = "Custom Header",
            FontSize = 10
        };
        renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
        {
            CenterText = "Page {page} of {total-pages}",
            FontSize = 10
        };

        var pdf = renderer.RenderHtmlAsPdf("<h1>Document with Header and Footer</h1><p>Main content goes here.</p>");
        pdf.SaveAs("document_with_headers.pdf");
        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}
$vbLabelText   $csharpLabel

The jsreport approach requires adding a Chrome object to the Template, setting DisplayHeaderFooter = true, and using HTML templates with special CSS class placeholders (<span class='pageNumber'></span>, <span class='totalPages'></span>). The header and footer templates must include full inline styling.

IronPDF provides a cleaner TextHeaderFooter configuration with dedicated properties for CenterText, LeftText, RightText, and FontSize. Page number placeholders use the simpler {page} and {total-pages} syntax. See the headers and footers documentation for HTML header options.


Critical Migration Notes

Eliminate Server Lifecycle Management

jsreport requires explicit server lifecycle management:

// jsreport (DELETE THIS):
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsUtility()
    .Create();

// Or for web server mode:
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsWebServer()
    .Create();

await rs.StartAsync();
// ... use rs ...
await rs.KillAsync();
// jsreport (DELETE THIS):
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsUtility()
    .Create();

// Or for web server mode:
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsWebServer()
    .Create();

await rs.StartAsync();
// ... use rs ...
await rs.KillAsync();
$vbLabelText   $csharpLabel

IronPDF runs entirely in-process—no server startup, no process management, no cleanup:

// IronPDF:
var renderer = new ChromePdfRenderer();
// Just use it—no lifecycle management needed
// IronPDF:
var renderer = new ChromePdfRenderer();
// Just use it—no lifecycle management needed
$vbLabelText   $csharpLabel

Remove Platform-Specific Binary Packages

jsreport requires separate NuGet packages for each target platform:

# DELETE these platform-specific packages:
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
# DELETE these platform-specific packages:
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
SHELL

IronPDF handles all platform requirements automatically through a single NuGet package.

Update Placeholder Syntax

jsreport uses CSS class-based or curly brace placeholders. IronPDF uses a different syntax:

// jsreport placeholders:
"<span class='pageNumber'></span>"  // or {#pageNum}
"<span class='totalPages'></span>"  // or {#numPages}

// IronPDF placeholders:
"{page}"
"{total-pages}"
"{date}"
"{html-title}"
// jsreport placeholders:
"<span class='pageNumber'></span>"  // or {#pageNum}
"<span class='totalPages'></span>"  // or {#numPages}

// IronPDF placeholders:
"{page}"
"{total-pages}"
"{date}"
"{html-title}"
$vbLabelText   $csharpLabel

Replace Handlebars with C# String Interpolation

jsreport often uses Handlebars templating with Engine.Handlebars:

// jsreport Handlebars (DELETE THIS):
Template = new Template
{
    Content = "<h1>Hello, {{name}}</h1>",
    Engine = Engine.Handlebars
},
Data = new { name = "World" }

// IronPDF with C# string interpolation:
string name = "World";
string html = $"<h1>Hello, {name}</h1>";
var pdf = renderer.RenderHtmlAsPdf(html);
// jsreport Handlebars (DELETE THIS):
Template = new Template
{
    Content = "<h1>Hello, {{name}}</h1>",
    Engine = Engine.Handlebars
},
Data = new { name = "World" }

// IronPDF with C# string interpolation:
string name = "World";
string html = $"<h1>Hello, {name}</h1>";
var pdf = renderer.RenderHtmlAsPdf(html);
$vbLabelText   $csharpLabel

Simplify Stream Handling

jsreport returns a stream that requires manual copying:

// jsreport stream handling (DELETE THIS):
using (var fileStream = File.Create("output.pdf"))
{
    report.Content.CopyTo(fileStream);
}

// Or for byte array:
using (var memoryStream = new MemoryStream())
{
    await report.Content.CopyToAsync(memoryStream);
    return memoryStream.ToArray();
}

// IronPDF direct access:
pdf.SaveAs("output.pdf");
// Or:
byte[] bytes = pdf.BinaryData;
// jsreport stream handling (DELETE THIS):
using (var fileStream = File.Create("output.pdf"))
{
    report.Content.CopyTo(fileStream);
}

// Or for byte array:
using (var memoryStream = new MemoryStream())
{
    await report.Content.CopyToAsync(memoryStream);
    return memoryStream.ToArray();
}

// IronPDF direct access:
pdf.SaveAs("output.pdf");
// Or:
byte[] bytes = pdf.BinaryData;
$vbLabelText   $csharpLabel

Troubleshooting

Issue 1: LocalReporting Not Found

Problem: Code references LocalReporting class which doesn't exist in IronPDF.

Solution: Replace with ChromePdfRenderer:

// jsreport
var rs = new LocalReporting().UseBinary().AsUtility().Create();

// IronPDF
var renderer = new ChromePdfRenderer();
// jsreport
var rs = new LocalReporting().UseBinary().AsUtility().Create();

// IronPDF
var renderer = new ChromePdfRenderer();
$vbLabelText   $csharpLabel

Issue 2: RenderRequest Not Found

Problem: Code uses RenderRequest and Template wrapper objects.

Solution: Pass HTML directly to render methods:

// jsreport
await rs.RenderAsync(new RenderRequest { Template = new Template { Content = html } });

// IronPDF
var pdf = renderer.RenderHtmlAsPdf(html);
// jsreport
await rs.RenderAsync(new RenderRequest { Template = new Template { Content = html } });

// IronPDF
var pdf = renderer.RenderHtmlAsPdf(html);
$vbLabelText   $csharpLabel

Issue 3: Page Numbers Not Appearing

Problem: Using jsreport placeholder syntax <span class='pageNumber'></span>.

Solution: Update to IronPDF placeholder syntax:

// jsreport syntax (won't work)
"Page <span class='pageNumber'></span> of <span class='totalPages'></span>"

// IronPDF syntax
"Page {page} of {total-pages}"
// jsreport syntax (won't work)
"Page <span class='pageNumber'></span> of <span class='totalPages'></span>"

// IronPDF syntax
"Page {page} of {total-pages}"
$vbLabelText   $csharpLabel

Issue 4: JsReportBinary Not Found

Problem: Code references JsReportBinary.GetBinary().

Solution: Delete entirely—IronPDF doesn't require external binaries:

// DELETE this jsreport pattern:
.UseBinary(JsReportBinary.GetBinary())

// IronPDF needs nothing—just create the renderer:
var renderer = new ChromePdfRenderer();
// DELETE this jsreport pattern:
.UseBinary(JsReportBinary.GetBinary())

// IronPDF needs nothing—just create the renderer:
var renderer = new ChromePdfRenderer();
$vbLabelText   $csharpLabel

Migration Checklist

Pre-Migration

  • Identify all jsreport using statements
  • List templates using Handlebars/JsRender (convert to C# string interpolation)
  • Document current Chrome options used (margins, paper size)
  • Check for web server vs utility mode (both become in-process)
  • Note platform-specific binary packages (delete all)
  • Obtain IronPDF license key

Package Changes

  • Remove jsreport.Binary package
  • Remove jsreport.Binary.Linux package
  • Remove jsreport.Binary.OSX package
  • Remove jsreport.Local package
  • Remove jsreport.Types package
  • Remove jsreport.Client package
  • Install IronPdf package

Code Changes

  • Add license key configuration at startup
  • Replace LocalReporting with ChromePdfRenderer
  • Remove RenderRequest wrapper
  • Remove Template wrapper
  • Update placeholder syntax (<span class='pageNumber'>{page})
  • Replace Handlebars with C# string interpolation
  • Remove StartAsync() / KillAsync() calls
  • Replace stream copying with BinaryData or SaveAs()

Testing

  • Test all PDF generation paths
  • Verify header/footer rendering
  • Check page numbering
  • Validate margin spacing
  • Test with complex CSS/JavaScript pages
  • Benchmark performance

Post-Migration

  • Delete jsreport binary files
  • Remove Node.js dependencies if no longer needed
  • Update deployment scripts
  • Update documentation

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...

Read More