KwatuPDF 1.0.0-beta.1
dotnet add package KwatuPDF --version 1.0.0-beta.1
NuGet\Install-Package KwatuPDF -Version 1.0.0-beta.1
<PackageReference Include="KwatuPDF" Version="1.0.0-beta.1" />
<PackageVersion Include="KwatuPDF" Version="1.0.0-beta.1" />
<PackageReference Include="KwatuPDF" />
paket add KwatuPDF --version 1.0.0-beta.1
#r "nuget: KwatuPDF, 1.0.0-beta.1"
#:package KwatuPDF@1.0.0-beta.1
#addin nuget:?package=KwatuPDF&version=1.0.0-beta.1&prerelease
#tool nuget:?package=KwatuPDF&version=1.0.0-beta.1&prerelease
KwatuPDF
Fast, fluent HTML / CSHTML / Razor to PDF for .NET 8+.
KwatuPDF gives you a lightweight PDF pipeline built on PdfSharpCore, HtmlRenderer, and RazorLight. There is no Chromium download, no browser process, and no separate rendering service to manage.
Why use it
- Render raw HTML when you already control the markup.
- Render Razor views when you want reusable templates with typed models.
- Render built-in templates when you want common business documents quickly.
- Render primitive documents when you want a structured builder instead of one large HTML string.
- Use it directly, through the fluent builder, or from ASP.NET Core dependency injection.
Install
For internal testing we are publishing to GitHub Packages first, not nuget.org.
Option 1. Consume from GitHub Packages
Create or update a NuGet.config in your app or solution:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="github" value="https://nuget.pkg.github.com/ngandugilbert/index.json" />
</packageSources>
<packageSourceCredentials>
<github>
<add key="Username" value="YOUR_GITHUB_USERNAME" />
<add key="ClearTextPassword" value="YOUR_GITHUB_PAT" />
</github>
</packageSourceCredentials>
</configuration>
Then install:
dotnet add package KwatuPDF --source github
The GitHub token needs at least read:packages.
Option 2. Consume from a local package folder
Build a local package:
dotnet pack src/KwatuPDF/KwatuPDF.csproj -c Release -o .artifacts/packages
Then install from that folder:
dotnet add package KwatuPDF --source .artifacts/packages
Linux prerequisites
On Linux, KwatuPDF depends on libgdiplus at runtime because the underlying HTML-to-PDF renderer uses System.Drawing.
For Ubuntu or Debian:
sudo apt-get update
sudo apt-get install -y libgdiplus libc6-dev
If you run tests or package builds in GitHub Actions on Linux, install the same dependency in the workflow before dotnet test.
Quick start
using KwatuPDF;
await using var engine = new KwatuPdfEngine();
await engine.RenderHtmlToFileAsync(
"<h1>Hello from KwatuPDF</h1><p>Your first PDF is ready.</p>",
"output/hello.pdf");
The output directory is created automatically if it does not already exist.
Pick the right entry point
| If you have | Use |
|---|---|
| An HTML string | RenderHtmlAsync, RenderHtmlToFileAsync, RenderHtmlToStreamAsync |
| A public web page | RenderUrlAsync, RenderUrlToFileAsync |
A local .cshtml / Razor view |
RenderViewAsync, RenderViewToFileAsync |
| A built-in document template | RenderTemplateAsync, RenderTemplateToFileAsync |
| A typed primitive document | RenderDocumentAsync, RenderDocumentToFileAsync |
Complete examples
1. HTML to file, bytes, and stream
using KwatuPDF;
await using var engine = new KwatuPdfEngine();
const string html = """
<html>
<body style="font-family: Arial; padding: 32px;">
<h1 style="color: #2563eb;">Monthly Status</h1>
<p>This PDF came from a raw HTML string.</p>
<p>Generated at: 2026-04-12 11:30</p>
</body>
</html>
""";
await engine.RenderHtmlToFileAsync(html, "output/status.pdf");
byte[] pdfBytes = await engine.RenderHtmlAsync(html);
await File.WriteAllBytesAsync("output/status-copy.pdf", pdfBytes);
await using Stream pdfStream = await engine.RenderHtmlToStreamAsync(html);
using var fileStream = File.Create("output/status-stream.pdf");
await pdfStream.CopyToAsync(fileStream);
2. Configure paper, orientation, margins, and password protection
using KwatuPDF;
await using var engine = new KwatuPdfEngine();
await engine.RenderHtmlToFileAsync(
"<h1>Quarterly Report</h1><p>Landscape, Letter, and password protected.</p>",
"output/report.pdf",
options =>
{
options.Format = KwatuPdfPaperFormat.Letter;
options.Landscape = true;
options.Margin = new KwatuPdfMargins
{
Top = 12,
Right = 12,
Bottom = 12,
Left = 12,
Unit = KwatuPdfMarginUnit.Millimeter
};
options.Security.UserPassword = "open-sesame";
options.Security.OwnerPassword = "owner-secret";
});
3. Use the fluent builder for reusable defaults
using KwatuPDF;
await using var engine = new KwatuPdfBuilder()
.UseA4()
.UsePortrait()
.WithMargins(PageMargins.Normal)
.WithFont(Fonts.Report)
.WithWatermark("INTERNAL", watermark =>
{
watermark.Opacity = 0.12;
watermark.RotationDegrees = -30;
})
.WithPassword("reader-password")
.Build();
await engine.RenderHtmlToFileAsync(
"""
<html>
<body>
<h1>Builder-based setup</h1>
<p>The engine keeps these defaults for every render call.</p>
</body>
</html>
""",
"output/builder.pdf");
4. Render a built-in invoice template
using KwatuPDF;
await using var engine = new KwatuPdfEngine();
await engine.RenderTemplateToFileAsync("Invoice", new
{
CompanyName = "KwatuPDF Inc.",
CompanyAddress = "123 PDF Street, Render City, RC 10001",
CompanyEmail = "billing@kwatupdf.dev",
InvoiceNumber = "INV-2026-0042",
Date = "April 12, 2026",
DueDate = "May 12, 2026",
CustomerName = "John Doe",
CustomerAddress = "456 Client Ave, Suite 200, Business Town",
CustomerEmail = "john@example.com",
Items = new[]
{
new { Description = "KwatuPDF Pro License", Quantity = 1, UnitPrice = "$299.00", Amount = "$299.00" },
new { Description = "Priority Support", Quantity = 1, UnitPrice = "$99.00", Amount = "$99.00" }
},
Subtotal = "$398.00",
TaxRate = "10%",
TaxAmount = "$39.80",
Total = "$437.80",
FooterNote = "Payment due within 30 days."
}, "output/invoice.pdf");
5. Render a custom Razor view
Templates/MyReport.cshtml
@model ReportModel
<html>
<body style="font-family: Arial; padding: 24px;">
<h1>@Model.Title</h1>
<p>Author: @Model.Author</p>
<p>Date: @Model.Date.ToString("yyyy-MM-dd")</p>
<ul>
@foreach (var item in Model.Items)
{
<li>@item</li>
}
</ul>
</body>
</html>
Program.cs
using KwatuPDF;
var model = new ReportModel
{
Title = "Q2 Delivery Review",
Author = "Engineering",
Date = DateTime.UtcNow,
Items = ["14 features shipped", "32% bug backlog reduction", "18% infrastructure savings"]
};
await using var engine = new KwatuPdfEngine();
await engine.RenderViewToFileAsync("Templates/MyReport.cshtml", model, "output/report.pdf");
public sealed class ReportModel
{
public string Title { get; init; } = string.Empty;
public string Author { get; init; } = string.Empty;
public DateTime Date { get; init; }
public string[] Items { get; init; } = [];
}
6. Build a document from primitives
using KwatuPDF;
var document = new KwatuPdfDocumentBuilder()
.WithTitle("Primitive Sample")
.WithWatermark("DRAFT", watermark =>
{
watermark.Opacity = 0.15;
watermark.RotationDegrees = -28;
})
.AddHtml("<h1 style='margin:0 0 8px 0; color:#1d4ed8;'>Primitive Pipeline</h1>")
.AddText("This document was assembled from typed primitives.")
.AddSpacer(16)
.AddImage(
"logo.png",
altText: "Company logo",
width: 180,
caption: "Loaded from a local file path.")
.Build();
await using var engine = new KwatuPdfEngine();
await engine.RenderDocumentToFileAsync(
document,
"output/primitives.pdf",
logger: KwatuPdfConsoleLogger.Instance);
7. Use a logo image as a watermark
using KwatuPDF;
var logoPath = Path.Combine(AppContext.BaseDirectory, "logo.png");
await using var engine = new KwatuPdfBuilder()
.UseA4()
.WithMargins(PageMargins.Normal)
.WithWatermarkImage(logoPath, watermark =>
{
watermark.Width = 240;
watermark.Opacity = 0.14;
watermark.RotationDegrees = -24;
})
.Build();
await engine.RenderHtmlToFileAsync(
"<html><body><h1>Logo watermark sample</h1><p>The logo is applied by the PDF engine on every page.</p></body></html>",
"output/logo-watermark.pdf");
Built-in templates
KwatuPDF currently ships with these embedded templates:
DashboardInvoiceLetterReceiptReport
You can inspect the available names at runtime:
var templates = new EmbeddedTemplateProvider();
foreach (var template in templates.ListTemplates())
{
Console.WriteLine(template);
}
ASP.NET Core registration
using KwatuPDF;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKwatuPdf(options =>
{
options.Format = KwatuPdfPaperFormat.A4;
options.Margin = PageMargins.Normal;
options.Font = Fonts.Document;
});
var app = builder.Build();
app.MapGet("/invoices/{id:int}/pdf", async (int id, IKwatuPdfEngine pdfEngine) =>
{
var invoice = new
{
CompanyName = "KwatuPDF Inc.",
CompanyAddress = "123 PDF Street",
CompanyEmail = "billing@kwatupdf.dev",
InvoiceNumber = $"INV-{id:0000}",
Date = "April 12, 2026",
DueDate = "May 12, 2026",
CustomerName = "John Doe",
CustomerAddress = "456 Client Ave",
CustomerEmail = "john@example.com",
Items = new[]
{
new { Description = "Consulting", Quantity = 2, UnitPrice = "$150.00", Amount = "$300.00" }
},
Subtotal = "$300.00",
TaxRate = "0%",
TaxAmount = "$0.00",
Total = "$300.00",
FooterNote = "Thank you for your business."
};
var pdf = await pdfEngine.RenderTemplateAsync("Invoice", invoice);
return Results.File(pdf, "application/pdf", $"invoice-{id}.pdf");
});
app.Run();
Defaults and behavior worth knowing
- Default paper format is
A4. - Default orientation is portrait.
- Default margins are
Top=20,Bottom=20,Left=15,Right=15in millimeters. TrimTrailingBlankPagesdefaults totrue.- File output methods create missing output directories automatically.
RenderUrlAsyncandRenderUrlToFileAsynconly accept absolutehttporhttpsURLs.- Manual page sizing is available through
KwatuPdfOptions.WidthandHeight.
Documentation
- Docs overview:
docs/README.md - Site home:
docs/index.md - Getting started:
docs/guide/getting-started.md - Configuration:
docs/guide/configuration.md - Templates and primitives:
docs/guide/templates-and-primitives.md - Engine API:
docs/api/engine.md
Internal publishing
Push a tag like v1.0.0 or run the Publish GitHub Package workflow manually in GitHub Actions. The workflow restores, tests, packs, and publishes KwatuPDF to:
https://nuget.pkg.github.com/ngandugilbert/index.json
Requirements
.NET 8.0+- On Linux:
libgdiplusand related native dependencies
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- HtmlRendererCore.PdfSharp (>= 1.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- PdfSharpCore (>= 1.3.67)
- 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-beta.1 | 82 | 4/13/2026 |