#import "utils.typ" // set rules #let setrules(uservars, doc) = { set text(font: uservars.bodyfont, size: uservars.fontsize, hyphenate: false) set list(spacing: uservars.linespacing) set par(leading: uservars.linespacing, justify: true) doc } // show rules #let showrules(uservars, doc) = { // uppercase section headings show heading.where(level: 2): it => block(width: 100%)[ #set align(left) #set text(font: uservars.headingfont, size: 1em, weight: "bold") #if (uservars.at("headingsmallcaps", default: false)) { smallcaps(it.body) } else { it.body } #v(-0.75em) #line(length: 100%, stroke: 1pt + black) // draw a line ] // name title show heading.where(level: 1): it => block(width: 100%)[ #set text(font: uservars.headingfont, size: 2em, weight: "bold") #if (uservars.at("headingsmallcaps", default: false)) { smallcaps(it.body) } else { // upper(it.body) it.body } #v(2pt) ] doc } // set page layout #let cvinit(doc) = { doc = setrules(doc) doc = showrules(doc) doc } // address #let addresstext(lang, info, uservars) = { if uservars.showAddress { block( width: 100%, )[ #info.personal.location.city, #info.personal.location.region, #info.personal.location.country #v(-4pt) ] } else { none } } #let fa(body) = { set text(font: "FontAwesome") [#body] } #let contacttext(lang, info, uservars) = block( width: 100%, )[ #let profiles = ( box(link("mailto:" + info.personal.email)[#fa(" ") #info.personal.email]), if uservars.showNumber { box(link("tel:" + info.personal.phone)[#fa("") #info.personal.phone]) } else { none }, if info.personal.at("url", default: none) != none { box( link(info.personal.url)[#fa(" ") #info.personal.url.split("//").at(1)], ) }, ).filter(it => it != none) // filter out none elements from the profile array #if info.personal.profiles.len() > 0 { for profile in info.personal.profiles { profiles.push(box(link(profile.url)[#profile.username])) } } #set text(font: uservars.bodyfont, weight: "light", size: uservars.fontsize * 1) #pad(x: 0em)[ #profiles.join([#sym.space.en|#sym.space.en]) ] ] #let cvheading(lang, info, uservars) = { align(center)[ = #info.personal.name #addresstext(lang, info, uservars) #contacttext(lang, info, uservars) ] } #let cvsummary(info, isbreakable: true) = { if info != none { block(width: 100%, inset: ("left": 3%, "right": 3%))[#h(1em) #text(weight: "regular")[#info.summary]] } } #let cvwork(lang, info, isbreakable: true) = { if info.experience != none { block[ == #info.experience.title #for w in info.experience.content { block(width: 100%, breakable: isbreakable)[ #if w.url != none [ *#link(w.url)[#w.with]* #h(1fr) *#w.where* \ ] else [ *#w.with* #h(1fr) *#w.where* \ ] ] // create a block layout for each work entry let index = 0 for p in w.what { if index != 0 { v(0.6em) } block( width: 100%, breakable: isbreakable, above: 0.6em, )[ // parse ISO date strings into datetime objects #let start = utils.strpdate(lang, p.startDate) #let end = utils.strpdate(lang, p.endDate) // line 2: position and date range #text(weight: "semibold", size: 9.0pt, fill: luma(30.6%))[#p.what #h(1fr) #start #sym.dash.en #end] \ // highlights or description #if "technologies" in p and p.technologies != none [ #text(weight: "light", style: "italic")[ Technologies: #eval(p.technologies.join(" #sym.circle.filled.small "), mode: "markup") ] ] #for hi in p.why [ - #text(weight: "light")[#eval(hi, mode: "markup")] ] ] index = index + 1 } } ] } } #let cveducation(lang, info, isbreakable: true) = { let education = info.education if education != none { block[ == #education.title #for edu in education.content { let start = utils.strpdate(lang, edu.startDate) let end = utils.strpdate(lang, edu.endDate) let eduurl = edu.at("url", default: none) // create a block layout for each education entry block( width: 100%, breakable: isbreakable, )[ // line 1: institution and location #if eduurl != none { text(weight: "extrabold")[ #link(eduurl)[#edu.with] #h(1fr) #edu.where \ ] } else { text(weight: "extrabold")[ #edu.with #h(1fr) #edu.where \ ] } // line 2: degree and date #text(weight: "semibold", size: 9.0pt, fill: luma(30.6%))[#edu.what #h(1fr) #start #sym.dash.en #end] \ #if edu.why != none and edu.why.len() > 0 { for hi in edu.why [ - #text(weight: "light")[#eval(hi, mode: "markup")] ] } ] } ] } } #let cvprojects(lang, info, isbreakable: true) = { let projects = info.projects if projects != none { block[ == #projects.title #for project in projects.content { // parse ISO date strings into datetime objects let start = utils.strpdate(lang, project.startDate) let end = utils.strpdate(lang, project.endDate) // create a block layout for each project entry block( width: 100%, breakable: isbreakable, )[ // line 1: project name #if project.url != none [ *#link(project.url)[#project.what]* \ ] else [ *#project.what* \ ] // line 2: organization and date #if "with" in project and project.with != none [ #text(style: "italic")[#project.with] #h(1fr) #start #sym.dash.en #end \ ] // summary or description #if "why" in project and project.why != none [ #for hi in project.why [ - #text(weight: "light")[#eval(hi, mode: "markup")] ] ] ] } ] } } #let cvaffiliations(lang, info, isbreakable: true) = { if info.affiliations != none { block[ == Leadership & Activities #for org in info.affiliations { // parse ISO date strings into datetime objects let start = utils.strpdate(lang, org.startDate) let end = utils.strpdate(lang, org.endDate) // create a block layout for each affiliation entry block(width: 100%, breakable: isbreakable)[ // line 1: organization and location #if org.url != none [ *#link(org.url)[#org.with]* #h(1fr) *#org.where* \ ] else [ *#org.with* #h(1fr) *#org.where* \ ] // line 2: position and date #text(style: "italic")[#org.position] #h(1fr) #start #sym.dash.en #end \ // highlights or description #if org.highlights != none { for hi in org.highlights [ - #eval(hi, mode: "markup") ] } else {} ] } ] } } #let cvawards(lang, info, isbreakable: true) = { if info.awards != none { block[ == Honors & Awards #for award in info.awards { // parse ISO date strings into datetime objects let date = utils.strpdate(lang, award.date) // create a block layout for each award entry block(width: 100%, breakable: isbreakable)[ // line 1: award title and location #if award.url != none [ *#link(award.url)[#award.title]* #h(1fr) *#award.location* \ ] else [ *#award.title* #h(1fr) *#award.location* \ ] // line 2: issuer and date Issued by #text(style: "italic")[#award.issuer] #h(1fr) #date \ // summary or description #if award.highlights != none { for hi in award.highlights [ - #eval(hi, mode: "markup") ] } else {} ] } ] } } #let cvcertificates(lang, info, isbreakable: true) = { if info.certificates != none { block[ == Licenses & Certifications #for cert in info.certificates { // parse ISO date strings into datetime objects let date = utils.strpdate(lang, cert.date) // create a block layout for each certificate entry block(width: 100%, breakable: isbreakable)[ // line 1: certificate name #if cert.url != none [ *#link(cert.url)[#cert.name]* \ ] else [ *#cert.name* \ ] // line 2: issuer and date Issued by #text(style: "italic")[#cert.issuer] #h(1fr) #date \ ] } ] } } #let cvpublications(lang, info, isbreakable: true) = { if info.publications != none { block[ == Research & Publications #for pub in info.publications { // parse ISO date strings into datetime objects let date = utils.strpdate(lang, pub.releaseDate) // create a block layout for each publication entry block(width: 100%, breakable: isbreakable)[ // line 1: publication title #if pub.url != none [ *#link(pub.url)[#pub.name]* \ ] else [ *#pub.name* \ ] // line 2: publisher and date Published on #text(style: "italic")[#pub.publisher] #h(1fr) #date \ ] } ] } } #let cvlanguages(lang, info, isbreakable: true) = { block(breakable: isbreakable)[ == #info.languages.title #for lang in info.languages.content { [- *#lang.title*#text(weight: "light")[: #lang.content]] } ] } #let cvskills(lang, info, isbreakable: true, sortLexically: true) = { block( breakable: isbreakable, )[ == #info.skills.title #for skill in info.skills.content { let baseContent = if sortLexically { skill.content.sorted(key: x => { if type(x) == dictionary { x.keys().first() } else { x } }) } else { skill.content } let skillContent = baseContent .map( x => { if type(x) == dictionary { [#x.keys().first() (#text(style: "italic")[#x.values().first().sorted().join[ #sym.circle.filled.tiny ]])] } else { [#x] } }, ) .join[ #sym.circle.filled.small ] [- *#skill.title*#text(weight: "light")[: #skillContent]] } ] } #let cvreferences(info, isbreakable: true) = { if info.references != none { block[ == References #for ref in info.references { block(width: 100%, breakable: isbreakable)[ #if ref.url != none [ - *#link(ref.url)[#ref.name]*: "#ref.reference" ] else [ - *#ref.name*: "#ref.reference" ] ] } ] } else {} } #let endnote() = { place(bottom + right, block[ #set text(size: 5pt, font: "Consolas", fill: silver) Félix Desmaretz #datetime.today().display("[year]-[month]-[day]") ]) }