Generate professional resumes from JSON with DOCX/PDF export, custom styling, and web framework support.
Project description
ResumeCraft
Generate professional resumes from JSON with DOCX/PDF export, custom styling, and web framework support.
Use it as a Python library, CLI tool, or integrate into your web app (FastAPI, Django, Flask, etc.).
Features
- JSON-driven — All resume content lives in a single JSON file, easy to version control
- Auto bold keywords — Define a list of keywords and they get bolded automatically in all bullet points
- Right-aligned dates — Company locations and durations are properly right-aligned using tab stops
- Clickable hyperlinks — Email, LinkedIn, GitHub, and project links are real clickable hyperlinks
- Smart page breaks — Section headers never get orphaned at the bottom of a page
- Separate project sections — Professional projects and personal/open source projects in distinct sections
- Tech stack tags — Italic grey tech stack line under each project
- Pydantic validation — JSON is validated against strict data models before building
- Custom section ordering — Control which sections appear and in what order
- Watch mode — Auto-rebuild on file save
- PDF output — Optional PDF conversion via
docx2pdf - Auto-open — Open the generated file after building with
--open
Installation
As a library
pip install resumecraft # Core library only
pip install resumecraft[pdf] # + PDF output support
As a CLI tool
pip install resumecraft[cli] # Core + CLI
pip install resumecraft[cli,pdf,watch] # Everything
pip install resumecraft[all] # Same as above
# Or install globally
pipx install "resumecraft[cli]"
uv tool install "resumecraft[cli]"
Quick Start
# 1. Generate a template
resumecraft init -o my-resume.json
# 2. Edit my-resume.json with your details
# 3. Build your resume
resumecraft build my-resume.json
# 4. Build and open immediately
resumecraft build my-resume.json --open
# 5. Validate without building
resumecraft validate my-resume.json
# 6. Watch for changes and rebuild automatically (defaults to PDF)
resumecraft watch my-resume.json --open
CLI Reference
resumecraft --help Show all commands
resumecraft --version Show version
resumecraft init -o FILE Generate a blank JSON template
resumecraft build FILE [-o FILE] [--open] Build .docx (or .pdf) from JSON
resumecraft validate FILE Validate JSON without building
resumecraft watch FILE [-o FILE] [--open] Watch and rebuild on file changes
When -o is omitted from build, the output file is automatically named with a timestamp, e.g., resume_2026-04-01_03-45pm.docx.
When -o is omitted from watch, the output defaults to <input-name>.pdf. PDF viewers don't lock files, so changes appear instantly.
Use as a Library
from resumecraft import ResumeCraft
# From a JSON file
rc = ResumeCraft.from_json("my-resume.json")
rc.to_docx("resume.docx")
# From a dict
rc = ResumeCraft({"name": "John Doe", "contact": {...}, "summary": "..."})
rc.to_docx("resume.docx")
# From a JSON string
rc = ResumeCraft('{"name": "John Doe", ...}')
# Get bytes (for web frameworks)
content = rc.to_bytes()
# Export as PDF (requires: pip install resumecraft[pdf])
rc.to_pdf("resume.pdf")
# Export back to dict
data = rc.to_dict()
# Get a sample template to see all fields
sample = ResumeCraft.sample()
# Get JSON schema for editor validation
schema = ResumeCraft.json_schema()
FastAPI example
import io
import tempfile
from fastapi import FastAPI
from fastapi.responses import FileResponse, StreamingResponse
from resumecraft import ResumeCraft
app = FastAPI()
@app.post("/resume/docx")
def generate_docx(data: dict):
rc = ResumeCraft(data)
return StreamingResponse(
io.BytesIO(rc.to_bytes()),
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
headers={"Content-Disposition": "attachment; filename=resume.docx"},
)
@app.post("/resume/pdf")
def generate_pdf(data: dict):
rc = ResumeCraft(data)
tmp = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False)
rc.to_pdf(tmp.name)
return FileResponse(tmp.name, filename="resume.pdf", media_type="application/pdf")
Flask example
import io
import tempfile
from flask import Flask, request, send_file
from resumecraft import ResumeCraft
app = Flask(__name__)
@app.post("/resume/docx")
def generate_docx():
rc = ResumeCraft(request.json)
return send_file(
io.BytesIO(rc.to_bytes()),
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
download_name="resume.docx",
)
@app.post("/resume/pdf")
def generate_pdf():
rc = ResumeCraft(request.json)
tmp = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False)
rc.to_pdf(tmp.name)
return send_file(tmp.name, mimetype="application/pdf", download_name="resume.pdf")
See the examples/ folder for more complete examples including Django.
Advanced usage
You can also use the lower-level Resume and DocxBuilder classes directly:
from resumecraft import Resume, DocxBuilder
resume = Resume.from_json("my-resume.json")
DocxBuilder(resume).save("resume.docx")
JSON Structure
Run resumecraft init to generate a full template. Here's the structure:
{
"name": "Your Name",
"contact": {
"location": "City, Country",
"email": "your@email.com",
"phone": "+1-234-567-8900",
"links": [
{ "label": "LinkedIn", "url": "https://linkedin.com/in/you" },
{ "label": "GitHub", "url": "https://github.com/you" }
]
},
"summary": "A brief professional summary...",
"bold_keywords": ["FastAPI", "React", "PostgreSQL"],
"experience": [
{
"company": "Company Name",
"location": "City, Country",
"title": "Your Title",
"duration": "JAN 2023 - PRESENT",
"bullets": ["What you did and the impact it had."]
}
],
"projects": [
{
"name": "Project Name",
"subtitle": "| Description",
"tech_stack": "Python, FastAPI",
"link": null,
"bullets": ["What you built."]
}
],
"professional_projects": [
{
"name": "Project Name",
"subtitle": "| Location | Type",
"tech_stack": "FastAPI, React, PostgreSQL",
"link": null,
"bullets": ["What you built."]
}
],
"personal_projects": [
{
"name": "Side Project",
"subtitle": "| Personal Project",
"tech_stack": null,
"link": { "label": "GitHub", "url": "https://github.com/you/project" },
"bullets": ["What you built and why."]
}
],
"skills": [
{ "category": "Backend", "items": "Python (FastAPI, Django), Node.js" },
{ "category": "Frontend", "items": "React, TypeScript" }
],
"education": [
{
"institution": "University Name",
"degree": "Bachelor of Computer Science",
"duration": "2019 - 2023"
}
],
"languages": "English - Native | Hindi - Professional",
"style": {
"font": "calibri",
"color": "black",
"spacing": "normal"
},
"section_order": [
"summary",
"experience",
"professional_projects",
"personal_projects",
"skills",
"education",
"languages"
]
}
Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Full name displayed at the top |
contact |
object | Yes | Location, email, phone, and links |
summary |
string | Yes | Professional summary paragraph |
bold_keywords |
string[] | No | Words to auto-bold in all bullet points |
experience |
object[] | No | Work experience entries |
projects |
object[] | No | Single unified projects section |
professional_projects |
object[] | No | Client/employer projects (use with personal_projects for split sections) |
personal_projects |
object[] | No | Side projects and open source work |
skills |
object[] | No | Categorized skill lists |
education |
object[] | No | Degrees and institutions |
languages |
string | No | Language proficiencies |
section_order |
string[] | No | Custom order of sections (omit for default). Only listed sections are rendered. |
style |
object | No | Styling options (font, color, spacing) |
Style Options
Add a style object to customize the look of your resume:
{
"style": {
"font": "garamond",
"color": "navy",
"spacing": "compact"
}
}
| Option | Choices | Default |
|---|---|---|
font |
calibri, arial, times, garamond, georgia, helvetica, cambria |
calibri |
color |
black, navy, forest, maroon, slate, royal |
black |
spacing |
compact, normal, relaxed |
normal |
Available sections for section_order
summary, experience, projects, professional_projects, personal_projects, skills, education, languages
Note: Use either
projectsfor a single section, orprofessional_projects+personal_projectsfor two separate sections. If you useprojects, include it insection_order— it's not part of the default order.
Project Structure
resumecraft/
├── pyproject.toml
├── README.md
├── LICENSE
├── examples/
│ ├── basic.py # Simple usage
│ ├── from_json.py # Build from JSON file
│ ├── styled.py # Custom fonts, colors, spacing
│ ├── fastapi_app.py # FastAPI REST API
│ ├── flask_app.py # Flask REST API
│ └── django_view.py # Django views
├── src/resumecraft/
│ ├── __init__.py # Public API (ResumeCraft, Resume, DocxBuilder)
│ ├── craft.py # ResumeCraft facade (simple high-level API)
│ ├── cli.py # Typer CLI commands (build, validate, init, watch)
│ ├── models.py # Pydantic data models
│ ├── builder.py # DocxBuilder — converts models to .docx
│ ├── styles.py # Styling constants (fonts, sizes, colors, margins)
│ └── utils.py # Helpers (hyperlinks, keep_with_next, bold patterns)
└── tests/
├── fixtures/
│ └── sample.json # Sample resume for tests
├── test_models.py
├── test_builder.py
├── test_craft.py
├── test_cli.py
└── test_utils.py
Development
git clone https://github.com/mdfarhankc/resumecraft.git
cd resumecraft
uv sync --extra dev
Run tests
uv run pytest
Build
uv build
This creates dist/resumecraft-x.x.x.tar.gz and dist/resumecraft-x.x.x-py3-none-any.whl.
Publish to PyPI
Create a GitHub release (e.g., v0.2.0) and the CI workflow will publish to PyPI automatically via trusted publishing.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file resumecraft-0.4.0.tar.gz.
File metadata
- Download URL: resumecraft-0.4.0.tar.gz
- Upload date:
- Size: 68.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c70998a8c521755563a68adee2bc7e48cc610a082737dc224cc1ccde932f9931
|
|
| MD5 |
7b096077708e957c916cf56e9290850c
|
|
| BLAKE2b-256 |
86afdc5811b4bc21e811ec9e2cfd1023b5b89d3031ee186d5a30882237941cf6
|
Provenance
The following attestation bundles were made for resumecraft-0.4.0.tar.gz:
Publisher:
publish.yml on mdfarhankc/resumecraft
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
resumecraft-0.4.0.tar.gz -
Subject digest:
c70998a8c521755563a68adee2bc7e48cc610a082737dc224cc1ccde932f9931 - Sigstore transparency entry: 1215499201
- Sigstore integration time:
-
Permalink:
mdfarhankc/resumecraft@3d9128aacaa043c277ff61c3ac90423c6c9c0331 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/mdfarhankc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d9128aacaa043c277ff61c3ac90423c6c9c0331 -
Trigger Event:
release
-
Statement type:
File details
Details for the file resumecraft-0.4.0-py3-none-any.whl.
File metadata
- Download URL: resumecraft-0.4.0-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
606493feb3193c5140d9cd4ab7960b1d0c3cae340d9a6be2d63245a7db692699
|
|
| MD5 |
6a43f11af598b3a4f07ed73e830bf21d
|
|
| BLAKE2b-256 |
39a298d24df470249113cffb5a072ace7a0a298877a750dc397bb4d841097bbb
|
Provenance
The following attestation bundles were made for resumecraft-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on mdfarhankc/resumecraft
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
resumecraft-0.4.0-py3-none-any.whl -
Subject digest:
606493feb3193c5140d9cd4ab7960b1d0c3cae340d9a6be2d63245a7db692699 - Sigstore transparency entry: 1215499277
- Sigstore integration time:
-
Permalink:
mdfarhankc/resumecraft@3d9128aacaa043c277ff61c3ac90423c6c9c0331 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/mdfarhankc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d9128aacaa043c277ff61c3ac90423c6c9c0331 -
Trigger Event:
release
-
Statement type: