Rend is an HTML/CSS rendering engine for .NET. It parses HTML, resolves CSS, computes layout, and renders the result to PDF or images.
It implements the rendering pipeline itself: HTML5 parsing, CSS cascade and inheritance, block/inline/flex/grid/table layout, text shaping with HarfBuzz, and painting through pluggable backends. It does not rely on a browser, WebView, or external rendering engine.
Playground | Visual Regression Report
using Rend;
using var pdfOutput = File.Create("report.pdf");
Render.ToPdf(html, pdfOutput);
using var imageOutput = File.Create("report.png");
Render.ToImage(html, imageOutput);await Render.ToPdfAsync(html, outputStream, cancellationToken: ct);var options = new RenderOptions
{
PageSize = PageSize.Letter,
MarginTop = 48f,
MarginRight = 48f,
MarginBottom = 48f,
MarginLeft = 48f,
Title = "Invoice #1234",
Author = "Acme Corp",
HeaderHtml = "<div style='font-size:10px;text-align:center'>Page {pageNumber} of {totalPages}</div>",
FooterHtml = "<div style='font-size:9px;text-align:right'>{date}</div>",
};
Render.ToPdf(html, output, options);using Rend.Pdf;
var options = new RenderOptions
{
PdfOptions = new PdfDocumentOptions
{
FontEmbedMode = FontEmbedMode.Subset, // Subset (default), Full, or None
Compression = PdfCompression.FlateFast, // FlateFast (default), Flate, FlateOptimal, or None
}
};
Render.ToPdf(html, output, options);Font embed modes:
| Mode | Speed | File Size | Use Case |
|---|---|---|---|
Subset |
Default | Smallest | Production documents with correct fonts |
Full |
Faster | Larger | Skip subsetting when file size doesn't matter |
None |
Fastest | Smallest | Maximum throughput, renders in Helvetica |
var options = new RenderOptions
{
ImageFormat = ImageOutputFormat.Jpeg, // Png, Jpeg, Webp
ImageQuality = 85,
Dpi = 150f,
};
Render.ToImage(html, output, options);var fonts = new FontCollection();
fonts.RegisterFromResolver(new SystemFontResolver());
fonts.RegisterFontDirectory("/path/to/fonts");
var options = new RenderOptions
{
FontProvider = fonts
};Use a custom resolver to control how images are loaded for <img>, background-image, border-image, and list-style-image.
public class S3ImageResolver : IImageResolver
{
public async Task<Stream?> ResolveAsync(string url, CancellationToken cancellationToken = default)
{
return await _s3Client.GetObjectStreamAsync(url, cancellationToken);
}
}
var options = new RenderOptions
{
ImageResolver = new S3ImageResolver()
};
await Render.ToPdfAsync(html, output, options);var progress = new Progress<RenderProgress>(p =>
Console.WriteLine($"[{p.Percentage}%] {p.Stage}: {p.Description}"));
Render.ToPdf(html, output, new RenderOptions
{
Progress = progress
});Sign any PDF using a local certificate or an external signer. Works with any PDF, not just ones generated by Rend.
using Rend.Pdf;
var signer = new Pkcs12Signer(File.ReadAllBytes("cert.pfx"), "password");
using var input = File.OpenRead("document.pdf");
using var output = File.Create("signed.pdf");
await PdfSigning.SignAsync(input, output, new PdfSignatureOptions
{
Signer = signer,
SignerName = "John Doe",
Reason = "Approved",
Location = "New York",
});For HSM or cloud KMS scenarios where the private key is not directly accessible, implement IPdfSigner:
public class AzureKeyVaultSigner : IPdfSigner
{
public int EstimatedSignatureSize => 8192;
public async Task<byte[]> SignAsync(byte[] data, CancellationToken cancellationToken = default)
{
return await _keyVaultClient.SignCmsAsync(data, cancellationToken);
}
}
await PdfSigning.SignAsync(input, output, new PdfSignatureOptions
{
Signer = new AzureKeyVaultSigner(),
});There is also a convenience overload for simple cases:
var cert = new X509Certificate2("cert.pfx", "password");
await PdfSigning.SignAsync(input, output, cert);For DI, use IPdfSigningService and PdfSigningService:
services.AddSingleton<IPdfSigningService, PdfSigningService>();Draw text and images onto specific positions on existing PDF pages. Useful for filling form fields, adding signatures, or placing watermarks.
using Rend.Pdf;
using var input = File.OpenRead("template.pdf");
using var output = File.Create("filled.pdf");
await PdfOverlays.ApplyAsync(input, output, new PdfOverlayElement[]
{
new TextOverlay
{
Page = 1,
X = 100,
Y = 200,
Text = "John Doe",
FontSize = 14,
FontFamily = "Helvetica",
},
new ImageOverlay
{
Page = 1,
X = 100,
Y = 500,
Width = 200,
Height = 80,
Data = File.ReadAllBytes("signature.png"),
},
});Coordinates use top-left origin. Supports Helvetica, Times, and Courier font families with bold and italic variants. JPEG and PNG image formats are supported.
For DI, use IPdfOverlay and PdfOverlay:
services.AddSingleton<IPdfOverlay, PdfOverlay>();Rend.Pdf can be used independently of the HTML/CSS renderer.
using Rend.Pdf;
var doc = new PdfDocument();
var page = doc.AddPage(595.28f, 841.89f);
var font = doc.AddFont(fontData);
var cs = page.ContentStream;
cs.BeginText();
cs.SetFont(font, 12f);
cs.SetTextPosition(72f, 750f);
cs.ShowText(font.Encode("Hello from Rend.Pdf!"));
cs.EndText();
using var stream = File.Create("output.pdf");
doc.Save(stream);- Block, inline, and inline-block formatting contexts
- Flexbox, including wrapping, alignment, gap, order, and flex grow/shrink
- CSS Grid, including auto-placement, fr, minmax(), repeat(), auto-fill/fit, named lines/areas, and subgrid
- Table layout, including fixed and auto layout, rowspan/colspan, and border-collapse
- Positioned elements: relative, absolute, fixed, and sticky
- Floats and clear
- Multi-column layout
- Pagination with page breaks, widows, and orphans
- Cascade, specificity, and inheritance
calc(),var(), and custom properties@media,@font-face,@import,@page, and@supports- CSS Color Level 4
- Shorthand expansion
- Backgrounds: solid colors, gradients, images, and multiple backgrounds
- Borders, border radius, and border images
- Box shadows and text shadows
- Transforms, opacity, filters, and clip paths
- Linear, radial, and conic gradients
- SVG rendering
text-overflow: ellipsis
- HarfBuzz text shaping
- Unicode line and word breaking
- Bidirectional text
- White-space handling, text transforms, and letter/word spacing
- Font fallback chains
- WOFF and WOFF2 support
- System font discovery
- PDF 1.4–1.7 writer with TrueType and CFF/OpenType font subsetting
- Configurable font embedding: Subset (used glyphs only), Full, or None (Standard14)
- Bookmarks, clickable links, and document metadata
- AcroForms: text fields, checkboxes, and dropdowns
- Digital signatures using local PKCS#12 certificates or external
IPdfSignerimplementations - Content overlays: draw text and images onto existing PDFs
- Encryption: RC4-128 and AES-128
- PDF/A conformance (A1b, A2b, A3b) with ICC output intents
- Linearization for fast web view
- Object streams and cross-reference streams (PDF 1.5+)
- ICC color profiles and XMP metadata
- PNG, JPEG, and WebP via SkiaSharp
- Configurable DPI
Measured with BenchmarkDotNet on .NET 8, warm font provider.
| Test | Time | Memory |
|---|---|---|
| Simple HTML | 1.17 ms | 498 KB |
| Styled HTML | 1.27 ms | 748 KB |
| Images | 1.42 ms | 912 KB |
| 50 Lines | 1.56 ms | 1.3 MB |
| Table (50 rows) | 7.86 ms | 4.7 MB |
With FontEmbedMode.None (Standard14 Helvetica): 126 µs for Simple HTML.
See docs/performance.md for details.
Every static API has a corresponding interface and implementation class for DI and testing.
services.AddSingleton<IRenderer, HtmlRenderer>();
services.AddSingleton<IPdfSigningService, PdfSigningService>();
services.AddSingleton<IPdfOverlay, PdfOverlay>();HTML string
|
v
[ HTML Parser ]
|
v
[ CSS Parser ]
|
v
[ Style Resolution ]
|
v
[ Layout Engine ]
|
v
[ Painter ]
|
+---------+---------+
| |
v v
[ PDF Target ] [ Skia Target ]
| |
v v
PDF bytes Image bytes
| Project | Description |
|---|---|
Rend.Core |
Shared types such as geometry, color, and units |
Rend.Html |
HTML5 parser, DOM, and selector engine |
Rend.Css |
CSS parser, cascade, and style resolution |
Rend.Pdf |
Standalone PDF writer, signing, and overlay support |
Rend |
Layout engine, rendering pipeline, text shaping, and public API |
All projects target netstandard2.0 (.NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+).
The Playground runs Rend entirely in the browser via Blazor WebAssembly. Type HTML/CSS in the editor, hit Render, and see the PDF output instantly. No server required.
Rend is tested against Chrome using a pixel-level comparison suite of 871 test cases covering layout, typography, gradients, shadows, tables, forms, and more.
The latest report shows Chrome vs Rend screenshots with diff overlays for every test.
Run the suite locally:
just visual # run all tests
just visual-filter foo # run tests matching "foo"
just visual-update # run and update checked-in results
just report # open the reportResults are saved to conformance/results/ for check-in.
Requires the .NET 8 SDK.
dotnet build Rend.sln
dotnet test Rend.slndotnet run -c Release --project benchmarks/Rend.Benchmarks -- --filter "*"Run a specific benchmark class:
dotnet run -c Release --project benchmarks/Rend.Benchmarks -- --filter "*RenderBenchmarks*"MIT