How the Resume PDF Extraction Pipeline Works with PyMuPDF: A Deep Dive into Hiring-Agent

The hiring-agent repository uses PyMuPDF (imported as fitz) to extract text from resume PDFs through a three-stage pipeline that loads documents via fitz.open(), cleans whitespace with regex, and parses structured data through regex-based rules in resume_parser.py.

The interviewstreet/hiring-agent repository implements a robust resume PDF extraction pipeline using PyMuPDF to convert unstructured PDF documents into structured candidate data. This system processes uploaded resume files through sequential extraction, cleaning, and parsing stages to produce JSON-serializable output for downstream ATS integration. Understanding this PyMuPDF-based architecture reveals how the platform handles diverse PDF formats while maintaining accurate text extraction across different resume layouts.

The Three-Stage Pipeline Architecture

The extraction logic resides in hiring_agent/extractors/pdf_extractor.py and operates through three distinct phases before handing off to the parser.

PDF Loading and Page Iteration

The pipeline initiates by invoking fitz.open(stream=file_bytes, filetype="pdf") to create a document object from raw PDF bytes. The extractor then iterates through every page object using for page in doc: and calls page.get_text("text") to obtain a plain-text representation of each page's contents. This method handles line-breaks, whitespace preservation, and includes OCR fallback capabilities when the page contains embedded images or scanned content.

Text Pre-processing and Cleaning

Raw page strings are concatenated using "\n".join(page_texts) to form a single document string. The system applies re.sub(r"\s+", " ", raw).strip() to collapse multiple whitespace characters into single spaces and strips control characters or non-ASCII symbols. Simple heuristics collapse excessive new-lines into single separators, producing a clean, searchable block of text that eliminates formatting artifacts from the original PDF.

Resume-Specific Parsing

The cleaned text is passed to ResumeParser(cleaned).as_dict() within hiring_agent/resume_parser.py. This module executes regular-expression based detectors to locate typical resume sections including Contact, Experience, Education, and Skills. The parser extracts structured fields such as name, email, phone numbers, and URLs, returning a JSON-serializable dictionary that downstream services consume for candidate scoring and ATS integration.

Key Implementation Details and Source Code

The pipeline orchestration occurs in hiring_agent/pipelines/resume_pipeline.py, which wires the extractor and parser together. The dependency is declared in requirements.txt as PyMuPDF (imported as fitz).

Step Implementation Source File
Open PDF doc = fitz.open(stream=file_bytes, filetype="pdf") hiring_agent/extractors/pdf_extractor.py
Extract text page_text = page.get_text("text") hiring_agent/extractors/pdf_extractor.py
Combine pages full_text = "\n".join(page_texts) hiring_agent/extractors/pdf_extractor.py
Clean whitespace cleaned = re.sub(r"\s+", " ", raw).strip() hiring_agent/extractors/pdf_extractor.py
Parse sections parsed = ResumeParser(cleaned).as_dict() hiring_agent/resume_parser.py
Orchestrate result = ResumePipeline.process(pdf_bytes) hiring_agent/pipelines/resume_pipeline.py

Code Examples

Extracting Raw Text with PdfExtractor

Access the underlying PyMuPDF extraction directly through the PdfExtractor class for cases where you need unprocessed text before resume parsing:

from hiring_agent.extractors.pdf_extractor import PdfExtractor

with open("candidate_resume.pdf", "rb") as f:
    pdf_bytes = f.read()

raw_text = PdfExtractor.extract_text(pdf_bytes)
print(raw_text[:500])  # Display first 500 characters

Full Pipeline Processing

Process a complete resume through the orchestrated pipeline to receive structured JSON output:

from hiring_agent.pipelines.resume_pipeline import ResumePipeline

with open("candidate_resume.pdf", "rb") as f:
    pdf_bytes = f.read()

structured_resume = ResumePipeline.process(pdf_bytes)

# Access structured fields

print(structured_resume["contact"]["email"])
print(structured_resume["education"])

Summary

  • PyMuPDF Integration: The pipeline uses fitz.open() and page.get_text("text") in hiring_agent/extractors/pdf_extractor.py to handle PDF loading and text extraction with OCR fallback support.
  • Text Normalization: Regex-based cleaning in the extractor collapses whitespace and removes formatting artifacts before parsing.
  • Structured Output: The ResumeParser class in hiring_agent/resume_parser.py converts cleaned text into JSON-serializable dictionaries with sections for Contact, Experience, Education, and Skills.
  • Pipeline Architecture: hiring_agent/pipelines/resume_pipeline.py orchestrates the flow from raw PDF bytes to structured data, serving as the primary entry point for the resume PDF extraction pipeline.

Frequently Asked Questions

How does PyMuPDF handle image-based or scanned PDFs in the resume pipeline?

PyMuPDF includes OCR fallback capabilities when calling page.get_text("text"), allowing the pipeline to extract text from image-based pages within PDFs. This ensures the hiring-agent system can process scanned resumes or documents containing embedded images without requiring separate OCR preprocessing, though extraction quality depends on the clarity of the source image.

What text cleaning operations are applied to extracted PDF text?

According to the source code in hiring_agent/extractors/pdf_extractor.py, the pipeline applies re.sub(r"\s+", " ", raw).strip() to collapse multiple whitespace characters into single spaces and remove leading or trailing whitespace. The system also strips control characters and non-ASCII symbols, then collapses excessive new-lines into single separators to produce a clean, searchable text block.

Where does the structured parsing logic live in the hiring-agent repository?

The structured parsing logic resides in hiring_agent/resume_parser.py, which receives cleaned text from the PDF extractor. This module runs regular-expression based detectors to identify typical resume sections (Contact, Experience, Education, Skills) and extracts specific fields like email addresses, phone numbers, and URLs using pattern matching heuristics.

Can I use the PDF extractor without running the full resume pipeline?

Yes, you can import and use PdfExtractor directly from hiring_agent/extractors/pdf_extractor.py to extract raw text without triggering the structured parsing stage. This is useful when you need only the plain text content from a PDF for custom processing or debugging purposes, bypassing the ResumePipeline.process() orchestration entirely.

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 →