Kaynağa Gözat

Switch to typst

Matthias Vogelgesang 3 ay önce
ebeveyn
işleme
ebffc9713f
8 değiştirilmiş dosya ile 360 ekleme ve 290 silme
  1. 0 9
      .gitignore
  2. 0 24
      Makefile
  3. 0 57
      convert.py
  4. 0 197
      cv.tex.in
  5. 194 0
      cv.typ
  6. 11 0
      justfile
  7. 3 3
      resume.de.json
  8. 152 0
      resume.en.json

+ 0 - 9
.gitignore

@@ -1,10 +1 @@
-*.log
-*.aux
-*.tex
-*.out
 *.pdf
-*.bbl
-*.blg
-*.bcf
-*.run.xml
-pubs.bib

+ 0 - 24
Makefile

@@ -1,24 +0,0 @@
-RESUME=resume.de.json
-TEMPLATES=cv.tex.in
-TEX=$(patsubst %.in,%,$(TEMPLATES))
-PDF=$(patsubst %.tex,%.pdf,$(TEX))
-
-.PHONY: all clean
-
-
-all: $(PDF)
-
-
-clean:
-	rm -f $(shell cat .gitignore)
-
-
-%.tex: %.tex.in $(RESUME)
-	@python3 convert.py --signature signature.png --template $< --resume $(RESUME) > $@
-
-
-%.pdf: %.tex $(TEX)
-	@rm -f pubs.bib
-	@xelatex $<
-	@biber cv
-	@xelatex $<

+ 0 - 57
convert.py

@@ -1,57 +0,0 @@
-import argparse
-import os
-import json
-import datetime
-import jinja2
-
-
-def is_valid_file(parser, arg):
-    if not os.path.exists(arg):
-        parser.error("%s does not exist." % arg)
-    else:
-        return arg
-
-
-def simpledate(date_string):
-    try:
-        d = datetime.datetime.strptime(date_string, '%Y-%M-%d')
-        return d.strftime('%M/%Y')
-    except ValueError:
-        # in case this is not a date, we return it verbatim
-        return date_string
-
-
-def create_template(args):
-    env = jinja2.Environment(loader=jinja2.FileSystemLoader('.'))
-    env.block_start_string = '((*'
-    env.block_end_string = '*))'
-    env.variable_start_string = '((('
-    env.variable_end_string = ')))'
-    env.comment_start_string = '((='
-    env.comment_end_string = '=))'
-    env.filters['simpledate'] = simpledate
-    return env.get_template(args.template)
-
-
-def main():
-    parser = argparse.ArgumentParser()
-
-    parser.add_argument('-t', '--template',
-                        type=lambda x: is_valid_file(parser, x),
-                        help="Jinja2 input template", required=True)
-    parser.add_argument('-r', '--resume', 
-                        type=lambda x: is_valid_file(parser, x),
-                        help="JSON resume", required=True)
-    parser.add_argument('-s', '--signature',
-                        type=lambda x: is_valid_file(parser, x),
-                        help="Signature image file", required=False)
-
-    args = parser.parse_args()
-    data = json.load(open(args.resume, 'r'))
-    data['basics']['signature'] = args.signature
-    template = create_template(args)
-    print(template.render(resume=data))
-
-
-if __name__ == '__main__':
-    main()

+ 0 - 197
cv.tex.in

@@ -1,197 +0,0 @@
-\documentclass[a4paper]{scrartcl}
-
-\usepackage[margin=2.5cm]{geometry}
-\usepackage[ngerman]{babel}
-\usepackage{fontspec}
-\usepackage{graphicx}
-\usepackage{lastpage}
-\usepackage{scrlayer-scrpage}
-\usepackage{enumitem}
-\usepackage[
-  hidelinks=true
-]{hyperref}
-
-((* if resume.publications *))
-\usepackage[
-  maxbibnames=99,
-  firstinits=true,
-]{biblatex}
-\usepackage{filecontents}
-\begin{filecontents}{pubs.bib}
-  ((* for publication in resume.publications|sort(attribute='year', reverse=True) *))
-  @((( publication.type ))){tmp((( loop.index ))),
-    title = {((( publication.title)))},
-    author = {((( publication.authors|join(' and ') )))},
-    year = {((( publication.year )))},
-    month = {((( publication.month )))},
-    pages = {((( publication.pages )))},
-    ((* if publication.journal *))
-    journal = {((( publication.journal )))},
-    volume = {((( publication.volume )))},
-    number = {((( publication.number )))},
-    ((* else *))
-    booktitle = {((( publication.booktitle )))},
-    ((* endif *))
-    doi = {((( publication.doi )))},
-  }
-  ((* endfor *))
-\end{filecontents}
-\bibliography{pubs}
-((* endif *))
-
-% --- Fonts
-
-\setmainfont[Mapping=tex-text]{Linux Libertine O}
-
-\newfontfamily\light[Mapping=tex-text, LetterSpace=12.0]{Archivo Narrow}
-
-\setlist[description]{%
-  font={\normalfont\light\footnotesize},
-  topsep=0em,
-  itemsep=0.2em
-}
-
-\setlist[itemize]{%
-  topsep=0.8em,
-  itemsep=0.15em,
-}
-
-((* if resume.publications *))
-\defbibheading{customheading}{\noindent\light\large
-\uppercase{Veröffentlichungen}\vspace{1em}}
-((* endif *))
-
-% --- Commands
-
-\newcommand*{\textlight}[1]{{\light #1}}
-
-\newcommand*{\cvperiod}[2]{%
-  \noindent
-  \begin{minipage}[t]{0.7\textwidth}
-    \raggedright #2
-  \end{minipage}
-  \hfill
-  \begin{minipage}[t]{0.3\textwidth}
-    \raggedleft #1
-  \end{minipage}
-}
-
-
-% --- Environments
-
-\newenvironment*{cvsection}[1]{%
-  \noindent\rule{\textwidth}{0.4pt}
-  \begin{flushleft}
-    \vspace{-0.2em}
-    \addfontfeature{LetterSpace=6.0}\footnotesize\light{\MakeUppercase{#1}}
-  \end{flushleft}
-}
-{}
-
-\cfoot{\normalfont\footnotesize\textlight{\thepage}}
-\pagestyle{empty}
-
-\begin{document}
-  \begin{center}
-    \light{\addfontfeature{LetterSpace=18.0}{\Large\MakeUppercase{((( resume.basics.title)))~((( resume.basics.name )))}}}
-    \vspace{1em}
-
-    {\footnotesize\textlight{\MakeUppercase{%
-      ((( resume.basics.location.address )))\hspace{1ex}•\hspace{1ex}((( resume.basics.location.postalCode )))
-      ((( resume.basics.location.city )))}}}
-    \vspace{.4em}
-
-    {\footnotesize\textlight{%
-      ((( resume.basics.phone )))\hspace{1ex}•\hspace{1ex}\href{mailto:((( resume.basics.email )))}{\MakeUppercase{((( resume.basics.email )))}}}}
-  \end{center}
-
-  \vspace{1em}
-
-  \begin{cvsection}{Berufserfahrung}
-    ((* for work in resume.work *))
-    \cvperiod{((( work.startDate|simpledate )))--((( work.endDate|simpledate )))}{((( work.company)))}
-    \vspace{0.4em}
-    \noindent\emph{((( work.position )))}
-    ((* if work.highlights *))
-      \vspace{-0.6em}
-      \begin{itemize}[leftmargin=1.8em, rightmargin=8em]
-        \raggedright
-        ((* for highlight in work.highlights *))
-        \item ((( highlight )))
-        ((* endfor *))
-      \end{itemize}
-    ((* endif *))
-    \vspace{0.6em}
-    ((* endfor *))
-  \end{cvsection}
-
-  \begin{cvsection}{Ausbildung}
-    ((* for stage in resume.education *))
-    \cvperiod{((( stage.startDate|simpledate )))--((( stage.endDate|simpledate)))}{((( stage.studyType ))) ((( stage.area ))) -- ((( stage.institution )))}
-    ((* if stage.highlights *))
-      \vspace{-1.8em}
-      \begin{itemize}[leftmargin=1.8em, rightmargin=8em]
-        \raggedright
-      ((* for highlight in stage.highlights *))
-        \item ((( highlight )))
-      ((* endfor *))
-      \end{itemize}
-    ((* endif *))
-    ((* endfor *))
-  \end{cvsection}
-
-  \begin{cvsection}{Kenntnisse}
-    \begin{description}[leftmargin=6.5em, style=nextline]
-      ((* for skill in resume.skills *))
-    \item[((( skill.name )))] ((( skill.keywords|join(', ') )))
-      ((* endfor *))
-    \end{description}
-  \end{cvsection}
-  \vspace{1em}
-
-  \begin{cvsection}{Sprachen}
-    \begin{description}[leftmargin=6.5em, style=nextline]
-    ((* for lang in resume.languages *))
-      \item[((( lang.language )))] ((( lang.fluency )))
-        ((* if lang.cert *))
-         ((( lang.cert )))
-        ((* endif *))
-    ((* endfor *))
-    \end{description}
-  \end{cvsection}
-
-  % \begin{cvsection}{Persönliche Interessen}
-  %   ((* for interest in resume.interests *))
-  %     ((( interest.name )))\vspace{.75em}
-  %   ((* endfor *))
-  % \end{cvsection}
-  \vspace{1em}
-
-  % \begin{cvsection}{Netz}
-  %   \begin{description}[leftmargin=4.2em, style=nextline]
-  %     \item[Webseite] \href{((( resume.basics.website )))}{((( resume.basics.shortWebsite )))}
-  %     ((* for profile in resume.basics.profiles *))
-  %     \item[((( profile.network )))] \href{((( profile.url )))}{((( profile.shortUrl )))}
-  %     ((* endfor *))
-  %   \end{description}
-  % \end{cvsection}
-
-  \vfill
-
-  \begin{flushleft}
-    ((* if resume.basics.signature *))
-      \includegraphics[width=2.0cm]{((( resume.basics.signature )))}
-    ((* endif *))
-    ((( resume.basics.location.city ))), \today
-  \end{flushleft}
-
-  ((* if resume.publications *))
-  \pagebreak
-  \nocite{*}
-  \begingroup
-    \newrefcontext[sorting=ydnt]
-    \printbibliography[heading=customheading]
-  \endgroup
-  ((* endif *))
-  \pagebreak
-\end{document}

+ 194 - 0
cv.typ

@@ -0,0 +1,194 @@
+#let lang = sys.inputs.at("lang", default: "de")
+
+#set page(paper: "a4", margin: 2.5cm)
+#set text(font: "Linux Libertine O", size: 11pt, lang: lang)
+#show link: set text(fill: black)
+
+#let light = "Archivo Narrow"
+#let data = json("resume." + lang + ".json")
+
+#let months = (
+  de: (
+    "Januar", "Februar", "März", "April", "Mai", "Juni",
+    "Juli", "August", "September", "Oktober", "November", "Dezember",
+  ),
+  en: (
+    "January", "February", "March", "April", "May", "June",
+    "July", "August", "September", "October", "November", "December",
+  ),
+)
+
+#let sections = (
+  de: (
+    work: "Berufserfahrung",
+    education: "Ausbildung",
+    skills: "Kenntnisse",
+    languages: "Sprachen",
+    publications: "Veröffentlichungen",
+  ),
+  en: (
+    work: "Work Experience",
+    education: "Education",
+    skills: "Skills",
+    languages: "Languages",
+    publications: "Publications",
+  ),
+)
+
+#let t = sections.at(lang)
+
+// Replace LaTeX-isms in JSON strings
+#let clean(s) = {
+  s.replace("\\LaTeX", "LaTeX")
+   .replace("\\-", "\u{00AD}")
+   .replace("--", "\u{2013}")
+}
+
+// Convert "YYYY-MM-DD" → "MM/YYYY", pass through anything else (e.g. "heute", "present")
+#let simpledate(s) = {
+  let parts = s.split("-")
+  if parts.len() == 3 {
+    parts.at(1) + "/" + parts.at(0)
+  } else {
+    s
+  }
+}
+
+#let cvsection(title, body) = {
+  line(length: 100%, stroke: 0.4pt)
+  text(font: light, size: 10pt, tracking: 0.06em, upper(title))
+  v(0.5em)
+  body
+}
+
+#let cvperiod(dates, body) = {
+  grid(
+    columns: (70%, 30%),
+    body,
+    align(right, dates),
+  )
+}
+
+#let cvhighlights(entry, fmt: h => [#h]) = {
+  if "highlights" in entry and entry.highlights.len() > 0 {
+    v(-0.2em)
+    pad(left: 0.8em,
+      list(spacing: 0.8em, ..entry.highlights.map(fmt))
+    )
+    v(0.3em)
+  }
+}
+
+// ── Header ──
+
+#align(center)[
+  #text(font: light, size: 16pt, tracking: 0.18em, upper[#data.basics.title~#data.basics.name])
+  #v(1em)
+  #text(font: light, size: 10pt, tracking: 0.12em, upper[
+    #data.basics.location.address
+    #h(0.5em) • #h(0.5em)
+    #data.basics.location.postalCode #data.basics.location.city
+    #h(0.5em) • #h(0.5em)
+    #data.basics.phone
+  ])
+  #v(0.4em)
+  #text(font: light, size: 10pt, tracking: 0.12em)[
+    #link("mailto:" + data.basics.email)[#upper[#data.basics.email]]
+    #h(0.5em) • #h(0.5em)
+    #link("https://github.com/matze")[#upper[github.com/matze]]
+    #h(0.5em) • #h(0.5em)
+    #link("https://bloerg.net")[#upper[bloerg.net]]
+  ]
+]
+
+#v(1em)
+
+// ── Work experience ──
+
+#cvsection(t.work)[
+  #v(-0.4em)
+  #for work in data.work {
+    cvperiod(
+      [#simpledate(work.startDate)–#simpledate(work.endDate)],
+      [#work.company],
+    )
+    v(-0.2em)
+    emph[#work.position]
+    cvhighlights(work)
+    v(0.2em)
+  }
+
+  #v(0.4em)
+]
+
+// ── Education ──
+
+#cvsection(t.education)[
+  #v(-0.4em)
+  #for stage in data.education {
+    let degree = if stage.area != "" {
+      stage.studyType + " " + stage.area
+    } else {
+      stage.studyType
+    }
+    cvperiod(
+      [#simpledate(stage.startDate)–#simpledate(stage.endDate)],
+      [#stage.institution],
+    )
+    emph[#degree]
+    cvhighlights(stage)
+    v(0.6em)
+  }
+
+  #v(0.4em)
+]
+
+// ── Skills ──
+#pagebreak(weak: true)
+
+#cvsection(t.skills)[
+  #v(-0.4em)
+  #for skill in data.skills {
+    grid(
+      columns: (6.0em, 1fr),
+      [#skill.name],
+      clean(skill.keywords.join(", ")),
+    )
+  }
+
+  #v(0.4em)
+]
+
+// ── Languages ──
+
+#cvsection(t.languages)[
+  #v(-0.4em)
+  #for l in data.languages {
+    grid(
+      columns: (6.0em, 1fr),
+      [#l.language],
+      [#l.fluency],
+    )
+  }
+]
+
+#v(4em)
+// #v(1fr)
+
+// ── Signature ──
+
+#image("signature.png", width: 2cm)
+#{
+  let today = datetime.today()
+  [#data.basics.location.city, #today.day(). #months.at(lang).at(today.month() - 1) #today.year()]
+}
+
+// ── Publications ──
+
+#pagebreak()
+#line(length: 100%, stroke: 0.4pt)
+#v(-0.2em)
+#text(font: light, size: 12pt, tracking: 0.12em, upper[#t.publications])
+#v(1em)
+
+#bibliography("publications.bib", full: true, title: none, style: "ieee")

+ 11 - 0
justfile

@@ -0,0 +1,11 @@
+lang := "de"
+
+default:
+    typst compile --input lang={{lang}} cv.typ cv.{{lang}}.pdf
+
+all:
+    just lang=de
+    just lang=en
+
+clean:
+    rm -f cv.*.pdf

+ 3 - 3
resume.de.json

@@ -7,7 +7,7 @@
         "shortWebsite": "bloerg.net",
         "phone": "0162 9769061",
         "location": {
-            "address": "Vorholzstrasse 36",
+            "address": "Vorholzstr. 36",
             "postalCode": "76137",
             "city": "Karlsruhe",
             "countryCode": "DE"
@@ -63,7 +63,7 @@
     "work": [
         {
             "company": "Cinemo GmbH",
-            "position": "Senior Software Engineer, Project Leader, Product Owner",
+            "position": "Principal Software Engineer, Project Leader, Product Owner",
             "startDate": "2018-07-23",
             "endDate": "heute",
             "summary": "foo"
@@ -115,7 +115,7 @@
         },
         {
             "name": "Protokolle",
-            "keywords": ["TCP", "UDP", "GRPC/Protobuf", "HTTP", "ZeroMQ", "CameraLink"]
+            "keywords": ["TCP", "UDP", "gRPC/Protobuf", "HTTP", "ZeroMQ", "CameraLink"]
         }
     ],
     "interests": [

+ 152 - 0
resume.en.json

@@ -0,0 +1,152 @@
+{
+    "basics": {
+        "name": "Matthias Vogelgesang",
+        "title": "Dr.",
+        "email": "matthias.vogelgesang@gmail.com",
+        "website": "https://bloerg.net",
+        "shortWebsite": "bloerg.net",
+        "phone": "+49 162 9769061",
+        "location": {
+            "address": "Vorholzstr. 36",
+            "postalCode": "76137",
+            "city": "Karlsruhe",
+            "countryCode": "DE"
+        },
+        "profiles": [
+            {
+                "network": "GitHub",
+                "username": "matze",
+                "url": "https://github.com/matze",
+                "shortUrl": "github.com/matze"
+            },
+            {
+                "network": "Xing",
+                "username": "Matthias.Vogelgesang",
+                "url": "https://xing.com/profile/matthias.vogelgesang",
+                "shortUrl": "xing.com/profile/matthias.vogelgesang"
+            }
+        ]
+    },
+    "education": [
+        {
+            "institution": "Karlsruhe Institute of Technology",
+            "area": "Computer Science",
+            "studyType": "Doctorate",
+            "startDate": "2011-01-01",
+            "endDate": "2014-11-01",
+            "highlights": [
+                "Completed with Dr.-Ing., magna cum laude (1.0)"
+            ]
+        },
+        {
+            "institution": "Karlsruhe Institute of Technology",
+            "area": "Computer Science",
+            "studyType": "Diploma",
+            "startDate": "2005-10-01",
+            "endDate": "2010-09-01",
+            "highlights": [
+                "Completed with Dipl.-Inform., very good (1.3)",
+                "Exchange year with goal of diploma thesis at Carnegie Mellon University, Pittsburgh, PA.",
+                "Focus on operating systems and parallel programming"
+            ]
+        },
+        {
+            "institution": "Pestalozzi Gymnasium Guben",
+            "area": "",
+            "studyType": "Abitur – General university entrance qualification (1.6)",
+            "startDate": "1997-07-01",
+            "endDate": "2004-05-01"
+        }
+    ],
+    "work": [
+        {
+            "company": "Cinemo GmbH",
+            "position": "Principal Software Engineer",
+            "startDate": "2018-07-23",
+            "endDate": "present",
+            "summary": "foo",
+            "highlights": [
+                "Developed a portable Rust-based audio/video system for end users",
+                "Led small team that implemented an in-car Rust service orchestrator",
+                "Developed in-house tooling (linting, documentation, ...) in Rust and Python",
+                "Initiated the transition of the in-house C++ build system to CMake",
+                "Involved in development of HTTP and Apple CarPlay components"
+            ]
+        },
+        {
+            "company": "Institute for Data Processing and Electronics",
+            "position": "Research Associate",
+            "startDate": "2011-01-01",
+            "endDate": "2018-06-30",
+            "summary": "foo",
+            "highlights": [
+              "Implemented a multi-GPU computed tomography reconstruction software package",
+              "Devised a high-throughput synchrotron experiment control system",
+              "Implemented a web-based catalog system for reconstructed specimen"
+            ]
+        },
+        {
+            "company": "Fraunhofer IOSB",
+            "position": "Research Assistant",
+            "startDate": "2006-08-01",
+            "endDate": "2009-09-01",
+            "summary": "foo",
+            "highlights": [
+              "Developed optical marker tracking for use in emergency threat situation handling",
+              "Devised and researched eye-tracking and prediction algorithm for gaze control"
+            ]
+        }
+    ],
+    "languages": [
+        {
+            "language": "German",
+            "fluency": "Native speaker"
+        },
+        {
+            "language": "English",
+            "fluency": "Business fluent, CEFR C1"
+        }
+    ],
+    "skills": [
+        {
+            "name": "General",
+            "keywords": [
+                "Service architectures",
+                "image processing",
+                "GPU programming"
+            ]
+        },
+        {
+            "name": "Languages",
+            "keywords": ["Rust", "Python", "C++23", "C11", "Bash", "JavaScript", "Groovy", "HTML", "CSS"]
+        },
+        {
+            "name": "Software",
+            "keywords": ["Linux", "Git", "Jujutsu", "Typst", "LaTeX", "Jenkins"]
+        },
+        {
+            "name": "Build tooling",
+            "keywords": ["Cargo", "uv", "poetry", "CMake", "Autotools", "meson"]
+        },
+        {
+            "name": "AI tooling",
+            "keywords": ["Claude Code", "GitHub Copilot"]
+        },
+        {
+            "name": "APIs",
+            "keywords": ["tokio", "tonic", "axum", "cxx", "OpenCL", "CUDA", "OpenMP", "GLib", "GTK", "Qt", "HDF5"]
+        },
+        {
+            "name": "Protocols",
+            "keywords": ["TCP", "UDP", "gRPC/Protobuf", "HTTP", "MCP", "ZeroMQ", "CameraLink"]
+        }
+    ],
+    "interests": [
+        {
+            "name": "Bass guitar (Funk, Jazz, RnB, Soul)"
+        },
+        {
+            "name": "DSLR photography and editing"
+        }
+    ]
+}