| Title: | Data-Driven CVs with 'Quarto' and 'Typst' |
|---|---|
| Description: | Provides tools for producing data-driven curriculum vitae documents from structured data stored in an Excel workbook. The core workflow reads CV content from a workbook, converts it into 'Typst' layout blocks, and renders a polished PDF via the 'Quarto' publishing system. Includes functions for reading and cleaning CV data, building 'Typst' section headings and entries, rendering CV sections from data frames, and scaffolding new CV projects with a standard folder structure and template workbook. Designed to separate content from layout: CV data lives in the spreadsheet, rendering configuration lives in 'Quarto', and transformation logic lives in small, reusable R functions. See the 'Typst' typesetting system at <https://typst.app> and the 'Quarto' publishing system at <https://quarto.org>. Inspired by the 'vitae' package <https://CRAN.R-project.org/package=vitae> and the 'Awesome CV' LaTeX template <https://github.com/posquit0/Awesome-CV>. |
| Authors: | Erwin Lares [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-3284-828X>) |
| Maintainer: | Erwin Lares <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.0 |
| Built: | 2026-05-13 12:09:02 UTC |
| Source: | https://github.com/cran/curriculr |
Adds a new sheet to an existing curriculr-formatted Excel workbook and
registers it in the sections control sheet. The new sheet is pre-populated
with the standard column spine so the user can start entering data
immediately without worrying about column names.
add_section( workbook, section, label = section, date_fun = "year_only", title_col = "title", org_col = "unit", detail_col = NA, where_col = "where", overwrite = FALSE )add_section( workbook, section, label = section, date_fun = "year_only", title_col = "title", org_col = "unit", detail_col = NA, where_col = "where", overwrite = FALSE )
workbook |
A character string. Path to an existing curriculr Excel workbook. |
section |
A character string. Internal name of the new section. Must
be a valid Excel sheet name (no more than 31 characters, no special
characters). This name must match the sheet name exactly when referenced
elsewhere in the workbook or in |
label |
A character string. Display label shown as the section heading
in the rendered CV. Defaults to |
date_fun |
A character string. Token controlling date formatting for
this section. One of |
title_col |
A character string. Name of the column used as the primary
entry label in the rendered CV. Defaults to |
org_col |
A character string or |
detail_col |
A character string or |
where_col |
A character string or |
overwrite |
A logical. Whether to overwrite an existing sheet of the
same name. Defaults to |
add_section() performs the following steps:
Validates that workbook exists and that section is not already
present (unless overwrite = TRUE).
Appends a new sheet named section with the standard column spine:
title | unit | startMonth | startYear | endMonth | endYear | where | detail | include_in_resume.
Appends a new row to the sections sheet registering the new section
with the supplied metadata. When overwrite = TRUE, any existing row
for this section is replaced.
Writes the modified workbook back to workbook in place.
The workbook is modified in place. There is no undo. Consider keeping a
backup copy before calling add_section() if the workbook contains data
you cannot reconstruct.
Control sheets (profile, sections, theme, readme) cannot be used
as section names.
Invisibly returns workbook. Called primarily for its side effect
of modifying the workbook on disk.
# Copy the template to a temp directory and add a section tmp <- file.path(tempdir(), "cv-data.xlsx") file.copy( system.file("extdata", "cv-data-template.xlsx", package = "curriculr"), tmp ) add_section(tmp, section = "patents") ## Not run: # Add a section with a display label that differs from the sheet name add_section("cv-data.xlsx", section = "invited_talks", label = "Invited Talks", date_fun = "month_year") # Add a section without an organization or location line add_section("cv-data.xlsx", section = "languages", label = "Languages", date_fun = "none", org_col = NA, where_col = NA) ## End(Not run)# Copy the template to a temp directory and add a section tmp <- file.path(tempdir(), "cv-data.xlsx") file.copy( system.file("extdata", "cv-data-template.xlsx", package = "curriculr"), tmp ) add_section(tmp, section = "patents") ## Not run: # Add a section with a display label that differs from the sheet name add_section("cv-data.xlsx", section = "invited_talks", label = "Invited Talks", date_fun = "month_year") # Add a section without an organization or location line add_section("cv-data.xlsx", section = "languages", label = "Languages", date_fun = "none", org_col = NA, where_col = NA) ## End(Not run)
Called with no arguments, create_cv() runs in scaffold mode: it
copies the template Excel workbook and placeholder profile image to the
current working directory and prints instructions for the next step. No
rendering takes place.
create_cv( data = NULL, photo = NULL, output_file = "CV.pdf", overwrite = FALSE, variant = "cv", use_icons = "fontawesome" )create_cv( data = NULL, photo = NULL, output_file = "CV.pdf", overwrite = FALSE, variant = "cv", use_icons = "fontawesome" )
data |
A character string or |
photo |
A character string or |
output_file |
A character string. Name of the output PDF file.
Defaults to |
overwrite |
A logical. Whether to overwrite existing files. Defaults
to |
variant |
A character string. Controls content scope. |
use_icons |
A character string. |
Called with data and photo arguments, create_cv() runs in render
mode: it reads the workbook, generates CV.qmd, and renders it to PDF
using Quarto's Typst engine. Both CV.qmd and CV.pdf are written to
the same directory as the workbook.
Scaffold mode (no arguments):
Copies cv-data-template.xlsx to getwd().
Copies placeholder.png to getwd().
Prints instructions for editing the workbook and rendering the CV.
Render mode (data supplied):
Resolves and validates the workbook and photo paths.
Reads the workbook with read_cv_data(), applying variant filtering.
Resolves theme values from the workbook or built-in defaults.
Writes CV.qmd by injecting all resolved values into the package
template via sentinel substitution.
Calls quarto::quarto_render() to produce the PDF.
When photo = NULL, the CV header renders as a single full-width column
containing the name, contact line, address, and profile statement. When
a photo path is supplied, the header uses a two-column layout with the
photo on the left.
When variant = "resume", row-level filtering is controlled entirely by
the include_in_resume column in each section sheet. Check the rows you
want included in the resume and leave the rest unchecked.
Theme values (fonts, colors, page layout) are read from the theme sheet
in the workbook. If the theme sheet is absent, built-in defaults are
used. Individual keys missing from a partial theme sheet are filled from
defaults.
In scaffold mode, invisibly returns the path to the directory where files were copied. In render mode, invisibly returns the path to the rendered PDF.
# Scaffold mode — copy template files to a temp directory withr::with_dir(tempdir(), create_cv()) ## Not run: # Render mode — requires cv-data.xlsx, Quarto, and Typst create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg" ) # Render mode — no photo, single-column header create_cv( data = "~/my_cv/cv-data.xlsx" ) # Render mode — resume variant create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg", variant = "resume", output_file = "resume.pdf" ) # Render mode — plain text contact line, custom output filename create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg", use_icons = "none", output_file = "erwin-lares-cv.pdf" ) ## End(Not run)# Scaffold mode — copy template files to a temp directory withr::with_dir(tempdir(), create_cv()) ## Not run: # Render mode — requires cv-data.xlsx, Quarto, and Typst create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg" ) # Render mode — no photo, single-column header create_cv( data = "~/my_cv/cv-data.xlsx" ) # Render mode — resume variant create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg", variant = "resume", output_file = "resume.pdf" ) # Render mode — plain text contact line, custom output filename create_cv( data = "~/my_cv/cv-data.xlsx", photo = "~/my_cv/me.jpeg", use_icons = "none", output_file = "erwin-lares-cv.pdf" ) ## End(Not run)
Assembles a Typst-formatted contact line from a profile vector. When
use_icons = "fontawesome", known contact fields are rendered with their
Font Awesome icon via the Typst @preview/fontawesome package. Fields with
no icon equivalent fall back to plain text with a warning. When
use_icons = "none", all fields render as plain text.
cv_contact_line(profile, use_icons = "fontawesome")cv_contact_line(profile, use_icons = "fontawesome")
profile |
A named character vector as returned by the |
use_icons |
A character string. |
This function is called inside CV.qmd and is exported so that users
building custom Quarto templates can call it directly.
A character string of raw Typst markup for the contact line.
Iterates over a CV data frame and writes each row as a Typst CV entry by
calling .cv_entry() for each row and passing the result to base::cat().
cv_render_section( data, title_col, org_col = NULL, detail_col = NULL, date_fun = .cv_date_range, where_col = "where" )cv_render_section( data, title_col, org_col = NULL, detail_col = NULL, date_fun = .cv_date_range, where_col = "where" )
data |
A data frame containing CV entries. Typically one element of the
list returned by read_cv_data(), e.g. |
title_col |
A character string. Name of the column to use as the entry title. Required. |
org_col |
A character string or |
detail_col |
A character string or |
date_fun |
A function or |
where_col |
A character string or |
This function is intended to be called inside a Quarto document chunk with
results = 'asis'. The cat() call writes raw Typst blocks directly into
the document output stream. Nothing is returned — the function is called
entirely for its side effect.
For sections that use dates, pass one of .cv_date_range() or
.cv_year_range() as date_fun. For sections where dates are not relevant
(skills, affiliations), pass date_fun = NULL.
Invisibly returns NULL. Called for its side effect of writing
Typst blocks to the Quarto document output stream.
# Load sample data and render the experience section cv <- read_cv_data( system.file("extdata", "cv-data-template.xlsx", package = "curriculr") ) cv_render_section(cv$experience, title_col = "title", org_col = "unit", detail_col = "detail") ## Not run: # The following examples are intended to be called inside a Quarto chunk # with results = 'asis'. They require internal helpers and a loaded cv object. # Year-only dates cat(.cv_section("Education")) cv_render_section(cv$education, title_col = "title", org_col = "institution", detail_col = "detail", date_fun = .cv_year_range) # No dates, no location cat(.cv_section("Skills")) cv_render_section(cv$skills, title_col = "title", org_col = "unit", date_fun = NULL, where_col = NULL) # Custom inline date function cat(.cv_section("Presentations")) cv_render_section(cv$presentations, title_col = "unit", org_col = "title", date_fun = function(row) { trimws(paste(.cv_value(row, "startMonth"), .cv_value(row, "startYear"))) }) ## End(Not run)# Load sample data and render the experience section cv <- read_cv_data( system.file("extdata", "cv-data-template.xlsx", package = "curriculr") ) cv_render_section(cv$experience, title_col = "title", org_col = "unit", detail_col = "detail") ## Not run: # The following examples are intended to be called inside a Quarto chunk # with results = 'asis'. They require internal helpers and a loaded cv object. # Year-only dates cat(.cv_section("Education")) cv_render_section(cv$education, title_col = "title", org_col = "institution", detail_col = "detail", date_fun = .cv_year_range) # No dates, no location cat(.cv_section("Skills")) cv_render_section(cv$skills, title_col = "title", org_col = "unit", date_fun = NULL, where_col = NULL) # Custom inline date function cat(.cv_section("Presentations")) cv_render_section(cv$presentations, title_col = "unit", org_col = "title", date_fun = function(row) { trimws(paste(.cv_value(row, "startMonth"), .cv_value(row, "startYear"))) }) ## End(Not run)
Generates a raw Typst block for a CV section heading. The first letter of the section title is styled with the CV accent color. The heading is followed by a horizontal rule that fills the remaining line width.
cv_section(title)cv_section(title)
title |
A character string. The section title to display, e.g.
|
This function is called inside CV.qmd to emit section headings. It is
exported so that users building custom Quarto templates can call it
directly without using :::.
A character string of raw Typst markup.
Reads all sheets from a curriculr-formatted Excel workbook and returns them
as a named list of data frames. Each sheet becomes one list element, named
after the sheet. The profile sheet is returned as a named character vector
for convenient scalar access. The theme sheet is returned as a named
character vector keyed by the key column. The sections sheet is returned
as a data frame in the order the rows appear in the workbook — row order
controls section render order and must not be sorted.
read_cv_data(path = "data/cv-data.xlsx", variant = "cv")read_cv_data(path = "data/cv-data.xlsx", variant = "cv")
path |
A character string. Path to the Excel workbook. Defaults to
|
variant |
A character string. Controls which rows are included from
each section sheet. |
Sheets containing a startYear column are sorted in descending order by
startYear so that the most recent entries appear first. The profile,
theme, and sections sheets are exempt from sorting.
The workbook must follow the curriculr schema. Every section sheet should
contain a title column as the primary entry label. The profile sheet
must contain field and value columns. The sections sheet must contain
at minimum section and label columns. The theme sheet, if present,
must contain key and value columns.
All cell values are read as character strings after import. Numeric columns
such as startYear and endYear are coerced to character so that
downstream rendering treats them uniformly. The include_in_resume column
is read as a logical before coercion and used for row filtering when
variant = "resume".
Empty cells and cells containing the literal string "NA" are both
converted to NA.
If the theme sheet is absent, cv$theme is NULL and create_cv()
will fall back to built-in defaults.
A named list with one element per sheet in the workbook. The
profile element is a named character vector; the theme element is a
named character vector (or NULL if the theme sheet is absent); all
other elements are data frames. The include_in_resume column is dropped
from returned data frames — it is used for filtering only and is not
passed to the rendering pipeline. Access sections as cv$education,
cv$experience, etc. Access profile fields as
cv$profile[["first_name"]]. Access theme values as
cv$theme[["accent_color"]]. Access the sections control sheet as
cv$sections.
# Read the sample data shipped with the package cv <- read_cv_data( system.file("extdata", "cv-data-template.xlsx", package = "curriculr") ) cv$education cv$profile[["first_name"]] ## Not run: # Read a user-supplied file cv <- read_cv_data("~/my_cv/cv-data.xlsx") # Resume variant cv <- read_cv_data("~/my_cv/cv-data.xlsx", variant = "resume") ## End(Not run)# Read the sample data shipped with the package cv <- read_cv_data( system.file("extdata", "cv-data-template.xlsx", package = "curriculr") ) cv$education cv$profile[["first_name"]] ## Not run: # Read a user-supplied file cv <- read_cv_data("~/my_cv/cv-data.xlsx") # Resume variant cv <- read_cv_data("~/my_cv/cv-data.xlsx", variant = "resume") ## End(Not run)
Maps a string token from the sections sheet to the corresponding date
formatting function used by cv_render_section(). This allows date
formatting behaviour to be controlled from the Excel workbook rather than
hardcoded in the Quarto template.
resolve_date_fun(token)resolve_date_fun(token)
token |
A character string. One of |
A function suitable for passing to the date_fun argument of
cv_render_section(), or NULL when token is "none".
Converts an input value to a Typst-safe character string. Removes simple HTML line break tags, collapses repeated whitespace, escapes Typst special characters, and trims leading and trailing whitespace.
typst_escape(x)typst_escape(x)
x |
A value or vector to escape. |
CV content comes from Excel and may contain characters that Typst treats as
markup: #, $, %, &, ~, _, ^, {, }, [, ], or @.
The @ character is included because Typst may interpret email addresses as
references to labels.
A character vector with Typst-sensitive characters escaped.