Generate a resume book as a single PDF with a terminal-style cover page, table of contents (grouped by graduation date), section title pages between resume groups, and merged resume PDFs. TOC names link to resume pages; section headings (e.g., "Dec 2024") link to their section title pages; LinkedIn and GitHub appear as icons after each name when present. All resume text remains selectable. Uses US Letter paper, GreyHat branding (logo, gray/cyan colors), binary matrix background, and Font Awesome icons.
- Python 3.10+
- Typst CLI (
typst compile) - Ghostscript (for PDF merge; optional—falls back to PyMuPDF)
- LibreOffice (for DOCX → PDF conversion;
scripts/convert_docx_to_pdf.py) - Typst package:
@preview/fontawesome:0.5.0(auto-fetched for LinkedIn/GitHub icons) assets/grey_hat_hat_small_400x400.png(club logo; copied to output when generating)
cd resume-book-generator
uv sync
# or: pip install -e .- Place resume PDFs and DOCX files in
input-raw/ - Prepare
input/(copies PDFs, converts DOCX to PDF, skips duplicates):
uv run python scripts/prepare_input.py --raw input-raw --output inputOr convert DOCX only to a separate folder:
uv run python scripts/convert_docx_to_pdf.py --input input-raw --output input-pdf- Generate a template CSV:
uv run python scripts/generate_csv_from_resumes.py --input input --output candidates.csv- Edit
candidates.csvto fill inmajor,grad_date,email,linkedin,github. Use the ChatGPT prompt to extract data from resumes in bulk. - Generate the resume book:
nix-shell --run "uv run python scripts/generate_book.py --csv candidates.csv --input input --output output/resume_book.pdf"| Column | Required | Description |
|---|---|---|
| filename | yes | PDF filename (e.g. john.pdf) |
| name | yes | Full name |
| major | no | Degree/major |
| grad_date | yes | Graduation (e.g. May 2025) |
| no | Email address | |
| no | LinkedIn URL (shown as icon after name) | |
| github | no | GitHub URL (shown as icon after name) |
| phone | no | Phone number |
| tags | no | Comma-separated tags (e.g. Leadership, Mentors, CTF Competitor) shown as badges |
Candidates are sorted by grad_date then name. Use "Unknown" for unknown graduation dates.
Tags that get a dedicated TOC section (Leadership, Mentor, CTF Competitor, etc.) are configured in resume_book.json:
{
"tag_sections": ["Leadership", "Mentor", "CTF Competitor"],
"hidden_tags": ["Leadership"]
}tag_sections: Tags that get a dedicated TOC section. Matching is case-insensitive.hidden_tags: Tags to hide from badge display (e.g. Leadership when already in the Leadership section).
Override via CLI: --tag-sections "..." and --hidden-tags "Leadership".
- Install sync dependencies:
uv sync --extra sync - Sync a Drive folder to
input/:
uv run python scripts/sync_drive_to_input.py "https://drive.google.com/drive/folders/FOLDER_ID"- Sync a Google Sheet to
candidates.csv:
uv run python scripts/sync_sheet_to_csv.py "https://docs.google.com/spreadsheets/d/SHEET_ID"- Drive folder must be shared with "Anyone with the link" (or use a different sync tool for private folders).
- Google Sheet: public sheets work without credentials; for private sheets, place service account JSON at
~/.config/gspread/service_account.jsonand share the sheet with the service account'sclient_email.
--title: Cover page title (default: "GreyHat Resume Book")--subtitle: Cover page subtitle (optional)
- Typst generates cover + TOC (no resume embedding). Names are plain text with optional tag badges and LinkedIn/GitHub icons; section headings link to section title pages.
- Section pages: A separate Typst file generates one title page per graduation group (e.g., "Dec 2024", "May 2025").
- Merge:
cover_toc.pdf+ section title pages + resume PDFs are merged (PyMuPDF, with section pages inserted between groups). - Link injection: PyMuPDF adds internal link annotations on TOC pages—each name becomes a clickable link to its resume page.
- Resume text stays selectable because PDFs are merged, not embedded as images.
resume-book-generator/
├── assets/ # Club logo
├── input-raw/ # Raw resumes (PDF + DOCX)
├── input/ # Prepared PDFs (from prepare_input)
├── output/ # cover_toc.typ/pdf, section_pages.typ/pdf, resume_book.pdf
├── docs/
│ └── CHATGPT_RESUME_EXTRACTION_PROMPT.md
├── candidates.csv # Candidate data
├── src/
│ ├── typst/
│ │ ├── generator.py # Typst cover + TOC + section page generation
│ │ └── templates/
│ │ ├── cover.typ # Terminal-style cover template
│ │ └── section_page.typ # Section title page template
│ └── merge/pdf_merger.py # PDF merge + internal link injection
├── scripts/
│ ├── generate_book.py # Main CLI
│ ├── prepare_input.py # Copy PDFs + convert DOCX → input/
│ ├── convert_docx_to_pdf.py # DOCX to PDF only
│ ├── generate_csv_from_resumes.py # Template CSV from filenames
│ ├── sync_drive_to_input.py # Sync Google Drive folder → input/
│ └── sync_sheet_to_csv.py # Sync Google Sheet → candidates.csv
├── shell.nix # typst, ghostscript, libreoffice
└── pyproject.toml
To populate the CSV from resume text, use the ChatGPT prompt for bulk extraction. Upload your resumes to ChatGPT and paste the prompt; it will output CSV rows you can merge into candidates.csv.