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}