mirror of https://github.com/eyhc1/rendercv.git
add custom fonts support
This commit is contained in:
parent
3bf176f689
commit
8790097351
|
@ -13,6 +13,7 @@ import re
|
||||||
import logging
|
import logging
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
import os
|
||||||
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
BaseModel,
|
BaseModel,
|
||||||
|
@ -409,12 +410,48 @@ class Design(BaseModel):
|
||||||
title="Theme name",
|
title="Theme name",
|
||||||
description='The only option is "Classic" for now.',
|
description='The only option is "Classic" for now.',
|
||||||
)
|
)
|
||||||
|
font: Literal["SourceSans3", "Roboto"] = Field(
|
||||||
|
default="SourceSans3",
|
||||||
|
title="Font",
|
||||||
|
description="The font of the CV.",
|
||||||
|
examples=["SourceSans3", "Roboto"],
|
||||||
|
)
|
||||||
|
font_size: Literal["10pt", "11pt", "12pt"] = Field(
|
||||||
|
default="10pt",
|
||||||
|
title="Font Size",
|
||||||
|
description="The font size of the CV. It can be 10pt, 11pt, or 12pt.",
|
||||||
|
examples=["10pt", "11pt", "12pt"],
|
||||||
|
)
|
||||||
options: ClassicThemeOptions = Field(
|
options: ClassicThemeOptions = Field(
|
||||||
default=ClassicThemeOptions(),
|
default=ClassicThemeOptions(),
|
||||||
title="Theme Options",
|
title="Theme Options",
|
||||||
description="The options of the theme.",
|
description="The options of the theme.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@field_validator("font")
|
||||||
|
@classmethod
|
||||||
|
def check_font(cls, font: str) -> str:
|
||||||
|
# Go to fonts directory and check if the font exists:
|
||||||
|
fonts_directory = os.path.join(os.path.dirname(__file__), "templates", "fonts")
|
||||||
|
if font not in os.listdir(fonts_directory):
|
||||||
|
raise ValueError(
|
||||||
|
f'The font "{font}" is not found in the "fonts" directory! To add a new'
|
||||||
|
" font, please see TO BE ADDED."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
font_directory = os.path.join(fonts_directory, font)
|
||||||
|
required_files = [
|
||||||
|
f"{font}-Bold.ttf",
|
||||||
|
f"{font}-BoldItalic.ttf",
|
||||||
|
f"{font}-Italic.ttf",
|
||||||
|
f"{font}-Regular.ttf",
|
||||||
|
]
|
||||||
|
for file in required_files:
|
||||||
|
if file not in os.listdir(font_directory):
|
||||||
|
raise ValueError(f"{file} is not found in the {font} directory!")
|
||||||
|
|
||||||
|
return font
|
||||||
|
|
||||||
|
|
||||||
# ======================================================================================
|
# ======================================================================================
|
||||||
# ======================================================================================
|
# ======================================================================================
|
||||||
|
|
|
@ -202,13 +202,13 @@ def print_today() -> str:
|
||||||
return today.strftime("%B %d, %Y")
|
return today.strftime("%B %d, %Y")
|
||||||
|
|
||||||
|
|
||||||
def get_path_to_fonts_directory() -> str:
|
def get_path_to_font_directory(font_name: str) -> str:
|
||||||
"""Return the path to the fonts directory.
|
"""Return the path to the fonts directory.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The path to the fonts directory.
|
str: The path to the fonts directory.
|
||||||
"""
|
"""
|
||||||
return os.path.join(os.path.dirname(__file__), "templates", "fonts")
|
return os.path.join(os.path.dirname(__file__), "templates", "fonts", font_name)
|
||||||
|
|
||||||
|
|
||||||
def render_template(data):
|
def render_template(data):
|
||||||
|
@ -252,10 +252,10 @@ def render_template(data):
|
||||||
environment.filters["make_it_italic"] = make_it_italic
|
environment.filters["make_it_italic"] = make_it_italic
|
||||||
|
|
||||||
output_latex_file = template.render(
|
output_latex_file = template.render(
|
||||||
design=data.design.options,
|
|
||||||
cv=data.cv,
|
cv=data.cv,
|
||||||
|
design=data.design,
|
||||||
|
theme_options=data.design.options,
|
||||||
today=print_today(),
|
today=print_today(),
|
||||||
fonts_directory=get_path_to_fonts_directory(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create an output file and write the rendered LaTeX code to it:
|
# Create an output file and write the rendered LaTeX code to it:
|
||||||
|
@ -265,19 +265,13 @@ def render_template(data):
|
||||||
file.write(output_latex_file)
|
file.write(output_latex_file)
|
||||||
|
|
||||||
# Copy the fonts directory to the output directory:
|
# Copy the fonts directory to the output directory:
|
||||||
fonts_directory = get_path_to_fonts_directory()
|
font_directory = get_path_to_font_directory(data.design.font)
|
||||||
output_fonts_directory = os.path.join(os.path.dirname(output_file_path), "fonts")
|
output_fonts_directory = os.path.join(os.path.dirname(output_file_path), "fonts")
|
||||||
os.makedirs(output_fonts_directory, exist_ok=True)
|
shutil.copytree(
|
||||||
for directory in os.listdir(fonts_directory):
|
font_directory,
|
||||||
if directory == "SourceSans3":
|
output_fonts_directory,
|
||||||
# copy the SourceSans3 fonts:
|
dirs_exist_ok=True,
|
||||||
source_directory = os.path.join(fonts_directory, directory)
|
)
|
||||||
|
|
||||||
shutil.copytree(
|
|
||||||
source_directory,
|
|
||||||
output_fonts_directory,
|
|
||||||
dirs_exist_ok=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return output_file_path
|
return output_file_path
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
((* from "components/classic/section_contents.tex.j2" import section_contents with context *))
|
((* from "components/classic/section_contents.tex.j2" import section_contents with context *))
|
||||||
((* from "components/classic/header.tex.j2" import header with context *))
|
((* from "components/classic/header.tex.j2" import header with context *))
|
||||||
|
|
||||||
\documentclass[10pt, a4paper]{article}
|
\documentclass[<<design.font_size>>, a4paper]{article}
|
||||||
|
|
||||||
% Packages:
|
% Packages:
|
||||||
\usepackage[
|
\usepackage[
|
||||||
ignoreheadfoot, % set margins without considering header and footer
|
ignoreheadfoot, % set margins without considering header and footer
|
||||||
top=<<design.margins.page.top>>, % seperation between body and page edge from the top
|
top=<<theme_options.margins.page.top>>, % seperation between body and page edge from the top
|
||||||
bottom=<<design.margins.page.bottom>>, % seperation between body and page edge from the bottom
|
bottom=<<theme_options.margins.page.bottom>>, % seperation between body and page edge from the bottom
|
||||||
left=<<design.margins.page.left>>, % seperation between body and page edge from the left
|
left=<<theme_options.margins.page.left>>, % seperation between body and page edge from the left
|
||||||
right=<<design.margins.page.right>>, % seperation between body and page edge from the right
|
right=<<theme_options.margins.page.right>>, % seperation between body and page edge from the right
|
||||||
% showframe % for debugging
|
% showframe % for debugging
|
||||||
]{geometry} % for adjusting page geometry
|
]{geometry} % for adjusting page geometry
|
||||||
\usepackage{fontspec} % for loading fonts
|
\usepackage{fontspec} % for loading fonts
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
\usepackage{tabularx} % for making tables with fixed width columns
|
\usepackage{tabularx} % for making tables with fixed width columns
|
||||||
\usepackage{array} % tabularx requires this
|
\usepackage{array} % tabularx requires this
|
||||||
\usepackage[dvipsnames]{xcolor} % for coloring text
|
\usepackage[dvipsnames]{xcolor} % for coloring text
|
||||||
\definecolor{primaryColor}{RGB}{<<design.primary_color.as_rgb_tuple()|join(", ")>>} % define primary color
|
\definecolor{primaryColor}{RGB}{<<theme_options.primary_color.as_rgb_tuple()|join(", ")>>} % define primary color
|
||||||
\usepackage{enumitem} % for customizing lists
|
\usepackage{enumitem} % for customizing lists
|
||||||
\usepackage{fontawesome5} % for using icons
|
\usepackage{fontawesome5} % for using icons
|
||||||
\usepackage[
|
\usepackage[
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
\pagenumbering{gobble} % no page numbering
|
\pagenumbering{gobble} % no page numbering
|
||||||
|
|
||||||
|
|
||||||
\setmainfont{SourceSans3}[
|
\setmainfont{<<design.font>>}[
|
||||||
Path= fonts/,
|
Path= fonts/,
|
||||||
Extension = .ttf,
|
Extension = .ttf,
|
||||||
UprightFont = *-Regular,
|
UprightFont = *-Regular,
|
||||||
|
@ -63,10 +63,10 @@
|
||||||
0pt
|
0pt
|
||||||
}{
|
}{
|
||||||
% top space:
|
% top space:
|
||||||
<<design.margins.section_title.top>>
|
<<theme_options.margins.section_title.top>>
|
||||||
}{
|
}{
|
||||||
% bottom space:
|
% bottom space:
|
||||||
<<design.margins.section_title.bottom>>
|
<<theme_options.margins.section_title.bottom>>
|
||||||
} % section title spacing
|
} % section title spacing
|
||||||
|
|
||||||
\newcolumntype{L}[1]{
|
\newcolumntype{L}[1]{
|
||||||
|
@ -82,11 +82,11 @@
|
||||||
\newenvironment{highlights}{
|
\newenvironment{highlights}{
|
||||||
\begin{itemize}[
|
\begin{itemize}[
|
||||||
topsep=0pt,
|
topsep=0pt,
|
||||||
parsep=<<design.margins.highlights_area.vertical_between_bullet_points>>,
|
parsep=<<theme_options.margins.highlights_area.vertical_between_bullet_points>>,
|
||||||
partopsep=0pt,
|
partopsep=0pt,
|
||||||
itemsep=0pt,
|
itemsep=0pt,
|
||||||
after=\vspace*{-1\baselineskip},
|
after=\vspace*{-1\baselineskip},
|
||||||
leftmargin=<<design.margins.highlights_area.left>> + 3pt
|
leftmargin=<<theme_options.margins.highlights_area.left>> + 3pt
|
||||||
]
|
]
|
||||||
}{
|
}{
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
\newcommand{\hrefExternal}[2]{\href{#1}{#2\, \raisebox{.1ex}{\footnotesize \faExternalLink*}}} % new command for external links
|
\newcommand{\hrefExternal}[2]{\href{#1}{#2\, \raisebox{.1ex}{\footnotesize \faExternalLink*}}} % new command for external links
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
((* if design.show_last_updated_date *))
|
((* if theme_options.show_last_updated_date *))
|
||||||
\placelastupdatedtext
|
\placelastupdatedtext
|
||||||
((* endif *))
|
((* endif *))
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
((* macro date_and_location_strings(date_and_location_strings) *))
|
((* macro date_and_location_strings(date_and_location_strings) *))
|
||||||
((* for item in date_and_location_strings *))
|
((* for item in date_and_location_strings *))
|
||||||
((* if loop.last *))
|
((* if loop.last *))
|
||||||
<<item>> \hspace*{-0.2cm + <<design.margins.entry_area.right>>}
|
<<item>> \hspace*{-0.2cm + <<theme_options.margins.entry_area.right>>}
|
||||||
((* else *))
|
((* else *))
|
||||||
<<item>> \hspace*{-0.2cm + <<design.margins.entry_area.right>>} \newline
|
<<item>> \hspace*{-0.2cm + <<theme_options.margins.entry_area.right>>} \newline
|
||||||
((* endif *))
|
((* endif *))
|
||||||
((* endfor *))
|
((* endfor *))
|
||||||
((* endmacro *))
|
((* endmacro *))
|
|
@ -6,9 +6,9 @@
|
||||||
((# width: \textwidth #))
|
((# width: \textwidth #))
|
||||||
((# preamble: first column, second column, third column #))
|
((# preamble: first column, second column, third column #))
|
||||||
((# first column: p{0.55cm}; constant width, ragged left column #))
|
((# first column: p{0.55cm}; constant width, ragged left column #))
|
||||||
((# second column: K{<<design.margins.entry_area.left>>}; variable width, ragged left column #))
|
((# second column: K{<<theme_options.margins.entry_area.left>>}; variable width, ragged left column #))
|
||||||
((# third column: R{<<design.date_and_location_width>>}; constant widthm ragged right column #))
|
((# third column: R{<<theme_options.date_and_location_width>>}; constant widthm ragged right column #))
|
||||||
\begin{tabularx}{\textwidth}{p{0.55cm} K{<<design.margins.entry_area.left>>} R{<<design.date_and_location_width>>}}
|
\begin{tabularx}{\textwidth}{p{0.55cm} K{<<theme_options.margins.entry_area.left>>} R{<<theme_options.date_and_location_width>>}}
|
||||||
\textbf{<<study_type if study_type is not none>>}
|
\textbf{<<study_type if study_type is not none>>}
|
||||||
&
|
&
|
||||||
\textbf{<<institution>>}, <<area>>
|
\textbf{<<institution>>}, <<area>>
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
||||||
((# width: \textwidth #))
|
((# width: \textwidth #))
|
||||||
((# preamble: first column, second column #))
|
((# preamble: first column, second column #))
|
||||||
((# first column:: K{<<design.margins.entry_area.left>>}; variable width, ragged left column #))
|
((# first column:: K{<<theme_options.margins.entry_area.left>>}; variable width, ragged left column #))
|
||||||
((# second column: R{<<design.date_and_location_width>>}; constant width ragged right column #))
|
((# second column: R{<<theme_options.date_and_location_width>>}; constant width ragged right column #))
|
||||||
\begin{tabularx}{\textwidth}{K{<<design.margins.entry_area.left>>} R{<<design.date_and_location_width>>}}
|
\begin{tabularx}{\textwidth}{K{<<theme_options.margins.entry_area.left>>} R{<<theme_options.date_and_location_width>>}}
|
||||||
\textbf{<<company>>}, <<position>>
|
\textbf{<<company>>}, <<position>>
|
||||||
<<print_higlights(highlights)|indent(4)->>
|
<<print_higlights(highlights)|indent(4)->>
|
||||||
&
|
&
|
||||||
|
@ -36,9 +36,9 @@
|
||||||
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
||||||
((# width: \textwidth #))
|
((# width: \textwidth #))
|
||||||
((# preamble: first column, second column #))
|
((# preamble: first column, second column #))
|
||||||
((# first column:: K{<<design.margins.entry_area.left>>}; variable width, ragged left column #))
|
((# first column:: K{<<theme_options.margins.entry_area.left>>}; variable width, ragged left column #))
|
||||||
((# second column: R{<<design.date_and_location_width>>}; constant width ragged right column #))
|
((# second column: R{<<theme_options.date_and_location_width>>}; constant width ragged right column #))
|
||||||
\begin{tabularx}{\textwidth}{K{<<design.margins.entry_area.left>>} R{<<design.date_and_location_width>>}}
|
\begin{tabularx}{\textwidth}{K{<<theme_options.margins.entry_area.left>>} R{<<theme_options.date_and_location_width>>}}
|
||||||
((* if markdown_url is not none *))
|
((* if markdown_url is not none *))
|
||||||
((* if link_text is not none *))
|
((* if link_text is not none *))
|
||||||
((* set markdown_url = "["+link_text+"]("+ markdown_url|markdown_url_to_url +")" *))
|
((* set markdown_url = "["+link_text+"]("+ markdown_url|markdown_url_to_url +")" *))
|
||||||
|
@ -59,9 +59,9 @@
|
||||||
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
((# \begin{tabularx}{⟨width⟩}[⟨pos⟩]{⟨preamble⟩} #))
|
||||||
((# width: \textwidth #))
|
((# width: \textwidth #))
|
||||||
((# preamble: first column, second column #))
|
((# preamble: first column, second column #))
|
||||||
((# first column:: K{<<design.margins.entry_area.left>>}; variable width, ragged left column #))
|
((# first column:: K{<<theme_options.margins.entry_area.left>>}; variable width, ragged left column #))
|
||||||
((# second column: R{<<design.date_and_location_width>>}; constant width ragged right column #))
|
((# second column: R{<<theme_options.date_and_location_width>>}; constant width ragged right column #))
|
||||||
\begin{tabularx}{\textwidth}{K{<<design.margins.entry_area.left>>} R{2 cm}}
|
\begin{tabularx}{\textwidth}{K{<<theme_options.margins.entry_area.left>>} R{2 cm}}
|
||||||
\textbf{<<title>>}
|
\textbf{<<title>>}
|
||||||
|
|
||||||
<<authors|join(", ")|make_it_italic(cv.name)>>
|
<<authors|join(", ")|make_it_italic(cv.name)>>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
((* macro highlights(highlights) *))
|
((* macro highlights(highlights) *))
|
||||||
\vspace*{<<design.margins.highlights_area.top>>}
|
\vspace*{<<theme_options.margins.highlights_area.top>>}
|
||||||
((* for item in highlights *))
|
((* for item in highlights *))
|
||||||
((* if loop.first *))
|
((* if loop.first *))
|
||||||
\begin{highlights}
|
\begin{highlights}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
date_and_location_strings=date_and_location_strings
|
date_and_location_strings=date_and_location_strings
|
||||||
)|indent(4)>>
|
)|indent(4)>>
|
||||||
((* elif entry_type == "ExperienceEntry" *))
|
((* elif entry_type == "ExperienceEntry" *))
|
||||||
((* if design.show_timespan_in_experience_entries *))
|
((* if theme_options.show_timespan_in_experience_entries *))
|
||||||
((* set date_and_location_strings = value.date_and_location_strings_with_timespan *))
|
((* set date_and_location_strings = value.date_and_location_strings_with_timespan *))
|
||||||
((* endif *))
|
((* endif *))
|
||||||
<<entry["experience"](
|
<<entry["experience"](
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
)|indent(4)>>
|
)|indent(4)>>
|
||||||
((* endif *))
|
((* endif *))
|
||||||
((* if not loop.last *))
|
((* if not loop.last *))
|
||||||
\vspace*{<<design.margins.entry_area.vertical_between>>}
|
\vspace*{<<theme_options.margins.entry_area.vertical_between>>}
|
||||||
|
|
||||||
((* endif *))
|
((* endif *))
|
||||||
((* endfor *))
|
((* endfor *))
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue