A local desktop tool to open a PDF, edit the text of existing text blocks in place, and save a copy. Runs entirely on your machine — no cloud.
This is v1: a precision text-correction / block-editing tool for
text-based (digitally generated) PDFs. See the design spec at
docs/superpowers/specs/2026-06-29-pdf-editor-design.md.
- Opens and renders PDFs faithfully (embedded fonts, vector art, images shown as authored — rendered by MuPDF).
- Navigate multi-page documents: a thumbnail rail (click to jump), prev/next buttons, a go-to-page box, and PageUp/PageDown + Home/End keys.
- Detects text blocks; click one to select it.
- Edit text in place, one span at a time, so surrounding content and table column alignment are preserved (see "Editing model" below).
- Find and replace text on a page (case-insensitive); Find cycles through matches, Replace all swaps every occurrence.
- Change font size and colour of the selected text (size box + colour picker in the panel).
- Add a new text box: click Add text, then click on the page and type — inserts fresh text without touching existing content.
- Reuses the original embedded font so the font and weight do not change — including subset Identity-H/CID fonts that ship no Unicode cmap (common in real documents): the font is augmented with a character map recovered from the document's own glyph usage. Substitutes a standard font (with a warning) only when a character isn't present in the embedded subset at all.
- Preserves a block's existing bold/italic formatting when you edit it (it is loaded into the editor as styled text, not flattened to one style).
- Bold/italic individual words: select text in the edit box and click B / I. Both are synthesized on the original font (bold = stroke, italic = shear), so the font family is preserved even with no embedded bold/italic variant.
- Move a selected span: drag its box on the page, or nudge it with the arrow keys (hold Shift for a 10 pt step). A ghost outline previews where it will land; the text is re-rendered at the new position in its original font.
- Delete the selected text (the Delete key, or the Delete button) — removes the glyphs without disturbing backgrounds underneath.
- Duplicate the selected text: places a copy nearby in the same font.
- Copy the selected text to the clipboard.
- Save a copy (the original file is never overwritten).
- Undo and Redo (every edit, move, delete and duplicate is reversible).
Insert new text boxes, edit/move images, vector editing, OCR / scanned PDFs, PDF→LaTeX, underline, font-size/color changes, annotations, forms, page operations (merge/split/reorder). These are deferred. Note: underline is not a font attribute in PDF (it's a drawn line), so it isn't detected or applied.
Requires Python 3.11 or 3.12 (recommended). Newer versions such as 3.13/3.14
may not yet have prebuilt PySide6/PyMuPDF wheels and can fail to install. On
macOS/Linux use python3; on Windows the launcher is usually python.
# macOS/Linux
python3 -m venv .venv
source .venv/bin/activate
# Windows
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txtAfter activating the venv, python points at the venv interpreter:
python main.py # open the app, then File → Open
python main.py path/to/file.pdf # open a file directlypytest # engine tests
QT_QPA_PLATFORM=offscreen pytest # include headless UI smoke testsapp/ (PySide6 GUI) ──depends on──▶ pdfcore/ (Qt-free engine)
main_window.py document.py open / render / save
page_view.py canvas + block boxes blocks.py extract TextBlocks
thumbnail_bar.py page nav rail fonts.py font resolution
controller.py UI↔engine, undo/redo editor.py redact + reinsert
main.py entry point surgical.py content-stream edit
search.py find / replace
insert.py add new text box
pdfcore has no Qt imports, so the engine is unit-tested directly.
When you edit a span, the tool first tries a surgical content-stream edit — it rewrites only the glyph codes inside the original text operators and leaves every positioning/font operator untouched. The font, weight, size, character spacing and justification are therefore the same objects the PDF already used and cannot change. This is how online editors stay faithful, and it gives pixel-identical results for value/number edits (the common case).
Surgical editing is used when the edit changes text without changing style and the new characters are drawable in the original font. It falls back to the redraw path (below) when: the text lives inside an XObject, a new character isn't available in the embedded subset, or you change bold/italic. The redraw path reuses the original embedded font but re-renders, so on justified prose its spacing is slightly tighter than the original.
Font fallback order on redraw: (1) the original embedded font (augmented with a
cmap if needed); (2) the same family from installed system fonts — e.g. real
Verdana-Bold when the embedded subset lacks a glyph you typed (a 7 that
wasn't used on that page), so the family/weight still match; (3) only as a last
resort, a generic Base-14 substitute (with a warning).
You click and edit one span — a single positioned run of text (a word, phrase, table cell, or value). The edit is drawn at that span's original position, and the rest of its line reflows:
- Words that follow on the same line shift by the width change, so a sentence closes up (when shorter) or makes room (when longer) — no gaps, no overlap.
- Shifting stops at a column-sized gap: content after a wide gap is treated as a separate column and stays put, so table/column alignment is preserved.
- The shift is measured as the change in the span's width (new vs. old text, measured with the same font), so an edit that doesn't change the text length leaves the rest of the line exactly where it was — neighbours don't drift.
- Other blocks (e.g. other table rows, other paragraphs) are never touched.
To reposition text outright (not just reflow it), select the span and drag it or use the arrow keys — see "Move a selected span" above.
This gives sentence-like reflow where you want it and column stability where you need it, from the same action. Full multi-line paragraph reflow (re-wrapping across lines) is not done — only the edited span's own line reflows.
- Editing redraws the edited block; minor spacing drift within that block is possible and expected. Untouched content is preserved.
- If replacement text is longer than the original block, it flows downward below the original box (and may overlap content beneath it) rather than being clipped or lost — you'll get an overflow warning. Shrink-to-fit is deferred.
- PyMuPDF is AGPL-3.0 — fine for personal/internal use; revisit before any distribution.
- Complex layouts (tables drawn as vector rules, justified text, overlapping elements) may detect/edit imperfectly.