cv.typ 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #let lang = sys.inputs.at("lang", default: "de")
  2. #set page(paper: "a4", margin: 2.5cm)
  3. #set text(font: "Linux Libertine O", size: 11pt, lang: lang)
  4. #show link: set text(fill: black)
  5. #let light = "Archivo Narrow"
  6. #let data = json("resume." + lang + ".json")
  7. #let months = (
  8. de: (
  9. "Januar", "Februar", "März", "April", "Mai", "Juni",
  10. "Juli", "August", "September", "Oktober", "November", "Dezember",
  11. ),
  12. en: (
  13. "January", "February", "March", "April", "May", "June",
  14. "July", "August", "September", "October", "November", "December",
  15. ),
  16. )
  17. #let sections = (
  18. de: (
  19. work: "Berufserfahrung",
  20. education: "Ausbildung",
  21. skills: "Kenntnisse",
  22. languages: "Sprachen",
  23. publications: "Veröffentlichungen",
  24. ),
  25. en: (
  26. work: "Work Experience",
  27. education: "Education",
  28. skills: "Skills",
  29. languages: "Languages",
  30. publications: "Publications",
  31. ),
  32. )
  33. #let t = sections.at(lang)
  34. // Replace LaTeX-isms in JSON strings
  35. #let clean(s) = {
  36. s.replace("\\LaTeX", "LaTeX")
  37. .replace("\\-", "\u{00AD}")
  38. .replace("--", "\u{2013}")
  39. }
  40. // Convert "YYYY-MM-DD" → "MM/YYYY", pass through anything else (e.g. "heute", "present")
  41. #let simpledate(s) = {
  42. let parts = s.split("-")
  43. if parts.len() == 3 {
  44. parts.at(1) + "/" + parts.at(0)
  45. } else {
  46. s
  47. }
  48. }
  49. #let cvsection(title, body) = {
  50. line(length: 100%, stroke: 0.4pt)
  51. text(font: light, size: 10pt, tracking: 0.06em, upper(title))
  52. v(0.5em)
  53. body
  54. }
  55. #let cvperiod(dates, body) = {
  56. grid(
  57. columns: (70%, 30%),
  58. body,
  59. align(right, dates),
  60. )
  61. }
  62. #let cvhighlights(entry, fmt: h => [#h]) = {
  63. if "highlights" in entry and entry.highlights.len() > 0 {
  64. v(-0.2em)
  65. pad(left: 0.8em,
  66. list(spacing: 0.8em, ..entry.highlights.map(fmt))
  67. )
  68. v(0.3em)
  69. }
  70. }
  71. // ── Header ──
  72. #align(center)[
  73. #text(font: light, size: 16pt, tracking: 0.18em, upper[#data.basics.title~#data.basics.name])
  74. #v(1em)
  75. #text(font: light, size: 10pt, tracking: 0.12em, upper[
  76. #data.basics.location.address
  77. #h(0.5em) • #h(0.5em)
  78. #data.basics.location.postalCode #data.basics.location.city
  79. #h(0.5em) • #h(0.5em)
  80. #data.basics.phone
  81. ])
  82. #v(0.4em)
  83. #text(font: light, size: 10pt, tracking: 0.12em)[
  84. #link("mailto:" + data.basics.email)[#upper[#data.basics.email]]
  85. #h(0.5em) • #h(0.5em)
  86. #link("https://github.com/matze")[#upper[github.com/matze]]
  87. #h(0.5em) • #h(0.5em)
  88. #link("https://bloerg.net")[#upper[bloerg.net]]
  89. ]
  90. ]
  91. #v(1em)
  92. // ── Work experience ──
  93. #cvsection(t.work)[
  94. #v(-0.4em)
  95. #for work in data.work {
  96. cvperiod(
  97. [#simpledate(work.startDate)–#simpledate(work.endDate)],
  98. [#work.company],
  99. )
  100. v(-0.2em)
  101. emph[#work.position]
  102. cvhighlights(work)
  103. v(0.2em)
  104. }
  105. #v(0.4em)
  106. ]
  107. // ── Education ──
  108. #cvsection(t.education)[
  109. #v(-0.4em)
  110. #for stage in data.education {
  111. let degree = if stage.area != "" {
  112. stage.studyType + " " + stage.area
  113. } else {
  114. stage.studyType
  115. }
  116. cvperiod(
  117. [#simpledate(stage.startDate)–#simpledate(stage.endDate)],
  118. [#stage.institution],
  119. )
  120. emph[#degree]
  121. cvhighlights(stage)
  122. v(0.6em)
  123. }
  124. #v(0.4em)
  125. ]
  126. // ── Skills ──
  127. #pagebreak(weak: true)
  128. #cvsection(t.skills)[
  129. #v(-0.4em)
  130. #for skill in data.skills {
  131. grid(
  132. columns: (6.0em, 1fr),
  133. [#skill.name],
  134. clean(skill.keywords.join(", ")),
  135. )
  136. }
  137. #v(0.4em)
  138. ]
  139. // ── Languages ──
  140. #cvsection(t.languages)[
  141. #v(-0.4em)
  142. #for l in data.languages {
  143. grid(
  144. columns: (6.0em, 1fr),
  145. [#l.language],
  146. [#l.fluency],
  147. )
  148. }
  149. ]
  150. #v(4em)
  151. // #v(1fr)
  152. // ── Signature ──
  153. #image("signature.png", width: 2cm)
  154. #{
  155. let today = datetime.today()
  156. [#data.basics.location.city, #today.day(). #months.at(lang).at(today.month() - 1) #today.year()]
  157. }
  158. // ── Publications ──
  159. #pagebreak()
  160. #line(length: 100%, stroke: 0.4pt)
  161. #v(-0.2em)
  162. #text(font: light, size: 12pt, tracking: 0.12em, upper[#t.publications])
  163. #v(1em)
  164. #bibliography("publications.bib", full: true, title: none, style: "ieee")