Files
rendercv/docs/update_entry_figures.py
Sina Atalay 4331110c55 Upgrade to v2 (#271)
* prepare the base for typst

* rename theme folders

* rename themes

* rename themes

* update testdata with new theme names

* rename themes

* fix docs issues

* fundamentals

* fundamental renames

* generalize `create_a_latex_file`

* generalize render_a_pdf_from_latex

* make latex optional dependency, and add typst as dependency

* first tests with typst

* finish `markdown_to_typst`

* fix `markdown_to_latex`

* finish `markdown_to_typst`

* first steps towards Typst RenderCV themes

* first draft of classic theme

* start working on new design options

* work on new design options

* make default theme: "classic"

* start integrating design options with templates

* rename typst variables

* start working on connections integration

* polish connections

* polish design options and themes

* fix spelling mistakes and improve typst themes

* use ms instead of s in printer

* improve templates

* fix typos

* use ms instead of s in printer

* improve typst templates

* improve

* improve

* improve

* improve

* make PyMuPDF optional

* rename last_updated_date_style to last_updated_date_template

* revert changelog

* progress

* improve

* exclude gifs from sdist

* update tests

* improve templates

* improve templater

* data: update `sample_content.yaml`

* improve

* remove latex support

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove testdata

* remove latex

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* rename `locale_catalog` to `locale`

* docs: update developer guide faq

* add new input, rendercv_settings.date

* add show_time_span_in

* create a new function, parse_validation_errors

* improve templates

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* improve

* update templates

* fix experience entry

* improve

* finish templates

* update tests

* update testdata

* remove time_machine

* update sample content

* improve

* add sb2nov theme

* update options

* update theme.options

* update theme.options

* update theme options

* create engineeringresumes templates

* add engineering resumes

* format

* update templates

* add new theme

* fix a typo in sample content

* update templating system

* update options

* add photo support

* update workflows

* improve templates

* improve parse_validation_errors

* create a new interface for web

* fix summary

* improve

* resolve typing issues

* update mkdocs.yaml

* update pyproject.toml

* update docs scripts

* update testdata

* update tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* rename column template fields

* update

* update test data

* add moderncv

* fix problems

* moderncv

* create moderncv

* fix tests

* update

* update

* update templates

* update

* use optional dependencies

* fix

* improve

* aa

* a

* update

* update

* update

* update

* rename

* update

* update

* update

* improve

* update

* update

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update

* update

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* format

* update changelog

* update examples

* update entry figures

* update schema

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-01-07 01:30:40 +03:00

249 lines
8.4 KiB
Python

"""This script generates the example entry figures and creates an environment for
documentation templates using `mkdocs-macros-plugin`. For example, the content of the
example entries found in
"[Structure of the YAML Input File](https://docs.rendercv.com/user_guide/structure_of_the_yaml_input_file/)"
are coming from this script.
"""
import io
import pathlib
import shutil
import tempfile
import fitz
import pdfCropMargins
import pydantic
import ruamel.yaml
import rendercv.data as data
import rendercv.renderer as renderer
repository_root = pathlib.Path(__file__).parent.parent
rendercv_path = repository_root / "rendercv"
image_assets_directory = pathlib.Path(__file__).parent / "assets" / "images"
class SampleEntries(pydantic.BaseModel):
education_entry: data.EducationEntry
experience_entry: data.ExperienceEntry
normal_entry: data.NormalEntry
publication_entry: data.PublicationEntry
one_line_entry: data.OneLineEntry
bullet_entry: data.BulletEntry
text_entry: str
def dictionary_to_yaml(dictionary: dict):
"""Converts a dictionary to a YAML string.
Args:
dictionary: The dictionary to be converted to YAML.
Returns:
The YAML string.
"""
yaml_object = ruamel.yaml.YAML()
yaml_object.width = 60
yaml_object.indent(mapping=2, sequence=4, offset=2)
with io.StringIO() as string_stream:
yaml_object.dump(dictionary, string_stream)
return string_stream.getvalue()
def define_env(env):
# See https://mkdocs-macros-plugin.readthedocs.io/en/latest/macros/
sample_entries = data.read_a_yaml_file(
repository_root / "docs" / "user_guide" / "sample_entries.yaml"
)
# validate the parsed dictionary by creating an instance of SampleEntries:
SampleEntries(**sample_entries)
entries_showcase = {}
for entry_name, entry in sample_entries.items():
proper_entry_name = entry_name.replace("_", " ").title().replace(" ", "")
entries_showcase[proper_entry_name] = {
"yaml": dictionary_to_yaml(entry),
"figures": [
{
"path": f"../assets/images/{theme}/{entry_name}.png",
"alt_text": f"{proper_entry_name} in {theme}",
"theme": theme,
}
for theme in data.available_themes
],
}
env.variables["showcase_entries"] = entries_showcase
# For theme templates reference docs
themes_path = rendercv_path / "themes"
theme_templates = {}
for theme in data.available_themes:
theme_templates[theme] = {}
for theme_file in themes_path.glob(f"{theme}/*.typ"):
theme_templates[theme][theme_file.stem] = theme_file.read_text()
# Update ordering of theme templates
order = [
"Preamble.j2",
"Header.j2",
"SectionBeginning.j2",
"SectionEnding.j2",
"TextEntry.j2",
"BulletEntry.j2",
"OneLineEntry.j2",
"EducationEntry.j2",
"ExperienceEntry.j2",
"NormalEntry.j2",
"PublicationEntry.j2",
]
theme_templates[theme] = {key: theme_templates[theme][key] for key in order}
if theme != "markdown":
theme_templates[theme] = {
f"{key}.typ": value for key, value in theme_templates[theme].items()
}
else:
theme_templates[theme] = {
f"{key}.md": value for key, value in theme_templates[theme].items()
}
env.variables["theme_templates"] = theme_templates
# Available themes strings (put available themes between ``)
themes = [f"`{theme}`" for theme in data.available_themes]
env.variables["available_themes"] = ", ".join(themes)
# Available social networks strings (put available social networks between ``)
social_networks = [
f"`{social_network}`" for social_network in data.available_social_networks
]
env.variables["available_social_networks"] = ", ".join(social_networks)
def render_pngs_from_pdf(pdf_file_path: pathlib.Path) -> list[pathlib.Path]:
"""Render a PNG file for each page of the given PDF file.
Args:
pdf_file_path: The path to the PDF file.
Returns:
The paths to the rendered PNG files.
"""
# check if the file exists:
if not pdf_file_path.is_file():
message = f"The file {pdf_file_path} doesn't exist!"
raise FileNotFoundError(message)
# convert the PDF to PNG:
png_directory = pdf_file_path.parent
png_file_name = pdf_file_path.stem
png_files = []
pdf = fitz.open(pdf_file_path) # open the PDF file
for page in pdf: # iterate the pages
image = page.get_pixmap(dpi=300) # type: ignore
png_file_path = png_directory / f"{png_file_name}_{page.number + 1}.png" # type: ignore
image.save(png_file_path)
png_files.append(png_file_path)
return png_files
def generate_entry_figures():
"""Generate an image for each entry type and theme."""
# Generate PDF figures for each entry type and theme
entries = data.read_a_yaml_file(
repository_root / "docs" / "user_guide" / "sample_entries.yaml"
)
entries = SampleEntries(**entries)
themes = data.available_themes
with tempfile.TemporaryDirectory() as temporary_directory:
# Create temporary directory
temporary_directory_path = pathlib.Path(temporary_directory)
for theme in themes:
design_dictionary = {
"theme": theme,
"page": {
"show_page_numbering": False,
"show_last_updated_date": False,
},
}
entry_types = [
"education_entry",
"experience_entry",
"normal_entry",
"publication_entry",
"one_line_entry",
"bullet_entry",
"text_entry",
]
for entry_type in entry_types:
# Create data model with only one section and one entry
data_model = data.RenderCVDataModel(
cv=data.CurriculumVitae(
sections={entry_type: [getattr(entries, entry_type)]}
),
design=design_dictionary,
)
# Render
typst_file_path = renderer.create_a_typst_file_and_copy_theme_files(
data_model, temporary_directory_path
)
pdf_file_path = renderer.render_a_pdf_from_typst(typst_file_path)
# Prepare output directory and file path
output_directory = image_assets_directory / theme
output_directory.mkdir(parents=True, exist_ok=True)
output_pdf_file_path = output_directory / f"{entry_type}.pdf"
# Remove file if it exists
if output_pdf_file_path.exists():
output_pdf_file_path.unlink()
# Crop margins
pdfCropMargins.crop(
argv_list=[
"-p4",
"100",
"0",
"100",
"0",
"-a4",
"0",
"-30",
"0",
"-30",
"-o",
str(output_pdf_file_path.absolute()),
str(pdf_file_path.absolute()),
]
)
# Convert PDF to image
png_file_path = render_pngs_from_pdf(output_pdf_file_path)[0]
desired_png_file_path = output_pdf_file_path.with_suffix(".png")
# If image exists, remove it
if desired_png_file_path.exists():
desired_png_file_path.unlink()
# Move image to desired location
png_file_path.rename(desired_png_file_path)
# Remove PDF file
output_pdf_file_path.unlink()
def update_index():
"""Update index.md file by copying README.md file."""
index_file_path = repository_root / "docs" / "index.md"
readme_file_path = repository_root / "README.md"
shutil.copy(readme_file_path, index_file_path)
if __name__ == "__main__":
generate_entry_figures()
print("Entry figures generated successfully.") # NOQA: T201