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
<PackageReference Include="Katalyst.Core.PDF" Version="1.0.0" />
<PackageVersion Include="Katalyst.Core.PDF" Version="1.0.0" />
<PackageReference Include="Katalyst.Core.PDF" />
paket add Katalyst.Core.PDF --version 1.0.0
#r "nuget: Katalyst.Core.PDF, 1.0.0"
#:package Katalyst.Core.PDF@1.0.0
#addin nuget:?package=Katalyst.Core.PDF&version=1.0.0
#tool nuget:?package=Katalyst.Core.PDF&version=1.0.0
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 chromiumfrom 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 underRazorTemplateBasePathRenderTemplateToPdfAsync(templateKey, model, cancellationToken)— HTML then PDFRenderHtmlToPdfAsync(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 | Versions 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. |
-
net10.0
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- Microsoft.Playwright (>= 1.59.0)
- RazorLight (>= 2.3.1)
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 |