Features
Highlights of Papyra's capabilities.
The engine
A PDF renderer written from scratch
Papyra does not wrap iText, PDFsharp, SkiaSharp, or a headless browser. The PDF renderer emits the file format directly - objects, cross-reference tables, content streams - which is why the whole library needs just three managed dependencies and no native binaries.
Documents flow through three independent stages: the fluent API builds an immutable model (Core), the layout engine computes positions and dimensions (Layout), and each renderer consumes that layout tree (Renderers). Your composition code never branches on the output format.
Document (fluent API) → LayoutBox tree → byte[] output
Core Layout RenderersCompliance
PDF/A that actually validates
Archival PDF is the default, not a mode. Papyra writes PDF/A-2b out of the box and PDF/A-3b or PDF/A-4 on request - with embedded fonts, an sRGB ICC output intent, and an XMP metadata packet kept consistent with the Info dictionary.
Conformance is not assumed: the test suite validates generated files against veraPDF, the industry-standard validator, for every supported standard.
byte[] pdf = document.RenderAsPdf(new PdfRenderOptions { Standard = PdfStandard.PdfA3 });Typography
Any font, full Unicode, subsetted
Register any TrueType font and reference it by name. The PDF renderer embeds fonts as Type0/CIDFontType2 - any script the font covers works - and subsets each embedded font to the glyphs the document actually uses.
Liberation Sans, Serif, and Mono ship inside the library, so an unconfigured document renders identically on every machine.
doc.RegisterFont("Brand Sans",
regular: File.ReadAllBytes("BrandSans-Regular.ttf"),
bold: File.ReadAllBytes("BrandSans-Bold.ttf"))
.Paragraph(p => p.Text("Custom type",
new TextStyle(FontFamily: "Brand Sans")));Tables
Tables built for line items
Fixed and relative columns, column and row spans, styled cells with padding, borders, backgrounds, and alignment. When a table runs past the page, rows split automatically - and header rows repeat at the top of every page, footer rows at the bottom.
doc.Table(table =>
{
table.Columns(c => c.FixedColumn(225f).FixedColumn(48f).RelativeColumn());
table.Header(row => row.Cell("Item", th).Cell("Qty", th).Cell("Total", th));
foreach (var item in invoice.Items)
table.Row(row => row.Cell(item.Name, td)
.Cell(item.Qty, num).Cell(item.Total, num));
});Composition
Layout that reads like the document
Side-by-side rows with relative weights and fixed widths, ordered and unordered lists, images, horizontal rules, and explicit page breaks. Builder calls stack the way the document flows - there is no separate layout language to learn.
doc.Row(row => row
.RelativeItem(l => l.Text("INVOICE", Title), weight: 1.35f)
.FixedItem(210f, r => r.Text("Invoice date", Label)
.Text("09 Jul 2026", Body)));Styling
Styles that merge like CSS
TextStyle is a record of optional properties. Set a document-wide DefaultTextStyle once; anything a text run leaves unset falls through to it. Colors parse from hex with alpha, and the complete Material palette ships as constants.
doc.DefaultTextStyle(new TextStyle(FontSize: 10f))
.Paragraph(p => p.Text("24pt, inherits everything else",
new TextStyle(FontSize: 24f, Color: Colors.Teal.Darken2)));Multi-format
One model, four renderers
The document model knows nothing about output formats. Render the same tree to a paginated PDF/A file, an editable Word document, a spreadsheet, and a self-contained HTML page - four lines, no branches.
Formats genuinely differ, and Papyra documents exactly how: the feature support matrix states what each renderer does with pagination, fonts, images, and colors.
byte[] pdf = document.RenderAsPdf();
byte[] docx = document.RenderAsDocx();
byte[] xlsx = document.RenderAsXlsx();
byte[] html = document.RenderAsHtml();Developer experienceBeta
A live preview that knows your source code
The companion app renders your document as you work: run under dotnet watch and every re-run pushes a new tab to the preview window, building a history you can flip back through. The structure tree and the page highlight each other bidirectionally, and Ctrl+Click on any element jumps to the builder call that created it - in Rider or VS Code, at the exact line.
It also detects layout issues - fixed widths wider than the page, text wider than its cell, elements taller than a page, and content clipped at page breaks - and shows them as overlays and a clickable issues panel, so layout problems surface while you compose instead of in the shipped PDF.
Document.Create(doc => doc
.PageSize(PageSize.A4)
.Paragraph(p => p.Text("Hello")))
.ShowInCompanion(); // preview at 127.0.0.1:12500Developer experienceBeta
Compose visually, get real code back
A standalone desktop app for building a report by clicking, not typing: an outline tree, a live preview rendered by the exact same engine as production, and a properties panel for text styles, table columns, colors, watermarks, and page backgrounds. No separate preview language - what you see is what the real renderer produces.
A generated-code panel mirrors every edit as C#: Document.Create(doc => doc. ...), ready to copy or save to a .cs file. It's a starting point you keep editing by hand from there, not a live link back to your source.
dotnet run --project Papyra.Designer
// compose visually, then copy the generated C#:
Document.Create(doc => doc
.Table(table => table
.Columns(c => c.RelativeColumn().RelativeColumn())
.Header(row => row.Cell("Item").Cell("Total"))
.Row(row => row.Cell("Widget").Cell("$12"))));