diff --git a/.gitignore b/.gitignore
index 5cb15f7..6472ea9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,17 @@
# Python
__pycache__/
*.pyc
+
+# LibreOffice lock files (e.g. .~lock.MikeEberlein_Resume.docx#)
+.~lock.*#
+
+# Linux temp / editor files
+*~
+.*.swp
+.*.swo
+*.bak
+*.tmp
+.fuse_hidden*
+.nfs*
+.directory
+.Trash-*
diff --git a/Makefile b/Makefile
index 96e4d9d..62c6f02 100644
--- a/Makefile
+++ b/Makefile
@@ -47,8 +47,8 @@ $(TEX): $(YAML) render_tex.py
python3 render_tex.py --level=short --output=$(TEX)
$(PDF): $(TEX)
- pdflatex -interaction=nonstopmode -halt-on-error $(TEX)
- pdflatex -interaction=nonstopmode -halt-on-error $(TEX)
+ lualatex -interaction=nonstopmode -halt-on-error $(TEX)
+ lualatex -interaction=nonstopmode -halt-on-error $(TEX)
$(ODT): $(YAML) render_odt.py
python3 render_odt.py --level=short --output=$(ODT)
@@ -61,8 +61,8 @@ $(TEX_D): $(YAML) render_tex.py
python3 render_tex.py --level=full --output=$(TEX_D)
$(PDF_D): $(TEX_D)
- pdflatex -interaction=nonstopmode -halt-on-error $(TEX_D)
- pdflatex -interaction=nonstopmode -halt-on-error $(TEX_D)
+ lualatex -interaction=nonstopmode -halt-on-error $(TEX_D)
+ lualatex -interaction=nonstopmode -halt-on-error $(TEX_D)
$(ODT_D): $(YAML) render_odt.py
python3 render_odt.py --level=full --output=$(ODT_D)
diff --git a/MikeEberlein_Resume.docx b/MikeEberlein_Resume.docx
index 192cd4b..9a1e85a 100644
Binary files a/MikeEberlein_Resume.docx and b/MikeEberlein_Resume.docx differ
diff --git a/MikeEberlein_Resume.odt b/MikeEberlein_Resume.odt
index 4642903..1f0be62 100644
Binary files a/MikeEberlein_Resume.odt and b/MikeEberlein_Resume.odt differ
diff --git a/MikeEberlein_Resume.pdf b/MikeEberlein_Resume.pdf
index acf1dc5..3a0cec9 100644
Binary files a/MikeEberlein_Resume.pdf and b/MikeEberlein_Resume.pdf differ
diff --git a/MikeEberlein_Resume.tex b/MikeEberlein_Resume.tex
index 1b9d265..0e8ed05 100644
--- a/MikeEberlein_Resume.tex
+++ b/MikeEberlein_Resume.tex
@@ -1,18 +1,35 @@
% Mike Eberlein — single-page resume (generated from resume.yaml).
-% Build: pdflatex MikeEberlein_Resume.tex (run twice for stable layout)
+% Build: lualatex MikeEberlein_Resume.tex (run twice for stable layout)
% Do not edit by hand — re-run `python3 render_tex.py` after changing resume.yaml.
%
-% Fonts: Roboto via the roboto LaTeX package (texlive-fonts-extra). The official
-% Google Roboto font repo (with OFL license) is also bundled at ./fonts/ —
-% individual weights live in ./fonts/static/, variable fonts at ./fonts/.
+% Fonts: Roboto, loaded directly from ./fonts/static/ via fontspec. Requires
+% lualatex (or xelatex). This makes the build self-contained — no dependency
+% on TeX Live's bundled `roboto` package.
\documentclass[letterpaper,10pt]{extarticle}
-\usepackage[T1]{fontenc}
-\usepackage[utf8]{inputenc}
-\usepackage[sfdefault]{roboto}
+\usepackage{fontspec}
+\setmainfont{Roboto}[
+ Path = fonts/static/,
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ ItalicFont = *-Italic,
+ BoldFont = *-Bold,
+ BoldItalicFont = *-BoldItalic,
+ Ligatures = NoCommon, % keep fi/fl as two letters so PDF text search / ATS works
+]
+\newfontfamily\RobotoLight{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Light, ItalicFont=*-LightItalic,
+ Ligatures=NoCommon,
+]
+\newfontfamily\RobotoMedium{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Medium, BoldFont=*-Bold,
+ Ligatures=NoCommon,
+]
+
\usepackage{microtype}
-\DisableLigatures{encoding = *, family = *} % keep fi/fl as two letters so PDF text search / ATS works
\usepackage[letterpaper,margin=0.5in,top=0.35in,bottom=0.3in]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{titlesec}
@@ -21,9 +38,6 @@
\usepackage{ragged2e}
\usepackage{parskip}
-\newcommand{\RobotoLight}{\fontseries{l}\selectfont}
-\newcommand{\RobotoMedium}{\fontseries{m}\selectfont}
-
\definecolor{accent}{HTML}{1F3864}
\definecolor{body}{HTML}{222222}
\definecolor{muted}{HTML}{555555}
diff --git a/MikeEberlein_Resume_Detailed.docx b/MikeEberlein_Resume_Detailed.docx
index 192cd4b..6d2ff53 100644
Binary files a/MikeEberlein_Resume_Detailed.docx and b/MikeEberlein_Resume_Detailed.docx differ
diff --git a/MikeEberlein_Resume_Detailed.odt b/MikeEberlein_Resume_Detailed.odt
index 143814e..1f0be62 100644
Binary files a/MikeEberlein_Resume_Detailed.odt and b/MikeEberlein_Resume_Detailed.odt differ
diff --git a/MikeEberlein_Resume_Detailed.pdf b/MikeEberlein_Resume_Detailed.pdf
index f557f3e..718b7f3 100644
Binary files a/MikeEberlein_Resume_Detailed.pdf and b/MikeEberlein_Resume_Detailed.pdf differ
diff --git a/MikeEberlein_Resume_Detailed.tex b/MikeEberlein_Resume_Detailed.tex
index 1b9d265..0e8ed05 100644
--- a/MikeEberlein_Resume_Detailed.tex
+++ b/MikeEberlein_Resume_Detailed.tex
@@ -1,18 +1,35 @@
% Mike Eberlein — single-page resume (generated from resume.yaml).
-% Build: pdflatex MikeEberlein_Resume.tex (run twice for stable layout)
+% Build: lualatex MikeEberlein_Resume.tex (run twice for stable layout)
% Do not edit by hand — re-run `python3 render_tex.py` after changing resume.yaml.
%
-% Fonts: Roboto via the roboto LaTeX package (texlive-fonts-extra). The official
-% Google Roboto font repo (with OFL license) is also bundled at ./fonts/ —
-% individual weights live in ./fonts/static/, variable fonts at ./fonts/.
+% Fonts: Roboto, loaded directly from ./fonts/static/ via fontspec. Requires
+% lualatex (or xelatex). This makes the build self-contained — no dependency
+% on TeX Live's bundled `roboto` package.
\documentclass[letterpaper,10pt]{extarticle}
-\usepackage[T1]{fontenc}
-\usepackage[utf8]{inputenc}
-\usepackage[sfdefault]{roboto}
+\usepackage{fontspec}
+\setmainfont{Roboto}[
+ Path = fonts/static/,
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ ItalicFont = *-Italic,
+ BoldFont = *-Bold,
+ BoldItalicFont = *-BoldItalic,
+ Ligatures = NoCommon, % keep fi/fl as two letters so PDF text search / ATS works
+]
+\newfontfamily\RobotoLight{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Light, ItalicFont=*-LightItalic,
+ Ligatures=NoCommon,
+]
+\newfontfamily\RobotoMedium{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Medium, BoldFont=*-Bold,
+ Ligatures=NoCommon,
+]
+
\usepackage{microtype}
-\DisableLigatures{encoding = *, family = *} % keep fi/fl as two letters so PDF text search / ATS works
\usepackage[letterpaper,margin=0.5in,top=0.35in,bottom=0.3in]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{titlesec}
@@ -21,9 +38,6 @@
\usepackage{ragged2e}
\usepackage{parskip}
-\newcommand{\RobotoLight}{\fontseries{l}\selectfont}
-\newcommand{\RobotoMedium}{\fontseries{m}\selectfont}
-
\definecolor{accent}{HTML}{1F3864}
\definecolor{body}{HTML}{222222}
\definecolor{muted}{HTML}{555555}
diff --git a/README.md b/README.md
index eac64c7..de4e9bd 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Two flavors are produced from the same YAML:
| `MikeEberlein_Resume.odt` | Built from YAML via `render_odt.py`. |
| `MikeEberlein_Resume.docx` | Built from `.odt` via LibreOffice. |
| `Makefile` | Build orchestration. |
-| `fonts/` | Official Google Roboto font repo. Variable fonts at the top level; individual weights under `fonts/static/`. License in `fonts/OFL.txt`. |
+| `fonts/` | Official Google Roboto font repo. Variable fonts at the top level; individual weights under `fonts/static/`. License in `fonts/OFL.txt`. **Used by both build paths**: lualatex loads from `fonts/static/`; the ODT renderer embeds these `.ttf` files inside the ODT zip. |
## Build
@@ -46,11 +46,13 @@ python3 render_odt.py --level=short --output=foo.odt
Required system packages (Debian/Ubuntu):
```bash
-sudo apt install texlive-latex-recommended texlive-latex-extra texlive-fonts-extra
+sudo apt install texlive-latex-recommended texlive-latex-extra texlive-luatex
sudo apt install libreoffice # for the .docx output
sudo apt install python3-yaml # for the renderers (usually pre-installed)
```
+The PDF build uses **lualatex** with `fontspec`, loading Roboto directly from `fonts/static/`. No dependency on TeX Live's `roboto` package or any system-installed Roboto. The ODT/DOCX outputs embed the Roboto faces inside the document zip, so the resume looks correct even on a machine with no Roboto installed.
+
## Editing
Open `resume.yaml`. The schema:
diff --git a/render_odt.py b/render_odt.py
index 8533788..ec10c35 100644
--- a/render_odt.py
+++ b/render_odt.py
@@ -26,12 +26,32 @@ DEFAULT_OUT = {
MIMETYPE = "application/vnd.oasis.opendocument.text"
-MANIFEST = '''
+# Roboto faces to embed in the zip, sourced from fonts/static/.
+# Each tuple: (filename in Fonts/, weight attr, style attr).
+EMBEDDED_FONTS = [
+ ("Roboto-Regular.ttf", "normal", "normal"),
+ ("Roboto-Italic.ttf", "normal", "italic"),
+ ("Roboto-Bold.ttf", "bold", "normal"),
+ ("Roboto-BoldItalic.ttf", "bold", "italic"),
+ ("Roboto-Light.ttf", "300", "normal"),
+ ("Roboto-LightItalic.ttf", "300", "italic"),
+ ("Roboto-Medium.ttf", "500", "normal"),
+]
+FONTS_SRC_DIR = os.path.join(HERE, "fonts", "static")
+
+def _font_manifest_entries():
+ rows = []
+ for name, _w, _s in EMBEDDED_FONTS:
+ rows.append(f' ')
+ return "\n".join(rows)
+
+MANIFEST = f'''
+{_font_manifest_entries()}
'''
@@ -41,19 +61,50 @@ META = '''
'''
-STYLES = '''
+def _font_face_uris(filter_family):
+ """Emit children for one logical family ('roboto' | 'light')."""
+ rows = []
+ for name, weight, style in EMBEDDED_FONTS:
+ # The 'Roboto Light' family only carries the Light faces; the main
+ # 'Roboto' family carries everything else (including Medium).
+ is_light = name.startswith("Roboto-Light")
+ if filter_family == "light" and not is_light:
+ continue
+ if filter_family == "roboto" and is_light:
+ continue
+ rows.append(
+ f' '
+ f''
+ f''
+ )
+ return "\n".join(rows)
+
+FONT_FACE_DECLS = f'''
+
+
+{_font_face_uris("roboto")}
+
+
+
+
+{_font_face_uris("light")}
+
+
+'''
+
+STYLES = f'''
-
-
-
-
+{FONT_FACE_DECLS}
@@ -94,7 +145,7 @@ STYLES = '''
-
+
@@ -108,6 +159,13 @@ STYLES = '''
+
+
+
+
+
+
+
@@ -170,7 +228,8 @@ def i(text):
TAB = ''
def bullet(text, style="Bullet"):
- return (f''
+ list_style = "L2" if style == "SubBullet" else "L1"
+ return (f''
f'{xesc(text)}'
f'')
@@ -235,6 +294,13 @@ def main():
z.writestr("meta.xml", META)
z.writestr("styles.xml", STYLES)
z.writestr("content.xml", content)
+ # Embed the Roboto faces inside the document for portability.
+ for name, _w, _s in EMBEDDED_FONTS:
+ src = os.path.join(FONTS_SRC_DIR, name)
+ if not os.path.exists(src):
+ raise SystemExit(f"font missing: {src}")
+ with open(src, "rb") as f:
+ z.writestr(f"Fonts/{name}", f.read())
print(f"wrote {out_path} (level={args.level})")
if __name__ == "__main__":
diff --git a/render_tex.py b/render_tex.py
index 7824242..b8e09de 100644
--- a/render_tex.py
+++ b/render_tex.py
@@ -21,20 +21,37 @@ DEFAULT_OUT = {
}
PREAMBLE = r"""% Mike Eberlein — single-page resume (generated from resume.yaml).
-% Build: pdflatex MikeEberlein_Resume.tex (run twice for stable layout)
+% Build: lualatex MikeEberlein_Resume.tex (run twice for stable layout)
% Do not edit by hand — re-run `python3 render_tex.py` after changing resume.yaml.
%
-% Fonts: Roboto via the roboto LaTeX package (texlive-fonts-extra). The official
-% Google Roboto font repo (with OFL license) is also bundled at ./fonts/ —
-% individual weights live in ./fonts/static/, variable fonts at ./fonts/.
+% Fonts: Roboto, loaded directly from ./fonts/static/ via fontspec. Requires
+% lualatex (or xelatex). This makes the build self-contained — no dependency
+% on TeX Live's bundled `roboto` package.
\documentclass[letterpaper,10pt]{extarticle}
-\usepackage[T1]{fontenc}
-\usepackage[utf8]{inputenc}
-\usepackage[sfdefault]{roboto}
+\usepackage{fontspec}
+\setmainfont{Roboto}[
+ Path = fonts/static/,
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ ItalicFont = *-Italic,
+ BoldFont = *-Bold,
+ BoldItalicFont = *-BoldItalic,
+ Ligatures = NoCommon, % keep fi/fl as two letters so PDF text search / ATS works
+]
+\newfontfamily\RobotoLight{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Light, ItalicFont=*-LightItalic,
+ Ligatures=NoCommon,
+]
+\newfontfamily\RobotoMedium{Roboto}[
+ Path=fonts/static/, Extension=.ttf,
+ UprightFont=*-Medium, BoldFont=*-Bold,
+ Ligatures=NoCommon,
+]
+
\usepackage{microtype}
-\DisableLigatures{encoding = *, family = *} % keep fi/fl as two letters so PDF text search / ATS works
\usepackage[letterpaper,margin=0.5in,top=0.35in,bottom=0.3in]{geometry}
\usepackage[dvipsnames]{xcolor}
\usepackage{titlesec}
@@ -43,9 +60,6 @@ PREAMBLE = r"""% Mike Eberlein — single-page resume (generated from resume.yam
\usepackage{ragged2e}
\usepackage{parskip}
-\newcommand{\RobotoLight}{\fontseries{l}\selectfont}
-\newcommand{\RobotoMedium}{\fontseries{m}\selectfont}
-
\definecolor{accent}{HTML}{1F3864}
\definecolor{body}{HTML}{222222}
\definecolor{muted}{HTML}{555555}