Resume parsing
How reveilio extracts text from resumes and what formats are supported.
Supported file formats
| Extension | Method | Notes |
|---|---|---|
.pdf | PyPDF2 | Works with text-based PDFs. Scanned/image PDFs require OCR (not built in). |
.docx | python-docx | Full support for modern Word files (Office 2007+). |
.doc | antiword / heuristic | Legacy Word binary format. Best results with antiword installed. Falls back to UTF-16LE extraction and printable ASCII runs. |
.txt | UTF-8 decode | Plain text with graceful handling of encoding errors. |
| Free text | Direct string | Pass text directly without any file. Useful for database rows or API responses. |
The Resume class
A Resume is a lightweight wrapper that holds the extracted plain text
and a filename. Unlike JobDescription, the Resume class
does not call the LLM on its own. Structured extraction happens inside
analyze_resume() as part of a single combined LLM call.
From a file
resume = reveilio.Resume.from_file("resumes/alice.pdf")
print(resume.filename) # "alice.pdf"
print(resume.text[:200]) # extracted plain text
From free text
resume = reveilio.Resume.from_text(
"Alice Example, Senior Python Engineer, 6 years experience...",
filename="alice.txt", # optional, used in output reports
)
From bytes
resume = reveilio.Resume.from_bytes(
uploaded_file.read(),
filename="alice.pdf", # extension determines the parser
)
What gets extracted during analysis
When you call reveilio.analyze_resume(resume, jd), the scorer
extracts the following structured data from the resume in a single LLM call:
| Field | Type | Description |
|---|---|---|
name | string | Candidate full name. |
email | string | Email address. |
phone | string | Phone number. |
linkedin | string | LinkedIn profile URL if present on the resume. |
skills | list | All technical and non-technical skills found. |
experience | list | Each position with title, company, duration, description, and location. |
education | list | Each degree with institution, year, and location. |
certifications | list | Professional certifications. |
total_experience | float | Calculated total years of experience. |
top_keywords | list | Most relevant keywords from the resume. |
career_gaps | list | Detected gaps in employment history. |
Handling .doc files (legacy Word binary)
The .doc format is a legacy binary format from Word 97-2003. Reveilio
handles it using a multi-step fallback strategy:
- RTF detection: some tools save RTF files with a
.docextension. These are detected by checking for the{\rtfheader and parsed as RTF. - OOXML detection: some tools save DOCX (ZIP-based) files with a
.docextension. These are detected by thePKheader and parsed as DOCX. - Antiword: if the file is truly binary Word, reveilio tries the
antiwordcommand-line tool. Install it withsudo apt-get install antiwordon Linux. - UTF-16LE extraction: Word binary files store body text as UTF-16LE runs. Reveilio extracts these directly.
- Printable ASCII runs: as a last resort, reveilio scans for sequences of printable ASCII characters in the raw binary.
Using the text extractor directly
The text extraction logic is available as a standalone utility:
from reveilio.parsing.text_extract import extract_text
text = extract_text("document.pdf") # from file path
text = extract_text(raw_bytes, filename="doc.docx") # from bytes
Convenience: skip the Resume class
You do not have to create a Resume object manually.
analyze_resume() accepts file paths and free text directly:
# All three are equivalent
reveilio.analyze_resume("resumes/alice.pdf", jd)
reveilio.analyze_resume(reveilio.Resume.from_file("resumes/alice.pdf"), jd)
# Free text works too
reveilio.analyze_resume("Alice, 6 years Python, FastAPI, AWS", jd)
Continue to Scoring & analysis.