# This file is part of genipe.
#
# This work is licensed under the Creative Commons Attribution-NonCommercial
# 4.0 International License. To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative
# Commons, PO Box 1866, Mountain View, CA 94042, USA.
import re
from textwrap import wrap
import jinja2
__author__ = "Louis-Philippe Lemieux Perreault"
__copyright__ = "Copyright 2014, Beaulieu-Saucier Pharmacogenomics Centre"
__license__ = "Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)"
__all__ = ["config_jinja2", "sanitize_tex", "format_tex", "wrap_tex",
"create_tabular", "create_float", "tex_inline_math", "format_time",
"colorize_time"]
_char_mod = {
"~": r"$\sim$",
}
_escaped_char = ["$", "%", "_", "}", "{", "&", "#"]
_valid_tex_formats = {"texttt", "emph", "textbf", "textit"}
[docs]def config_jinja2():
"""Configure the jinja2 environment for LaTeX.
Note
----
The configuration used is for LaTeX documents. Hence, a block command
is done using ``\BLOCK{}`` and variables using ``\VAR{}`` in the Jinja2
template.
"""
return jinja2.Environment(
block_start_string='\BLOCK{',
block_end_string='}',
variable_start_string='\VAR{',
variable_end_string='}',
comment_start_string='\#{',
comment_end_string='}',
line_statement_prefix='%-',
line_comment_prefix='%#',
trim_blocks=True,
autoescape=False,
loader=jinja2.PackageLoader(__name__, "templates"),
)
[docs]def sanitize_tex(original_text):
"""Sanitize TeX text.
Args:
original_text (str): the text to sanitize for LaTeX
Text is sanitized by following these steps:
1. Replaces ``\\\\`` by ``\\textbackslash``
2. Escapes certain characters (such as ``$``, ``%``, ``_``, ``}``, ``{``,
``&`` and ``#``) by adding a backslash (*e.g.* from ``&`` to ``\\&``).
3. Replaces special characters such as ``~`` by the LaTeX equivalent
(*e.g.* from ``~`` to ``$\\sim$``).
"""
# The backslashes
sanitized_tex = original_text.replace("\\", r"\textbackslash ")
# Escaping
sanitized_tex = re.sub(r"([{}])".format("".join(_escaped_char)),
r"\\\g<1>", sanitized_tex)
# Replacing
for character, mod in _char_mod.items():
sanitized_tex = sanitized_tex.replace(character, mod)
return sanitized_tex
[docs]def wrap_tex(original_text):
"""Wraps the text.
Args:
original_text (str): the text to wrap
Returns:
str: a string where the original text was wrapped
Wraps the text so that lines are no longer than 80 characters. Uses the
:py:func:`str.join` function on the results of the :py:func:`wrap`
function, so that a single string is returned.
"""
return "\n".join(wrap(original_text))
[docs]def tex_inline_math(content):
"""Creates an inline mathematical formula in TeX.
Args:
content (str): the content of the mathematical formula
Returns:
str: the formatted mathematical formula
The function only adds ``$`` symbols before and after the content (*e.g.*
from ``\\pi`` to ``$\\pi$``).
"""
return "${}$".format(content)
def _is_sanitized(text):
"""Check if text is sanitized.
Args:
text (str): the text to check
Returns:
bool: ``True`` if the text is sanitized, ``False`` otherwise
"""
# Checking the escaped characters
sanitized = re.search(r"[^\\][{}]".format("".join(_escaped_char)), text)
sanitized = sanitized is None
# Checking the characters to replace
for character in _char_mod.keys():
sanitized = sanitized and (character not in text)
return sanitized
[docs]def create_tabular(template, header, data, header_multicol=None,
col_align=None):
"""Creates a TeX tabular.
Args:
template (jinja2.Template): the tabular template
header (list): the header of the tabular
data (list): the tabular data
header_multicol (list): the number of columns for the header
col_align (list): the column alignement
Returns:
str: a string representation of a LaTeX tabular
"""
if header_multicol is None:
header_multicol = [1 for i in header]
# Getting the number of columns
nb_col = sum(header_multicol)
if col_align is None:
col_align = ["c"] * nb_col
# Checking that the number of columns holds
assert len(header) == len(header_multicol), "len(header) != len(multicol)"
assert len(col_align) == nb_col, "len(align) != number of columns"
# Generating the tabular data
tabular_data = {
"col_alignments": "".join(col_align),
"header_data": zip(header, header_multicol),
"tabular_data": data,
}
# Rendering
return template.render(**tabular_data)
[docs]def create_float(template, float_type, caption, label, content, placement="H"):
"""Creates a TeX float.
Args:
template (jinja2.Template): the float template
float_type (str): the type of float (``figure`` or ``table``)
caption (str): the caption of the float
label (str): the label of the float
content (str): the content of the float
placement (str): the float placement (*e.g.* ``H``)
Returns:
str: a string representation of a LaTeX float
"""
# Some assertions
assert float_type in ["figure", "table"], "invalid float type"
for character in placement:
assert character in "htbp!H", "invalid placement"
if "H" in placement:
assert placement == "H", "placement 'H' should be alone"
# Generating the float data
float_data = {
"float_type": float_type,
"float_placement": placement,
"float_caption": caption,
"float_label": label,
"float_content": content,
}
# Rendering
return template.render(**float_data)
[docs]def colorize_time(total_seconds):
"""Colorize the time.
Args:
total_seconds (int): the total number of seconds
Returns:
str: a colorized LaTeX string representation of time
The time is displayed as ``HH:MM:SS``, but insignificant zeros are
grayed-out.
"""
# Formatting the time
formatted_time = format_time(total_seconds)
# Setting the color
colored_time = formatted_time
to_color = re.match("([0:]+)", formatted_time)
if to_color is not None:
colored_time = r"{\color{light_gray}"
colored_time += formatted_time[:to_color.end()]
colored_time += "}" + formatted_time[to_color.end():]
# Formatting the time
return colored_time