В последнее время мне нужно было создавать отчеты в формате PDF для приложения Django; лицензия ReportLab была доступна, но в итоге я выбрал LaTeX. Преимущество этого подхода заключается в том, что мы можем использовать шаблоны Django для генерации исходного кода LaTeX, и не перегружать себя написанием большого количества кода для многих отчетов, которые нам нужно было создать. Кроме того, мы могли бы воспользоваться сравнительно более лаконичным синтаксисом LaTeX (у которого есть много особенностей и он не подходит для любых целей).
Этот фрагмент содержит общий обзор подхода. Я счел необходимым внести некоторые изменения, которые я представил в конце этого вопроса. Основным дополнением является обнаружение для Rerun LaTeX
сообщений, которое указывает на необходимость дополнительного прохода. Использование так же просто, как:
def my_view(request):
pdf_stream = process_latex(
'latex_template.tex',
context=RequestContext(request, {'context_obj': context_obj})
)
return HttpResponse(pdf_stream, content_type='application/pdf')
Можно вставлять видео в PDF-файлы, сгенерированные LaTeX, однако у меня нет никакого опыта с этим. Здесь является верхним результатом Google .
Это решение требует запуска нового процесса (pdflatex
), поэтому, если вам нужно чистое решение Python, продолжайте искать.
import os
from subprocess import Popen, PIPE
from tempfile import NamedTemporaryFile
from django.template import loader, Context
class LaTeXException(Exception):
pass
def process_latex(template, context={}, type='pdf', outfile=None):
"""
Processes a template as a LaTeX source file.
Output is either being returned or stored in outfile.
At the moment only pdf output is supported.
"""
t = loader.get_template(template)
c = Context(context)
r = t.render(c)
tex = NamedTemporaryFile()
tex.write(r)
tex.flush()
base = tex.name
names = dict((x, '%s.%s' % (base, x)) for x in (
'log', 'aux', 'pdf', 'dvi', 'png'))
output = names[type]
stdout = None
if type == 'pdf' or type == 'dvi':
stdout = pdflatex(base, type)
elif type == 'png':
stdout = pdflatex(base, 'dvi')
out, err = Popen(
['dvipng', '-bg', '-transparent', names['dvi'], '-o', names['png']],
cwd=os.path.dirname(base), stdout=PIPE, stderr=PIPE
).communicate()
os.remove(names['log'])
os.remove(names['aux'])
# pdflatex appears to ALWAYS return 1, never returning 0 on success, at
# least on the version installed from the Ubuntu apt repository.
# so instead of relying on the return code to determine if it failed,
# check if it successfully created the pdf on disk.
if not os.path.exists(output):
details = '*** pdflatex output: ***\n%s\n*** LaTeX source: ***\n%s' % (
stdout, r)
raise LaTeXException(details)
if not outfile:
o = file(output).read()
os.remove(output)
return o
else:
os.rename(output, outfile)
def pdflatex(file, type='pdf'):
out, err = Popen(
['pdflatex', '-interaction=nonstopmode', '-output-format', type, file],
cwd=os.path.dirname(file), stdout=PIPE, stderr=PIPE
).communicate()
# If the output tells us to rerun, do it by recursing over ourself.
if 'Rerun LaTeX.' in out:
return pdflatex(file, type)
else:
return out