diff --git a/README.md b/README.md index c9a1776..35b841c 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,23 @@ from flask_tex import run_tex pdf = run_tex(source, command="pdflatex", directory="/foo/bar/") ``` + +## Jinja integration + +For deeper jinja integration, use `flask_tex` as an extension: + +```python +from flask import Flask +from flask_tex import TeX + +app = Flask(__app__) +TeX(app) +``` + +This adds some LaTeX specific filters to the jinja environment. + +- LaTeX escape: `{{ foo | latex_escape }}` where `foo = "&$%#_{}"` renders as `"\\&\\$\\%\\#\\_\\{\\}"`. + Danger: Do not use this in a html template, because the escaped string is marked as safe + with `Markup`, so code that might be dangerous in html does not get escaped. + +- LaTeX linebreaks: The `linebreaks` filter converts `\n` into `\\\\\n`. diff --git a/flask_tex/__init__.py b/flask_tex/__init__.py index 3fb585e..882964a 100644 --- a/flask_tex/__init__.py +++ b/flask_tex/__init__.py @@ -2,9 +2,46 @@ import tempfile from subprocess import run, PIPE, CalledProcessError +from markupsafe import Markup from flask import render_template, make_response +class TexError(Exception): + pass + + +class TeX: + """TeX Flask extension""" + + def __init__(self, app=None): + self.app = app + if app: + self.init_app(app) + + def init_app(self, app): + self.app = app + + app.jinja_env.filters.update( + linebreaks=do_linebreaks, latex_escape=do_latex_escape, + ) + + +def do_linebreaks(value): + return value.replace("\n", "\\\\\n") + + +def do_latex_escape(value): + return Markup( + value.replace("&", "\\&") + .replace("$", "\\$") + .replace("%", "\\%") + .replace("#", "\\#") + .replace("_", "\\_") + .replace("{", "\\{") + .replace("}", "\\}") + ) + + def render_to_pdf(template_name, filename="flask.pdf", **kwargs): source = render_template(template_name, **kwargs) pdf = compile_source(source) @@ -14,10 +51,6 @@ def render_to_pdf(template_name, filename="flask.pdf", **kwargs): return response -class TexError(Exception): - pass - - def compile_source(source, command="pdflatex"): with tempfile.TemporaryDirectory() as tempdir: return run_tex(source, command, tempdir) diff --git a/tests/test_tex.py b/tests/test_tex.py index a70dd64..9a0c2b9 100644 --- a/tests/test_tex.py +++ b/tests/test_tex.py @@ -1,9 +1,9 @@ import pytest from subprocess import CalledProcessError -from flask import Flask +from flask import Flask, render_template_string, render_template -from flask_tex import compile_source, TexError, render_to_pdf +from flask_tex import compile_source, TexError, render_to_pdf, TeX class TestRunTex: @@ -48,3 +48,38 @@ def test_render_template_to_pdf(self): rv = render_to_pdf("test.tex", foo="bar") assert rv.status_code == 200 + + def test_no_autoescaping_in_tex_templates(self): + app = Flask(__name__) + + with app.test_request_context(): + rv = render_template("test.tex", foo="This \\& sign will not be escaped") + + assert "This \\& sign will not be escaped" in rv + + def test_newline_filter(self): + app = Flask(__name__) + TeX(app) + + template_string = "{{ foo | linebreaks }}" + + foo = "bar\nbaz" + + with app.test_request_context(): + rv = render_template_string(template_string, foo=foo) + + assert rv == "bar\\\\\nbaz" + + def test_latex_escape(self): + app = Flask(__name__) + TeX(app) + + template_string = "{{ foo | latex_escape }}" + foo = "&$%#_{}" + + with app.test_request_context(): + rv = render_template_string(template_string, foo=foo) + + assert ( + rv == "\\&\\$\\%\\#\\_\\{\\}" + ) # This works because latex_escape marks the escaped string as safe with `Markup`