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.9 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.1.tar.gz (45.8 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.1-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pdfredactcli-0.2.1.tar.gz
  • Upload date:
  • Size: 45.8 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.1.tar.gz
Algorithm Hash digest
SHA256 f2626745822e2d8561cd8acea565cdf791d611b1c17a697060d1ad2b2c4042b3
MD5 6384bc149f4c57086cb0e918d90b6bb0
BLAKE2b-256 c5bf65094e9e08a541f19b6a9d067d14bc5f9b2aca836f2f183294afe29ff6ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfredactcli-0.2.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: pdfredactcli-0.2.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 86d3c22f459ca728cf7d318c18d001722cb96678f8dd2504bb354f4169fc371f
MD5 3a5044ab2ad70b8138752e621e1a136a
BLAKE2b-256 13b8daf4281c91ae1d6eb6a10728f8a396f0fdf689f2fc638197092fe73a5bc3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfredactcli-0.2.1-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