Resume parsing

How reveilio extracts text from resumes and what formats are supported.

Supported file formats

ExtensionMethodNotes
.pdfPyPDF2Works with text-based PDFs. Scanned/image PDFs require OCR (not built in).
.docxpython-docxFull support for modern Word files (Office 2007+).
.docantiword / heuristicLegacy Word binary format. Best results with antiword installed. Falls back to UTF-16LE extraction and printable ASCII runs.
.txtUTF-8 decodePlain text with graceful handling of encoding errors.
Free textDirect stringPass 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:

FieldTypeDescription
namestringCandidate full name.
emailstringEmail address.
phonestringPhone number.
linkedinstringLinkedIn profile URL if present on the resume.
skillslistAll technical and non-technical skills found.
experiencelistEach position with title, company, duration, description, and location.
educationlistEach degree with institution, year, and location.
certificationslistProfessional certifications.
total_experiencefloatCalculated total years of experience.
top_keywordslistMost relevant keywords from the resume.
career_gapslistDetected 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:

  1. RTF detection: some tools save RTF files with a .doc extension. These are detected by checking for the {\rtf header and parsed as RTF.
  2. OOXML detection: some tools save DOCX (ZIP-based) files with a .doc extension. These are detected by the PK header and parsed as DOCX.
  3. Antiword: if the file is truly binary Word, reveilio tries the antiword command-line tool. Install it with sudo apt-get install antiword on Linux.
  4. UTF-16LE extraction: Word binary files store body text as UTF-16LE runs. Reveilio extracts these directly.
  5. 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.