How ATS Verification Extracts and Validates PDF Text Layers with pdftotext: A 3-Step Implementation

The ATS verification utility in the MadsLorentzen/ai-job-search repository validates PDF text layers by invoking pdftotext with the -layout flag, normalizing whitespace, and enforcing checks for minimum character counts and required text substrings.

The ai-job-search repository provides a robust ATS (Applicant Tracking System) verification tool that ensures generated PDFs contain machine-readable text layers essential for automated resume parsing. Located in tools/verify_pdf.py, this utility leverages Poppler utilities—specifically pdftotext and pdfinfo—to validate that PDF documents meet specific content and structure requirements before submission to ATS platforms.

The Three-Step Verification Pipeline

The verification process implemented in tools/verify_pdf.py follows a strict three-step pipeline to guarantee PDF accessibility. Each step utilizes external Poppler command-line tools invoked through a unified helper function.

Step 1: Page Count Validation with pdfinfo

First, the script validates document length using the external pdfinfo command. The run_tool helper function executes this binary and captures its output. The parse_page_count function then extracts the integer value following the Pages: line. If the caller provides an expected_pages argument, the actual count is compared against this value, and a VerificationError is raised immediately upon mismatch.

Step 2: Text Layer Extraction Using pdftotext

For ATS readability, the core extraction relies on pdftotext executed with the -layout flag. This flag preserves the physical layout structure, which improves parsing accuracy for resume formats. The run_tool function invokes pdftotext -layout <pdf> -, outputting the raw text stream to stdout. This approach ensures that even complex multi-column PDFs yield coherent text extraction suitable for automated screening systems.

Step 3: Content Validation and Normalization

Once extracted, the raw text undergoes processing through normalize_text, which collapses consecutive whitespace characters into single spaces. This normalization makes string matching resilient against layout variations.

The validation then performs two critical checks:

  • Minimum character verification: The normalized text length is compared against the min_chars parameter (defaulting to 1). If the extracted content falls short, a VerificationError signals that the PDF lacks a proper searchable text layer.
  • Required text verification: When provided via the required_text parameter or --contains CLI flag, each specified string is normalized and verified as a substring of the extracted content. Missing entries trigger a VerificationError.

Error Handling and Reporting

All validation failures funnel through the custom VerificationError exception class. The main entry point catches these exceptions at lines 94-96, reporting failures to stderr with descriptive messages. Successful verification prints a concise confirmation message: Verified <pdf>.

Command-Line and Programmatic Usage

The tool supports both CLI invocation and direct Python integration.

Command-line usage allows quick verification with page counts, character minimums, and required text strings:

python -m tools.verify_pdf \
    resume.pdf \
    --pages 2 \
    --min-chars 100 \
    --contains "Professional Experience" \
    --contains "your.email@example.com"

For integration into larger workflows, import the verify_pdf function directly:

from pathlib import Path
from tools.verify_pdf import verify_pdf, VerificationError

pdf_path = Path("resume.pdf")

try:
    verify_pdf(
        pdf_path,
        expected_pages=2,
        min_chars=100,
        required_text=("Professional Experience", "your.email@example.com")
    )
    print("PDF passed ATS verification")
except VerificationError as exc:
    print(f"Verification failed: {exc}")

Testing the Verification Logic

The test suite in [tests/test_verify_pdf.py](https://github.com/MadsLorentzen/ai-job-search/blob/master/tests/test_verify_pdf.py) mocks the external pdfinfo and pdftotext commands to validate each logic branch. These unit tests ensure the verification pipeline behaves correctly regardless of whether the underlying Poppler utilities are installed on the system, covering edge cases such as missing text layers and page count mismatches.

Summary

  • The tools/verify_pdf.py script validates PDFs for ATS compatibility using Poppler utilities pdfinfo and pdftotext.
  • Page count validation uses pdfinfo output parsed by parse_page_count to verify document length against expected_pages.
  • Text extraction runs pdftotext -layout to preserve formatting while extracting searchable text layers.
  • Content validation enforces minimum character requirements via min_chars and verifies the presence of required text substrings after whitespace normalization.
  • The custom VerificationError exception provides unified error handling for all validation failures, caught and reported by the main entry point.
  • Comprehensive unit tests in tests/test_verify_pdf.py mock external dependencies to ensure reliability without requiring actual PDF tool installations.

Frequently Asked Questions

What is pdftotext and why is it used for ATS verification?

pdftotext is a command-line utility from the Poppler suite that converts PDF documents to plain text. The ai-job-search repository uses it specifically because it reliably extracts hidden text layers from PDFs, which is essential for verifying that resumes remain machine-readable by Applicant Tracking Systems that cannot parse image-based or scanned documents.

How does the tool handle PDFs with missing text layers?

If a PDF contains no extractable text layer, pdftotext returns minimal or empty output. The normalize_text function processes this result, and the subsequent min_chars check (defaulting to 1 character) fails, raising a VerificationError. This ensures that image-only PDFs or improperly exported documents are flagged before submission to ATS platforms.

Can I verify multiple PDFs at once using the CLI?

The current implementation in tools/verify_pdf.py processes a single PDF path per invocation. To batch-process multiple files, you would need to wrap the CLI call in a shell loop or script that iterates through your PDF directory, invoking python -m tools.verify_pdf for each document individually.

What happens if pdfinfo or pdftotext is not installed?

The run_tool helper function executes these external commands via subprocess. If either pdfinfo or pdftotext is missing from the system PATH, the subprocess call will fail and raise an exception. The test suite mitigates this dependency by mocking these external tools, but production usage requires installing the Poppler utilities package (typically poppler-utils on Linux/Unix systems).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →