Concatenate PDFs with table of contents, cover pages, and optional LLM summaries
Project description
pdf-concatenator
Bundle many PDFs into a single submission-ready document.
This tool was built to pull together a large set of PDFs for a contract submission: one combined file with a table of contents, cover pages, and optional short summaries so reviewers can navigate the bundle easily.
Features
- Recursively discover PDFs from a directory or glob pattern
- Sort files by path and concatenate them into one output PDF
- Generate a table of contents with folder structure, page numbers, and alternating row shading
- Insert a cover page before each source PDF (path, optional summary, page number)
- Optionally generate LLM summaries via a sidecar file per PDF (
*.pdf.sidecar.json) - Regenerate sidecars without concatenating (
--regenerate-summaries) - Exclude specific files or patterns (
--exclude) - Progress bar while summaries are processed
Installation
With pipx (recommended):
pipx install pdf-concatenator
With pip:
pip install pdf-concatenator
For development:
git clone https://github.com/lorenzowood/pdf-concatenator.git
cd pdf-concatenator
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Quick start
Concatenate all PDFs under a folder:
pdf-concatenator -o submission.pdf contracts/
With summaries (requires LLM config — see below):
pdf-concatenator -o submission.pdf --include-summaries contracts/
Regenerate sidecar summaries only:
pdf-concatenator --regenerate-summaries contracts/
Exclude files:
pdf-concatenator -o submission.pdf \
--exclude "drafts/*" \
--exclude "broken.pdf" \
contracts/
Patterns can be a directory (all PDFs beneath it) or a glob, e.g. contracts/**/*.pdf.
LLM configuration
When using --include-summaries or --regenerate-summaries, create ~/.config/pdf-concatenator:
LLM_API=open_ai
LLM_SERVER=127.0.0.1:28911
LLM_API_KEY=your-api-key
LLM_MODEL=your-model-id
LLM_PROMPT_TITLE_AND_SUMMARY=Your prompt here
The server should expose an OpenAI-compatible /v1/chat/completions endpoint. The whole PDF is sent to the model. If the prompt key is missing but everything else is valid, a default prompt is written to the config file.
Summaries are stored beside each PDF as document.pdf.sidecar.json and reused when the file hash matches.
Output structure
- Contents — tree of folders and files; page numbers point to each document's cover page. When summaries are included, a disclaimer appears in the footer.
- Cover page per PDF — relative path, optional summary, page number.
- Original PDF pages — unchanged (no added page numbers).
If any PDF cannot be read, or summary generation fails when required, the run aborts and no output file is produced.
Splitting large outputs
Upload limits (e.g. 50 MB) can be handled by splitting:
pdf-concatenator -o submission.pdf --max-output-size 50M contracts/
This produces submission_part_1.pdf, submission_part_2.pdf, and so on. Each part stays under the limit. Every part includes the full table of contents; entries in other parts are labelled Part 2, Part 3, etc. Under the Contents heading, each part also notes:
This archive is split into n parts. This is part m.
If everything fits in one file, the original output name is used with no _part_ suffix.
Options
usage: pdf-concatenator [-h] [-o filename] [--include-summaries]
[--regenerate-summaries] [--exclude pattern]
[--config CONFIG] [--verbose]
[--max-output-size SIZE]
pattern
| Option | Description |
|---|---|
-o, --output |
Output PDF path (required unless --regenerate-summaries) |
--include-summaries |
Include summaries in contents and cover pages |
--regenerate-summaries |
Regenerate sidecar files only; do not concatenate |
--exclude |
Glob pattern to exclude (repeatable) |
--config |
Path to LLM config (default: ~/.config/pdf-concatenator) |
--verbose |
Show library warnings while reading/merging PDFs |
--max-output-size |
Split output into parts under this size (e.g. 50M, 2G) |
Development
pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pdf_concatenator-1.1.0.tar.gz.
File metadata
- Download URL: pdf_concatenator-1.1.0.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dab90ef50de028dfd36b988cf32229f9f44d94b1f820280293d49d25c81169b0
|
|
| MD5 |
e622931871bb9558e8e62f56b261e6c3
|
|
| BLAKE2b-256 |
fa04b428f878e6ff0cc36a89425cd5fc35d5c7ffdfa1c82d51c6d2caa1035b88
|
File details
Details for the file pdf_concatenator-1.1.0-py3-none-any.whl.
File metadata
- Download URL: pdf_concatenator-1.1.0-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7a4fb351da271f2bb4a73591b9c42f67abf003c672a17b74fb8b6caf3b8f454
|
|
| MD5 |
7ddbed9477beacc7a743a66ad75718a8
|
|
| BLAKE2b-256 |
61249af76b45fe55d19f0fc8c149f57f1534617f34bcefb35bf5e9f71cfe808a
|