Skip to main content

toc-this

Adds a clickable table of contents (the bookmarks sidebar) to a PDF that has none. It works on born-digital and scanned PDFs.

uv tool install toc-this              # or: pipx install toc-this
toc-this book.pdf                     # writes book.toc.pdf

To run it once without installing, use uvx toc-this book.pdf. It needs Python 3.9 or newer. Scanned PDFs also need Tesseract (see Scanned PDFs).

How it finds the headings

  1. The book's printed Contents page. The titles and the nesting (from indentation) come from the Contents page. If the Contents lines link to their pages, as Word and LaTeX exports usually do, those links give the pages. Otherwise, each title is found in the book itself. The search goes forward from the previous entry and tolerates OCR typos. The printed page numbers are not used, because OCR misreads them next to dot leaders, and front matter often uses separate numbering.
  2. Font sizes. This is used when there is no Contents page. Lines in a larger font than the body text become headings.
  3. Your own list. This is used when the guess is wrong:
toc-this book.pdf --print > toc.txt   # write what it found, then edit it
toc-this book.pdf --from toc.txt      # use your list

In toc.txt, each level is indented two more spaces, and the page number goes last:

Chapter 1 Getting started  5
  1.1 Install  6

--offset N adds N to every page in --from. Use it when you type page numbers from the printed book and printed page 1 is PDF page 1+N.

Scanned PDFs

When most pages have no text, toc-this OCRs every page on all CPU cores with Tesseract. A 469-page book took about 2 minutes on an 8-core Mac. Install Tesseract first:

brew install tesseract        # macOS
sudo apt install tesseract-ocr  # Debian/Ubuntu

Use --lang for books that are not in English, for example --lang deu or --lang eng+fra. The language data must be installed too. OCR only feeds heading detection. The output PDF is still an image PDF and cannot be searched. To get a searchable PDF, run OCRmyPDF first, then run toc-this on its output.

Limits

  • PDFs that already have a ToC are refused.
  • On scans, OCR sometimes garbles a Contents line beyond recognition, often short ones next to dense dot leaders. Those entries are left out of the PDF. --print shows each one as a # line in its place. To add one back, delete the # , fix the title, add the page number, and pass the file with --from.
  • On scans, a title is taken from the matching heading in the body when that heading is the same length, because body headings are larger and OCR more cleanly. Otherwise the Contents wording is kept.
  • toc-this looks for a Contents page only in the first 30 pages.
  • When the fallback guesses from font sizes, it misses subheadings set in the same size as body text. Use --from for those.

Development

uv run python test_toc.py                       # self-check, runs the OCR case when tesseract is on PATH
uv run python check_sample.py some.pdf [count]  # every entry's title must appear on its page

To release, bump version in pyproject.toml, commit, and push a tag v<version>. The publish workflow runs the tests (with Tesseract) and uploads to PyPI.

Run check_sample.py on a long scanned book before changing how titles are matched. One rule is covered only there: short, generic titles such as "Summary" may not move the search forward. The unit fixture is too short to show the cascade that rule prevents.

License

AGPL-3.0-or-later. toc-this depends on PyMuPDF, which is AGPL.

Metadata

Release files for toc-this 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for toc-this 0.1.0
File Size Uploaded Provenance Actions
toc_this-0.1.0.tar.gz 25.9 kB Without provenance Details for toc_this-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for toc-this 0.1.0
File Interpreter ABI Platform Actions
toc_this-0.1.0-py3-none-any.whl Python 3 none any Details for toc_this-0.1.0-py3-none-any.whl

Total release size: 46.0 kB

Release files / toc_this-0.1.0.tar.gz

Download URL toc_this-0.1.0.tar.gz
Size 25.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ca77c69e5dd15a79d06573c6ad5c9726a9e2cf10715e01305f65979547a8f145
BLAKE2b-256 checksum
How to use checksums
b53374fab89461a0637c3ee9cf07296ea45d6efd19a0e14622897a4a145dbc99
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / toc_this-0.1.0-py3-none-any.whl

Download URL toc_this-0.1.0-py3-none-any.whl
Size 20.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2a97a204d53817d5edb344e33c875dda3073f8567a8d1dab9a7f4e15531816f2
BLAKE2b-256 checksum
How to use checksums
dd6955a05544e2d426284981178bbe970f4783a00261cdec1e8cf04417b10b0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for toc-this 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page