mirror of
https://github.com/nanoy42/coope
synced 2024-12-24 07:53:47 +00:00
Fix 3.2.1
This commit is contained in:
parent
b5f1f99150
commit
0fa1ab34ea
8 changed files with 152 additions and 0 deletions
0
django_tex/__init__.py
Normal file
0
django_tex/__init__.py
Normal file
40
django_tex/core.py
Normal file
40
django_tex/core.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
import os
|
||||||
|
from subprocess import PIPE, run
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from django.template.loader import get_template
|
||||||
|
|
||||||
|
from django_tex.exceptions import TexError
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
DEFAULT_INTERPRETER = 'lualatex'
|
||||||
|
|
||||||
|
def run_tex(source):
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
filename = os.path.join(tempdir, 'texput.tex')
|
||||||
|
with open(filename, 'x', encoding='utf-8') as f:
|
||||||
|
f.write(source)
|
||||||
|
latex_interpreter = getattr(settings, 'LATEX_INTERPRETER', DEFAULT_INTERPRETER)
|
||||||
|
latex_command = f'cd "{tempdir}" && {latex_interpreter} -interaction=batchmode {os.path.basename(filename)}'
|
||||||
|
process = run(latex_command, shell=True, stdout=PIPE, stderr=PIPE)
|
||||||
|
try:
|
||||||
|
if process.returncode == 1:
|
||||||
|
with open(os.path.join(tempdir, 'texput.log'), encoding='utf8') as f:
|
||||||
|
log = f.read()
|
||||||
|
raise TexError(log=log, source=source)
|
||||||
|
with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as pdf_file:
|
||||||
|
pdf = pdf_file.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
if process.stderr:
|
||||||
|
raise Exception(process.stderr.decode('utf-8'))
|
||||||
|
raise
|
||||||
|
return pdf
|
||||||
|
|
||||||
|
def compile_template_to_pdf(template_name, context):
|
||||||
|
source = render_template_with_context(template_name, context)
|
||||||
|
return run_tex(source)
|
||||||
|
|
||||||
|
def render_template_with_context(template_name, context):
|
||||||
|
template = get_template(template_name, using='tex')
|
||||||
|
return template.render(context)
|
11
django_tex/engine.py
Normal file
11
django_tex/engine.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
from django.template.backends.jinja2 import Jinja2
|
||||||
|
|
||||||
|
class TeXEngine(Jinja2):
|
||||||
|
app_dirname = 'templates'
|
||||||
|
|
||||||
|
def __init__(self, params):
|
||||||
|
default_environment = 'django_tex.environment.environment'
|
||||||
|
if 'environment' not in params['OPTIONS'] or not params['OPTIONS']['environment']:
|
||||||
|
params['OPTIONS']['environment'] = default_environment
|
||||||
|
super().__init__(params)
|
16
django_tex/environment.py
Normal file
16
django_tex/environment.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
from jinja2 import Environment
|
||||||
|
|
||||||
|
from django.template.defaultfilters import register
|
||||||
|
|
||||||
|
from django_tex.filters import FILTERS as tex_specific_filters
|
||||||
|
|
||||||
|
# Django's built-in filters ...
|
||||||
|
filters = register.filters
|
||||||
|
# ... updated with tex specific filters
|
||||||
|
filters.update(tex_specific_filters)
|
||||||
|
|
||||||
|
def environment(**options):
|
||||||
|
env = Environment(**options)
|
||||||
|
env.filters = filters
|
||||||
|
return env
|
40
django_tex/exceptions.py
Normal file
40
django_tex/exceptions.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
def prettify_message(message):
|
||||||
|
'''
|
||||||
|
Helper methods that removes consecutive whitespaces and newline characters
|
||||||
|
'''
|
||||||
|
# Replace consecutive whitespaces with a single whitespace
|
||||||
|
message = re.sub(r'[ ]{2,}', ' ', message)
|
||||||
|
# Replace consecutive newline characters, optionally separated by whitespace, with a single newline
|
||||||
|
message = re.sub(r'([\r\n][ \t]*)+', '\n', message)
|
||||||
|
return message
|
||||||
|
|
||||||
|
def tokenizer(code):
|
||||||
|
token_specification = [
|
||||||
|
('ERROR', r'\! (?:.+[\r\n])+[\r\n]+'),
|
||||||
|
('WARNING', r'latex warning.*'),
|
||||||
|
('NOFILE', r'no file.*')
|
||||||
|
]
|
||||||
|
token_regex = '|'.join('(?P<{}>{})'.format(label, regex) for label, regex in token_specification)
|
||||||
|
for m in re.finditer(token_regex, code, re.IGNORECASE):
|
||||||
|
token_dict = dict(type=m.lastgroup, message=prettify_message(m.group()))
|
||||||
|
yield token_dict
|
||||||
|
|
||||||
|
class TexError(Exception):
|
||||||
|
|
||||||
|
def __init__(self, log, source):
|
||||||
|
self.log = log
|
||||||
|
self.source = source
|
||||||
|
self.tokens = list(tokenizer(self.log))
|
||||||
|
self.message = self.get_message()
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
for token in self.tokens:
|
||||||
|
if token['type'] == 'ERROR':
|
||||||
|
return token['message']
|
||||||
|
return 'No error message found in log'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message
|
9
django_tex/filters.py
Normal file
9
django_tex/filters.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.utils.formats import localize_input
|
||||||
|
|
||||||
|
def do_linebreaks(value):
|
||||||
|
return value.replace('\n', '\\\\\n')
|
||||||
|
|
||||||
|
FILTERS = {
|
||||||
|
'localize': localize_input,
|
||||||
|
'linebreaks': do_linebreaks
|
||||||
|
}
|
19
django_tex/models.py
Normal file
19
django_tex/models.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.template import TemplateDoesNotExist
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.template.loader import get_template
|
||||||
|
|
||||||
|
def validate_template_path(name):
|
||||||
|
try:
|
||||||
|
get_template(name, using='tex')
|
||||||
|
except TemplateDoesNotExist:
|
||||||
|
raise ValidationError(_('Template not found.'))
|
||||||
|
|
||||||
|
class TeXTemplateFile(models.Model):
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
name = models.CharField(max_length=255, validators=[validate_template_path,])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
17
django_tex/views.py
Normal file
17
django_tex/views.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
from django_tex.core import compile_template_to_pdf
|
||||||
|
|
||||||
|
class PDFResponse(HttpResponse):
|
||||||
|
|
||||||
|
def __init__(self, content, filename=None):
|
||||||
|
super(PDFResponse, self).__init__(content_type='application/pdf')
|
||||||
|
self['Content-Disposition'] = 'filename="{}"'.format(filename)
|
||||||
|
self.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def render_to_pdf(request, template_name, context=None, filename=None):
|
||||||
|
# Request is not needed and only included to make the signature conform to django's render function
|
||||||
|
pdf = compile_template_to_pdf(template_name, context)
|
||||||
|
return PDFResponse(pdf, filename=filename)
|
Loading…
Reference in a new issue