A .NET library for business documents.
PDF, DOCX, XLSX and HTML from one model.

Papyra is a .NET library that composes documents with a fluent API and can render them to four formats from the same model.

For PDF generation, it uses a hand-written PDF engine with veraPDF-verified PDF/A output, full Unicode font subsetting, and tables that paginate.

  • .NET 10
  • PDF/A-2b · 3b · 4
  • No native code

The API

Describe the document once. Render it four times.

This is the exact composition behind the invoice above - one builder chain, no format-specific branches. The last four lines are the whole rendering story.

var document = Document.Create(doc => doc
    .PageSize(PageSize.A4)
    .Margin(new Thickness(48f))
    .Header(h => h.Text("Northwind Studio Ltd | 12 Harbour Lane | London EC1V 4AB", Small))
    .Footer(f => f.Text("Northwind Studio Ltd | VAT ID GB123456789", Small))
    .Row(row => row
        .RelativeItem(left => left
            .Text("INVOICE", Title)
            .Text("INV-20260709-001", new TextStyle(Color: Colors.Blue.Medium)))
        .RelativeItem(right => right
            .Text("Northwind Studio Ltd", Section)
            .Text("12 Harbour Lane", Body)))
    .Table(table =>
    {
        table.Columns(c => c.FixedColumn(225f).FixedColumn(48f)
            .FixedColumn(72f).FixedColumn(48f).RelativeColumn());
        table.Header(row => row
            .Cell("Item", TH).Cell("Qty", TH).Cell("Unit price", TH)
            .Cell("VAT", TH).Cell("Total", TH));
        foreach (var item in invoice.Items)
            table.Row(row => row
                .Cell(item.Description, Cell)
                .Cell(item.Quantity, Numeric)
                .Cell(item.UnitPrice, Numeric)
                .Cell(item.TaxRate, Numeric)
                .Cell(item.NetTotal, Numeric));
    }));

byte[] pdf  = document.RenderAsPdf();
byte[] docx = document.RenderAsDocx();
byte[] xlsx = document.RenderAsXlsx();
byte[] html = document.RenderAsHtml();

Capabilities

Built for business documents

Invoices, reports, confirmations - the documents that must be correct, archivable, and readable in ten years.

  • Its own PDF engine

    The PDF renderer is written from scratch - no iText, no Skia, no wrapper around a browser.

    Learn more
  • Archival-grade PDF/A

    PDF/A-2b, 3b and 4 output, with XMP metadata and an embedded sRGB ICC profile. Conformance is verified against veraPDF in the test suite.

    Learn more
  • Real typography

    Register any TrueType font. Papyra embeds full-Unicode CID fonts and subsets them to the glyphs your document actually uses.

    Learn more
  • Tables that paginate

    Fixed and relative columns, column and row spans. Header and footer rows that repeat on every page when a table breaks.

    Learn more
  • Composable layout

    Side-by-side rows with relative or fixed widths, ordered and unordered lists, images, horizontal rules, and explicit page breaks.

    Learn more
  • Styling that inherits

    TextStyle merges down the tree like CSS. Cell padding, borders, backgrounds, alpha colors, and the full Material palette built in.

    Learn more
  • One model, four formats

    The document model knows nothing about output formats. Render the same tree to PDF, DOCX, XLSX, and HTML - and see exactly what each format supports.

    Learn more
  • Live preview while you code

    The companion app (beta) re-renders every time your program runs under dotnet watch, shows the structure tree, and jumps to the builder call when you Ctrl+Click an element.

    Learn more

Format support

What each format actually does

Formats differ, and pretending otherwise produces broken documents. This table is kept in sync with the full matrix in the docs - including the gaps.

SupportedPartly supportedNot supported

CapabilityPDFDOCXXLSXHTML
Pagination with repeating table headersSupported: Headers + footersPartly supported: Headers onlyNot supported: -Supported: Headers + footers in print
Right-to-left and bidirectional textSupported: Resolved and reordered per lineSupported: Native w:bidiSupported: Excel reading orderSupported: dir attribute
Font embeddingSupported: Subsetted, full UnicodePartly supported: Font name onlyPartly supported: Font name onlyPartly supported: Font name only
ImagesSupported: All ImageSharp formatsSupported: All ImageSharp formatsSupported: Anchored drawingsSupported: Embedded data: URIs
SVG (Papyra.Svg package)Supported: True vectorsSupported: Native + PNG fallbackSupported: Native + PNG fallbackSupported: Browser-rendered SVG
HyperlinksSupported: YesSupported: YesSupported: YesSupported: Yes
Document metadataSupported: XMP + Info dictSupported: YesSupported: YesSupported: Title + meta tags
PDF/A conformanceSupported: 2b / 3b / 4Not supported: n/aNot supported: n/aNot supported: n/a
Encryption (AES-256, permissions)Supported: Passwords + permission flagsNot supported: n/aNot supported: n/aNot supported: n/a
Alpha colorsSupported: True transparencyPartly supported: Blended to whiteSupported: ARGBSupported: Native rgba()
WatermarksSupported: Behind content, true opacitySupported: VML shape in headersNot supported: Ignored (no pages)Partly supported: Overlay per section
Page backgroundsSupported: Color, gradient, imageSupported: Full-page shape in headersPartly supported: Sheet fill, no imageSupported: Native CSS backgrounds
Headings & table of contentsSupported: Bookmarks + page numbersSupported: Outline levels + TOC fieldNot supported: Ignored (no pages)Partly supported: h1–h6 + anchor nav
SectionsSupported: Per-section page geometrySupported: Native w:sectPr per sectionSupported: One worksheet per sectionSupported: Container + named @page

The remaining gaps and the details live on the public roadmap.

Companion app Beta

See the document while you write it

A cross-platform desktop preview (Windows, macOS, Linux) that receives documents from your running code on localhost.

  • A tab per push

    Every ShowInCompanion() call opens a new tab. - no export, no PDF viewer juggling, no temporary file needed.

  • Structure tree with bidirectional highlighting

    Click a tree node to highlight the element on the page; click the page to select its node in the tree.

  • Ctrl+Click jumps to your code

    Every element knows the builder call that created it. Ctrl+Click (Cmd+Click on macOS) opens that exact line in Rider,VS Code and Visual Studio.

  • Layout issues, flagged

    Overflowing fixed widths, text wider than its cell, clipped page breaks - shown as overlays and a clickable issues panel.

Companion app guide