Skip to content

Commit 5a06e96

Browse files
committed
latest changes
1 parent bbc2b0e commit 5a06e96

File tree

6 files changed

+386
-125
lines changed

6 files changed

+386
-125
lines changed

docs/index.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ You can contribute to `pypdf on GitHub <https://github.com/py-pdf/pypdf>`_.
4242
user/file-size
4343
user/pdf-version-support
4444
user/pdfa-compliance
45-
user/outlines
45+
user/reading-pdf-outlines
46+
user/adding-pdf-outlines
4647

4748

4849
.. toctree::
@@ -66,6 +67,7 @@ You can contribute to `pypdf on GitHub <https://github.com/py-pdf/pypdf>`_.
6667
modules/errors
6768
modules/generic
6869
modules/PdfDocCommon
70+
6971

7072
.. toctree::
7173
:caption: Developer Guide

docs/user/adding-pdf-outlines.md

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Adding PDF Outlines
2+
3+
```{note}
4+
PDF outlines — also known as bookmarks — provide a structured navigation panel in PDF readers.
5+
With pypdf, you can create simple or deeply nested outlines programmatically.
6+
```
7+
8+
## `PdfWriter.add_outline_item()`
9+
10+
**Source:** `pypdf/_writer.py`
11+
Adds an outline (bookmark) entry to the PDF document.
12+
13+
14+
## **Syntax**
15+
16+
```python
17+
add_outline_item(
18+
title: str,
19+
page_number: int | None = None,
20+
parent: Any | None = None,
21+
color: tuple | None = None,
22+
bold: bool = False,
23+
italic: bool = False,
24+
is_open: bool = True,
25+
fit: str | None = None,
26+
zoom: float | None = None
27+
) -> Any
28+
```
29+
30+
31+
## Parameters
32+
33+
The following parameters are available for `add_outline_item()`:
34+
35+
| Name | Type | Default | Description |
36+
|--------------|---------------------------|---------|-------------|
37+
| `title` | `str` || The visible text label shown in the PDF outline panel. |
38+
| `page_number`| `int`, optional | `None` | Zero-based target page index. If `None`, the item becomes a non-clickable parent/group header. |
39+
| `parent` | `Any`, optional | `None` | The parent outline item under which this one will be nested. If omitted, this becomes a top-level outline. |
40+
| `color` | `tuple`, optional | `None` | RGB color tuple with values between `0–1`. Example: `(1, 0, 0)` for red. |
41+
| `bold` | `bool` | `False` | If `True`, the outline title is displayed in bold. |
42+
| `italic` | `bool` | `False` | If `True`, the outline title is displayed in italic. |
43+
| `is_open` | `bool` | `True` | Whether the outline node is expanded when the PDF opens. |
44+
| `fit` | `str`, optional | `None` | Controls how the destination page is displayed (Fit, FitH, FitV, FitR, XYZ). |
45+
| `zoom` | `float`, optional | `None` | Used only when `fit="XYZ"`. Example: `1.0` = 100% zoom. |
46+
47+
### Fit Mode Options
48+
49+
| Value | Meaning |
50+
|--------|---------|
51+
| `"Fit"` | Display the entire page. |
52+
| `"FitH"` | Fit to width, aligned at the top. |
53+
| `"FitV"` | Fit to height. |
54+
| `"FitR"` | Fit a specific rectangle region. |
55+
| `"XYZ"` | Use a custom zoom level (`zoom=` required). |
56+
57+
58+
59+
## **Return Type:** `Any`
60+
61+
The method returns a reference to the created outline item.
62+
This reference is typically used when creating nested (child) outline items.
63+
64+
### Example
65+
```python
66+
parent = writer.add_outline_item("Chapter 1", page_number=0)
67+
writer.add_outline_item("Section 1.1", page_number=1, parent=parent)
68+
```
69+
70+
71+
## Exceptions
72+
73+
The `add_outline_item()` method may raise the following exceptions:
74+
75+
| Exception | When it occurs |
76+
|-----------------|----------------|
77+
| `ValueError` | Raised when `page_number` is out of range, `fit` is invalid, or `color` is not a valid `(r, g, b)` tuple (each value must be a float between 0–1). |
78+
| Internal errors | Occur if an invalid `parent` reference is passed, or if the outline tree becomes corrupted internally. |
79+
80+
81+
82+
## Example: Full PDF Outline with All Parameters
83+
84+
```python
85+
from pypdf import PdfReader, PdfWriter
86+
from pypdf.generic._fit import Fit # Use Fit only
87+
88+
reader = PdfReader("input.pdf") # Replace with your input PDF
89+
writer = PdfWriter()
90+
91+
# Copy all pages into the writer
92+
for page in reader.pages:
93+
writer.add_page(page)
94+
95+
# Top-level chapter (blue, bold, expanded)
96+
chapter1 = writer.add_outline_item(
97+
title="Chapter 1: Introduction",
98+
page_number=0,
99+
color=(0, 0, 1),
100+
bold=True,
101+
italic=False,
102+
is_open=True,
103+
fit=Fit.fit()
104+
)
105+
106+
# Section under Chapter 1 (dark green, italic, collapsed)
107+
section1_1 = writer.add_outline_item(
108+
title="Section 1.1: Overview",
109+
page_number=1,
110+
parent=chapter1,
111+
color=(0, 0.5, 0),
112+
bold=False,
113+
italic=True,
114+
is_open=False,
115+
fit=Fit.fit_horizontally(top=800)
116+
)
117+
118+
# Section with custom zoom
119+
section1_2 = writer.add_outline_item(
120+
title="Section 1.2: Details",
121+
page_number=2,
122+
parent=chapter1,
123+
color=(1, 0, 0),
124+
bold=True,
125+
italic=True,
126+
is_open=True,
127+
fit=Fit.xyz(left=0, top=800, zoom=1.25)
128+
)
129+
130+
# Non-clickable parent (no page number)
131+
appendix = writer.add_outline_item(
132+
title="Appendices",
133+
page_number=None,
134+
color=(0.5, 0, 0.5),
135+
bold=True,
136+
italic=False,
137+
is_open=False
138+
)
139+
140+
# Child under non-clickable parent
141+
writer.add_outline_item(
142+
title="Appendix A: Extra Data",
143+
page_number=3,
144+
parent=appendix,
145+
color=(0, 0, 0),
146+
bold=False,
147+
italic=False,
148+
is_open=True,
149+
fit=Fit.fit_vertically(left=50)
150+
)
151+
152+
# Save the PDF
153+
output_path = "output.pdf"
154+
with open(output_path, "wb") as f:
155+
writer.write(f)
156+
157+
print(f"PDF with outlines created successfully: {output_path}")
158+
```
159+
160+
### What this code demonstrates
161+
162+
* Adding top-level and nested bookmarks.
163+
* Using all parameters: `title`, `page_number`, `parent`, `color`, `bold`, `italic`, `is_open`, `fit.`
164+
* Creating non-clickable parent nodes (`page_number=None`).
165+
* Using different page view modes: `Fit, FitH, FitV, XYZ.`
166+
* Produces a navigable outline tree in the PDF reader.
167+
168+
169+
## Adding a Simple Outline
170+
171+
Use this when you want a single top-level bookmark pointing to a page.
172+
```python
173+
from pypdf import PdfReader, PdfWriter
174+
175+
reader = PdfReader("input.pdf")
176+
writer = PdfWriter()
177+
178+
# Copy all pages into the writer
179+
for page in reader.pages:
180+
writer.add_page(page)
181+
182+
# Add a top-level bookmark
183+
writer.add_outline_item(
184+
title="Introduction",
185+
page_number=0 # zero-based index
186+
)
187+
188+
with open("output.pdf", "wb") as f:
189+
writer.write(f)
190+
```
191+
192+
### What the simple outline code does
193+
194+
* Loads the original PDF
195+
* Copies each page
196+
* Adds an outlines named "Introduction"
197+
* Save the updated PDF
198+
199+
![PDF outline output](simple-outline.png)
200+
201+
202+
## Adding Nested (Hierarchical) Outlines
203+
204+
Nested outlines create structures like:
205+
206+
```text
207+
Chapter 1
208+
├── Section 1.1
209+
└── Section 1.2
210+
```
211+
212+
```python
213+
from pypdf import PdfReader, PdfWriter
214+
215+
reader = PdfReader("input.pdf")
216+
writer = PdfWriter()
217+
218+
# Copy pages
219+
for page in reader.pages:
220+
writer.add_page(page)
221+
222+
# Add parent (chapter)
223+
chapter = writer.add_outline_item(
224+
title="Chapter 1",
225+
page_number=0
226+
)
227+
228+
# Add children (sections)
229+
writer.add_outline_item(
230+
title="Section 1.1",
231+
page_number=1,
232+
parent=chapter
233+
)
234+
235+
writer.add_outline_item(
236+
title="Section 1.2",
237+
page_number=2,
238+
parent=chapter
239+
)
240+
241+
with open("output.pdf", "wb") as f:
242+
writer.write(f)
243+
```
244+
245+
### What the nested outline code does
246+
247+
* Copies all pages into the new PDF
248+
* Creates a top-level outline called Chapter 1
249+
* Adds Section 1.1 under that chapter
250+
* Adds Section 1.2 under the same chapter
251+
* Produces an outline tree like:
252+
253+
![PDF outline output](nested-outline.png)
254+
255+
### Key points
256+
257+
- `parent=` creates nested outlines — without it, all outlines become top level.
258+
- `page_number` is zero-based (`0` = first page).
259+
- You must **add pages before** adding outlines — otherwise bookmarks won’t work.
260+
- You can build multiple hierarchical levels (chapter → section → subsection → etc.).
261+
- A bookmark must point to a valid page, or the PDF reader may hide or ignore it.
262+
- Nested outlines improve navigation for large PDFs.
263+
264+
265+
266+

docs/user/nested-outline.png

28.9 KB
Loading

0 commit comments

Comments
 (0)