A desktop toolkit for everyday PDF work — merging, splitting, reordering, converting — that runs entirely on your machine. No uploads, no accounts, no network access at any point.
Built with Python and Tkinter, so it launches from a standard Python installation without a packaging step or a browser.
| Tab | What it does |
|---|---|
| Merge PDFs | Merge any number of files. Optionally interleave them, taking page 1 from each file, then page 2 from each — the way to reassemble a double-sided scan made as two single-sided passes. |
| Extract Pages | Pull a page selection such as 1-5, 7, 9-12 into a new file. |
| Split PDF | Split one document into many, using ranges you define or ranges imported from the PDF's own bookmarks. |
| Reorder Pages | Reorder pages by hand, reverse them, or de-interleave odd and even positions. |
| Convert PDF | Export pages as JPG or PNG at 72–600 DPI, or the whole document as DOCX or HTML. |
| Text to PDF | Typeset plain-text files, one PDF each or all combined, with a choice of font family, size, and page size. |
| Insert Pages | Insert another PDF's pages, or blank pages, at any position. |
Encrypted PDFs are supported throughout: the application asks for the password once, when you pick the file, and remembers it for the rest of the session.
- Python 3.11 or newer, built with Tk support (
python -m tkintershould open a window) - The four runtime dependencies listed in
requirements.txt
Dependencies are never installed automatically. If one is missing, the application says exactly which, and which command fixes it.
git clone https://github.com/eulerchavesneto/pdf_manager.git
cd pdf_manager
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .pip install -e . installs the four runtime dependencies and puts the package
on the path. requirements.txt alone is not enough: the code lives under src/,
so Python would not find it.
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pdf_managerInstalling also registers a pdf-manager command, which runs the same thing:
pdf-managerOr without activating the environment at all:
./.venv/bin/python -m pdf_managerWindow size and the folders you last used are remembered between sessions.
Three rules hold across every operation, and each is covered by tests:
Writes are atomic. Output goes to a temporary file beside the destination and is moved into place only once it is complete. An error, a full disk, or a crash halfway through can never leave a truncated PDF where a good one used to be.
A source is never used as a destination. PDF pages are read lazily, so writing over a file that is still being read is how an original gets destroyed. The application detects this — including when the same file is reached by two different paths — and asks for a different destination instead.
Filenames cannot collide silently. Bookmark titles become filenames when you
import an outline, and two chapters may well share a title. Names are sanitised
for every platform (illegal characters, Windows device names such as CON,
overlong titles, trailing dots) and disambiguated as name (2).pdf rather than
overwriting one another.
- Page selections keep the order you type.
3,1produces a document with page 3 first. Descending ranges work too:9-5means those five pages, backwards. - Text conversion is Unicode-safe. The application resolves a real TrueType
font from your system rather than using reportlab's Latin-1-only built-ins, so
accented text, typographic quotes, and non-Latin scripts survive. A character
the chosen font genuinely cannot draw is replaced with
�, never dropped in silence. - Long operations run in the background. The window stays responsive, the progress bar reflects real work, and a Cancel button stops the job at the next checkpoint. Action buttons are disabled while a job is running, so the same operation cannot be started twice.
- Unexpected errors are logged with their traceback, not printed to a console that may not exist. Anticipated problems — a bad page range, a locked file — are reported as plain messages instead.
Settings and logs live in the standard per-user location:
| Platform | Location |
|---|---|
| macOS | ~/Library/Application Support/pdf-manager/ |
| Linux | ~/.config/pdf-manager/ |
| Windows | %APPDATA%\pdf-manager\ |
The project is split into two halves that never reach into each other:
src/pdf_manager/
├── core/ pure PDF logic — imports no GUI toolkit
│ ├── documents.py safe opening, atomic writing, path guards
│ ├── pageranges.py parsing "1-5, 7" and formatting it back
│ ├── naming.py filename sanitising and collision avoidance
│ ├── merge.py concatenating and interleaving
│ ├── pages.py extract, split, reorder, insert
│ ├── outline.py reading bookmarks into split definitions
│ ├── convert.py images, DOCX, HTML
│ ├── text_to_pdf.py typesetting plain text
│ ├── fonts.py resolving Unicode-capable fonts
│ ├── progress.py progress reporting and cancellation
│ └── errors.py typed, user-facing failures
└── gui/ Tkinter shell
├── app.py main window, status bar, progress
├── worker.py background thread ↔ Tk queue bridge
├── context.py services handed to each tab
├── widgets.py reusable path picker and reorderable list
└── tabs/ one module per tab
core takes plain paths and values, reports progress through an optional
callback, and raises typed errors. That is what lets the same functions run
under the GUI's worker thread and directly inside the test suite. gui collects
inputs and renders outcomes; it contains no PDF logic.
pip install -e ".[dev]"
pytest # 224 tests
ruff check src tests # lintingThe test suite generates every PDF it needs at run time — each page stamped with its own marker — so an assertion can state exactly which original page ended up where. It covers the core end to end and builds the real Tkinter window to catch a tab that fails to construct. GUI tests skip themselves where no display is available.
pypdf for page-level manipulation, PyMuPDF for rendering and text extraction, reportlab for typesetting, and python-docx for Word output.
MIT.