Feature parity checklist¶
What it takes to be a complete alternative to QuestPDF: every capability its 2026.5.0 release offers, whether we have it, and the phase that delivered it. The reference column names QuestPDF's public API so coverage can be audited against it; our own names come from the glossary and differ by design (ADR 0002). How the two libraries relate is set out in Rustaveli.Pdf and QuestPDF.
Source: dotnet run eng/parity-surface.cs, which enumerates the oracle's public API by reflection
(ADR 0008). Rerun it and diff against this file when auditing.
Status: ✅ have · 🟡 partial · ❌ missing · ➖ deliberately not matched (reason given)
Phases: 1 vocabulary · 2 managed writer · 3 text engine · 4 layout and styling · 5 images and SVG · 6 output and conformance · 7 document operations · 8 preview tool · 9 docs and first release
Document and page¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Build a document from pages; reusable document classes | Document.Create, IDocument |
✅ | — |
| Page size, named sizes, landscape/portrait | PageDescriptor.Size, PageSizes, PageSizeExtensions |
✅ | — |
| Margins, per side | Margin* |
✅ | — |
| Continuous (single tall) page | ContinuousSize |
✅ | — |
| Page size bounded by content (min/max) | MinSize, MaxSize |
✅ | — |
| Header, footer, content, background, foreground slots | Header, Footer, Content, Background, Foreground |
✅ | — |
| Page colour | PageColor |
✅ | — |
| Page-level default text style and direction | DefaultTextStyle, ContentFrom* |
✅ | — |
| Units: pt, mm, cm, inch, plus metre, feet, mil | Unit |
✅ plus pica | — |
| Metadata: title, author, subject, keywords, creator, producer, dates | DocumentMetadata |
✅ | — |
| Document language | DocumentMetadata.Language |
✅ DocumentInfo.Language |
— |
| Merge generated documents, continuous or original numbering | Document.Merge, MergedDocument |
✅ Document.Merge, NumberPartsSeparately |
— |
Layout¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Vertical sequence with spacing | Column, Stack |
✅ | — |
| Horizontal sequence: constant, relative, auto items, spacing | Row |
✅ | — |
| Tables: constant/relative columns, auto and explicit placement, spans, header/footer bands | Table |
✅ | — |
| Table: stretch last cells to table bottom | ExtendLastCellsToTableBottom |
✅ | — |
| Uniform grid | Grid |
✅ | — |
| Flow of inline items with wrapping, alignment, baseline | Inlined |
✅ | — |
| Newspaper columns, balanced | MultiColumn |
✅ | — |
| Stacked layers, one primary | Layers |
✅ | — |
| Content with repeating before/after bands | Decoration |
✅ | — |
| Bulleted, numbered, lettered and roman lists | — | ✅ ours only | — |
| Padding, per side | Padding* |
✅ | — |
| Width/height, min/max | Width, MinWidth, … |
✅ | — |
| Extend to fill | Extend* |
✅ | — |
| Shrink to content | Shrink*, MinimalBox |
✅ | — |
| Aspect ratio | AspectRatio |
✅ | — |
| Alignment, horizontal and vertical | Align* |
✅ FlushLeft, Centered, FlushRight, FlushTop, Middle, FlushBottom: measured as their content, placed in the room granted |
— |
| Unconstrained | Unconstrained |
✅ | — |
| Z-order | ZIndex |
✅ | — |
Transforms¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Translate | TranslateX/Y |
✅ | — |
| Scale, uniform and per axis | Scale, ScaleHorizontal/Vertical |
✅ | — |
| Scale to fit | ScaleToFit |
✅ | — |
| Quarter-turn rotation | RotateLeft/Right |
✅ | — |
| Arbitrary-angle rotation | Rotate(angle) |
✅ | — |
| Flip | Flip* |
✅ | — |
Flow control and pagination¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Page break | PageBreak |
✅ | — |
| Show if (static condition) | ShowIf(bool) |
✅ | — |
| Show if (per page: page number, total pages) | ShowIf(Predicate<ShowIfContext>) |
✅ | — |
| Show once, skip once | ShowOnce, SkipOnce |
✅ | — |
| Keep together / never split | ShowEntire, PreventPageBreak |
✅ KeepTogether, KeepTogetherWherePossible |
— |
| Ensure space | EnsureSpace |
✅ | — |
| Repeat on every page | Repeat |
✅ | — |
| Stop paging (draw first page only) | StopPaging |
✅ | — |
| Lazy composition for very large documents, optionally cached | Lazy, LazyWithCache |
✅ | — |
| Reusable components | IComponent, Component |
✅ | — |
| Dynamic components with state, per-page composition | IDynamicComponent, Dynamic, DynamicContext |
✅ state handed from page to page | — |
| Capture a content position; query it later | CaptureContentPosition, GetContentCapturedPositions |
✅ | — |
Styling¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Solid background | Background |
✅ | — |
| Linear-gradient background | BackgroundLinearGradient |
✅ | — |
| Borders per side, colour | Border*, BorderColor |
✅ | — |
| Border alignment inside/middle/outside | BorderAlignment* |
✅ | — |
| Linear-gradient border | BorderLinearGradient |
✅ | — |
| Corner radius, uniform | CornerRadius |
✅ | — |
| Corner radius per corner | CornerRadiusTopLeft, … |
✅ | — |
| Box shadow (blur, spread, offset, colour) | Shadow, BoxShadowStyle |
✅ | — |
| Lines: thickness | LineHorizontal/Vertical |
✅ | — |
| Lines: colour, dash pattern | LineDescriptor |
✅ | — |
| Lines: gradient | LineDescriptor.LineGradient |
✅ | — |
| Colour: RGB(A), hex | Color |
✅ | — |
| Colour: CMYK and spot inks, tints | — | ✅ ours only: process colour, separations with a fallback | — |
| Colour palette | Colors (Material) |
➖ replaced by Ink basics (ADR 0004) |
1 |
| Named style sheets | — | ✅ ours only (ADR 0003): StyleSheet of type, paragraph and frame styles, basedOn, Style(name) |
— |
| Placeholder box | Placeholder |
✅ Placeholder(label) |
— |
| Sample data for prototyping | Placeholders (lorem ipsum, dates, prices, …) |
✅ seeded, so the same every time | — |
Text¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Spans with style; plain text; object text | Text, Span, Line, EmptyLine |
✅ | — |
| Font family, size, colour, background | ✅ | — | |
| Font family fallback list | FontFamily(params string[]), Fallback |
✅ per style, then per library | — |
| Weights Thin…Black | Thin … Black |
✅ | — |
| Weight ExtraBlack (950) | ExtraBlack |
✅ | — |
| Italic | Italic |
✅ | — |
| Underline, strikethrough | Underline, Strikethrough |
✅ | — |
| Overline | Overline |
✅ | — |
| Decoration style (solid, double, dotted, dashed, wavy), colour, thickness | Decoration* |
✅ placed by the font's own metrics | — |
| Line height, letter spacing | LineHeight, LetterSpacing |
✅ | — |
| Word spacing | WordSpacing |
✅ | — |
| Subscript, superscript | Subscript, Superscript |
✅ | — |
| Alignment left/centre/right | AlignLeft, … |
✅ | — |
| Alignment start/end (direction-aware) | AlignStart, AlignEnd |
✅ | — |
| Justify | Justify |
✅ | — |
| Clamp to N lines with ellipsis | ClampLines |
✅ | — |
| First-line indent, paragraph spacing | ParagraphFirstLineIndentation, ParagraphSpacing |
✅ | — |
| Break anywhere | WrapAnywhere |
✅ | — |
| Unicode line breaking (UAX #14) | — | ✅ Unicode 16, default rules | — |
| Per-span direction; bidi reordering | Direction* |
✅ UAX #9, runs isolated in their own direction | — |
| OpenType features (ligatures, small caps, figures, …) | EnableFontFeature, FontFeatures |
✅ every GSUB lookup type, per script | — |
| Complex-script shaping | — (built in) | ✅ Shaping package (HarfBuzz) |
— |
| Inline elements in a paragraph, with vertical alignment | Element(TextInjectedElementAlignment) |
✅ | — |
| Current page, total pages | CurrentPageNumber, TotalPages |
✅ | — |
| Page number formatting (roman, custom) | TextPageNumberDescriptor.Format |
✅ | — |
| Section page numbers: begin, end, within, total within | BeginPageNumberOfSection, … |
✅ | — |
| Page number of a captured location | PageNumberOfLocation |
✅ FolioOf (their former name for BeginPageNumberOfSection) |
— |
| Glyph-availability check | Settings.CheckIfAllTextGlyphsAreAvailable |
✅ per export | — |
Links and navigation¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| External hyperlink on a region or text | Hyperlink, ExternalLink |
✅ | — |
| Internal link to a named section | SectionLink, InternalLink |
✅ | — |
| Named destinations | Section, Location |
✅ | — |
| Links to captured locations from text | ExternalLocation, InternalLocation |
✅ TextComposer.Link, CrossReference (their former names for Hyperlink, SectionLink) |
— |
| Outline / bookmarks | — (derived from semantic tags) | ✅ Bookmark(title, level) |
— |
Images and vector graphics¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Image from bytes, stream, file; shared image object | Image, Image.From* |
✅ | — |
| Fit width, height, area, unproportional | ImageScaling, Fit* |
✅ | — |
| Per-image compression quality and target DPI | WithCompressionQuality, WithRasterDpi |
✅ per image and per export, by SkiaImageProcessor from the Raster package |
— |
| Keep original image bytes | UseOriginalImage |
✅ always, wherever PDF can carry the encoding | — |
| Dynamic images generated at the final size | GenerateDynamicImageDelegate |
✅ | — |
| SVG, static | Svg, SvgImage |
✅ read into vector Artwork; radial gradients as their mean colour, no filters or masks |
— |
| SVG, dynamic (at the final size) | Svg(Func<Size, string>) |
✅ Artwork(size => Artwork.FromSvg(...)) |
— |
| Custom vector drawing | Canvas(DrawOnCanvas) |
✅ Artwork, our own drawing API, vector in the PDF |
— |
Fonts¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Register fonts from stream, file, embedded resource, custom name | FontManager.Register* |
✅ | — |
| System font discovery; extra discovery paths; opt out | Settings.UseEnvironmentFonts, FontDiscoveryPaths |
✅ per library | — |
| Font subsetting | — (built in) | ✅ TrueType subset, CFF whole | — |
| Bundled default font | Lato | ✅ Noto Sans, the default typeface: Latin, Greek and Cyrillic in regular, bold, italic and bold italic, the same on every machine | — |
| Fallback per character | built in | ✅ named fallbacks, registered, installed, bundled | — |
| Pair kerning | built in (HarfBuzz) | ✅ GPOS and kern |
— |
Output¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| PDF to bytes, stream, file | GeneratePdf |
✅ | — |
| Generate and open in the default viewer | GeneratePdfAndShow |
✅ ExportPdfAndOpen |
— |
| Page images: PNG, JPEG, WebP, DPI, quality | GenerateImages, ImageGenerationSettings |
✅ ExportImages in Rustaveli.Pdf.Raster |
— |
| Parallel generation | built in | ✅ no process-wide lock; one document exported from many threads at once, each export after the first composing its own copy | — |
| SVG pages | GenerateSvg |
✅ ExportSvg, text as outlines |
— |
| XPS | GenerateXps |
✅ ExportXps, Windows only as theirs is |
— |
| Stream compression switch | DocumentSettings.CompressDocument |
✅ PdfExportOptions.Compress |
— |
| PDF/A-2b | PDFA_Conformance.PDFA_2B |
✅ PdfAConformance.PdfA2B |
— |
| PDF/A-2u, 3b, 3u | PDFA_Conformance |
✅ PdfA2U, PdfA3B, PdfA3U |
— |
| PDF/A-2a, 3a (tagged) | PDFA_Conformance |
✅ PdfA2A, PdfA3A |
— |
| PDF/UA-1 with semantic tagging (headings, lists, tables, figures, alt text, language, artifacts) | PDFUA_Conformance, Semantic*, AsSemanticHorizontalHeader |
✅ PdfUAConformance.PdfUA1, Tagged(ContentTag), Untagged, Language, RowHeading; paragraphs, lists and links tagged without asking |
— |
| Every standard checked by the reference validator | — | ✅ veraPDF over every specimen at 2b, 3u, 2a with UA-1, and UA-1 alone | — |
Document operations¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Load an existing PDF, with password | DocumentOperation.LoadFile |
✅ PdfFile.Open(path, password), damaged files repaired |
— |
| Merge files | MergeFile |
✅ PdfFile.Append |
— |
| Select pages | TakePages |
✅ PdfFile.KeepPages("1-3, 5, 8-last") |
— |
| Overlay and underlay with page mapping | OverlayFile, UnderlayFile, LayerConfiguration |
✅ PdfFile.Overlay, Underlay with onto and from pages |
— |
| Attachments with relationship (ZUGFeRD / Factur-X) | AddAttachment, DocumentAttachment |
✅ PdfFile.Attach(FileAttachment); a Factur-X PDF/A-3 file passes veraPDF |
— |
| Encrypt 40/128/256-bit with permissions; decrypt; remove restrictions | Encrypt, Decrypt, RemoveRestrictions |
✅ Protect, Unprotect, LiftRestrictions, and PdfExportOptions.Protection when generating; checked against qpdf both ways |
— |
| Linearise (fast web view) | Linearize |
✅ PdfFile.OptimizeForWeb, hint tables and outline included; qpdf finds no linearization errors |
— |
| Extend XMP metadata | ExtendMetadata |
✅ PdfFile.AddMetadata |
— |
Diagnostics and tooling¶
| Capability | Reference | Status | Phase |
|---|---|---|---|
| Layout errors raised with explanation | DocumentLayoutException |
✅ | — |
| Visual debug areas and pointers | DebugArea, DebugPointer |
✅ ShowFrameEdges(label, ink), Named(name) |
— |
| Debug mode with element-tree trace of a layout failure | Settings.EnableDebugging |
✅ always: a layout failure traces the frames down to the one that could not fit, named frames by name, at no cost until one fails | — |
| Layout exception threshold | DocumentLayoutExceptionThreshold |
✅ Document.PageLimit |
— |
| Caching switch | Settings.EnableCaching |
➖ no global switch; caching is internal | 2 |
| Live previewer with hot reload, element inspector, source navigation | ShowInCompanion, ShowInPreviewer |
✅ DocumentPreview.Preview in the browser, redrawn on hot reload under dotnet watch, layout failures traced on the page; an inspector outlines the frame under the pointer, lists every frame drawn on a page with where it lies and its room, and opens the line that made it in the editor |
— |
| Licence selection | Settings.License, LicenseType |
➖ MIT, nothing to select | — |