Skip to content

rendercv.renderer.renderer

The rendercv.renderer.renderer module contains the necessary functions for rendering Typst, PDF, Markdown, HTML, and PNG files from the RenderCVDataModel object.

TypstCompiler

A singleton class for the Typst compiler.

Source code in rendercv/renderer/renderer.py
class TypstCompiler:
    """A singleton class for the Typst compiler."""

    instance: "TypstCompiler"
    compiler: Any
    file_path: pathlib.Path

    def __new__(cls, file_path: pathlib.Path):
        if not hasattr(cls, "instance") or cls.instance.file_path != file_path:
            try:
                import rendercv_fonts
                import typst
            except Exception as e:
                from .. import _parial_install_error_message

                raise ImportError(_parial_install_error_message) from e

            cls.instance = super().__new__(cls)
            cls.instance.file_path = file_path
            cls.instance.compiler = typst.Compiler(
                file_path, font_paths=rendercv_fonts.paths_to_font_folders
            )

        return cls.instance

    def run(
        self,
        output: pathlib.Path,
        format: Literal["png", "pdf"],
        ppi: Optional[float] = None,
    ) -> pathlib.Path | list[pathlib.Path]:
        return self.instance.compiler.compile(format=format, output=output, ppi=ppi)

create_a_file_name_without_extension_from_name(name)

Create a file name from the given name by replacing the spaces with underscores and removing typst commands.

Parameters:

  • name (Optional[str]) –

    The name to be converted.

Returns:

  • str

    The converted name (without the extension).

Source code in rendercv/renderer/renderer.py
def create_a_file_name_without_extension_from_name(name: Optional[str]) -> str:
    """Create a file name from the given name by replacing the spaces with underscores
    and removing typst commands.

    Args:
        name: The name to be converted.

    Returns:
        The converted name (without the extension).
    """
    name_without_typst_commands = templater.remove_typst_commands(str(name))
    return f"{name_without_typst_commands.replace(' ', '_')}_CV"

create_a_file_and_write_contents_to_it(contents, file_name, output_directory)

Create a file with the given contents in the output directory.

Parameters:

  • contents (str) –

    The contents of the file.

  • file_name (str) –

    The name of the file.

  • output_directory (Path) –

    Path to the output directory.

Returns:

  • Path

    The path to the created file.

Source code in rendercv/renderer/renderer.py
def create_a_file_and_write_contents_to_it(
    contents: str, file_name: str, output_directory: pathlib.Path
) -> pathlib.Path:
    """Create a file with the given contents in the output directory.

    Args:
        contents: The contents of the file.
        file_name: The name of the file.
        output_directory: Path to the output directory.

    Returns:
        The path to the created file.
    """
    # Create output directory if it doesn't exist:
    if not output_directory.is_dir():
        output_directory.mkdir(parents=True)

    file_path = output_directory / file_name
    file_path.write_text(contents, encoding="utf-8")

    return file_path

copy_theme_files_to_output_directory(theme_name, output_directory_path)

Copy the auxiliary files (all the files that don't end with .j2.typ and .py) of the theme to the output directory.

Parameters:

  • theme_name (str) –

    The name of the theme.

  • output_directory_path (Path) –

    Path to the output directory.

Source code in rendercv/renderer/renderer.py
def copy_theme_files_to_output_directory(
    theme_name: str,
    output_directory_path: pathlib.Path,
):
    """Copy the auxiliary files (all the files that don't end with `.j2.typ` and `.py`)
    of the theme to the output directory.

    Args:
        theme_name: The name of the theme.
        output_directory_path: Path to the output directory.
    """
    if theme_name in data.available_themes:
        theme_directory_path = importlib.resources.files(
            f"rendercv.themes.{theme_name}"
        )
    else:
        # Then it means the theme is a custom theme. If theme_directory is not given
        # as an argument, then look for the theme in the current working directory.
        theme_directory_path = pathlib.Path.cwd() / theme_name

        if not theme_directory_path.is_dir():
            message = (
                f"The theme {theme_name} doesn't exist in the available themes and"
                " the current working directory!"
            )
            raise FileNotFoundError(message)

    dont_copy_files_with_these_extensions = [".py", ".j2.typ"]
    for theme_file in theme_directory_path.iterdir():
        # theme_file.suffix returns the latest part of the file name after the last dot.
        # But we need the latest part of the file name after the first dot:
        try:
            suffix = re.search(r"\..*", theme_file.name)[0]  # type: ignore
        except TypeError:
            suffix = ""

        if suffix not in dont_copy_files_with_these_extensions:
            if theme_file.is_dir():
                shutil.copytree(
                    str(theme_file),
                    output_directory_path / theme_file.name,
                    dirs_exist_ok=True,
                )
            else:
                shutil.copyfile(
                    str(theme_file), output_directory_path / theme_file.name
                )

create_contents_of_a_typst_file(rendercv_data_model)

Create a Typst file with the given data model and return it as a string.

Parameters:

Returns:

  • str

    The path to the generated Typst file.

Source code in rendercv/renderer/renderer.py
def create_contents_of_a_typst_file(
    rendercv_data_model: data.RenderCVDataModel,
) -> str:
    """Create a Typst file with the given data model and return it as a string.

    Args:
        rendercv_data_model: The data model.

    Returns:
        The path to the generated Typst file.
    """
    jinja2_environment = templater.Jinja2Environment().environment

    file_object = templater.TypstFile(
        rendercv_data_model,
        jinja2_environment,
    )

    return file_object.get_full_code()

create_a_typst_file(rendercv_data_model, output_directory)

Create a Typst file (depending on the theme) with the given data model and write it to the output directory.

Parameters:

  • rendercv_data_model (RenderCVDataModel) –

    The data model.

  • output_directory (Path) –

    Path to the output directory. If not given, the Typst file will be returned as a string.

Returns:

  • Path

    The path to the generated Typst file.

Source code in rendercv/renderer/renderer.py
def create_a_typst_file(
    rendercv_data_model: data.RenderCVDataModel,
    output_directory: pathlib.Path,
) -> pathlib.Path:
    """Create a Typst file (depending on the theme) with the given data model and write
    it to the output directory.

    Args:
        rendercv_data_model: The data model.
        output_directory: Path to the output directory. If not given, the Typst file
            will be returned as a string.

    Returns:
        The path to the generated Typst file.
    """

    typst_contents = create_contents_of_a_typst_file(rendercv_data_model)

    file_name_without_extension = create_a_file_name_without_extension_from_name(
        rendercv_data_model.cv.name
    )
    file_name = f"{file_name_without_extension}.typ"

    return create_a_file_and_write_contents_to_it(
        typst_contents,
        file_name,
        output_directory,
    )

create_contents_of_a_markdown_file(rendercv_data_model)

Create a Markdown file with the given data model and return it as a string.

Parameters:

Returns:

  • str

    The path to the generated Markdown file.

Source code in rendercv/renderer/renderer.py
def create_contents_of_a_markdown_file(
    rendercv_data_model: data.RenderCVDataModel,
) -> str:
    """Create a Markdown file with the given data model and return it as a string.

    Args:
        rendercv_data_model: The data model.

    Returns:
        The path to the generated Markdown file.
    """
    jinja2_environment = templater.Jinja2Environment().environment

    markdown_file_object = templater.MarkdownFile(
        rendercv_data_model,
        jinja2_environment,
    )

    return markdown_file_object.get_full_code()

create_a_markdown_file(rendercv_data_model, output_directory)

Render the Markdown file with the given data model and write it to the output directory.

Parameters:

  • rendercv_data_model (RenderCVDataModel) –

    The data model.

  • output_directory (Path) –

    Path to the output directory.

Returns:

  • Path

    The path to the rendered Markdown file.

Source code in rendercv/renderer/renderer.py
def create_a_markdown_file(
    rendercv_data_model: data.RenderCVDataModel, output_directory: pathlib.Path
) -> pathlib.Path:
    """Render the Markdown file with the given data model and write it to the output
    directory.

    Args:
        rendercv_data_model: The data model.
        output_directory: Path to the output directory.

    Returns:
        The path to the rendered Markdown file.
    """
    markdown_contents = create_contents_of_a_markdown_file(rendercv_data_model)

    file_name_without_extension = create_a_file_name_without_extension_from_name(
        rendercv_data_model.cv.name
    )
    file_name = f"{file_name_without_extension}.md"

    return create_a_file_and_write_contents_to_it(
        markdown_contents,
        file_name,
        output_directory,
    )

create_a_typst_file_and_copy_theme_files(rendercv_data_model, output_directory)

Render the Typst file with the given data model in the output directory and copy the auxiliary theme files to the output directory.

Parameters:

  • rendercv_data_model (RenderCVDataModel) –

    The data model.

  • output_directory (Path) –

    Path to the output directory.

Returns:

  • Path

    The path to the rendered Typst file.

Source code in rendercv/renderer/renderer.py
def create_a_typst_file_and_copy_theme_files(
    rendercv_data_model: data.RenderCVDataModel, output_directory: pathlib.Path
) -> pathlib.Path:
    """Render the Typst file with the given data model in the output directory and
    copy the auxiliary theme files to the output directory.

    Args:
        rendercv_data_model: The data model.
        output_directory: Path to the output directory.

    Returns:
        The path to the rendered Typst file.
    """
    file_path = create_a_typst_file(rendercv_data_model, output_directory)
    copy_theme_files_to_output_directory(
        rendercv_data_model.design.theme, output_directory
    )

    # Copy the profile picture to the output directory, if it exists:
    if rendercv_data_model.cv.photo:
        shutil.copyfile(
            rendercv_data_model.cv.photo,
            output_directory / rendercv_data_model.cv.photo.name,
        )

    return file_path

render_a_pdf_from_typst(file_path)

Run TinyTeX with the given Typst file to render the PDF.

Parameters:

  • file_path (Path) –

    The path to the Typst file.

Returns:

  • Path

    The path to the rendered PDF file.

Source code in rendercv/renderer/renderer.py
def render_a_pdf_from_typst(file_path: pathlib.Path) -> pathlib.Path:
    """Run TinyTeX with the given Typst file to render the PDF.

    Args:
        file_path: The path to the Typst file.

    Returns:
        The path to the rendered PDF file.
    """
    typst_compiler = TypstCompiler(file_path)

    # Before running Typst, make sure the PDF file is not open in another program,
    # that wouldn't allow Typst to write to it. Remove the PDF file if it exists,
    # if it's not removable, then raise an error:
    pdf_output_path = file_path.with_suffix(".pdf")

    if sys.platform == "win32":
        if pdf_output_path.is_file():
            try:
                pdf_output_path.unlink()
            except PermissionError as e:
                message = (
                    f"The PDF file {pdf_output_path} is open in another program and"
                    " doesn't allow RenderCV to rewrite it. Please close the PDF file."
                )
                raise RuntimeError(message) from e

    typst_compiler.run(output=pdf_output_path, format="pdf")

    return pdf_output_path

render_pngs_from_typst(file_path, ppi=150)

Run Typst with the given Typst file to render the PNG files.

Parameters:

  • file_path (Path) –

    The path to the Typst file.

  • ppi (float, default: 150 ) –

    Pixels per inch for PNG output, defaults to 150.

Returns:

  • list[Path]

    Paths to the rendered PNG files.

Source code in rendercv/renderer/renderer.py
def render_pngs_from_typst(
    file_path: pathlib.Path, ppi: float = 150
) -> list[pathlib.Path]:
    """Run Typst with the given Typst file to render the PNG files.

    Args:
        file_path: The path to the Typst file.
        ppi: Pixels per inch for PNG output, defaults to 150.

    Returns:
        Paths to the rendered PNG files.
    """
    typst_compiler = TypstCompiler(file_path)
    output_path = file_path.parent / (file_path.stem + "_{p}.png")
    typst_compiler.run(format="png", ppi=ppi, output=output_path)

    # Look at the outtput folder and find the PNG files:
    png_files = list(output_path.parent.glob(f"{file_path.stem}_*.png"))
    return sorted(png_files, key=lambda x: int(x.stem.split("_")[-1]))

render_an_html_from_markdown(markdown_file_path)

Render an HTML file from a Markdown file with the same name and in the same directory. It uses rendercv/themes/main.j2.html as the Jinja2 template.

Parameters:

  • markdown_file_path (Path) –

    The path to the Markdown file.

Returns:

  • Path

    The path to the rendered HTML file.

Source code in rendercv/renderer/renderer.py
def render_an_html_from_markdown(markdown_file_path: pathlib.Path) -> pathlib.Path:
    """Render an HTML file from a Markdown file with the same name and in the same
    directory. It uses `rendercv/themes/main.j2.html` as the Jinja2 template.

    Args:
        markdown_file_path: The path to the Markdown file.

    Returns:
        The path to the rendered HTML file.
    """
    try:
        import markdown
    except Exception as e:
        from .. import _parial_install_error_message

        raise ImportError(_parial_install_error_message) from e

    # check if the file exists:
    if not markdown_file_path.is_file():
        message = f"The file {markdown_file_path} doesn't exist!"
        raise FileNotFoundError(message)

    # Convert the markdown file to HTML:
    markdown_text = markdown_file_path.read_text(encoding="utf-8")
    html_body = markdown.markdown(markdown_text)

    # Get the title of the markdown content:
    title = re.search(r"# (.*)\n", markdown_text)
    title = title.group(1) if title else None

    jinja2_environment = templater.Jinja2Environment().environment
    html_template = jinja2_environment.get_template("main.j2.html")
    html = html_template.render(html_body=html_body, title=title)

    # Write html into a file:
    html_file_path = markdown_file_path.parent / f"{markdown_file_path.stem}.html"
    html_file_path.write_text(html, encoding="utf-8")

    return html_file_path