IRONSOFTWAREHOME
VIDEOS

Cómo convertir Razor a PDF en Blazor Server usando C#

Curtis Chau
Curtis Chau
Updated: 19 de julio de 2026

Migrating from Kaizen.io HTML-to-PDF to IronPDF replaces a self-hosted Docker container exposed over HTTP with an in-process .NET library. Kaizen.io HTML-to-PDF ships only as the Docker image kaizenio.azurecr.io/html-to-pdf and exposes a single REST endpoint at POST /html-to-pdf on port 8080 - there is no official .NET SDK or NuGet package, so every C# call has to be hand-rolled HttpClient against the running container. This guide walks through swapping that pattern for IronPDF's ChromePdfRenderer.

Why Migrate from Kaizen.io to IronPDF

The Container-API Challenges

Kaizen.io HTML-to-PDF is a Docker container with a deliberately small v1.x API, and the C# integration story is whatever you build yourself on top of HttpClient. That shape has real limits in production:

  1. Container to Operate: You run, monitor, and update kaizenio.azurecr.io/html-to-pdf:latest somewhere reachable from your app - local Docker, sidecar, or a separate host.
  2. No .NET SDK: Every C# call is hand-rolled HttpClient POST + JSON. No IntelliSense, no compile-time checks on the request shape.
  3. Tiny v1.x API Surface: The documented JSON body accepts only an html field. URL-to-PDF, custom stylesheets, headers/footers, page size, orientation, and margins are listed as roadmap items rather than shipping features - anything beyond rendering a string of HTML must be expressed inside the HTML itself, typically via @page CSS and absolute-positioned divs.
  4. No Page-Number Support: The API has no {page} / {total} placeholders, so "Page X of Y" footers cannot be produced server-side.
  5. HTTP Round-Trip Per PDF: Even when the container runs on localhost, every PDF pays JSON serialization and a socket hop.
  6. Watermark on Free Tier: Without the KAIZEN_PDF_LICENSE environment variable set on the container, output is watermarked.

Kaizen.io vs IronPDF Comparison

FeatureKaizen.io HTML-to-PDFIronPDF
DistributionDocker image kaizenio.azurecr.io/html-to-pdfNuGet IronPdf
C# IntegrationHand-rolled HttpClient POST (no SDK)Strongly-typed ChromePdfRenderer API
Process ModelOut-of-process containerIn-process
Endpoint Surface (v1.x)POST /html-to-pdf with { "html": ... }Methods for HTML, file, URL
URL InputRoadmapRenderUrlAsPdf(url)
Headers / FootersNot in v1.x API; fake via fixed-position CSSTextHeader/Footer and HtmlHeader/Footer
Page NumbersUnsupported{page} and {total-pages} placeholders
Page Size / OrientationEmbed @page CSS in HTMLRenderingOptions.PaperSize etc.
LicensingOne-time license; watermarked free tierCommercial (annual or perpetual)

For teams standardizing on modern .NET, IronPDF removes the sidecar container and HTTP hop from the request path while exposing a strongly-typed configuration surface.


Migration Complexity Assessment

Estimated Effort by Feature

FeatureMigration Complexity
Basic HTML to PDFVery Low
HTML File to PDFVery Low
URL to PDFLow (Kaizen v1.x has no URL endpoint - workaround is replaced wholesale)
Headers/FootersMedium (fake CSS divs replaced with real header zones)
Page NumbersNew capability (not available in Kaizen v1.x)
Page SettingsLow (@page CSS moves to RenderingOptions)

Paradigm Shift

The fundamental shift is from out-of-process HTTP calls to a Docker container to in-process rendering:

Kaizen.io:  HttpClient.PostAsync("http://.../html-to-pdf", { html }) → byte[]
IronPDF:    ChromePdfRenderer → RenderHtmlAsPdf(html) → PdfDocument
Text

Before You Start

Prerequisites

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

Package Changes

There is no Kaizen NuGet package to remove - Kaizen ships only as a Docker image. Stop the container and add the IronPDF package:

# Stop and remove the running Kaizen container (if any)
docker stop kaizen-pdf
docker rm kaizen-pdf

# Install IronPDF
dotnet add package IronPdf
SHELL

License Configuration

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

Identify Kaizen.io Usage

Because Kaizen has no SDK, calls into it look like generic HttpClient POSTs. Search for the endpoint, image name, and license env var rather than a namespace:

grep -r "html-to-pdf\|KAIZEN_PDF_LICENSE\|kaizenio.azurecr.io\|localhost:8080" \
  --include="*.cs" --include="*.json" --include="*.yml" .
SHELL

Complete API Reference

Concept Mappings

There is no Kaizen .NET class to map - only the JSON request shape and the conventions you built around it.

Kaizen.io ConceptIronPDF Equivalent
HttpClient + POST /html-to-pdfChromePdfRenderer
JSON { "html": "..." } bodyRenderHtmlAsPdf(string html)
(No URL field in v1.x)RenderUrlAsPdf(string url)
(No file field in v1.x)RenderHtmlFileAsPdf(string path)
Inline @page CSS for sizeRenderingOptions.PaperSize
Inline @page CSS for orientationRenderingOptions.PaperOrientation
Inline @page { margin: ... }RenderingOptions.MarginTop/Bottom/Left/Right
Fixed-position <div class='header'> hackRenderingOptions.TextHeader / HtmlHeader
Fixed-position <div class='footer'> hackRenderingOptions.TextFooter / HtmlFooter
HttpClient.PostAsyncRenderHtmlAsPdfAsync
Container env var KAIZEN_PDF_LICENSEIronPdf.License.LicenseKey
HTTP byte[] responsepdf.BinaryData / pdf.SaveAs(path)

Placeholder Mappings

Kaizen v1.x has no server-side placeholders. If you previously hand-substituted strings into the HTML before POSTing, switch to IronPDF's render-time placeholders:

What you used to doIronPDF Placeholder
html.Replace("{page}", currentPage.ToString()){page}
html.Replace("{total}", total.ToString()){total-pages}
html.Replace("{date}", DateTime.Now.ToShortDateString()){date}
html.Replace("{title}", docTitle){html-title}

Code Migration Examples

Example 1: Basic HTML to PDF

Before (Kaizen.io - POST JSON to the container):

// Container must be running:
//   docker run -d -p 8080:8080 -e KAIZEN_PDF_LICENSE=... \
//     kaizenio.azurecr.io/html-to-pdf:latest
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using var http = new HttpClient();
        var payload = JsonSerializer.Serialize(new { html = "<html><body><h1>Hello World</h1></body></html>" });
        var content = new StringContent(payload, Encoding.UTF8, "application/json");
        var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);
        response.EnsureSuccessStatusCode();
        var pdfBytes = await response.Content.ReadAsByteArrayAsync();
        File.WriteAllBytes("output.pdf", pdfBytes);
    }
}

After (IronPDF):

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

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var html = "<html><body><h1>Hello World</h1></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("output.pdf");
    }
}

The Kaizen.io approach serializes JSON, posts it to the container's REST endpoint, checks the status code, reads the response as bytes, and writes those bytes to disk. IronPDF's ChromePdfRenderer runs in-process - RenderHtmlAsPdf() returns a PdfDocument with a SaveAs() method, so the round-trip and the manual byte handling go away. See the HTML to PDF documentation for additional rendering options.

Example 2: HTML File to PDF with Page Settings

Kaizen v1.x has no file endpoint and no page-layout fields - you read the file yourself and embed page size/orientation in @page CSS.

Before (Kaizen.io):

using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var body = File.ReadAllText("input.html");
        var html = "<style>@page { size: A4 portrait; }</style>" + body;

        using var http = new HttpClient();
        var payload = JsonSerializer.Serialize(new { html });
        var response = await http.PostAsync(
            "http://localhost:8080/html-to-pdf",
            new StringContent(payload, Encoding.UTF8, "application/json"));
        response.EnsureSuccessStatusCode();
        File.WriteAllBytes("document.pdf", await response.Content.ReadAsByteArrayAsync());
    }
}

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
        var pdf = renderer.RenderHtmlFileAsPdf("input.html");
        pdf.SaveAs("document.pdf");
    }
}

RenderHtmlFileAsPdf() reads the file directly, and page size and orientation move from inline @page CSS to RenderingOptions properties.

Example 3: URL to PDF with Headers and Footers

Kaizen v1.x has no ConvertUrl, no header/footer fields, and no page-number placeholders. The workaround is to fetch the page yourself and wrap it with @page CSS and fixed-position divs to fake header/footer - with no way to render page numbers.

Before (Kaizen.io):

using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using var http = new HttpClient();
        var page = await http.GetStringAsync("https://example.com");
        var html = $@"<!doctype html><html><head><style>
            @page {{ margin: 20mm; }}
            .h {{ position: fixed; top: -15mm; left: 0; right: 0; text-align: center; }}
            .f {{ position: fixed; bottom: -15mm; left: 0; right: 0; text-align: center; }}
        </style></head><body>
            <div class='h'>Company Header</div>
            <div class='f'>Footer (page numbers unsupported in Kaizen v1.x)</div>
            {page}
        </body></html>";

        var payload = JsonSerializer.Serialize(new { html });
        var response = await http.PostAsync(
            "http://localhost:8080/html-to-pdf",
            new StringContent(payload, Encoding.UTF8, "application/json"));
        response.EnsureSuccessStatusCode();
        File.WriteAllBytes("webpage.pdf", await response.Content.ReadAsByteArrayAsync());
    }
}

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.TextHeader.CenterText = "Company Header";
        renderer.RenderingOptions.TextFooter.CenterText = "Page {page} of {total-pages}";
        renderer.RenderingOptions.MarginTop = 20;
        renderer.RenderingOptions.MarginBottom = 20;
        var pdf = renderer.RenderUrlAsPdf("https://example.com");
        pdf.SaveAs("webpage.pdf");
    }
}

RenderUrlAsPdf() fetches and renders the URL directly - no client-side page fetch - and the fake fixed-position divs are replaced with TextHeader / TextFooter zones. Page numbers are produced via the {page} and {total-pages} placeholders, which Kaizen v1.x does not provide. Learn more about URL to PDF conversion and headers and footers.


Critical Migration Notes

License Lives in Code, Not the Container

Kaizen's license is configured via the KAIZEN_PDF_LICENSE environment variable on the Docker container. IronPDF's license is a static property set once at application startup:

// DELETE the Kaizen container env var:
//   docker run ... -e KAIZEN_PDF_LICENSE=... kaizenio.azurecr.io/html-to-pdf

// IronPDF: set once at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();

Placeholder Syntax

If you previously hand-substituted strings into the HTML before POSTing, switch to IronPDF's render-time placeholders:

  • {page} (unchanged)
  • {total}{total-pages}
  • {title}{html-title}
  • {date}{date} (unchanged)

Return Type Change

Kaizen returns raw bytes over HTTP. IronPDF returns a PdfDocument:

// Kaizen.io returns byte[] via HTTP
byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("output.pdf", pdfBytes);

// IronPDF returns PdfDocument
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");           // Direct save
byte[] bytes = pdf.BinaryData;      // Or get bytes if needed

Delete the HTTP Plumbing

Once the renderer is in-process, the supporting code disappears:

// DELETE all of:
// - HttpClient lifetime management
// - JSON serialization of { html }
// - response.EnsureSuccessStatusCode() / status-code branches
// - "container unreachable" retry/backoff
// - ReadAsByteArrayAsync()

// Replaced with a single in-process call:
var pdf = renderer.RenderHtmlAsPdf(html);

Troubleshooting

Issue 1: No HtmlToPdfConverter Class

Problem: There is no HtmlToPdfConverter class to swap for - Kaizen has no .NET SDK at all.

Solution: The migration is from HttpClient POSTs against the container to ChromePdfRenderer:

// Kaizen.io: hand-rolled HTTP
using var http = new HttpClient();
var response = await http.PostAsync("http://localhost:8080/html-to-pdf", content);

// IronPDF: in-process renderer
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);

Issue 2: Where Did the Options Object Go?

Problem: Kaizen v1.x has no ConversionOptions - page layout was inline @page CSS, headers/footers were fixed-position divs.

Solution: Move that configuration to RenderingOptions on the renderer:

// Before: <style>@page { size: A4 portrait; margin: 20mm; }</style>
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;

Issue 3: Page Numbers Don't Render

Problem: Kaizen v1.x has no {page} / {total} placeholder support, so any pre-migration "Page X of Y" footer was either missing or static text.

Solution: Use IronPDF's header/footer placeholders:

renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
    CenterText = "Page {page} of {total-pages}"
};

If you previously hand-substituted {total} in your own code before POSTing, remove that substitution - IronPDF resolves {total-pages} at render time.

Issue 4: Container Connection Errors

Problem: Code paths handling "container unreachable" / port-8080 errors.

Solution: IronPDF runs in-process - there is no endpoint to connect to. Delete the connection error handling.

Issue 5: First Render Slow

Problem: First PDF generation takes 1-3 seconds.

Solution: IronPDF initializes Chromium on first use. Warm up at application startup:

// In Program.cs or Startup.cs:
new ChromePdfRenderer().RenderHtmlAsPdf("<html></html>");

Migration Checklist

Pre-Migration

  • Locate every Kaizen call site (search for html-to-pdf, KAIZEN_PDF_LICENSE, kaizenio.azurecr.io, localhost:8080)
  • Document inline @page CSS used for size, orientation, margins
  • Document fake header/footer divs (fixed-position with negative offsets)
  • List any client-side placeholder substitution (html.Replace("{page}", ...) etc.)
  • Note container lifecycle: pull, run, restart, license env var
  • Obtain IronPDF license key

Container Teardown

  • Stop and remove the running Kaizen container (docker stop kaizen-pdf && docker rm kaizen-pdf)
  • Remove Kaizen pull/run steps from CI/CD
  • Remove KAIZEN_PDF_LICENSE from secrets/env
  • Install IronPdf NuGet package (dotnet add package IronPdf)

Code Changes

  • Add license key configuration at startup
  • Replace HttpClient POST with ChromePdfRenderer
  • Move embedded @page CSS into RenderingOptions properties
  • Replace JSON POST with RenderHtmlAsPdf() / RenderHtmlFileAsPdf() / RenderUrlAsPdf()
  • Replace fake header/footer divs with TextHeader/Footer or HtmlHeader/Footer
  • Update placeholder syntax ({total}{total-pages}, {title}{html-title})
  • Replace byte[] HTTP body with pdf.BinaryData
  • Use pdf.SaveAs() instead of File.WriteAllBytes()
  • Delete container-reachability error handling and retry/backoff

Testing

  • Test all PDF generation paths
  • Verify header/footer rendering and page numbers (new capability)
  • Validate margins, page size, and orientation
  • Test offline operation (no container required)

Post-Migration

  • Tear down Kaizen container infrastructure
  • Update environment variables / secrets
  • Remove container health checks from monitoring/alerting
  • Document the new typed-exception error patterns

Please note: Kaizen.io is a registered trademark of its respective owner. This site is not affiliated with, endorsed by, or sponsored by Kaizen.io. All product names, logos, and brands are property of their respective owners. Comparisons are for informational purposes only and reflect publicly available information at the time of writing.
Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien estructurados y visualmente atractivos.

...
Leer más

Artículos Relacionados

Key in blue circle

Obtenga su clave de prueba gratuita de 30 días al instante.

Your trial license will be sent to your email address

Sin limitaciones. 100 % desbloqueado. Sin tarjeta de crédito.

bullet_checkedNo se requiere tarjeta de crédito ni creación de cuentaSin limitaciones. 100 % desbloqueado. Sin tarjeta de crédito.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Reserve su Demostración en Vivo gratuita
Booking Badge

Confiado por millones de ingenieros en todo el mundo

Logos de clientes de Iron Software
Obtén tu Consulta Sin Compromiso
Completa el formulario a continuación o envía un correo a sales@ironsoftware.com
Tus detalles siempre serán mantenidos confidenciales.
Confiado por millones de ingenieros en todo el mundo
Logos de clientes de Iron Software
Obtenga su Clave de Prueba de 30 días gratis al instante.
No se requiere tarjeta de crédito ni creación de cuenta