# How to Perform ATS Verification and Keyword Coverage Checking Using pdftotext

> Learn ATS verification and keyword coverage checking using pdftotext. Ensure your CVs are parsable by Applicant Tracking Systems with this essential guide.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-07-26

---

**The `verify_pdf` tool in the ai-job-search repository extracts embedded text layers from CV PDFs using `pdftotext` from the poppler suite, verifying page count, character density, and required contact details to ensure Applicant Tracking Systems can parse the document correctly.**

The `ai-job-search` repository provides a dedicated command-line utility for ATS verification and keyword coverage checking using pdftotext. This tool validates that LaTeX-generated CVs contain machine-readable text layers rather than just visual glyphs, preventing your application from being rejected by automated screening systems that cannot interpret scanned images or corrupted encodings.

## Why ATS Verification Depends on pdftotext

Applicant Tracking Systems read the **embedded text layer** of a PDF, not the visual layout. A LaTeX-generated CV may appear perfect on screen while containing garbled glyphs or incorrect reading order that renders keywords and contact details invisible to parsers. According to the [README.md](https://github.com/MadsLorentzen/ai-job-search/blob/master/README.md) § "ATS-check the CV" (lines 231‑239), the workflow extracts text using **`pdftotext`** and validates it against structural and content requirements before submission.

## Core Verification Logic in verify_pdf.py

The [[`tools/verify_pdf.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py)](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py) module implements three sequential validation checks to guarantee ATS compatibility.

### Page Count Validation

The tool first invokes `pdfinfo` via the `parse_page_count` helper to confirm the document contains the expected number of pages. This prevents incomplete renders or concatenation errors in multi-page CVs.

> *Source:* [`parse_page_count`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L33-L38) – lines 33‑38.

### Text Layer Extraction and Normalization

Next, `verify_pdf` executes `pdftotext -layout` to extract the raw text stream, then applies `normalize_text` to collapse whitespace and standardize encoding. If the resulting string falls below the configurable `min_chars` threshold, the PDF is deemed unreadable and rejected.

> *Source:* [`normalize_text`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L40-L42) – lines 40‑42; [`run_tool`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L24-L30) error handling – lines 24‑30.

### Required Text Verification

Finally, the tool validates that specific strings—such as email addresses, phone numbers, or section headings—are present in the extracted text. Each argument passed via `--contains` is normalized and searched; missing strings raise a `VerificationError` with detailed diagnostics.

> *Source:* [`verify_pdf`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L44-L66) core implementation – lines 44‑66; [`build_parser`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L70-L86) argument definitions – lines 70‑86.

## Handling Missing Dependencies Gracefully

If `pdftotext` or `pdfinfo` is not installed, the `run_tool` function catches `FileNotFoundError` and raises a descriptive `VerificationError` instructing the user to install **poppler-utils**. This graceful degradation aligns with the repository’s policy described in [SETUP.md](https://github.com/MadsLorentzen/ai-job-search/blob/master/SETUP.md) (line 152), ensuring the pipeline fails explicitly rather than silently.

> *Source:* [`run_tool`](https://github.com/MadsLorentzen/ai-job-search/blob/master/tools/verify_pdf.py#L23-L26) dependency checking – lines 23‑26.

## Keyword Coverage Checking Workflow

After successful text-layer verification, the keyword coverage check compares extracted posting keywords against the CV content. As documented in [CLAUDE.md](https://github.com/MadsLorentzen/ai-job-search/blob/master/CLAUDE.md) (lines 138‑140 and 261‑270), the system performs literal string matching to mirror ATS behavior, reporting genuine gaps rather than silently stuffing synonyms.

## Command-Line Usage Examples

Verify a two-page CV contains minimum contact details and readable text:

```bash
python -m tools.verify_pdf \
    path/to/cv.pdf \
    --pages 2 \
    --min-chars 100 \
    --contains "Professional Experience" \
    --contains "john.doe@example.com"

```

The arguments map directly to the `argparse` configuration in `build_parser`, supporting multiple `--contains` flags for complex validation scenarios.

## Programmatic Integration

Integrate the verifier into custom pipelines by importing the core function:

```python
from tools.verify_pdf import verify_pdf, VerificationError

try:
    verify_pdf(
        pdf_path="cv.pdf",
        expected_pages=2,
        min_chars=150,
        required_text=(
            "Education",
            "github.com/username",
        ),
    )
    print("PDF passes ATS verification")
except VerificationError as err:
    print(f"ATS verification failed: {err}")

```

This pattern allows automated build systems to gate PDF generation on ATS compatibility before submission.

## Summary

- **Text-layer validation** uses `pdftotext` from poppler to extract embedded PDF content, ensuring ATS parsers can read your CV.
- **Structural checks** verify page count via `pdfinfo` and enforce minimum character density to detect corrupted or image-based PDFs.
- **Content validation** confirms required strings (contact info, section headers) are present in the extracted text.
- **Graceful error handling** provides clear instructions when poppler-utils is missing, preventing silent failures.
- **Keyword coverage** leverages the extracted text to match job posting requirements against your CV content.

## Frequently Asked Questions

### What is pdftotext and why is it required for ATS verification?

`pdftotext` is a command-line utility from the poppler-utils suite that extracts the embedded text layer from PDF files. ATS software relies on this text layer to parse candidate information; without it, visually perfect PDFs containing only image data or corrupted encodings will be rejected as unreadable.

### How does the tool handle PDFs with missing text layers?

If `pdftotext` extracts fewer characters than the `--min-chars` threshold, `verify_pdf` raises a `VerificationError` indicating the PDF lacks sufficient machine-readable content. This typically occurs when a PDF contains scanned images without OCR or when font encoding issues render text as garbled symbols.

### Can I verify multiple required strings in a single command?

Yes. The `--contains` argument accepts multiple values and can be specified repeatedly. The tool normalizes each string and verifies its presence in the extracted text, failing only if any required string is missing. This allows simultaneous validation of email addresses, phone numbers, and section headers.

### What happens if poppler-utils is not installed on my system?

The `run_tool` function catches the resulting `FileNotFoundError` and raises a `VerificationError` with instructions to install poppler-utils via your package manager (e.g., `apt install poppler-utils`, `brew install poppler`, or `choco install poppler`). The repository treats this as a hard failure to ensure you do not submit unverified PDFs.