Working with the builder
Rivoli PDF gives you three ways to construct the same document tree, and they interoperate freely. This page is a decision guide: which style to reach for, and how to mix them without friction.
The three styles are:
DocumentBuilder: a dedicated builder withWithSection/Build.- Fluent helpers: extension methods like
H2,AddParagraphdirectly on the model types. - Direct POCO: set properties and call
Addon plain objects.
All three produce identical trees. There is no “blessed” path. Pick whichever reads best for the code in front of you.
DocumentBuilder
Section titled “DocumentBuilder”DocumentBuilder (namespace Rivoli.Pdf.Builders) wraps a Document and offers a
declarative, top-down API that ends in Build():
using Rivoli.Pdf.Builders;
var doc = DocumentBuilder.Create("Report", "Finance") .WithPageSize(PageSize.A4) .WithMargin(36) .WithDefaultStyle(s => s.WithFontFamily("Helvetica").WithFontSize(11)) .WithSection(s => s.H1("Overview").AddParagraph("...")) .Build();Create() has overloads for title and author; From(existingDoc) wraps a document
you already have. Reach for DocumentBuilder when you want document-wide setup
(page size, margins, default style) and structure in one fluent expression, and the
structure is mostly fixed.
Fluent helpers on the model
Section titled “Fluent helpers on the model”The extension methods (H1–H6, AddParagraph, AddTable,
Header, Row, Item, …) read naturally and chain. They’re the everyday way to add
content:
var doc = Document.Create("Report");var section = doc.AddSection();
section .H1("Quarterly Report") .AddParagraph("Prepared by the finance team.") .AddTable(t => t .Header("Quarter", "Revenue") .Row("Q1", "$1.2M") .Row("Q2", "$1.5M"));Reach for the fluent helpers for concise, readable content: the bulk of normal document building.
Direct POCO
Section titled “Direct POCO”Every type is a plain object, so you can assign properties and manipulate collections directly. This shines in loops, conditionals, and when deserializing from data, where a fluent chain would fight the control flow:
var section = doc.AddSection();
foreach (var product in products){ var para = new Paragraph(); para.Inlines.Add(new Run(product.Name) { Style = boldStyle }); para.Inlines.Add(new Run($", ${product.Price}")); section.Blocks.Add(para);}
// Fine-grained tweaks after the fact:section.Blocks[0].StartOnNewPage = true;section.Blocks[0].Id = "first-para";Reach for direct POCO when you need fine-grained control, are building from loops or conditionals, or are integrating with an existing object model.
Mix them freely
Section titled “Mix them freely”The three styles share the same types, so you switch mid-stream whenever it reads better:
// Start with the builder for setup...var doc = DocumentBuilder.Create("My Document") .WithPageSize(PageSize.A4) .Build();
var section = doc.AddSection();
// ...fluent helpers for content...section.H2("Items");
// ...direct POCO for a data-driven loop...foreach (var item in items) section.Blocks.Add(new Paragraph(item.Name));
// ...and a direct tweak at the end.section.Blocks[^1].KeepWithNext = true;Choosing at a glance
Section titled “Choosing at a glance”| Use… | When |
|---|---|
DocumentBuilder | Document-wide setup + fixed structure in one expression |
| Fluent helpers | Everyday content; concise, readable chains |
| Direct POCO | Loops, conditionals, deserialization, fine-grained control |
Guidelines
Section titled “Guidelines”- Don’t force a single style. The library is designed for mixing; optimize each passage for readability.
- Use
DocumentBuilderfor the frame, helpers for the content, POCO for the data-driven bits: a common and comfortable division. - For larger documents, combine any of these with document structure and components so the top level stays a clean outline.