PDF Rendering
The Pragmatic.Documents.Pdf package renders a DocumentModel to a PDF byte stream. It uses a native-backed renderer for output quality and font handling.
API surface
Section titled “API surface”public static class PdfRenderer{ public static byte[] Render(DocumentModel model, PdfResources? resources = null); public static void RenderTo(Stream output, DocumentModel model, PdfResources? resources = null);}
public static class PdfOperations{ public static uint GetPageCount(ReadOnlySpan<byte> pdf);}Rendermaterialises the whole PDF in memory. Fine for invoices, letters, one-offs.RenderTo(Stream, ...)streams directly to the destination. Use this for large reports.GetPageCountis a lightweight PDF-aware scan — no full parsing.
Basic example
Section titled “Basic example”using Pragmatic.Documents.Model;using Pragmatic.Documents.Pdf;
var doc = new DocumentBuilder() .Title("Sample") .Page(p => p .Heading("Hello, PDF") .Paragraph(new TextNode { Content = "Rendered by Pragmatic." })) .Build();
using var fs = File.Create("sample.pdf");PdfRenderer.RenderTo(fs, doc);Nodes supported
Section titled “Nodes supported”| Node | Effect in PDF |
|---|---|
TextNode | Text run with optional NodeStyle (font, size, colour, weight) |
HeadingNode | Heading with auto-generated bookmark (for TOC) |
ParagraphNode | Block-level paragraph with inline children |
TableNode | Table with header row, optional styling |
ImageNode | Raster image (PNG, JPEG) with width/height/alt |
BarcodeNode | Rendered barcode (default QR code; several types supported) |
HyperlinkNode | Clickable link inside the PDF |
TocNode | Table of contents built from heading bookmarks |
PageBreakNode | Starts a new page |
Spacer, HorizontalRule | Layout separators |
FieldNode | Dynamic field — PageNumber, PageCount, Date, etc. |
BookmarkNode | Named anchor for internal navigation |
FootnoteNode | Footnote marker + footer entry on the same page |
Fields (FieldType.PageNumber, FieldType.PageCount, FieldType.Date) are resolved during rendering; you don’t supply current-page information.
Page configuration
Section titled “Page configuration”var doc = new DocumentBuilder() .Size(PageSize.A4) // A4, Letter, Legal, Custom .Landscape() // or WithMargins(Margins.Normal | Margins.Narrow | Margins.Wide) .WithMargins(new Margins(top: 72, right: 72, bottom: 72, left: 72)) .Page(p => { /* ... */ }) .Build();All dimensions are in points (1 point = 1/72 inch). PageSize.A4 is 595 × 842 points.
Headers and footers
Section titled “Headers and footers”.Page(p => p .Header( new TextNode { Content = "Pragmatic Quarterly Report", Style = new NodeStyle { FontSize = 9, Color = "#666" } }) .Footer( new TextNode { Content = "Page " }, new FieldNode { FieldType = FieldType.PageNumber }, new TextNode { Content = " of " }, new FieldNode { FieldType = FieldType.PageCount }) .Heading("Introduction") .Paragraph(...))Header and footer nodes repeat on every page of the same Page block.
Tables
Section titled “Tables”.Table(t => t .HeaderRow("Item", "Quantity", "Unit Price", "Total") .Row("Consulting", "10 h", "€150", "€1500") .Row("Implementation", "40 h", "€120", "€4800") .Row("QA", "12 h", "€90", "€1080"))Tables size their columns automatically. For manual widths or per-cell styling, build the TableNode / TableCell directly rather than via the HeaderRow/Row shortcuts.
Table of contents
Section titled “Table of contents”.Page(p => p .Toc(maxLevel: 3, title: "Contents")).Page(p => p .Heading("Chapter 1", level: 1) .Paragraph(...) .Heading("Section 1.1", level: 2) .Paragraph(...)).Page(p => p .Heading("Chapter 2", level: 1) .Paragraph(...))The TOC is generated during rendering by walking the heading bookmarks that appeared before it. Each entry includes the heading text and the target page number.
Limitations:
TocNodemust appear before the headings it references in document order- Page numbers are resolved after layout, so very long headings that wrap differently than expected may shift page numbers
Fonts and images
Section titled “Fonts and images”Attach fonts and images through PdfResources:
var resources = new PdfResources();resources.AddFont("Inter", File.ReadAllBytes("fonts/Inter-Regular.ttf"));resources.AddImage("logo", File.ReadAllBytes("assets/logo.png"));
// Reference them in nodes.Image("logo") // uses embedded image by name.Text("Branded title", style: new NodeStyle { FontFamily = "Inter" })
PdfRenderer.RenderTo(fs, doc, resources);If no resources are supplied the renderer falls back to embedded default fonts. See the samples for concrete font / image wiring.
Barcodes and QR codes
Section titled “Barcodes and QR codes”.Barcode("https://pragmaticdesign.net", type: BarcodeType.QrCode).Barcode("1234567890128", type: BarcodeType.Ean13)Available types include QrCode, Code128, Code39, Ean13. The barcode renders at a default size; set it explicitly via BarcodeNode { Width = 120, Height = 120 }.
Performance
Section titled “Performance”- For large reports (many pages, embedded images) use
RenderTo(Stream, ...)instead ofRenderso you don’t hold the whole output in memory. - The renderer is deterministic — the same model produces byte-identical output (modulo creation dates), which makes PDFs snapshot-testable.
- Typical single-page invoice renders in ~5-15ms on modern hardware.
Known limitations
Section titled “Known limitations”- No full CSS/HTML input — feed the document model directly.
- No form fields (fillable PDFs are out of scope).
- No digital signatures — sign with a separate step after rendering.
- No PDF/A profile tagging yet.
Related
Section titled “Related”- docx-rendering.md — same document model, DOCX output
- markup-parser.md —
.pdxdocmarkup →DocumentTemplate - templating.md — bind data into templates before rendering