* Rename `data` folder with schema * Start refactoring data models * Work on entry models * Keep working on entries * Keep working on data models * Push old data files * Keep working on data models * First draft of schema.cv * Keep working on schema * Keep working on schema * Improve schema.models * Keep working on rendercv.schema * Work on schema.design * Keep working on rendercv.schema * Complete variant_class_generator * Keep working rendercv.schema * Keep working on rendercv.schema * Final touches to rendercv.schema * Improve json schema descriptions in rendercv.schema * Start working on rendercv.schema tests * Keep implementing rendercv.schema tests * Add more tests for rendercv.schema * Improve rendercv.schema * Improve docstrings and comments in rendercv.schema * Implement better pydantic error handling in `rendercv.schema` * Improve variant class system * Fix rendercv.schema tests * Start working on rendercv.templater * Update template names * Switching to new rendercv typst template soon * Work on new templater * Rename renderer with renderer_old * Don't use utils in rendercv.schema * Complete connections * Update renderer folder structure * Work on new renderer * Work on new renderer * Date processing on new renderer * Improve date processing, support multiple emails, phones, and websites * Improve markdown to Typst * Complete entry template processing * Time span computation in new renderer * Better entry templates * Setup new templates * Improve rendercv.schema * Start adding tests for rendercv.renderer * New markdown parser! * Improve markdown to typst conversion * Finalize markdown parser * Add new test files for rendercv.renderer * Fix cv and connections * Add connections test * Improve connection tests * Improve entry templates * Add model processor tests * Improve templater * Rename old folders * Improve schema * Add file generation logic to renderer * Fix naming issues * Fix schema tests * Add path type tests * Add font family and typst dimension type tests * Rename old tests * Fix design tests * Start integration testing of renderer * Improve entry tempates * Handle nested highlights properly * Finalize Typst preamble template * Start working on new CLI * Remove old test files * Implement override dictionary in new schema * Start working on new CLI * Better prints on render command * New structure * New render printer * Add all the commands to new CLI * Work on new command in new cli * Improve new command * Add error handler to new cli * Work on create theme command * Complete create theme command * Remove old source files * Improve exceptions * Create new docs * Add writing tests guide * Fix cli printer and write tests * Test copy templates * Add app tests * Bring back accidentally removed files * Imporve cli and tests * Fix path issues * Improve * Improve * Add reference file comparison tests * Fix path resolver * Start working on test_pdf_png * Implement comparison of multiple files (png) * Start testing typst * Fix templating issues * Fix header and entry templates issues * Implement short second rows * Fix date issues * Fix nested bullets and add summary * Update testdata * Implement footer * Update testdata * Reimagined design and locale schema, first iteration * Reimagined design and locale second iteration * Update design and locale schemas * Adapt templater to the new design and locale * Fix tests * Update lib.typ and testdata for the new locale and design * Implement proper tests with all combinations of entries * Remove some docstrings * fix connections logic * Improve * Start working on examples * Update testdata * Fix long second row issue * fix templating issues * Fix lib.typ issues * Update testdata * Fix clean_trailing_parts * Update test cv * update test cv * Update theme defaults * update schema and fix moderncv * Fix moderncv issues * Update testdata * Update testdata and examples * Fix issues about photo * Fix typst photo path issues * improve entry templates from yaml * add new locale * Rename writing tests doc * Update writing tests * Improve tests * Add more cli tests * Increase test coverage * Rename variant pydantic model generator * Improve tests * Update testdata and improve tests * Format, fix pre-commit errors * Fix scripts and update entry figures * Improve tests * Write docstrings of schema * Write schema docstrings * Setup api reference * Start working on new docs * Work on docs * Improve progress panel of render command * Finalize new docs index * Complete CLI docs * Work on YAML input structure page * Finalize user guide * Start working on developer guide * Improve api reference * Improve developer guide * Improve developer guide * Improve developer gide * Improve developer guide * Improve developer guide * Update developer guide * Improve developer guide * Improve developer guide * Improve developer guide * Developer guide first draft * update developer guide * Update examples * Update testdata * Handle wrong installation (rendercv instead of rendercv[full]) * Remove unnecessary files * Write set up vs code page * Update README.md * Change docs description * Compress design options gif * minor updates * Polish all the json schema descriptions * Update testdata and examples * Remove some emdashed from docs * Add whatsapp support * Add TestEscapeTypstCharacters to tests * Implement custom connections * Add page break before sections feature * Revert page break before sections feature * Rebase to main * Fix social network tests, update schema
9.1 KiB
toc_depth
| toc_depth |
|---|
| 3 |
Understanding RenderCV
This guide walks you through how RenderCV works, explaining each step and the tools we use.
The Core Workflow
RenderCV does more than this (Markdown, HTML, PNG outputs, watching files, etc.), but at its core, what happens is:
flowchart LR
A[YAML file] --> B[Typst file]
B --> C[PDF]
Read a YAML file, generate a Typst file, compile it to PDF. Everything else is built on top of this foundation.
Let's understand each step.
Step 1: Reading the YAML File
When a user gives us a YAML file like this:
cv:
name: John Doe
location: San Francisco, CA
sections:
education:
- institution: MIT
degree: PhD
start_date: 2020-09
end_date: 2024-05
We need to:
- Parse the YAML into Python dictionaries
- Validate the data (Does
start_datecome beforeend_date? Isnameactually provided and is it a string?)
ruamel.yaml: YAML Parser
First problem: reading YAML files.
Python doesn't have a built-in YAML library. To read YAML files, you need a library. We use ruamel.yaml, one of the best YAML parsers available.
What does it do? Simple: converts YAML text into Python dictionaries.
YAML file (cv.yaml):
cv:
name: John Doe
location: San Francisco, CA
sections:
education:
- institution: MIT
degree: PhD
start_date: 2020-09
After parsing with ruamel.yaml:
from ruamel.yaml import YAML
yaml = YAML()
data = yaml.load(open("cv.yaml"))
# Now data is a Python dictionary:
{
"cv": {
"name": "John Doe",
"location": "San Francisco, CA",
"sections": {
"education": [
{
"institution": "MIT",
"degree": "PhD",
"start_date": "2020-09"
}
]
}
}
}
# You can access it like any Python dict:
data["cv"]["name"] # "John Doe"
data["cv"]["sections"]["education"][0]["institution"] # "MIT"
That's it. YAML text becomes a Python dictionary we can work with.
ruamel.yaml is being called in src/rendercv/schema/yaml_reader.py.
pydantic: Python Dictionary Validator
Now we have a dictionary. We need to validate it. Without a library, you'd write:
if "name" not in data["cv"]:
raise ValueError("Missing 'name' field")
if not isinstance(data["cv"]["name"], str):
raise ValueError("name must be a string")
if "sections" in data["cv"]:
for section_name, entries in data["cv"]["sections"].items():
for entry in entries:
if "start_date" in entry and "end_date" in entry:
# Parse dates, compare them...
# This is already hundreds of lines and we're barely started
With pydantic, we can define the structure once:
from pydantic import BaseModel
from datetime import date as Date
class Education(BaseModel):
institution: str
start_date: Date
end_date: Date
@pydantic.model_validator(mode="after")
def check_dates(self):
if self.start_date > self.end_date:
raise ValueError("start_date cannot be after end_date")
return self
class Cv(BaseModel):
name: str
location: str | None = None
education: list[Education]
Then validate:
# This dictionary (from ruamel.yaml):
data = {
"name": "John Doe",
"location": "San Francisco",
"education": [
{
"institution": "MIT",
"start_date": "2020-09",
"end_date": "2024-05"
}
]
}
# Becomes this validated object:
cv = Cv.model_validate(data)
# Now you have clean, validated objects:
cv.name # "John Doe"
cv.education[0].institution # "MIT"
cv.education[0].start_date # "2020-09", guaranteed dates are valid
That's the power. Dictionary goes in, pydantic checks everything, clean Python object comes out.
RenderCV's entire data model is pydantic models all the way down:
class RenderCVModel(BaseModel):
cv: Cv # ← pydantic model
design: Design # ← pydantic model
locale: Locale # ← pydantic model
settings: Settings # ← pydantic model
Each field is another pydantic model. Cv contains more pydantic models like EducationEntry, ExperienceEntry, etc. It's nested validation: when you validate RenderCVModel, pydantic automatically validates every nested model too. One model_validate() call checks the entire structure.
See src/rendercv/schema/models/rendercv_model.py for the top-level model.
Step 2: Generating the Typst File
Now we need to generate a Typst file:
= John Doe
San Francisco, CA
== Education
#strong[MIT] #h(1fr) 2020 – 2024
PhD in Computer Science
You could try string concatenation:
typst = f"= {cv.name}\n"
if cv.location:
typst += f"{cv.location}\n"
typst += "\n"
for section_title, entries in cv.sections.items():
typst += f"== {section_title}\n"
for entry in entries:
typst += f"#strong[{entry.institution}]"
# What about optional fields? Spacing? Line breaks?
# Multiple themes with different layouts?
# This is impossible to maintain!
This doesn't work. You're building hundreds of lines of string concatenation logic, handling conditionals, managing whitespace. It's unworkable.
This is why templating engines were invented. When you need to programmatically generate complex text files, you need templates.
jinja2: Templating Engine
jinja2 is the most famous templating engine for Python.
Template file (Header.j2.typ):
= {{ cv.name }}
{% if cv.location %}
{{ cv.location }}
{% endif %}
{% if cv.email %}
#link("mailto:{{ cv.email }}")
{% endif %}
Python code:
template = jinja2_env.get_template("Header.j2.typ")
output = template.render(cv=cv)
Result:
= John Doe
San Francisco, CA
#link("mailto:john@example.com")
Clean separation: templates define layout, Python code provides data. Users can override templates to customize their CV without touching Python code.
Typst templates live in src/rendercv/renderer/templater/templates/typst/.
jinja2 is being called in src/rendercv/renderer/templater/templater.py.
markdown: Markdown to Typst
Users want to write Markdown in their YAML:
highlights:
- "**Published** [3 papers](https://example.com) on neural networks"
- "Collaborated with *Professor Smith*"
But Typst doesn't understand **bold** or [links](url). We need Typst syntax: #strong[bold] and #link("url")[text].
We use the markdown library. It parses Markdown into an XML tree. Then we walk the tree and convert each element to Typst:
match element.tag:
case "strong":
return f"#strong[{content}]"
case "em":
return f"#emph[{content}]"
case "a":
href = element.get("href")
return f'#link("{href}")[{content}]'
Result: #strong[Published] #link("https://example.com")[3 papers]
See src/rendercv/renderer/templater/markdown_parser.py. The markdown_to_typst() function does this conversion.
Step 3: Compiling to PDF
typst: Typst Compiler
typst library is the Python bindings for the Typst compiler.
from typst import compile
compile("cv.typ", output="cv.pdf")
Done. Typst file has been compiled to PDF.
typst is being called in src/rendercv/renderer/pdf_png.py.
The Complete Pipeline
When you run rendercv render cv.yaml:
- Parse -
ruamel.yamlreads YAML → Python dict - Validate -
pydanticvalidates dict →RenderCVModelobject - Generate -
jinja2renders templates with data → Typst file - Compile -
typstcompiles Typst → PDF
Everything else (Markdown support, watch mode, PNG output, HTML export) builds on this core.
Learn More
src/rendercv/cli/render_command/run_rendercv.py: The complete flowsrc/rendercv/schema/models/rendercv_model.py: The top-level Pydantic modelsrc/rendercv/renderer/templater/templater.py: Template rendering
