Coming from QuestPDF
Papyra’s API is openly influenced by QuestPDF - if you’ve composed documents there, the builder style will feel familiar. The differences are architectural: Papyra renders one document model to four formats through its own PDF engine, while QuestPDF renders to PDF (and images) through SkiaSharp.
Why teams pick one or the other
Section titled “Why teams pick one or the other”Papyra when you need DOCX/XLSX from the same composition, archival PDF/A output verified against veraPDF, or a dependency tree without native binaries (no SkiaSharp).
Wondering how both compare to DevExpress Reporting, Telerik Reporting and the other .NET reporting suites? That is a different category - see the comparison page.
QuestPDF when you need its mature layout engine today - rich cells and its broad element catalog are ahead of Papyra right now (see known limitations - we publish this honestly).
Concept mapping
Section titled “Concept mapping”| QuestPDF | Papyra |
|---|---|
Document.Create(container => container.Page(...)) |
Document.Create(doc => ...) - no page wrapper; page setup is doc.PageSize(...), doc.Margin(...) |
page.Header() / page.Footer() |
doc.Header(h => ...) / doc.Footer(f => ...) |
page.Content().Column(...) |
implicit - builder calls stack vertically |
.Text("…").FontSize(10).Bold() |
.Text("…", TextStyle.Default.WithFontSize(10).Bold()) - chainable the same way, starting from TextStyle.Default |
.Strikethrough() / .Superscript() / .LetterSpacing(0.1f) |
TextStyle.Default.WithStrikethrough() / .Superscript() / .WithLetterSpacing(1.5f) - spacing is absolute points per glyph, not an em factor |
.Width(120).Image(x) / .Height(40).Image(x) / .Image(x).FitArea() |
doc.Image(x, width: 120f) / (height: 40f) / both together = contain-in-box; alignment: replaces .AlignCenter() around the image |
.Svg(svg) / .Svg(svg).FitArea() |
doc.Svg(bytes, ...) from the optional Papyra.Svg package - same sizing parameters as Image. QuestPDF rasterizes SVG through Skia; Papyra emits true PDF vectors where the SVG’s features allow it |
hand-built bullets (row.ConstantItem(12).Text("•")) |
doc.List(l => l.Item("…", sub => sub.Item("nested"))) - native nesting, number formats, start values |
column.Spacing(12) |
new ParagraphStyle(SpacingAfter: 12f) per paragraph or via doc.DefaultParagraphStyle(...) - between non-paragraph blocks (tables, rows), insert a spacer paragraph |
row.RelativeItem(2) / row.ConstantItem(210) |
row.RelativeItem(item => ..., weight: 2f) / row.FixedItem(210f, item => ...) |
table.ColumnsDefinition(c => c.ConstantColumn(48).RelativeColumn()) |
table.Columns(c => c.FixedColumn(48f).RelativeColumn()) |
table.Header(...) (repeats) |
table.Header(row => ..., TableRowRepeat.EveryPage) - footers can repeat too |
cell.Element(...) composition |
Cell(p => ...) takes the full paragraph API - mixed runs, links, images |
Colors.Blue.Medium |
Colors.Blue.Medium - same palette, same names |
QuestPDF.Settings.License = LicenseType.Community |
<PapyraLicense>…</PapyraLicense> in the project file, checked at build time (licensing) |
.GeneratePdf(path) |
document.RenderAsPdf() returns byte[] - plus DOCX and XLSX |
Side by side
Section titled “Side by side”The Papyra repository ships the same invoice implemented twice - once per library - so you can diff real code instead of toy snippets:
dotnet run --project Samples# writes invoice-sample-….pdf (Papyra), invoice-sample-….docx, invoice-sample-….xlsx# and invoice-sample-…-questpdf.pdf (QuestPDF) side by sideCompare Samples/PapyraInvoiceReport.cs with Samples/QuestPdfInvoiceReport.cs.
The shape is close: both compose a header, address rows, a line-item table with repeating header, totals, and payment details. The Papyra version has no Page/Content nesting; styles (TextStyle, ParagraphStyle, CellStyle) are immutable records, buildable either with named arguments (new TextStyle(FontSize: 10f, ...)) or chained fluently from a .Default (TextStyle.Default.WithFontSize(10f)...) - same end result, pick whichever reads better at the call site.
What you’ll miss (today)
Section titled “What you’ll miss (today)”Moving from QuestPDF you will notice these gaps first:
- Automatic hyphenation is opt-in via
doc.Hyphenation("de")(seven embedded languages); soft hyphens and CJK line breaking work out of the box - Page numbers work in headers/footers and in body text (QuestPDF’s
CurrentPageNumber()/TotalPages()map toPageNumber()/TotalPages()/PageXofY()); noShowOnce/SkipOncevariants - Smaller element catalog: no
Decoration,Inlined,Layers, canvas drawing (a public vector drawing API is on the roadmap)
What you’ll gain
Section titled “What you’ll gain”document.RenderAsDocx()andXLSXfrom the unchanged composition- PDF/A-2b/3b/4 with veraPDF-verified conformance
- Three managed dependencies, no native SkiaSharp binaries to deploy
- The companion app (beta): live preview with click-to-source and layout issue detection
© 2026 Stefan Nikolei. Payments are processed by Paddle.com as merchant of record.