Swevo.FluentPdf
1.2.0
Prefix Reserved
dotnet add package Swevo.FluentPdf --version 1.2.0
NuGet\Install-Package Swevo.FluentPdf -Version 1.2.0
<PackageReference Include="Swevo.FluentPdf" Version="1.2.0" />
<PackageVersion Include="Swevo.FluentPdf" Version="1.2.0" />
<PackageReference Include="Swevo.FluentPdf" />
paket add Swevo.FluentPdf --version 1.2.0
#r "nuget: Swevo.FluentPdf, 1.2.0"
#:package Swevo.FluentPdf@1.2.0
#addin nuget:?package=Swevo.FluentPdf&version=1.2.0
#tool nuget:?package=Swevo.FluentPdf&version=1.2.0
FluentPdf
Free, MIT-licensed fluent PDF generation for .NET. No revenue threshold, no commercial license — ever.
Why FluentPdf?
QuestPDF's Community License (v3.0, effective July 2026) is free only for individuals, academia, charities, and businesses under $1M annual revenue — public companies and government agencies don't qualify at any revenue. If your product grows, or you're a public-sector team, you need a paid QuestPDF license. FluentPdf uses the same declarative, fluent building-block style but is MIT-licensed for everyone, regardless of revenue or company type.
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(40);
page.Header().Text("Invoice #1042").FontSize(20).Bold();
page.Content().Column(column =>
{
column.Item().Text("Bill to: Acme Corp");
column.Item().Padding(0, 10, 0, 0).Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3);
columns.RelativeColumn(1);
});
table.HeaderCell().Text("Item").Bold();
table.HeaderCell().Text("Price").Bold();
foreach (var line in invoiceLines)
{
table.Cell().Text(line.Name);
table.Cell().Text(line.Price.ToString("C"));
}
});
});
page.Footer().AlignCenter().Text(x => $"Page {x}");
});
}).GeneratePdf("invoice.pdf");
Install
dotnet add package Swevo.FluentPdf
FluentPdf is built on PdfSharp (MIT-licensed) for low-level PDF drawing, with its own layout/pagination engine on top.
Linux font setup
PdfSharp has no bundled fonts and Linux containers often ship without any TrueType fonts at
all. FluentPdf automatically scans common OS font directories (/usr/share/fonts,
/usr/local/share/fonts, the Windows Fonts folder, macOS's /System/Library/Fonts) and picks
the first match from DejaVu Sans, Liberation Sans, Noto Sans, Arial, Segoe UI, or Helvetica. On
a bare Linux image (e.g. a minimal Docker base or a fresh GitHub Actions runner), install one:
apt-get install -y fonts-dejavu-core
Or register your own font explicitly, bypassing OS discovery entirely:
FluentPdfFonts.RegisterFont(
"Roboto",
regular: File.ReadAllBytes("fonts/Roboto-Regular.ttf"),
bold: File.ReadAllBytes("fonts/Roboto-Bold.ttf"));
Core concepts
Document.Create(...)— top-level entry point; add one or morePagetemplates.page.Header()/page.Content()/page.Footer()— Header and Footer are fixed-height and rendered fully on every page; Content is automatically paginated across as many pages as needed.Column/Row— vertical/horizontal layout containers;Rowitems use relative (weighted) widths.Table— relative-width columns with an optional repeating header row; splits at row boundaries across pages.Text— automatic word-wrapping; splits mid-paragraph across pages if needed.Text(x => $"Page {x}")(inside Header/Footer only) renders the current page number.- Decorators —
.Padding(...),.Background(color),.Border(thickness, color),.AlignCenter()/.AlignRight()wrap whatever comes next in the chain.
Editing existing PDFs
FluentPdf.Editing.PdfEditor opens and manipulates PDFs that already exist on disk or in memory —
merge, split, reorder, delete, and rotate pages — complementing Document, which only generates
new PDFs from scratch.
using FluentPdf.Editing;
// Merge two files into one.
using var merged = PdfEditor.Merge("report-part1.pdf", "report-part2.pdf");
merged.Save("report-full.pdf");
// Edit a document in place.
using var editor = PdfEditor.Open("input.pdf");
editor.RotatePage(0, 90).RemovePage(2);
editor.Save("output.pdf");
// Reorder and split.
editor.Reorder(new[] { 2, 0, 1 });
var perPageDocuments = editor.Split();
var chunks = editor.SplitEvery(5);
PdfEditor.Open(path | bytes | stream)— opens an existing PDF for in-place editing.RemovePage(index)/RotatePage(index, degrees)— modify pages in place;degreesmust be a multiple of 90.Reorder(newOrder)— rearranges all pages according to a full permutation of indices.Split()/SplitEvery(pagesPerChunk)— produce onePdfEditorper page, or per contiguous chunk.PdfEditor.Merge(...)— combines multiple files or openPdfEditorinstances into one new document, in order.AddAnnotation(index, annotation)— draws aHighlightAnnotation,FreehandStrokeAnnotation,TextCommentAnnotation, orImageStampAnnotationonto the page atindex. Annotations are burned into the page content the next time the document is materialized.
editor.AddAnnotation(0, new HighlightAnnotation(x: 50, y: 100, width: 200, height: 18));
editor.AddAnnotation(0, new TextCommentAnnotation(x: 50, y: 140, "Looks good to me"));
editor.AddAnnotation(0, new ImageStampAnnotation(signatureBytes, x: 300, y: 700, width: 120, height: 40));
Note:
ImageStampAnnotationflattens the supplied image onto an opaque white background before stamping it (PdfSharp does not reliably alpha-blend semi-transparent PNGs). This usesSystem.Drawing.Common, which requires Windows at runtime.
Protect(userPassword, ownerPassword?)/RemoveProtection()— password-protects the output;PdfEditor.Open(path | bytes | stream, password)overloads open protected PDFs.Compress(level)— recompresses embedded JPEG images to shrink output size.PdfCompressionLevelisLow/Medium(default) /High. UsesSystem.Drawing.Common(Windows-only at runtime).Watermark(text, colorHex?, opacity?, fontSize?)— stamps diagonal, semi-transparent text across every page (e.g. "CONFIDENTIAL" or "DRAFT"). Passnullto remove a previously set watermark.Bookmark(pageIndex, title)/ClearBookmarks()/Bookmarks— adds table-of-contents/outline entries that are written into the output PDF's outline tree, so the entries appear as bookmarks in any PDF viewer.
editor.Protect(userPassword: "letMeIn", ownerPassword: "adminOnly");
editor.Compress(PdfCompressionLevel.Medium);
editor.Watermark("CONFIDENTIAL");
editor.Bookmark(0, "Introduction");
editor.Save("protected-compressed.pdf");
using var reopened = PdfEditor.Open("protected-compressed.pdf", "letMeIn");
ToArray()/Save(path)— serialize the current state.
Pagination model
Every layout node knows how to measure itself and render as much of itself as fits in the
remaining space, handing back a "continuation" for whatever didn't fit. Text and Table
split mid-content (mid-line, mid-row); Row, Border, and Align move as a whole block to the
next page if they don't fit (documented below as current limitations).
Known limitations (v1.0)
Row,Border, andAlignare non-splittable — they move wholesale to the next page rather than splitting internally.ColumnandTableare the primary paginated containers.- No image support yet.
- No cell-level borders/backgrounds inside
Table(wrap individual cells with.Border()/.Background()as a workaround).
Contributions and issues for any of the above are welcome.
Design goals
- MIT licensed, forever. No commercial tier, no revenue threshold.
- Small, understandable engine. A handful of composable
IElementnodes rather than a large proprietary layout DSL. - Works out of the box on Linux CI, given a system font is installed.
Related Packages
| Package | Downloads | Description |
|---|---|---|
| Swevo.FluentExcel | A free, MIT-licensed fluent Excel generation library for |
💼 Need .NET consulting?
I'm the author of FluentPdf and a suite of compile-time source generators (AutoWire, AutoMap.Generator) and 28+ Polly v8 resilience packages. I'm available for consulting on Polly v8 resilience, Azure cloud architecture, and clean .NET design.
→ solidqualitysolutions.com · LinkedIn
Also by the same author
🌐 Full suite overview: swevo.github.io
| Package | Description |
|---|---|
| AutoBus | Free, MIT-licensed message bus — alternative to MassTransit's commercial license. |
| AutoArchitecture | Free, MIT-licensed compile-time architecture rule enforcement — alternative to NDepend. |
| EFCore.Outbox | Transactional outbox pattern for EF Core. |
| Swevo.AutoAssert | Free, MIT-licensed fluent assertions — alternative to FluentAssertions' commercial license. |
| EFCore.BulkOperations | Free, MIT-licensed bulk insert/update/delete for EF Core. |
| AutoWire | Compile-time DI auto-registration. |
| AutoDispatch.Generator | Compile-time CQRS dispatcher — free alternative to MediatR's commercial license. |
| PollyAnalyzers | Free Roslyn analyzers for async/resilience anti-patterns — blocking calls, async void, fire-and-forget tasks, swallowed exceptions. |
| PollyAction | Free retry/backoff GitHub Action — wrap any CI step with exponential-backoff retries. |
License
MIT © Justin Bannister
| 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
- PdfSharp (>= 6.2.4)
- System.Drawing.Common (>= 10.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.2.0: Add PdfEditor.Watermark for diagonal repeated text stamping across every page, and PdfEditor.Bookmark/ClearBookmarks/Bookmarks for adding table-of-contents/outline entries that are written into the output PDF's outline tree. 1.1.0: Add PdfEditor.Protect/RemoveProtection for password protection (with password-aware Open overloads), and PdfEditor.Compress for JPEG image recompression (PdfCompressionLevel: Low/Medium/High). PdfEditor.Split/SplitEvery for splitting a document into separate files were already present and are now documented as part of the monetizable feature set. 1.0.2: Fix ImageStampAnnotation rendering as invisible for semi-transparent PNGs (now flattened onto an opaque background before stamping; requires Windows via System.Drawing.Common). 1.0.1: Add missing package icon. 1.0.0: Initial release. Fluent document builder with Text/Column/Row/Table components, automatic pagination (including mid-table and mid-text page breaks), padding/border/background decorators, and page numbering.