If you use your Flask application to primarily generate (X)HTML content, but you also have views where you need to do some LaTeX, you will soon run into a problem: Jinja’s default syntax doesn’t really play well when creating LaTeX documents, as curly braces are an integral part of LaTeX’s syntax as well.
Of course, you could change Jinja’s delimiters for this application. You cannot do it for a single call to render_template() though, so this means you’d have to change the markup in all (X)HTML templates as well. As a consequence, this wouldn’t exactly leverage the re-use of a template file in other project, e.g., one that contains only macros.
A more viable solution is to create a second Jinja environment with a different set of delimiters. (Hail to EftarjinK for proposing this solution on the Pocoo IRC channel!) Bonus feature: you can also create a custom filter function that escapes LaTeX’s reserved characters for you.
app = Flask(__name__)
LATEX_SUBS = (
(re.compile(r'\\'), r'\\textbackslash'),
(re.compile(r'([{}_#%&$])'), r'\\\1'),
(re.compile(r'~'), r'\~{}'),
(re.compile(r'\^'), r'\^{}'),
(re.compile(r'"'), r"''"),
(re.compile(r'\.\.\.+'), r'\\ldots'),
)
def escape_tex(value):
newval = value
for pattern, replacement in LATEX_SUBS:
newval = pattern.sub(replacement, newval)
return newval
texenv = app.create_jinja_environment()
texenv.block_start_string = '((*'
texenv.block_end_string = '*))'
texenv.variable_start_string = '((('
texenv.variable_end_string = ')))'
texenv.comment_start_string = '((='
texenv.comment_end_string = '=))'
texenv.filters['escape_tex'] = escape_tex
Once setup, you can use the new environment to render your LaTeX templates.
template = texenv.get_template('template.tex')
template.render(name='Tom')
Your LaTeX template might look like this:
\documentclass{article}
\begin{document}
Hello, ((( name|escape_tex )))!
\end{document}
This snippet by Clemens Kaposi can be used freely for anything you like. Consider it public domain.
Comments
Once again by Simon Sapin on 2011-07-13 @ 10:15
Once again, when trying to do something unusual, we discover that Armin had already thought of this particular use case and made a function/method doing precisely what we need, before we knew we needed it.
Alternative solution by Armin Ronacher on 2011-07-13 @ 07:52
Alternatively you can use an overlay Jinja environment (
app.jinja_env.overlay()). It will create a Jinja2 environment that inherits all the settings from another one, which however also means that filters are shared.