aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Turnbull <james@jamesturnbull.org>2013-01-16 07:41:28 -0800
committerJames Turnbull <james@jamesturnbull.org>2013-01-16 07:41:28 -0800
commita03c1e14b0da1999590442719c42293a88ab9e3b (patch)
tree45a1966ab8da505e62490c5a8651075c4ed157a1
parent887d3c6600750dd573e768c6fdf370a6a607d39b (diff)
parentcda678a3ea9b01a684c6e2d3ed636834f41c96f8 (diff)
downloaddjango-wkhtmltopdf-a03c1e14b0da1999590442719c42293a88ab9e3b.tar.gz
django-wkhtmltopdf-a03c1e14b0da1999590442719c42293a88ab9e3b.tar.bz2
django-wkhtmltopdf-a03c1e14b0da1999590442719c42293a88ab9e3b.zip
Merge pull request #28 from incuna/downloading-option
Make PDF downloading an option
-rw-r--r--wkhtmltopdf/test_settings.py16
-rw-r--r--wkhtmltopdf/tests/templates/footer.html5
-rw-r--r--wkhtmltopdf/tests/tests.py328
-rw-r--r--wkhtmltopdf/utils.py35
-rw-r--r--wkhtmltopdf/views.py52
5 files changed, 211 insertions, 225 deletions
diff --git a/wkhtmltopdf/test_settings.py b/wkhtmltopdf/test_settings.py
index 37d086f..9f6e461 100644
--- a/wkhtmltopdf/test_settings.py
+++ b/wkhtmltopdf/test_settings.py
@@ -1,5 +1,9 @@
+import os
+
DEBUG = True
+DIRNAME = os.path.abspath(os.path.dirname(__file__))
+
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
@@ -7,10 +11,18 @@ DATABASES = {
}
}
-MEDIA_URL = ''
-STATIC_URL = ''
+MEDIA_ROOT = os.path.join(DIRNAME, 'media')
+MEDIA_URL = '/media/'
+STATIC_ROOT = os.path.join(DIRNAME, 'static')
+STATIC_URL = '/static/'
INSTALLED_APPS = (
'wkhtmltopdf.tests',
'wkhtmltopdf',
)
+
+TEMPLATE_DIRS = [
+ os.path.join(DIRNAME, 'testproject', 'tests', 'templates'),
+]
+
+WKHTMLTOPDF_DEBUG = DEBUG
diff --git a/wkhtmltopdf/tests/templates/footer.html b/wkhtmltopdf/tests/templates/footer.html
index 3ae09cb..cf98267 100644
--- a/wkhtmltopdf/tests/templates/footer.html
+++ b/wkhtmltopdf/tests/templates/footer.html
@@ -1,2 +1,3 @@
-MEDIA_URL = {{ MEDIA_URL }}
-STATIC_URL = {{ STATIC_URL }}
+<script src="{{ STATIC_URL }}sample_js_not_existing.js"></script>
+
+<img src="{{ MEDIA_URL }}sample_image_not_existing.png" />
diff --git a/wkhtmltopdf/tests/tests.py b/wkhtmltopdf/tests/tests.py
index 16e89ba..b007687 100644
--- a/wkhtmltopdf/tests/tests.py
+++ b/wkhtmltopdf/tests/tests.py
@@ -5,11 +5,13 @@ from __future__ import absolute_import
import os
import sys
+from django.conf import settings
from django.test import TestCase
from django.test.client import RequestFactory
from wkhtmltopdf.subprocess import CalledProcessError
-from wkhtmltopdf.utils import override_settings, _options_to_args, wkhtmltopdf
+from wkhtmltopdf.utils import (_options_to_args, make_absolute_paths,
+ wkhtmltopdf)
from wkhtmltopdf.views import PDFResponse, PDFTemplateView, PDFTemplateResponse
@@ -67,6 +69,12 @@ class TestUtils(TestCase):
class TestViews(TestCase):
+ template = 'sample.html'
+ footer_template = 'footer.html'
+ pdf_filename = 'output.pdf'
+ attached_fileheader = 'attachment; filename="{0}"'
+ inline_fileheader = 'inline; filename="{0}"'
+
def test_pdf_response(self):
"""Should generate the correct HttpResponse object and mimetype"""
# 404
@@ -98,6 +106,24 @@ class TestViews(TestCase):
self.assertEqual(response['Content-Disposition'],
'attachment; filename="?.pdf"')
+ # Content as a direct output
+ response = PDFResponse(content=content, filename="nospace.pdf",
+ show_content_in_browser=True)
+ self.assertEqual(response['Content-Disposition'],
+ 'inline; filename="nospace.pdf"')
+ response = PDFResponse(content=content, filename="one space.pdf",
+ show_content_in_browser=True)
+ self.assertEqual(response['Content-Disposition'],
+ 'inline; filename="one space.pdf"')
+ response = PDFResponse(content=content, filename="4'5\".pdf",
+ show_content_in_browser=True)
+ self.assertEqual(response['Content-Disposition'],
+ 'inline; filename="4\'5.pdf"')
+ response = PDFResponse(content=content, filename=u"♥.pdf",
+ show_content_in_browser=True)
+ self.assertEqual(response['Content-Disposition'],
+ 'inline; filename="?.pdf"')
+
# Content-Type
response = PDFResponse(content=content,
content_type='application/x-pdf')
@@ -106,185 +132,133 @@ class TestViews(TestCase):
mimetype='application/x-pdf')
self.assertEqual(response['Content-Type'], 'application/x-pdf')
- def test_pdf_template_response(self):
+ def test_pdf_template_response(self, show_content=False):
"""Test PDFTemplateResponse."""
- from django.conf import settings
-
- with override_settings(
- MEDIA_URL='/media/',
- MEDIA_ROOT='/tmp/media',
- STATIC_URL='/static/',
- STATIC_ROOT='/tmp/static',
- TEMPLATE_CONTEXT_PROCESSORS=[
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- ],
- TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'],
- TEMPLATE_DIRS=[os.path.join(os.path.dirname(__file__),
- '_testproject', 'templates')],
- WKHTMLTOPDF_DEBUG=False,
- ):
- # Setup sample.html
- template = 'sample.html'
- context = {'title': 'Heading'}
- request = RequestFactory().get('/')
- response = PDFTemplateResponse(request=request,
- template=template,
- context=context)
- self.assertEqual(response._request, request)
- self.assertEqual(response.template_name, template)
- self.assertEqual(response.context_data, context)
- self.assertEqual(response.filename, None)
- self.assertEqual(response.header_template, None)
- self.assertEqual(response.footer_template, None)
- self.assertEqual(response.cmd_options, {})
- self.assertFalse(response.has_header('Content-Disposition'))
-
- # Render to temporary file
- tempfile = response.render_to_temporary_file(template)
- tempfile.seek(0)
- html_content = tempfile.read()
- self.assertTrue(html_content.startswith('<html>'))
- self.assertTrue('<h1>{title}</h1>'.format(**context)
- in html_content)
-
- pdf_content = response.rendered_content
- self.assertTrue(pdf_content.startswith('%PDF-'))
- self.assertTrue(pdf_content.endswith('%%EOF\n'))
-
- # Footer
- filename = 'output.pdf'
- footer_template = 'footer.html'
- cmd_options = {'title': 'Test PDF'}
- response = PDFTemplateResponse(request=request,
- template=template,
- context=context,
- filename=filename,
- footer_template=footer_template,
- cmd_options=cmd_options)
- self.assertEqual(response.filename, filename)
- self.assertEqual(response.header_template, None)
- self.assertEqual(response.footer_template, footer_template)
- self.assertEqual(response.cmd_options, cmd_options)
- self.assertTrue(response.has_header('Content-Disposition'))
-
- tempfile = response.render_to_temporary_file(footer_template)
- tempfile.seek(0)
- footer_content = tempfile.read()
-
- media_url = 'MEDIA_URL = file://{0}/'.format(settings.MEDIA_ROOT)
- self.assertTrue(
- media_url in footer_content,
- "{0!r} not in {1!r}".format(media_url, footer_content)
- )
-
- static_url = 'STATIC_URL = file://{0}/'.format(settings.STATIC_ROOT)
- self.assertTrue(
- static_url in footer_content,
- "{0!r} not in {1!r}".format(static_url, footer_content)
- )
-
- pdf_content = response.rendered_content
- self.assertTrue('\0'.join('{title}'.format(**cmd_options))
- in pdf_content)
-
- # Override settings
- response = PDFTemplateResponse(request=request,
- template=template,
- context=context,
- filename=filename,
- footer_template=footer_template,
- cmd_options=cmd_options,
- override_settings={
- 'STATIC_URL': 'file:///tmp/s/'
- })
- tempfile = response.render_to_temporary_file(footer_template)
- tempfile.seek(0)
- footer_content = tempfile.read()
-
- static_url = 'STATIC_URL = {0}'.format('file:///tmp/s/')
- self.assertTrue(
- static_url in footer_content,
- "{0!r} not in {1!r}".format(static_url, footer_content)
- )
- self.assertEqual(settings.STATIC_URL, '/static/')
-
- def test_pdf_template_view(self):
+
+ context = {'title': 'Heading'}
+ request = RequestFactory().get('/')
+ response = PDFTemplateResponse(request=request,
+ template=self.template,
+ context=context,
+ show_content_in_browser=show_content)
+ self.assertEqual(response._request, request)
+ self.assertEqual(response.template_name, self.template)
+ self.assertEqual(response.context_data, context)
+ self.assertEqual(response.filename, None)
+ self.assertEqual(response.header_template, None)
+ self.assertEqual(response.footer_template, None)
+ self.assertEqual(response.cmd_options, {})
+ self.assertFalse(response.has_header('Content-Disposition'))
+
+ # Render to temporary file
+ tempfile = response.render_to_temporary_file(self.template)
+ tempfile.seek(0)
+ html_content = tempfile.read()
+ self.assertTrue(html_content.startswith('<html>'))
+ self.assertTrue('<h1>{title}</h1>'.format(**context)
+ in html_content)
+
+ pdf_content = response.rendered_content
+ self.assertTrue(pdf_content.startswith('%PDF-'))
+ self.assertTrue(pdf_content.endswith('%%EOF\n'))
+
+ # Footer
+ cmd_options = {'title': 'Test PDF'}
+ response = PDFTemplateResponse(request=request,
+ template=self.template,
+ context=context,
+ filename=self.pdf_filename,
+ show_content_in_browser=show_content,
+ footer_template=self.footer_template,
+ cmd_options=cmd_options)
+ self.assertEqual(response.filename, self.pdf_filename)
+ self.assertEqual(response.header_template, None)
+ self.assertEqual(response.footer_template, self.footer_template)
+ self.assertEqual(response.cmd_options, cmd_options)
+ self.assertTrue(response.has_header('Content-Disposition'))
+
+ tempfile = response.render_to_temporary_file(self.footer_template)
+ tempfile.seek(0)
+ footer_content = tempfile.read()
+ footer_content = make_absolute_paths(footer_content)
+
+ media_url = 'file://{0}/'.format(settings.MEDIA_ROOT)
+ self.assertTrue(media_url in footer_content, True)
+
+ static_url = 'file://{0}/'.format(settings.STATIC_ROOT)
+ self.assertTrue(static_url in footer_content, True)
+
+ pdf_content = response.rendered_content
+ self.assertTrue('\0'.join('{title}'.format(**cmd_options))
+ in pdf_content)
+
+ def test_pdf_template_response_to_browser(self):
+ self.test_pdf_template_response(show_content=True)
+
+ def test_pdf_template_view(self, show_content=False):
"""Test PDFTemplateView."""
- with override_settings(
- MEDIA_URL='/media/',
- STATIC_URL='/static/',
- TEMPLATE_CONTEXT_PROCESSORS=[
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- ],
- TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'],
- TEMPLATE_DIRS=[os.path.join(os.path.dirname(__file__),
- '_testproject', 'templates')],
- WKHTMLTOPDF_DEBUG=False,
- ):
- # Setup sample.html
- template = 'sample.html'
- filename = 'output.pdf'
- view = PDFTemplateView.as_view(filename=filename,
- template_name=template,
- footer_template='footer.html')
-
- # As PDF
- request = RequestFactory().get('/')
- response = view(request)
- self.assertEqual(response.status_code, 200)
- response.render()
- self.assertEqual(response['Content-Disposition'],
- 'attachment; filename="{0}"'.format(filename))
- self.assertTrue(response.content.startswith('%PDF-'))
- self.assertTrue(response.content.endswith('%%EOF\n'))
-
- # As HTML
- request = RequestFactory().get('/?as=html')
- response = view(request)
- self.assertEqual(response.status_code, 200)
- response.render()
- self.assertFalse(response.has_header('Content-Disposition'))
- self.assertTrue(response.content.startswith('<html>'))
-
- # POST
- request = RequestFactory().post('/')
- response = view(request)
- self.assertEqual(response.status_code, 405)
-
- def test_pdf_template_view_unicode(self):
+
+ view = PDFTemplateView.as_view(filename=self.pdf_filename,
+ show_content_in_browser=show_content,
+ template_name=self.template,
+ footer_template=self.footer_template)
+
+ # As PDF
+ request = RequestFactory().get('/')
+ response = view(request)
+ self.assertEqual(response.status_code, 200)
+ response.render()
+
+ fileheader = self.attached_fileheader
+ if show_content:
+ fileheader = self.inline_fileheader
+ self.assertEqual(response['Content-Disposition'],
+ fileheader.format(self.pdf_filename))
+ self.assertTrue(response.content.startswith('%PDF-'))
+ self.assertTrue(response.content.endswith('%%EOF\n'))
+
+ # As HTML
+ request = RequestFactory().get('/?as=html')
+ response = view(request)
+ self.assertEqual(response.status_code, 200)
+ response.render()
+ self.assertFalse(response.has_header('Content-Disposition'))
+ self.assertTrue(response.content.startswith('<html>'))
+
+ # POST
+ request = RequestFactory().post('/')
+ response = view(request)
+ self.assertEqual(response.status_code, 405)
+
+ def test_pdf_template_view_to_browser(self):
+ self.test_pdf_template_view(show_content=True)
+
+ def test_pdf_template_view_unicode(self, show_content=False):
"""Test PDFTemplateView."""
- with override_settings(
- MEDIA_URL='/media/',
- STATIC_URL='/static/',
- TEMPLATE_CONTEXT_PROCESSORS=[
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- ],
- TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'],
- TEMPLATE_DIRS=[os.path.join(os.path.dirname(__file__),
- '_testproject', 'templates')],
- WKHTMLTOPDF_DEBUG=False,
- ):
- # Setup sample.html
- template = 'unicode.html'
- filename = 'output.pdf'
- view = PDFTemplateView.as_view(filename=filename,
- template_name=template)
-
- # As PDF
- request = RequestFactory().get('/')
- response = view(request)
- self.assertEqual(response.status_code, 200)
- response.render()
- self.assertEqual(response['Content-Disposition'],
- 'attachment; filename="{0}"'.format(filename))
- # not sure how we can test this as the contents is all encoded...
- # best we can do for the moment is check it's a pdf and it worked.
- # self.assertTrue('☃' in response.content)
- self.assertTrue(response.content.startswith('%PDF-'))
- self.assertTrue(response.content.endswith('%%EOF\n'))
+
+ view = PDFTemplateView.as_view(filename=self.pdf_filename,
+ show_content_in_browser=show_content,
+ template_name=self.template)
+
+ # As PDF
+ request = RequestFactory().get('/')
+ response = view(request)
+ self.assertEqual(response.status_code, 200)
+ response.render()
+
+ fileheader = self.attached_fileheader
+ if show_content:
+ fileheader = self.inline_fileheader
+ self.assertEqual(response['Content-Disposition'],
+ fileheader.format(self.pdf_filename))
+ # not sure how we can test this as the contents is all encoded...
+ # best we can do for the moment is check it's a pdf and it worked.
+ # self.assertTrue('☃' in response.content)
+ self.assertTrue(response.content.startswith('%PDF-'))
+ self.assertTrue(response.content.endswith('%%EOF\n'))
+
+ def test_pdf_template_view_unicode_to_browser(self):
+ self.test_pdf_template_view_unicode(show_content=True)
def test_get_cmd_options(self):
# Default cmd_options
diff --git a/wkhtmltopdf/utils.py b/wkhtmltopdf/utils.py
index 63310c3..e5bd102 100644
--- a/wkhtmltopdf/utils.py
+++ b/wkhtmltopdf/utils.py
@@ -4,6 +4,7 @@ from copy import copy
from functools import wraps
from itertools import chain
import os
+import re
import sys
import urllib
from urlparse import urljoin
@@ -121,3 +122,37 @@ def http_quote(string):
def pathname2fileurl(pathname):
"""Returns a file:// URL for pathname. Handles OS-specific conversions."""
return urljoin('file:', urllib.pathname2url(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,
+ 'url': settings.MEDIA_URL,
+ },
+ {
+ 'root': settings.STATIC_ROOT,
+ 'url': settings.STATIC_URL,
+ }
+ ]
+ has_scheme = re.compile(r'^[^:/]+://')
+
+ for x in overrides:
+ if has_scheme.match(x['url']):
+ continue
+
+ if not x['root'].endswith('/'):
+ x['root'] += '/'
+
+ occur_pattern = '''["|']({0}.*?)["|']'''
+ occurences = re.findall(occur_pattern.format(x['url']), content)
+ occurences = list(set(occurences)) # Remove dups
+ for occur in occurences:
+ content = content.replace(occur,
+ pathname2fileurl(x['root']) +
+ occur[len(x['url']):])
+
+ return content
diff --git a/wkhtmltopdf/views.py b/wkhtmltopdf/views.py
index 94cbbf9..5a46490 100644
--- a/wkhtmltopdf/views.py
+++ b/wkhtmltopdf/views.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import
from tempfile import NamedTemporaryFile
-import re
from django.conf import settings
from django.http import HttpResponse
@@ -9,14 +8,15 @@ from django.template.response import TemplateResponse
from django.utils.encoding import smart_str
from django.views.generic import TemplateView
-from .utils import content_disposition_filename, pathname2fileurl, wkhtmltopdf
+from .utils import (content_disposition_filename, make_absolute_paths,
+ wkhtmltopdf)
class PDFResponse(HttpResponse):
"""HttpResponse that sets the headers for PDF output."""
def __init__(self, content, mimetype=None, status=200, content_type=None,
- filename=None, show_content_in_browser=None, *args, **kwargs):
+ filename=None, show_content_in_browser=None, *args, **kwargs):
if content_type is None:
content_type = 'application/pdf'
@@ -32,7 +32,7 @@ class PDFResponse(HttpResponse):
if filename:
fileheader = 'attachment; filename={0}'
if show_content_in_browser:
- fileheader = 'filename={0}'
+ fileheader = 'inline; filename={0}'
filename = content_disposition_filename(filename)
header_content = fileheader.format(filename)
@@ -48,8 +48,7 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse):
status=None, content_type=None, current_app=None,
filename=None, show_content_in_browser=None,
header_template=None, footer_template=None,
- cmd_options=None, override_settings=None,
- *args, **kwargs):
+ cmd_options=None, *args, **kwargs):
super(PDFTemplateResponse, self).__init__(request=request,
template=template,
@@ -74,9 +73,9 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse):
template = self.resolve_template(template_name)
context = self.resolve_context(self.context_data)
- content = smart_str(template.render(context))
- content = self.make_absolute_paths(content)
+ content = smart_str(template.render(context))
+ content = make_absolute_paths(content)
tempfile = NamedTemporaryFile(mode=mode, bufsize=bufsize,
suffix=suffix, prefix=prefix,
@@ -148,41 +147,6 @@ class PDFTemplateResponse(TemplateResponse, PDFResponse):
for f in filter(None, (input_file, header_file, footer_file)):
f.close()
- def make_absolute_paths(self, content):
- """Convert all MEDIA files into a file://URL paths in order to correctly get it displayed in PDFs
-
- mattl's disclaimer: I know it sucks, but it works and I haz no time for better solution now
- """
-
- overrides = [
- {
- 'root': settings.MEDIA_ROOT,
- 'url': settings.MEDIA_URL,
- },
- {
- 'root': settings.STATIC_ROOT,
- 'url': settings.STATIC_URL,
- }
- ]
- has_scheme = re.compile(r'^[^:/]+://')
-
- for x in overrides:
- if has_scheme.match(x['url']):
- continue
-
- if not x['root'].endswith('/'):
- x['root'] += '/'
-
- occurences = re.findall('''["|']({0}.*?)["|']'''.format(x['url']),
- content)
- occurences = list(set(occurences)) # Remove dups
- for occur in occurences:
- content = content.replace(occur,
- pathname2fileurl(x['root']) +
- occur[len(x['url']):])
-
- return content
-
class PDFTemplateView(TemplateView):
"""Class-based view for HTML templates rendered to PDF."""
@@ -190,7 +154,7 @@ class PDFTemplateView(TemplateView):
# Filename for downloaded PDF. If None, the response is inline.
filename = 'rendered_pdf.pdf'
- # Send file as attachement, or if True render content in the browser.
+ # Send file as attachement. If True render content in the browser.
show_content_in_browser = False
# Filenames for the content, header, and footer templates.