Convert plain-text CVs into styled A4 PDFs using RCSS
Project description
ResumeForge — README
🌐 Website: https://resume-forge-cli.web.app/
Table of Contents
About
ResumeForge converts a plain UTF-8 text CV into a styled multi-page A4 PDF using a small CSS-like DSL (.rcss). Supports two layout modes: standard (single-column) and grid (2-column). MVP excludes font-face loading and decorative assets.
Setup
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Usage
# Render a plain-text CV to styled PDF
resumeforge render --input examples/resume.txt --style examples/valid.rcss --output resume.pdf
# Validate an RCSS style file for syntax errors
resumeforge validate --style examples/valid.rcss
# Print CLI version
resumeforge version
Troubleshooting
"Unexpected token ... Expected one of: HEADING, LAYOUT, @font-face, SECTION"
Your .rcss file has an invalid selector. Only layout { ... }, heading { ... }, @font-face { ... }, and section[name="..."] { ... } are valid. Check for typos in the selector keyword.
"Unexpected token ... Expected one of: SEMICOLON"
A property declaration is missing its trailing semicolon. Every declaration must end with ;.
"RCSS must contain a layout { ... } rule"
The transformer could not find a layout block in your .rcss file. Every stylesheet requires one.
"RCSS must contain at least one section[name=...] rule"
Your .rcss defines a layout but no section rules. Add at least one section[name="..."] { ... } block.
"No sections found in CV text matching the stylesheet"
The headings in your .txt file don't match any section[name="..."] values in the stylesheet. Headings must match exactly (case-sensitive, full line).
"CV text is missing one or more sections defined in the stylesheet"
Your .txt file is missing a heading that the stylesheet expects. Ensure every section[name="..."] in the .rcss has a corresponding heading line in the CV text.
"No raw sections to apply rules to" The section mapper received no parsed sections to style. This typically means your CV text was empty or contained no lines matching any stylesheet section names.
"No matching stylesheet rule for one or more sections"
A section was parsed from the CV text but has no corresponding section[name="..."] rule in the stylesheet. Ensure every heading in your .txt file has a matching rule in the .rcss.
"column-widths must sum to 100%"
The percentage values in column-widths do not add up to 100. For example, column-widths: 30% 60%; totals 90%. Adjust so they equal 100%.
"column-widths values must be whole numbers with %"
Each value in column-widths must be an integer followed by %. Decimal values like 33.3% and bare numbers like 35 are not allowed.
"layout property '...' is not valid"
The layout adapter encountered an unrecognised property in layout { ... }. Check for typos. Valid properties: mode, columns, column-widths, column-gap, margins, font-family.
"CV text must have heading content (name/contact) before the first section"
Your .txt file begins immediately with a section heading (e.g. Skills or Experience) with no name or contact information above it. Every CV must have at least one line of text before the first section — typically your full name, job title, and contact details (email, phone, LinkedIn). This heading block is rendered at the top of the PDF before any sections.
Testing
pip install pytest
pytest
RCSS DSL
Grammar definition:
src/resumeforge/grammar/rcss.lark
Section identification
- A section begins at a heading line that matches the pattern ^{HEADING} (a full-line header like: LINKS, WORK EXPERIENCE, EDUCATION).
- A section contains all text from that heading line up to the next heading line or EOF.
- Section selectors in .rcss match the heading text exactly (e.g., section[name="WORK EXPERIENCE"]).
.rcss basics (MVP)
- File extension: .rcss
- Grid mode supports exactly 2 columns. grid-column must be 1 or 2 for each section in grid mode.
Layout properties (in layout { ... })
| Property | Values | Description |
|---|---|---|
mode |
single, grid |
Page layout mode |
columns |
2 |
Number of columns (grid mode) |
column-widths |
e.g. 35% 65% |
Width of each column as percentages (grid mode, must sum to 100%) |
column-gap |
e.g. 6mm |
Gap between columns |
margins |
e.g. 20mm 18mm 20mm 18mm |
Page margins (top right bottom left) |
font-family |
e.g. "Helvetica" |
Default font (overridden by @font-face) |
Font face properties (in @font-face { ... }) — optional
| Property | Values | Description |
|---|---|---|
font-family |
e.g. "Carlito" |
Font family name to register |
src |
e.g. "fonts/Carlito-Regular.ttf" |
Path to regular weight TTF |
src-bold |
e.g. "fonts/Carlito-Bold.ttf" |
Path to bold weight TTF |
Heading properties (in heading { ... }) — optional
The heading block styles the resume header (name, title, contact info) that appears before the first section. If omitted, ATS-friendly defaults are applied automatically.
| Property | Values | Default | Description |
|---|---|---|---|
font-size |
e.g. 20pt |
20pt |
Name/first line font size. Subsequent lines (contact info, title) are scaled down proportionally |
align |
left, center, right |
center |
Text alignment |
line-height |
e.g. 7 |
7 |
Line height in mm |
color |
e.g. #333333 |
black | Text color (hex) |
Section properties (in section[name="..."] { ... })
Style properties (PDF render mode — applied as PDF state before writing):
| Property | Values | Default | Description |
|---|---|---|---|
font-size |
e.g. 12pt |
11pt |
Text size |
color |
e.g. #333333 |
black | Text color (hex) |
background-color |
e.g. #f0f0f0 |
none | Section fill color (hex) |
Write properties (PDF render mode — control how content is rendered):
| Property | Values | Default | Description |
|---|---|---|---|
align |
left, center, right |
left |
Text alignment |
line-height |
e.g. 7 |
5 |
Line height in mm |
display |
block, inline |
block |
Block wraps text (multi-line), inline flows horizontally |
Layout positioning (grid mode only):
| Property | Values | Default | Description |
|---|---|---|---|
grid-column |
1, 2 |
— | Which column to place the section in |
padding |
e.g. 8mm |
0 |
Inner spacing |
width |
e.g. 1fr |
1fr |
Proportional column width |
Example .rcss snippets
Single-column (resume-single.rcss)
layout { mode: single; margins: 20mm 18mm 20mm 18mm; }
section[name="HEADER"] {
padding: 8mm;
align: center;
}
Two-column grid (resume-grid.rcss)
layout { mode: grid; columns: 2; column-widths: 35% 65%; column-gap: 6mm; margins: 20mm 18mm 20mm 18mm; }
/* Place by heading text and explicit column (1 or 2) */
section[name="SIDEBAR"] {
grid-column: 1;
padding: 6mm;
width: 1fr;
}
section[name="MAIN"] {
grid-column: 2;
padding: 6mm;
width: 1fr;
}
section[name="HEADER"] {
grid-column: 1;
padding: 8mm;
align: center;
}
Contributing
- Create a feature branch from
main:git checkout -b feature/your-feature - Make changes, commit using Conventional Commits (
feat:,fix:,docs:,refactor:,test:) - Ensure all tests pass:
pytest - Open a pull request for code review before merging
- Merge to
mainwith--no-ffto preserve branch history
Examples
Step 1: Write your CV as plain text (examples/resume.txt)
Lorem Ipsum
Senior Software Engineer
lorem.ipsum@fakeemail.xyz | +44 0000 000000
Links
github.com/loremipsum
linkedin.com/in/loremipsum
loremipsum.dev
Skills
- Python,
- TypeScript
- Go
- Rust
- AWS (Lambda, DynamoDB, ECS, CDK)
- Terraform
- PostgreSQL,
- Redis,
- Kafka
- System Design
- CI/CD
- Kubernetes
- Observability
Work Experience
Senior Software Engineer - Acme Widget Corp
Jan 2021 - Present
- Architected event-driven microservices processing 2M+ events/day
- Led monolith-to-ECS migration reducing deploy times by 70%
- Designed real-time analytics pipeline using Kafka and Flink
- Mentored 4 junior engineers through pairing and code review
Software Engineer - Placeholder Technologies Inc
Mar 2018 - Dec 2020
- Built REST and gRPC APIs serving 500K daily active users
- Implemented canary deployments reducing rollback incidents by 85%
- Developed internal CLI tooling adopted by 3 engineering teams
Software Engineer - Foobar Systems Ltd
Sep 2015 - Feb 2018
- Developed customer-facing dashboard using React and TypeScript
- Designed multi-tenant SaaS schema in PostgreSQL
- Reduced API response times by 40% through caching
Education
MSc Computer Science - University of Nowhere, 2015
BSc Mathematics - University of Somewhere, 2013
References
Dolor Sit Amet
Engineering Director, Acme Widget Corp
dolor.sit@fakecorp.xyz
Consectetur Adipiscing
CTO, Placeholder Technologies Inc
consectetur@faketech.xyz
Step 2: Style it with RCSS (examples/valid.rcss)
@font-face { font-family: "Carlito"; src: "examples/fonts/Carlito-Regular.ttf"; src-bold: "examples/fonts/Carlito-Bold.ttf"; }
layout { mode: grid; columns: 2; column-widths: 30% 70%; column-gap: 6mm; margins: 20mm 18mm 20mm 18mm; font-family: "Carlito"; }
heading { font-size: 20pt; align: center; line-height: 7; color: #555555; }
section[name="Links"] {
color: #336699;
line-height: 5;
grid-column: 1;
}
section[name="Skills"] {
line-height: 5;
grid-column: 1;
}
section[name="Work Experience"] {
line-height: 5;
grid-column: 2;
}
section[name="Education"] {
line-height: 5;
grid-column: 2;
}
section[name="References"] {
color: #555555;
line-height: 5;
grid-column: 2;
}
Step 3: Render to PDF
resumeforge render --input examples/resume.txt --style examples/valid.rcss --output examples/resume.pdf
Result
Project details
Release history Release notifications | RSS feed
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 resumeforge-0.1.0.tar.gz.
File metadata
- Download URL: resumeforge-0.1.0.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
837d48d5101411a7d6225f1d919f1636a8d62e6e188e5dac257b360ac81e3386
|
|
| MD5 |
13d40c6b08203054f959560af308afd7
|
|
| BLAKE2b-256 |
85e43e3ea8c81d932fb6f3f61891441340ab769079badeed148a6f9d0ea55043
|
File details
Details for the file resumeforge-0.1.0-py3-none-any.whl.
File metadata
- Download URL: resumeforge-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f620ec0501add2faafd0f1cb4f73c31d15cc1819cc5c133ed8e22c09d223cbd8
|
|
| MD5 |
7ff1db0591980693daae7dbaa8236425
|
|
| BLAKE2b-256 |
a1b806dccfcf4e06fdb8afb6681695ce211fdb9c7457fe4f373add658067c69e
|