rusty-pdfgrep
Grep through PDF text content. Rust port of Hans-Peter Deifel's pdfgrep(1) 2.2.0.
lopdf-backed text extraction, pluggable regex engines (regex default, fancy-regex for -P), --password retry for encrypted PDFs, GNU-grep-compatible color output, & a typed library API. No C toolchain required: PCRE features come from pure-Rust fancy-regex. Default mode adds --help, --version, & a completions subcommand; Strict mode reverts to byte-equal pdfgrep 2.2.0 stderr.
Part of the Rusty portfolio.
Install
# or, with prebuilt binaries:
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-pdfgrep/releases
Usage
# Search a single PDF for a phrase
# Show which page each match lives on
# Recursive search across a directory tree, case-insensitive
# Try multiple passwords against an encrypted PDF
# Fixed-string search (special regex chars are literal)
# PCRE features (lookahead, backref) via the fancy-regex engine
# Count matches per file (no per-line output)
# List only filenames containing matches (good for dialog --gauge style scripts)
# Strict pdfgrep-compat mode (drop-in pdfgrep 2.2.0 replacement)
RUSTY_PDFGREP_STRICT=1
# Shell completions
Library API
The library exposes the PdfGrep / PdfGrepBuilder / Match / PageIterator / PdfGrepError types without any CLI deps. Use it when you want pdfgrep semantics inside another Rust tool.
use PdfGrepBuilder;
use Path;
let pdfgrep = new
.pattern
.case_insensitive
.build
.unwrap;
for result in pdfgrep.search_file
For library-only consumers without CLI deps see the Cargo Features section.
Cargo Features
default enables full, which (for this tightly-coupled-capability port) resolves to the cli umbrella. pdfgrep-classic reproduces v0.1.x bare-port behavior matching upstream pdfgrep 2.2.0 1:1. To strip the CLI surface use default-features = false or --no-default-features & add the features you want.
rusty-pdfgrep is a tightly-coupled-capability port: its one documented job is "grep through PDF text content". The CLI sub-capabilities (recursive walking, GREP_COLORS color output, encrypted-PDF retry, --include / --exclude glob filters, Strict-mode dispatcher, completions subcommand) are tightly coupled inside the cli umbrella. No optional feature leaves are carved beyond the required umbrellas; see docs/feature-layout.md for why.
Feature matrix
| Feature | Description | Umbrella(s) |
|---|---|---|
cli |
All CLI-only dependencies (clap, clap_complete, anyhow, termcolor, anstyle, walkdir, globset) and the binary entry point, recursive walker, color writer, include/exclude glob filters, encrypted-PDF retry CLI dispatcher, Strict-mode argv pre-scanner, and completions subcommand. Library consumers strip via default-features = false. |
full, pdfgrep-classic, pdfgrep-minimal |
Preset bundles
| Bundle | Composition | Use case |
|---|---|---|
pdfgrep-classic |
cli |
Drop-in upstream pdfgrep 2.2.0 replacement. Strict mode is invoked via --strict, RUSTY_PDFGREP_STRICT, or pdfgrep/pdfgrep-alias argv[0] auto-detect. |
pdfgrep-minimal |
cli |
Explicit minimal-CLI alias for users who prefer the <port>-minimal naming convention seen across other portfolio ports. Identical composition to pdfgrep-classic. |
Keep-list workaround (Cargo features are union-only)
Cargo features cannot subtract from default. To get "everything except a specific feature," disable defaults & enumerate the features you want:
# → bare CLI. Equivalent to pdfgrep-classic / pdfgrep-minimal.
For the common cases the named preset bundles are usually sufficient.
Library-only consumers
[]
= { = "0.2", = false }
This strips clap, clap_complete, anyhow, termcolor, anstyle, walkdir, & globset. The resulting build pulls only thiserror (required by the always-on PdfGrepError enum) plus the foundational PDF + regex stack (lopdf + regex + fancy-regex). The CI test-no-default job runs cargo tree --no-default-features on every PR & fails the build if any CLI-only dep leaks back in.
Convention authority
This layout follows the portfolio-wide Cargo Features Convention. The "why" lives in ADR-0006; the "what" lives in project-instructions.md §Cargo Feature Surface. Every Rusty port from v0.2 onward exposes the same umbrella set (default / full / cli / <port>-classic), per-port leaves named in kebab-case, & 2 to 4 preset bundles.
Compatibility
rusty-pdfgrep has two modes:
- Default mode. clap-styled flag parser. Conflicting flag pairs MUST be rejected at parse time.
--help,--version, & thecompletionssubcommand are all available. - Strict mode (activated by
--strict,RUSTY_PDFGREP_STRICT=1, or invoking the binary aspdfgrep/pdfgrep-alias). Byte-equal stderr against upstream v2.2.0 for documented diagnostics. Last-wins flag resolution.--help,--version, &completionsMUST be rejected.
-P / --perl-regexp engine
The -P engine is fancy-regex instead of upstream's libpcre2. Pure-Rust, no C toolchain. Edge-case PCRE features (recursive patterns, callouts, conditional patterns) diverge from upstream pdfgrep & MUST be tested against your specific patterns before relying on byte-equal results.
BREAKING-CHANGE vs upstream
stdin is buffered into memory with a configurable cap (default 512 MiB). Upstream pdfgrep buffers unbounded; rusty-pdfgrep refuses to OOM on huge inputs.
See docs/COMPATIBILITY.md for the full per-flag matrix.
What's not shipped
-w/--word-regexp. Not in upstream pdfgrep; omitted.--password-list FILE. Upstream uses repeated--passwordflags; rusty-pdfgrep matches that surface.-A/-B/-Cpage-context. Deferred.--cache. Page-extraction cache; deferred.--unac. Unicode accent normalization; deferred.-Rsymlink-follow. Recursive walk useswalkdir's default (no follow). Deferred.pdfium-renderbackend.lopdfcovers the 99% case without a C toolchain dependency.- Unbounded stdin buffering. Capped at 512 MiB by default; configurable via the library builder.
MSRV
Rust 1.85 (edition 2024). Re-verified against the portfolio's stable-minus-two policy at each release.
License
Dual-licensed under MIT or Apache-2.0 at your option.