4. Headers, footers, page layout
Page geometry, running headers and footers, and multi-page layout.
Up to now you have been adding content and letting the page take care of itself. This chapter is about the page itself: how big it is, which way up it sits, how many columns the text flows into, what runs along the top and bottom of every page, and how you control where one page ends and the next begins.
The unit that owns all of this is the section. A Section
(namespace Rivoli.Pdf.Model) is a run of pages that share one page setup. Change
the geometry and you start a new section; everything inside a section gets the
same size, margins, columns, and header/footer.
Page setup lives on the section
Section titled “Page setup lives on the section”A section inherits the document defaults unless it overrides them. Leave
PageSize or Margin null and the section uses Document.DefaultPageSize /
Document.DefaultMargin; set them and the override applies to that section only.
using Rivoli.Pdf;using Rivoli.Pdf.Model;
var doc = Document.Create("Field Report", "Survey Team");doc.DefaultPageSize = PageSize.A4;doc.DefaultMargin = Margin.Standard; // 1 inch (72 pt) all round
var body = doc.AddSection(); // inherits A4 + Standardbody.AddHeading("Summary");
var appendix = doc.AddSection();appendix.PageSize = PageSize.Legal; // this section onlyappendix.Margin = Margin.Narrow; // 0.5 inchappendix.AddHeading("Raw measurements");PageSize is a readonly record struct of width and height in points. The named
sizes are Letter, Legal, A3, A4, A5, and Tabloid; build a custom size
with PageSize.FromInches(w, h) or PageSize.FromMillimeters(w, h). Margin
offers None, Narrow (36 pt), Standard (72 pt), Wide (108 pt), plus
Margin.All(x) and Margin.Symmetric(horizontal, vertical).
Landscape with Rotate()
Section titled “Landscape with Rotate()”PageSize.Rotate() returns the same size with width and height swapped. That is
all “landscape” means: a wider-than-tall page:
var charts = doc.AddSection();charts.PageSize = PageSize.A4.Rotate(); // 842 x 595 instead of 595 x 842charts.AddHeading("Trends");Columns
Section titled “Columns”A section flows its body into one or more columns. ColumnLayout is a
readonly record struct of (ColumnCount, ColumnGap) in points, with named
presets and factory methods:
var news = doc.AddSection();news.WithColumns(ColumnLayout.TwoColumns); // 2 columns, 36 pt gap
news.WithColumns(ColumnLayout.Create(3)); // 3 columns, default gapnews.WithColumns(ColumnLayout.Create(2, 24)); // 2 columns, 24 pt gap| Member | Layout |
|---|---|
ColumnLayout.OneColumn | 1 column (the default) |
ColumnLayout.TwoColumns | 2 columns, 36 pt gap |
ColumnLayout.ThreeColumns | 3 columns, 24 pt gap |
ColumnLayout.FourColumns | 4 columns, 18 pt gap |
ColumnLayout.Create(n) | n columns, gap of 36 / n pt |
ColumnLayout.Create(n, gap) | n columns, explicit gap |
WithColumns returns the section, so it chains. You can also set the property
directly: news.ColumnLayout = ColumnLayout.TwoColumns;.
Headers and footers
Section titled “Headers and footers”Running headers and footers are configured per section through a
HeaderFooterConfig, which carries Header and Footer objects. Each Header
or Footer is a small container with its own Blocks collection and an optional
Height: you fill it with the same blocks you use in the body.
using Rivoli.Pdf.Builders;
var report = doc.AddSection();
report.WithHeaderFooter(new HeaderFooterConfig{ Header = (Header)new Header().Add(new Paragraph("Quarterly Report")), Footer = (Footer)new Footer().Add( new Paragraph().Add("Confidential")),});HeaderFooterConfig also lets you vary the running content by page position:
useful for two-sided print layouts and a distinct cover page:
| Member | When it applies |
|---|---|
Header / Footer | Default for every page in the section |
FirstPageHeader / FirstPageFooter | Overrides on the section’s first page |
EvenPageHeader / EvenPageFooter | Overrides on even pages |
OddPageHeader / OddPageFooter | Overrides on odd pages |
The read-only HasFirstPageDifference and HasEvenOddDifference flags report
whether any of those overrides are set.
Controlling page breaks
Section titled “Controlling page breaks”The model carries intent about breaking; the layout engine makes the actual decision. Four controls cover the common cases.
Force a break. Add a PageBreak block: the following content starts on a
fresh page:
section.AddParagraph("End of part one.");section.AddPageBreak(); // or section.Add(new PageBreak());section.AddHeading("Part two");Start a block on a new page. Every block carries StartOnNewPage; set it and
the block begins a page rather than continuing the current one:
var chapter = new Paragraph("Chapter 2") { StartOnNewPage = true };section.Add(chapter);Keep a block intact. KeepTogether asks the layout engine not to split a
block across a page boundary: push the whole thing to the next page rather than
break it:
section.Add(new Table { KeepTogether = true } .Header("Metric", "Value") .Row("Latency", "12 ms"));Keep two blocks adjacent. KeepWithNext binds a block to the one after it, so
a heading never strands at the bottom of a page away from its paragraph:
section.Blocks[^1].KeepWithNext = true; // e.g. a heading + its first paragraphParagraphs additionally honour WidowControl and OrphanControl (both default to
2 lines) so the engine avoids leaving a single line alone at the top or bottom of
a page.
- The layout engine: how these hints become real page breaks.
- 5. Forms & interactivity: links, anchors, and form fields.
- Document structure: keeping large multi-section documents readable.