Skip to main content

pdfredact

Leggi questo in italiano

Redact text in a PDF (true redaction, not just a visual overlay) using PyMuPDF. Finds occurrences of the specified text/pattern, applies a redaction annotation, and "burns" it into the page content, physically removing the underlying text (not recoverable via copy-paste or text extraction).

Installation

Requires Python 3.10 or later. Dependencies are PyMuPDF and PyYAML (for --config), both of which publish prebuilt wheels for Linux, Windows, and macOS (no compiler required).

The package is published on PyPI as pdfredactcli (the installed command is pdfredact).

With pip

pip install pdfredactcli

or, from a local clone of the repository:

pip install .

or, for development (editable install with test dependencies):

pip install -e .[test]

With pipx (recommended for a command-line tool)

pipx installs the tool in an isolated virtual environment and exposes only the pdfredact command on PATH, without touching the system Python.

Linux/macOS:

python3 -m pip install --user pipx
python3 -m pipx ensurepath
pipx install pdfredactcli

Or, from a local clone of the repository, replace the last line with pipx install . (run from the root of the repository).

Windows:

On Windows it's convenient to install pipx via Scoop, which also manages updating Python itself if needed:

# If Scoop isn't already installed:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression

scoop install pipx
pipx ensurepath

Then, in a new terminal (so ensurepath takes effect):

pipx install pdfredactcli

Or, from a local clone of the repository, run pipx install . from the repository folder instead.

In both cases, after installation the pdfredact command is available directly in a new terminal.

Usage

pdfredact input.pdf output.pdf -t "Mario Rossi" -t "CF: ABCDEF"
pdfredact input.pdf output.pdf -r "\bMCNP-\d{4}\b"
pdfredact input.pdf output.pdf -t "Confidential" --case-sensitive
pdfredact input.pdf output.pdf -t "Mario" --no-whole-word  # also matches "Mariotti"
pdfredact input.pdf output.pdf -t "foo" --pages 1,2,5-7
pdfredact input.pdf output.pdf --box "1:56,700,300,730"
pdfredact input.pdf output.pdf -t "foo" --fill-color "#ff0000"
pdfredact input.pdf -t "Mario Rossi"              # writes input_redacted.pdf
pdfredact --config job.yaml
pdfredact input.pdf output.pdf --config rules.yaml -t "extra one-off term"
pdfredact --version

The output path is optional: if omitted, it defaults to <input>_redacted.pdf next to the input file.

Equivalent without installing, from the root of the repository:

python -m pdfredact input.pdf output.pdf -t "Mario Rossi"

Whole-word matching

By default, -t/--text terms only match on word boundaries: -t "Mario" redacts a standalone "Mario" but leaves "Mariotti" or "mario2" untouched. This only constrains the edges of a term that are themselves letters/digits, so a term ending in punctuation (e.g. "Confidential:") still correctly matches even though nothing changes on that side - it just won't match inside "NonConfidential:" either. Use --no-whole-word (or whole_word: false in --config) to go back to plain substring matching. This only affects -t; -r/--regex patterns are never touched, since you already have full manual control there via your own \b.

Word boundaries are decided from where the characters actually sit on the page, not just from the extracted text: many PDFs separate words (table columns, form fields, right-aligned values) by moving the text cursor instead of writing a space, so a visible gap counts as a boundary even when there is no whitespace character between the two words.

Rectangle coordinates (--box)

Format: PAGE:x0,y0,x1,y1

  • PAGE is 1-based (page 1 = first page)
  • x0,y0,x1,y1 in PDF points (72 pt = 1 inch), origin at the top-left (same coordinate system returned by page.search_for())
  • Corner order doesn't matter: the rectangle is normalized.
  • A box that falls entirely outside the page redacts nothing: it's reported on stderr and not counted as a redacted occurrence, so a typo'd coordinate can't look like a success.

Config file (--config)

Any option can also be set in a YAML file instead of retyped on every invocation:

# job.yaml
input: input.pdf              # optional if given positionally on the command line
output: output.pdf            # optional; defaults to <input>_redacted.pdf if unset everywhere

text:                         # literal terms to redact (like repeated -t)
  - "Mario Rossi"
  - "CF: ABCDEF"

regex:                        # regex patterns to redact (like repeated -r)
  - '\bMCNP-\d{4}\b'

boxes:                        # explicit rectangles, same "PAGE:x0,y0,x1,y1" format as --box
  - "1:56,700,300,730"

case_sensitive: false          # like --case-sensitive
whole_word: true               # like --whole-word
pages: "1,2,5-7"               # like --pages
fill_color: "#000000"          # like --fill-color; keep the quotes, an unquoted
                               # '#' would start a YAML comment

All keys are optional, and pdfredact --config job.yaml alone is a valid invocation if input is set in the file. Values from the config file and the command line are merged:

  • text, regex, and boxes from the command line are added to the config file's lists.
  • input, output, pages, fill_color, case-sensitive, and whole-word from the command line override the config file's value when explicitly passed. To override a config file's case_sensitive: true back to false, pass --no-case-sensitive (plain --case-sensitive can only set it to true); likewise --no-whole-word overrides a config file's whole_word: true back to false.

Each key is validated the same way as its CLI equivalent (same --box/--pages/--fill-color formats); an unknown key or a value of the wrong type/shape fails immediately with exit code 2 rather than being silently ignored.

Exit codes

0 = success, 2 = input/usage error.

Known limitations

  • Only PDF input is accepted. PyMuPDF can also open TXT, EPUB, SVG, CBZ and image files, but redaction annotations are PDF-only, so those inputs are rejected with exit code 2; convert them to PDF first.
  • Document metadata (Author, Title, XMP) and annotation/comment content are not handled, since they don't appear in get_text().
  • A term split across multiple lines in the PDF layout might not be found.
  • Scanned PDFs (image-only, with no extractable text) require OCR upstream: the tool finds nothing to redact in that case.
  • Whole-word matching (the default for -t) only checks the character immediately before/after a match on the same line, so it's most reliable for terms that don't themselves span a line break. If a PDF packs two words so tightly that the extracted text runs them together with no gap at all, there is no boundary left to find: --no-whole-word is the fallback there.

Always verify the output with pdftotext and pdfinfo -meta before distribution.

Windows compatibility

The project is tested in CI on Linux, Windows, and macOS (see .github/workflows/tests.yml) and is compatible with Windows without modifications: it only uses os.path (no hardcoded separators), no POSIX-only calls, and os.path.samefile has worked correctly on Windows since Python 3.2.

Development

pip install -e .[test]
pytest
pytest tests/test_core.py::test_redact_pdf_literal_term   # single test

AI-assisted development

This project's code, tests, and documentation were developed with the assistance of AI tools (Claude Code). Every change was reviewed before being published; please report any issues you find via the project's issue tracker.

License

MPL-2.0. The repository is REUSE compliant; to verify: pipx run reuse lint.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pdfredactcli-0.2.2.tar.gz (45.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pdfredactcli-0.2.2-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file pdfredactcli-0.2.2.tar.gz.

File metadata

  • Download URL: pdfredactcli-0.2.2.tar.gz
  • Upload date:
  • Size: 45.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pdfredactcli-0.2.2.tar.gz
Algorithm Hash digest
SHA256 6940e20d95f2b114c054aa17f1ec99802a0f2255ddb083e3990daf582a126fee
MD5 f470b04e9e1295304d212c2e0a8e9edd
BLAKE2b-256 11bc360ba69c00237caa9498c74c6d818596e6656017d7337a807ae3bfab876a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfredactcli-0.2.2.tar.gz:

Publisher: pypi.yml on alberto743/pdfredact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdfredactcli-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: pdfredactcli-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 23.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pdfredactcli-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 77af90a7c988b325ce96f59e3debf6a0813108d1caef9205761a92b439b15020
MD5 add56f7ffcfefaef09a334a333a4e8da
BLAKE2b-256 aa901128dcbeee8f948b76bff8cc0e05a25922a95f41199f3a9186dbb9fb2dcd

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfredactcli-0.2.2-py3-none-any.whl:

Publisher: pypi.yml on alberto743/pdfredact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page