From fbf54be1da1cc90490336a57df6678a2f833a5f7 Mon Sep 17 00:00:00 2001 From: Jonathan Liuti Date: Wed, 25 Nov 2015 13:29:18 +0100 Subject: Extracted logic from views - fixed tests. The logic was coupled with the views which made things difficult to reuse if you wanted to use the pdf generation somehwere else than in a view. With this patch, the logic has been moved to `utils.py` and should be more easy to reuse. Tests have been adapted and made compatible with django > 1.7 --- wkhtmltopdf/utils.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) (limited to 'wkhtmltopdf/utils.py') diff --git a/wkhtmltopdf/utils.py b/wkhtmltopdf/utils.py index f9d7ac1..87b214c 100644 --- a/wkhtmltopdf/utils.py +++ b/wkhtmltopdf/utils.py @@ -6,6 +6,10 @@ import os import re import sys import shlex +from tempfile import NamedTemporaryFile + +from django.template.context import Context, RequestContext +from django.utils.encoding import smart_text try: from urllib.request import pathname2url @@ -103,6 +107,59 @@ def wkhtmltopdf(pages, output=None, **kwargs): return check_output(ck_args, **ck_kwargs) +def convert_to_pdf(filename, header_filename=None, footer_filename=None, cmd_options=None): + # Clobber header_html and footer_html only if filenames are + # provided. These keys may be in self.cmd_options as hardcoded + # static files. + cmd_options = cmd_options if cmd_options else {} + + if header_filename is not None: + cmd_options['header_html'] = header_filename + if footer_filename is not None: + cmd_options['footer_html'] = footer_filename + return wkhtmltopdf(pages=[filename], **cmd_options) + +def render_pdf_from_template(input_template, header_template, footer_template, context, cmd_options=None): + debug = getattr(settings, 'WKHTMLTOPDF_DEBUG', settings.DEBUG) + cmd_options = cmd_options if cmd_options else {} + + input_file = header_file = footer_file = None + header_filename = footer_filename = None + + try: + input_file = render_to_temporary_file( + template=input_template, + context=context, + prefix='wkhtmltopdf', suffix='.html', + delete=(not debug) + ) + + if header_template: + header_file = render_to_temporary_file( + template=header_template, + context=context, + prefix='wkhtmltopdf', suffix='.html', + delete=(not debug) + ) + header_filename = header_file.name + + if footer_template: + footer_file = render_to_temporary_file( + template=footer_template, + context=context, + prefix='wkhtmltopdf', suffix='.html', + delete=(not debug) + ) + footer_filename = footer_file.name + + return convert_to_pdf(filename=input_file.name, + header_filename=header_filename, + footer_filename=footer_filename, + cmd_options=cmd_options) + finally: + # Clean up temporary files + for f in filter(None, (input_file, header_file, footer_file)): + f.close() def content_disposition_filename(filename): """ @@ -144,7 +201,6 @@ def pathname2fileurl(pathname): def make_absolute_paths(content): """Convert all MEDIA files into a file://URL paths in order to correctly get it displayed in PDFs.""" - overrides = [ { 'root': settings.MEDIA_ROOT, @@ -173,3 +229,32 @@ def make_absolute_paths(content): occur[len(x['url']):]) return content + +def render_to_temporary_file(template, context, mode='w+b', bufsize=-1, + suffix='.html', prefix='tmp', dir=None, + delete=True): + # make sure the context is a context object + if not isinstance(context, (Context, RequestContext)): + context = Context(context) + + content = smart_text(template.render(context)) + content = make_absolute_paths(content) + + try: + # Python3 has 'buffering' arg instead of 'bufsize' + tempfile = NamedTemporaryFile(mode=mode, buffering=bufsize, + suffix=suffix, prefix=prefix, + dir=dir, delete=delete) + except TypeError: + tempfile = NamedTemporaryFile(mode=mode, bufsize=bufsize, + suffix=suffix, prefix=prefix, + dir=dir, delete=delete) + + try: + tempfile.write(content.encode('utf-8')) + tempfile.flush() + return tempfile + except: + # Clean-up tempfile if an Exception is raised. + tempfile.close() + raise -- cgit v1.2.3