Print-ready HTML reports, no PDF engine required
A fluent, dependency-free library for paginated reports with fixed page sizes, measured content flow, and self-contained HTML/CSS — opens in any browser, prints to PDF.
dotnet add package TerraFluent.Html.Reporting
A complete report in one fluent chain
Fixed page geometry, a repeating header, a page-numbered footer, and measured content flow — then render to a string or straight to a file. Open the result in a browser, or print/save it as PDF.
- A4, Letter, Legal, portrait/landscape, or custom page sizes
{page}/{totalPages}footer templates- Self-contained HTML/CSS output — nothing else to deploy
using TerraFluent.Html.Reporting.Model;
var report = ReportDocument.Create(PageSize.A4, PageOrientation.Portrait)
.SetMargins(40, 40, 60, 60)
.Header(h => h.AddText("Monthly Sales Report").AlignCenter().Bold())
.Footer(f => f.AddPageNumber("Page {page} of {totalPages}"))
.Content(c =>
{
c.AddHeading("Sales Summary", HeadingLevel.H1);
c.AddParagraph("This report summarizes sales activity for the period.");
c.AddTable(t =>
{
t.AddColumns("Product", "Qty", "Revenue");
t.AddRow("Widget A", "120", "$2,400");
t.AddRow("Widget B", "85", "$1,700");
});
})
.Build();
report.RenderHtmlDocument("monthly-sales.html");Merged cells that survive pagination
ColSpan and RowSpan handle merged summary cells and
category groupings — and the layout engine treats a RowSpan group
as atomic, so grouped rows never split across a page boundary.
- Repeated table headers on every continued page
- Configurable row splitting via
RowSplitBehavior - Cell-count validation throws early, not at render time
table.AddColumns("Category", "Item", "Qty", "Total");
table.AddRow(new TableCell[]
{
new TableCell("Electronics") { RowSpan = 3 },
"Wireless Mouse", "2", "$39.98",
});
table.AddRow(new TableCell[] { "Mechanical Keyboard", "1", "$89.00" });
table.AddRow(new TableCell[] { "USB-C Dock", "1", "$64.50" });
table.AddRow(new TableCell[]
{
new TableCell("Grand Total") { ColSpan = 3 },
"$193.48",
});Native Code 128 — no external library
Barcodes are generated in-library as PNG images: no barcode package, no web service. Available on content, header/footer, and row-column builders, with the same alignment/margin/padding modifiers as any image.
- Invoice numbers in headers, tracking numbers on labels
- Tunable module width, height, and quiet zone
- Accepts printable ASCII values
// Defaults are fine for most cases:
c.AddBarcode("INV-1042");
// Or tune the geometry:
c.AddBarcode("20264471",
moduleWidthPx: 2, heightPx: 40, quietZoneModules: 10);
// In an invoice header, right-aligned:
h.AddBarcode(invoiceNumber, moduleWidthPx: 2, heightPx: 40)
.AlignRight();Know before you ship: layout warnings
Content that can't fit on an empty page — an oversized image, an unsplittable
block — raises a LayoutWarning instead of silently clipping.
Inspect warnings after pagination and reject a bad report before a user sees it.
- Line-level paragraph splitting and numbered-list continuation
- Explicit page breaks where you want them
- Warnings name the page and the element that didn't fit
using TerraFluent.Html.Reporting.Layout;
var layout = LayoutEngine.Paginate(report);
foreach (var warning in layout.Warnings)
{
// "Page 1: A ReportImage required 300px but only ..."
logger.LogWarning("{ReportWarning}", warning);
}Stream large reports to disk
For reports too large to hold as one in-memory string, the async streaming API writes one page at a time — and won't block a thread-pool thread on the final flush inside an ASP.NET request handler.
RenderHtml()for strings,RenderHtmlDocument()for filesRenderHtmlDocumentAsync()with cancellation support- Pluggable
ITextMeasurerviaUseTextMeasurer(...)when page breaks must match a specific engine pixel-perfectly
await report.RenderHtmlDocumentAsync(
Path.Combine(outputDir, "report.html"),
cancellationToken: cancellationToken);What's new
- Native Code 128 barcodes —
AddBarcode(...)on content, header/footer, and row-column builders; returns the same builder asAddImage. - Table cell
ColSpan/RowSpan— merged header/summary cells and category groups, atomic during pagination.
Documented like a product
Thirteen guides from first report to custom renderers, plus a cookbook of runnable recipes.
Frequently asked questions
How do I convert the HTML report output to PDF?
The output is print-perfect, self-contained HTML with fixed page geometry — print or save it as PDF from any browser, or feed it to any HTML-to-PDF converter you already trust. If you want PDF bytes server-side with no browser in the loop, use TerraFluent.Pdf.Reporting instead.
Does TerraFluent.Html.Reporting need a browser or PDF engine at runtime?
No. It is pure managed code that writes self-contained HTML/CSS — no headless browser, no PDF engine, no native binaries — so it deploys cleanly to Linux, Docker, and serverless.
Which .NET versions does TerraFluent.Html.Reporting support?
It targets netstandard2.0 and net10.0, so it runs on .NET Framework 4.6.1+, .NET Core 2.0+, and every modern .NET release on Windows, Linux, and macOS.
Can it generate barcodes?
Yes. Code 128 barcodes are generated in-library as crisp images — no external services or third-party barcode packages required.
Thirteen runnable samples included
Invoices, certificates, barcoded labels, row layouts, span tables — run
dotnet run --project samples/TerraFluent.Html.Reporting.Sample or browse the rendered output online.
Need true PDF bytes server-side, with no browser in the loop? See TerraFluent.Pdf.Reporting.