KwatuPDF 1.0.0-beta.1

This is a prerelease version of KwatuPDF.
dotnet add package KwatuPDF --version 1.0.0-beta.1
                    
NuGet\Install-Package KwatuPDF -Version 1.0.0-beta.1
                    
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="KwatuPDF" Version="1.0.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KwatuPDF" Version="1.0.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="KwatuPDF" />
                    
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 KwatuPDF --version 1.0.0-beta.1
                    
#r "nuget: KwatuPDF, 1.0.0-beta.1"
                    
#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 KwatuPDF@1.0.0-beta.1
                    
#: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=KwatuPDF&version=1.0.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=KwatuPDF&version=1.0.0-beta.1&prerelease
                    
Install as a Cake Tool

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:

  • Dashboard
  • Invoice
  • Letter
  • Receipt
  • Report

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=15 in millimeters.
  • TrimTrailingBlankPages defaults to true.
  • File output methods create missing output directories automatically.
  • RenderUrlAsync and RenderUrlToFileAsync only accept absolute http or https URLs.
  • Manual page sizing is available through KwatuPdfOptions.Width and Height.

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: libgdiplus and related native dependencies

License

MIT

Product 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. 
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-beta.1 82 4/13/2026