Katalyst.Library.PDF 1.0.0

dotnet add package Katalyst.Library.PDF --version 1.0.0
                    
NuGet\Install-Package Katalyst.Library.PDF -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Katalyst.Library.PDF" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Katalyst.Library.PDF" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Katalyst.Library.PDF" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Katalyst.Library.PDF --version 1.0.0
                    
#r "nuget: Katalyst.Library.PDF, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Katalyst.Library.PDF@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Katalyst.Library.PDF&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Katalyst.Library.PDF&version=1.0.0
                    
Install as a Cake Tool

Katalyst.Library.PDF

Overview

Library.PDF renders Razor templates to HTML or PDF using RazorLight and Playwright (Chromium). Templates are loaded from the file system under the host content root; configure the subdirectory with RazorTemplateBasePath.

Capabilities

  • Razor to HTML or PDF — Compile and render .cshtml keys under the configured template root, then optionally print to PDF through the active provider.
  • Configuration-driven provider — Choose the PDF engine with Documents:PDF:Provider; built-in support is Playwright (PDFConstants.ProviderNames). Comparison is case-insensitive.
  • Shared infrastructure — RazorLight engine, Playwright browser host, and concurrency gate are registered as singletons; IPDFService is scoped so each request (or explicit scope) gets its own coordinator while heavy resources stay shared.

PDFConstants is a static class for shared literals (for example PDFConstants.ProviderNames.Playwright), aligned with PDFSettings.Provider and the checks in PDFServiceCollectionExtensions.

  • Concurrency cap — Playwright:MaxConcurrentTasks limits concurrent PDF pipelines (each uses a dedicated browser context) to control Chromium memory use (PlaywrightConcurrencyGate).

Configuration shape

{
  "Documents": {
    "PDF": {
      "Provider": "Playwright",
      "Playwright": {
        "MaxConcurrentTasks": 4
      },
      "RazorTemplateBasePath": "Templates/PDF",
      "OutputPath": "Outputs/PDF",
      "Settings": {
        "PrintBackground": true,
        "Format": "A4",
        "Margin": {
          "Top": "1cm",
          "Bottom": "1cm",
          "Left": "1cm",
          "Right": "1cm"
        }
      }
    }
  }
}

Everything sits under the Documents:PDF section (see PDFSettings, constant PDFSettings.SectionName). Provider must resolve to a registered IPDFGenerator: AddAppPDF (PDFServiceCollectionExtensions) wires Playwright only. If Provider is not Playwright, registration throws InvalidOperationException unless you change that extension or supply your own composition root.

Calling AddAppPDF binds PDFSettings with services.Configure<PDFSettings>(configuration.GetSection(PDFSettings.SectionName)), registers RazorLight against Path.Combine(hostEnvironment.ContentRootPath, RazorTemplateBasePath), registers Playwright infrastructure, and registers IPDFService as scoped (implementation PlaywrightPDFService when Provider is Playwright).

Path (under Documents:PDF) Description
Provider Built-in: Playwright. Other values require a different IPDFGenerator registration strategy than the stock extension.
Playwright:MaxConcurrentTasks Caps concurrent PDF pipelines (PlaywrightConcurrencyGate). Binds to PlaywrightSettings under Documents:PDF:Playwright.
RazorTemplateBasePath Folder relative to content root containing .cshtml templates (default Templates).
OutputPath Optional folder relative to host content root; used by GetOutputDirectory and Render*ToFileAsync on IPDFService.
Settings Passed to Playwright Page.PdfAsync: PrintBackground, Format, Margin (DocumentSettings, MarginSettings).

IPDFService methods

Method Description
RenderHtmlAsync Compiles and renders a template under RazorTemplateBasePath to HTML.
RenderTemplateToPdfAsync HTML from template, then PDF bytes (subject to the concurrency gate).
RenderHtmlToPdfAsync PDF bytes from existing HTML.
RenderTemplateToPdfToFileAsync Renders a template to PDF and writes to disk; returns the absolute file path.
RenderHtmlToPdfToFileAsync Writes HTML-based PDF to disk; returns the absolute file path.
GetOutputDirectory Resolves the configured output directory (with optional explicit content root overload).

Scoped lifetime — In ASP.NET Core the host opens a scope per HTTP request, so endpoints and controllers receive a fresh IPDFService per request. Outside the web pipeline (console hosts, background work), create a scope with IServiceScopeFactory.CreateScope (or CreateAsyncScope), resolve IPDFService from that scope’s IServiceProvider, then dispose the scope when finished (see the PDF.Utility sample host).

templateKey is the key RazorLight uses inside the template root—for example Invoice for root-level Invoice.cshtml, or a relative path for nested templates.


Playwright provider

When it runs

Selected when Documents:PDF:Provider is Playwright. Registers PlaywrightPDFGenerator as IPDFGenerator, PlaywrightPDFService as IPDFService, plus PlaywrightBrowserHost, PlaywrightConcurrencyGate, and a file-system IRazorLightEngine.

How it works

  • RazorTemplateBasePath is combined with IHostEnvironment.ContentRootPath to build the RazorLight project root; templates are compiled with an in-memory cache.
  • PDF generation uses a shared Chromium instance (PlaywrightBrowserHost); each operation uses a new browser context.
  • Documents:PDF:Settings map to Playwright print options (PrintBackground, Format, Margin strings such as 1cm).
  • MaxConcurrentTasks throttles how many PDF pipelines run at once across the process.

Configuration example

{
  "Documents": {
    "PDF": {
      "Provider": "Playwright",
      "Playwright": {
        "MaxConcurrentTasks": 4
      },
      "RazorTemplateBasePath": "Templates/PDF",
      "OutputPath": "Outputs/PDF",
      "Settings": {
        "PrintBackground": true,
        "Format": "A4",
        "Margin": {
          "Top": "1cm",
          "Bottom": "1cm",
          "Left": "1cm",
          "Right": "1cm"
        }
      }
    }
  }
}

Chromium installation

Playwright’s .NET package does not ship the Chromium binary inside Library.PDF; the host executable (web app, console, or test project) that references Microsoft.Playwright gets playwright.ps1 (and related CLI assets) in its build output after dotnet build. Install browsers once per machine or container image from that output folder.

Typical commands (adjust net10.0 / Debug to match your build):

  • PowerShell Core (Windows, macOS, Linux): from the host project directory, run
    pwsh bin/Debug/net10.0/playwright.ps1 install chromium
  • Windows PowerShell:
    powershell -NoProfile -ExecutionPolicy Bypass -File .\bin\Debug\net10.0\playwright.ps1 install chromium
  • Bash (Linux, macOS): bash bin/Debug/net10.0/playwright.sh install chromium

If you run dotnet run from the repository root, use the path to the built host output (for example YourApp/bin/Debug/net10.0/playwright.ps1). In CI or Docker, run the same install step after dotnet publish so the image contains Chromium under the Playwright browser cache. See Install browsers in the Playwright for .NET documentation.

Browser cache location (optional)

Playwright stores downloaded browsers in a cache directory. You can control it with the PLAYWRIGHT_BROWSERS_PATH environment variable (shared cache on build agents, read-only pre-seeded paths in containers, etc.). PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 is useful when the image already contains browsers. Details are in Playwright’s browsers guide.

Launch behavior in this library

PlaywrightBrowserHost always launches Playwright’s Chromium with Headless = true and does not read Documents:PDF for executable path, channel, or extra BrowserTypeLaunchOptions. To use a system Chrome build, custom flags, or a non-default install location, replace or extend PlaywrightBrowserHost (or register your own IPDFGenerator) in your composition root.

Requirements

  • .NET 10 SDK (matches this library’s net10.0 target).
  • Chromium installed for the host application’s Playwright runtime, as in Chromium installation above.

Examples

ASP.NET Core (minimal hosting)

using Library.PDF;
using Library.PDF.Contracts;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAppPDF(builder.Configuration, builder.Environment);

var app = builder.Build();

app.MapGet("/reports/sample.pdf", async (IPDFService pdf, CancellationToken ct) =>
{
    var model = new Dictionary<string, object>
    {
        ["Title"] = "Monthly report",
        ["Items"] = new List<object> { "A", "B" },
    };

    var bytes = await pdf.RenderTemplateToPdfAsync("Sample", model, ct);
    return Results.File(bytes, "application/pdf", "sample.pdf");
});

app.Run();

appsettings.json

{
  "Documents": {
    "PDF": {
      "Provider": "Playwright",
      "RazorTemplateBasePath": "Templates/PDF",
      "OutputPath": "Outputs/PDF",
      "Playwright": {
        "MaxConcurrentTasks": 4
      },
      "Settings": {
        "PrintBackground": true,
        "Format": "A4",
        "Margin": {
          "Top": "1cm",
          "Bottom": "1cm",
          "Left": "1cm",
          "Right": "1cm"
        }
      }
    }
  }
}

Put .cshtml files under {ContentRoot}/{RazorTemplateBasePath} (for example Templates/Invoice.cshtml). The template key is usually the file name without .cshtml, for example "Invoice" for Invoice.cshtml.

Controller

using Library.PDF.Contracts;
using Microsoft.AspNetCore.Mvc;

public class DocumentsController(IPDFService pdf) : ControllerBase
{
    [HttpGet("invoice.pdf")]
    public async Task<IActionResult> Invoice(CancellationToken cancellationToken)
    {
        var model = new Dictionary<string, object>
        {
            ["DocumentTitle"] = "Invoice INV-001",
            ["GrandTotal"] = "$199.50",
            ["DueAmount"] = "$199.50",
        };

        var bytes = await pdf.RenderTemplateToPdfAsync("Invoice", model, cancellationToken);
        return File(bytes, "application/pdf", "invoice.pdf");
    }
}

For a full nested dictionary shape that drives every section of the bundled Invoice.cshtml, see the PDF.Utility sample host (BuildMinimalInvoiceModel).

Strongly typed @model

public sealed record InvoiceViewModel(string DocumentTitle, decimal Total);

var model = new InvoiceViewModel("INV-001", 199.50m);
var bytes = await pdf.RenderTemplateToPdfAsync("Invoice", model, cancellationToken);

HTML only: call RenderHtmlAsync. To write PDFs under the configured output folder, use RenderTemplateToPdfToFileAsync and GetOutputDirectory().

Class library, console, or tests (Host + explicit scope)

Use this when you are not using WebApplicationBuilder but want the same registration and a scoped IPDFService (mirrors PDF.Utility):

using Library.PDF;
using Library.PDF.Contracts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddAppPDF(builder.Configuration, builder.Environment);

using var host = builder.Build();

using var scope = host.Services.CreateScope();
var pdf = scope.ServiceProvider.GetRequiredService<IPDFService>();

var bytes = await pdf.RenderTemplateToPdfAsync("Sample", model, CancellationToken.None);

For manual ConfigurationBuilder without a host, add IConfiguration, IHostEnvironment (or a test stub with ContentRootPath pointing at your template folder), logging, then AddAppPDF—the content root you pass must contain RazorTemplateBasePath.

Custom implementation

If you replace PDF generation entirely, register your own IPDFService (and any supporting types) instead of relying on AddAppPDF, or extend the library by registering a custom IPDFGenerator and adjusting AddAppPDF to select it for your Provider value.


Referenced packages

The project references RazorLight, Microsoft.Playwright, Microsoft.Extensions.DependencyInjection.Abstractions, Microsoft.Extensions.Hosting.Abstractions, Microsoft.Extensions.Logging.Abstractions, Microsoft.Extensions.Options.ConfigurationExtensions, and Microsoft.Extensions.Caching.Memory. The explicit Microsoft.Extensions.Caching.Memory reference ensures restore resolves a current version (RazorLight still pulls an older transitive copy affected by GHSA-qj66-m88j-hmgj). Hosting apps typically already reference Microsoft.AspNetCore.App or Microsoft.Extensions.Hosting, which supply IServiceCollection extensions and IConfiguration.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 123 5/14/2026