New in v1.4.0. Give long documents a navigable bookmark tree (
/Outlines) and logical page numbering (/PageLabels) — roman-numbered front matter, prefixed appendices, custom starting numbers. Both are opt-inDocumentParamsfields and add zero overhead when unused.
import { buildDocumentPDFBytes } from 'pdfnative';
const bytes = buildDocumentPDFBytes({
title: 'Annual Report',
outline: 'auto', // bookmarks derived from headings
pageLabels: [
{ startPage: 0, style: 'roman' }, // startPage is 0-based — i, ii, iii … (front matter)
{ startPage: 3, style: 'decimal' }, // 1, 2, 3 … (body, from the 4th page)
],
blocks: [
{ type: 'heading', level: 1, text: 'Overview' },
{ type: 'paragraph', text: '…' },
{ type: 'heading', level: 2, text: 'Highlights' },
{ type: 'paragraph', text: '…' },
],
});The outline appears in the viewer's sidebar / bookmarks panel and lets readers
jump straight to a section. Set DocumentParams.outline to either 'auto' or
an explicit nested tree.
outline: 'auto'pdfnative walks your HeadingBlocks and builds a nested tree by heading level
(level: 1 → top level, level: 2 → child, …), each bookmark linking to the
page the heading lands on. This is the zero-effort option for structured
reports.
For full control over titles, nesting, ordering, styling, and destinations,
pass an array of OutlineItem:
import type { OutlineItem } from 'pdfnative';
const outline: OutlineItem[] = [
{
title: 'Part I — Introduction',
pageIndex: 0, // 0-based page index
bold: true,
children: [
{ title: 'Background', pageIndex: 0 },
{ title: 'Scope', pageIndex: 1 },
],
},
{
title: 'Part II — Results',
pageIndex: 2,
color: [0.1, 0.3, 0.9], // RGB 0–1; also accepts '#1a4fd6'
children: [
{ title: 'Findings', pageIndex: 2, italic: true },
],
},
];OutlineItem field |
Type | Description |
|---|---|---|
title |
string |
Bookmark label (encoded as PDF text, UTF-16BE when needed) |
pageIndex |
number |
0-based page index to jump to |
y |
number? |
Optional vertical destination (PDF user units from the bottom); defaults to the top of the page |
bold |
boolean? |
Render the label bold (/F flag 2) |
italic |
boolean? |
Render the label italic (/F flag 1) |
color |
PdfColor? |
Label colour (/C) — [r,g,b] 0–1 or a hex string |
open |
boolean? |
Initial expansion state. true (default) renders the bookmark expanded; false renders it collapsed (negative /Count), hiding its children until the reader expands it. Only meaningful with children. |
children |
OutlineItem[]? |
Nested bookmarks |
Destinations use /XYZ with the page's top-left as the default anchor, so the
viewer scrolls the target page into view at 100 % zoom.
Deep hierarchies read better when some branches start collapsed. Set
open: false on any item with children:
const outline: OutlineItem[] = [
{ title: 'Front matter', pageIndex: 0 },
{
title: 'Appendices',
pageIndex: 12,
open: false, // collapsed on open — children hidden until expanded
children: [
{ title: 'Appendix A', pageIndex: 12 },
{ title: 'Appendix B', pageIndex: 18 },
],
},
];pdfnative emits the spec-correct signed /Count (ISO 32000-1 §12.3.3): a
positive count for open items, a negative count for collapsed ones, and a
collapsed node contributes only itself — not its hidden descendants — to its
ancestors' visible counts.
By default a viewer numbers pages 1, 2, 3 …. /PageLabels overrides that with
logical numbering — front matter in lowercase roman, the body in decimal,
appendices with an A- prefix, and so on. The labels show in the viewer's page
thumbnail / "go to page" box and in printed page references.
import type { PageLabelRange } from 'pdfnative';
const pageLabels: PageLabelRange[] = [
{ startPage: 0, style: 'roman' }, // i, ii, iii
{ startPage: 3, style: 'decimal' }, // 1, 2, 3
{ startPage: 20, style: 'decimal', prefix: 'A-', start: 1 }, // A-1, A-2
];PageLabelRange field |
Type | Description |
|---|---|---|
startPage |
number |
0-based page index where this range begins |
style |
PageLabelStyle? |
'decimal' · 'roman' (i, ii) · 'Roman' (I, II) · 'alpha' (a, b) · 'Alpha' (A, B) · 'none' (label is the prefix only) |
prefix |
string? |
Text prepended to each label (e.g. 'A-') |
start |
number? |
First number in the range (default 1) |
Ranges must be ordered by startPage and stay within the document's page
count — both are validated at the boundary with a descriptive error.
- Outline —
buildOutlineObjects()(src/core/pdf-outline.ts) emits the/Outlinesdictionary plus one indirect object per bookmark, wired with/First /Last /Next /Prev /Parent /Count. The objects are appended as trailing indirect objects and the catalog gains/Outlines N 0 R. - Page labels —
buildPageLabelsDict()(src/core/pdf-page-labels.ts) emits an inline/PageLabels << /Nums [...] >>number tree in the catalog, so it adds no indirect objects.
Both features are fully additive: a document with neither field is byte-identical to the pre-v1.4.0 output.
- Quick start
- PDF manipulation — merge/split/extract
- Accessibility — tagged structure & TOC
- CHANGELOG