Katalyst.Core.PDF 1.0.0

dotnet add package Katalyst.Core.PDF --version 1.0.0
                    
NuGet\Install-Package Katalyst.Core.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.Core.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.Core.PDF" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Katalyst.Core.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.Core.PDF --version 1.0.0
                    
#r "nuget: Katalyst.Core.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.Core.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.Core.PDF&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Katalyst.Core.PDF&version=1.0.0
                    
Install as a Cake Tool

Katalyst.Core.PDF

Class library that 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.

Requirements

  • .NET 10 SDK
  • For PDF output: Playwright browsers must be installed in the consuming application (for example run pwsh bin/Debug/net10.0/playwright.ps1 install chromium from the app output directory, or bake that into your container image)

Adding to your app

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

Configuration binds to section Documents:PDF (PDFOptions, constant PDFOptions.SectionName). Example appsettings.json fragment:

{
  "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"
        }
      }
    }
  }
}
Path (under Documents:PDF) Description
Provider Built-in: Playwright (PDFConstants.ProviderNames). AddCorePdf wires only Playwright; another implementation would need custom DI registration instead of (or wrapping) this extension.
Playwright:MaxConcurrentTasks Caps concurrent PDF pipelines (semaphore in PlaywrightConcurrencyGate) to limit Chromium memory use.
RazorTemplateBasePath Folder relative to content root containing .cshtml templates (default Templates).
OutputPath Optional folder relative to host content root (for AddCorePdf) used by IPDFService.GetOutputDirectory; callers decide whether to write files there.
Settings Passed to Playwright Page.PdfAsync: PrintBackground, Format, Margin (DocumentSettings).

IPDFService and dependency injection

AddCorePdf registers IPDFService with scoped lifetime. The implementation type is PDFService; it is wired through that registration only, so consuming code should take IPDFService (constructor injection, minimal API parameters, or GetRequiredService<IPDFService>()) rather than the concrete class. That keeps tests and future swaps behind one abstraction—you only need a different registration if you replace or decorate the implementation.

Scoped means the container creates one PDFService instance per service scope. In ASP.NET Core the host opens a scope around each HTTP request, so a controller or endpoint typically receives a fresh IPDFService for that request. Long-lived pieces—RazorLight compilation/caching, Playwright browser hosting, and the PDF concurrency gate implementation—remain registered as singletons and are shared; PDFService coordinates them per scope without storing request data on those singletons itself.

Outside a web request pipeline (for example a console utility or a background task), there is no implicit scope: create one with IServiceScopeFactory (or CreateAsyncScope), resolve IPDFService from that scope’s IServiceProvider, then dispose the scope when finished—see the PDF.Utility host for the same pattern.

Use the interface like this:

  • RenderHtmlAsync(templateKey, model, cancellationToken) — compile and render a template under RazorTemplateBasePath
  • RenderTemplateToPdfAsync(templateKey, model, cancellationToken) — HTML then PDF
  • RenderHtmlToPdfAsync(html, cancellationToken) — PDF from existing HTML (subject to the same concurrency gate)

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

ASP.NET Core examples

Register in Program.cs as shown under Adding to your app. 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.

Inject IPDFService into endpoints or controllers as shown below (scoped lifetime supplies one instance per request—see IPDFService and dependency injection). Pass whatever your template’s @model declares—common choices are a POCO/record or Dictionary<string, object> (the sample templates under PDF.Utility use Dictionary<string, object>).

Minimal API (using Core.PDF.Core;):

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");
});

Controller:

using Core.PDF.Core;
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 (your own .cshtml):

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(templateKey, model, cancellationToken) instead of RenderTemplateToPdfAsync. To write PDFs under the configured output folder, use RenderTemplateToPdfToFileAsync and GetOutputDirectory() on IPDFService.

PDF generation uses a shared Chromium instance (PlaywrightBrowserHost); each operation uses a new browser context.

Packages

References are listed in Core.PDF.csproj: RazorLight, Microsoft.Playwright, and Microsoft.Extensions options/DI/hosting/logging packages. Microsoft.Extensions.Caching.Memory is referenced explicitly so restore resolves a current version (RazorLight still pulls an older transitive copy affected by GHSA-qj66-m88j-hmgj).

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 103 5/8/2026