# pdfq
[](https://crates.io/crates/pdfq)
[](https://docs.rs/crate/pdfq)
[](LICENSE)
Fast, parallel PDF search with a command line compatible with
[pdfgrep](https://pdfgrep.org/).
pdfq is a from-scratch rewrite in Rust. It extracts text page by page with
`pdf-extract`, matches with the `regex` crate (or `fancy-regex` for `-P`),
and searches pages on all cores with rayon. Output format, flags, and exit
codes follow pdfgrep so it can be used as a drop-in replacement.
## Features
- pdfgrep-compatible flags: `-i`, `-n`, `-c`, `-o`, `-l`, `-L`, `-q`, `-F`,
`-P`, `-A/-B/-C`, `-m`, `-r`, `-R`, `--color`, `--page-range`, and more
- Parallel page and file processing (rayon)
- Modern regex engines: Rust `regex` by default, `fancy-regex` for `-P`
- Optional on-disk cache of extracted page text (`--cache`)
- GNU-grep-style exit codes: `0` match, `1` no match, `2` error
- Colored output honoring `GREP_COLORS` (`mt`/`ms`/`mc`, `fn`, `ln`, `se`)
## Build
```sh
cargo build --release
```
The binary is `target/release/pdfq`.
## Usage
```sh
pdfq [OPTION]... PATTERN FILE...
```
Examples:
```sh
pdfq -n "error" report.pdf
pdfq -r -i "todo" ./docs
pdfq -c -e foo -e bar a.pdf b.pdf
pdfq -n -C2 --page-range 3-5 "summary" thesis.pdf
pdfq -l -F "exact string" .
```
Run `pdfq --help` for the full option list.
## Exit codes
| 0 | at least one match |
| 1 | no match |
| 2 | error (bad usage, unreadable file, ...) |
With `-q`, a match wins over errors: exit `0` as soon as anything matches.
Errors while walking directories recursively (`-r`) are reported on stderr
but do not change the exit code, matching pdfgrep.
## Output format
```
[filename<sep>][pagenum<sep>]text
```
- `sep` is `:` on match lines and `-` on context lines
- `-n` prints the page number (pdfgrep has no line numbers, PDFs are pages)
- `-Z` replaces the filename separator with NUL
- context groups are separated by `--`, but only when lines were actually
skipped
## Differences from pdfgrep
- Text extraction uses `pdf-extract` (pure Rust) instead of poppler, so
layout reconstruction can differ slightly on complex PDFs
- `.` in a pattern does not match newline (Rust regex semantics; POSIX
`.` does). Multi-line anchors: `^` matches only the start of the page
text, `$` only the end
- Recursive walk output is sorted by name for reproducible results;
pdfgrep prints raw readdir order
- `--help` is generated by clap and lists every option, not pdfgrep's
short "commonly used" list
- No `--unac` accent folding (option is rejected with a clear error)
## Testing
Unit and integration tests (includes a regression suite for the
encrypted-PDF password check vs `--cache`):
```sh
cargo test
```
Differential tests against a reference pdfgrep binary (compares stdout,
stderr, and exit codes; program name normalized):
```sh
cargo build --release
PDFGREP=/path/to/pdfgrep bash tests/difftest.sh
```
If `PDFGREP` is not set, the script uses `pdfgrep` from `PATH` and exits 2
when it is missing.
## License
MIT