Skip to main content

Convert plain-text CVs into styled A4 PDFs using RCSS

Project description

ResumeForge — README

build coverage version python license

🌐 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

  1. Create a feature branch from main: git checkout -b feature/your-feature
  2. Make changes, commit using Conventional Commits (feat:, fix:, docs:, refactor:, test:)
  3. Ensure all tests pass: pytest
  4. Open a pull request for code review before merging
  5. Merge to main with --no-ff to 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

Resume PDF output

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

resumeforge-0.1.0.tar.gz (28.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

resumeforge-0.1.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

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

Hashes for resumeforge-0.1.0.tar.gz
Algorithm Hash digest
SHA256 837d48d5101411a7d6225f1d919f1636a8d62e6e188e5dac257b360ac81e3386
MD5 13d40c6b08203054f959560af308afd7
BLAKE2b-256 85e43e3ea8c81d932fb6f3f61891441340ab769079badeed148a6f9d0ea55043

See more details on using hashes here.

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

Hashes for resumeforge-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f620ec0501add2faafd0f1cb4f73c31d15cc1819cc5c133ed8e22c09d223cbd8
MD5 7ff1db0591980693daae7dbaa8236425
BLAKE2b-256 a1b806dccfcf4e06fdb8afb6681695ce211fdb9c7457fe4f373add658067c69e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page