Skip to content

3. Tables, lists, images

Structured content: tables, ordered and unordered lists, and embedded images.

Paragraphs carry prose; this chapter covers the three block types that carry structure. Tables, lists, and images are all blocks, so they sit alongside paragraphs in a section and inherit the same styling and layout machinery. Each has a fluent helper that keeps the common case short.

using Rivoli.Pdf;
using Rivoli.Pdf.Builders;
using Rivoli.Pdf.Model;

A Table has an optional header row and any number of data rows. The fluent helper is AddTable, which hands you a Table to configure:

section.AddTable(table => table
.Header("Quarter", "Revenue", "Profit")
.Row("Q1", "$1.2M", "$200K")
.Row("Q2", "$1.5M", "$250K")
.Row("Q3", "$1.8M", "$310K"));

Header, Row, and Rows each return the table, so they chain. Use Rows when you already have the data as a collection: it adds many data rows at once:

section.AddTable(table => table
.Header("Name", "Role")
.Rows(
new[] { "Ada", "Engineer" },
new[] { "Grace", "Architect" },
new[] { "Alan", "Researcher" }));

Header and Row are fluent aliases for the underlying AddHeaderRow and AddDataRow methods on Table; reach for those directly if you prefer.

By default columns size themselves to their content. To pin them, set fixed widths (in points) on the Table:

section.AddTable(table =>
{
table.SetFixedColumnWidths(120, 240);
table.Header("Field", "Value")
.Row("Order ID", "A-10293")
.Row("Status", "Shipped");
});

SetColumnWidths accepts ColumnWidth values for proportional or auto widths when you need a mix; SetFixedColumnWidths is the simple all-points form.

Lists come in two flavours, with dedicated helpers for each:

section.AddUnorderedList(list => list
.Item("First finding")
.Item("Second finding")
.Item("Third finding"));
section.AddOrderedList(list => list
.Items("Gather data", "Run analysis", "Publish report"));

Item(text) adds one item and returns the list; Items(...) adds several at once, accepting either a params string[] or an IEnumerable<string>. Both helpers chain.

Behind the helpers, a list is a List block created by List.Unordered() or List.Ordered(). You can build one directly and add it with section.Add(...): useful when you want to set list properties:

var list = List.Ordered(startNumber: 5); // numbering starts at 5
list.Add("Fifth step");
list.Add("Sixth step");
section.Add(list);

List also exposes MarkerStyle (Disc, Circle, Square, or Custom with a CustomMarker character) for unordered lists, and StartNumber for ordered ones.

List items nest. A ListItem has its own Items collection and an Add method, so you build sub-lists by adding items to an item rather than to the list:

var outline = List.Unordered();
var chapter = new ListItem("Chapter 1");
chapter.Add("Section 1.1")
.Add("Section 1.2");
outline.Add(chapter);
outline.Add("Chapter 2");
section.Add(outline);

ListItem.Add(string) appends a nested item and returns the parent item, so a chain of Add calls builds siblings under the same parent. Nest as deeply as the content requires: each level indents under its parent’s marker.

Images appear at two scopes: as a block on their own, or inline within a line of text.

AddImage places an image as its own block, loaded from a file path. Width and height are optional points; omit them to use the image’s natural size:

section.AddImage("logo.png"); // natural size
section.AddImage("chart.png", width: 360, height: 200);

Under the helper this builds an ImageBlock. Build one directly for more control : alignment, or sizing mode:

var figure = new ImageBlock("diagram.png")
.WithSize(400, 260)
.WithAlignment(Rivoli.Pdf.Styling.TextAlignment.Center);
section.Add(figure);

An InlineImage flows inside a paragraph, on the text baseline, for an icon next to a word, or a small inline glyph. Add it to a paragraph’s inlines:

var para = new Paragraph();
para.Add("Build passing ");
para.Inlines.Add(new InlineImage("check.png", width: 12, height: 12));
para.Add(", all checks green.");
section.Add(para);

InlineImage carries a BaselineOffset you can set to raise or lower it relative to the surrounding text, which is handy for fine vertical alignment against the line.

using Rivoli.Pdf;
using Rivoli.Pdf.Builders;
using Rivoli.Pdf.Model;
var doc = Document.Create("Structured Content");
doc.DefaultPageSize = PageSize.A4;
doc.DefaultMargin = Margin.Standard;
var section = doc.AddSection();
section.H1("Results");
section.AddTable(table => table
.Header("Metric", "Value")
.Row("Revenue", "$5.4M")
.Row("Margin", "22%"));
section.AddSpace();
section.H2("Next steps");
section.AddOrderedList(list => list
.Items("Review with the board", "Approve budget", "Kick off Q4 plan"));
doc.SavePdf("structured.pdf");

You’ve now covered the document model, styling, and structured content. From here:

  • How-to guides: task-focused recipes for headers and footers, page numbers, security, and more.
  • Components & reuse: packaging repeated structure into reusable building blocks.