1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 | import os
from cgi import escape
from cStringIO import StringIO
from django.template import loader, Context, RequestContext
from django.views.generic.base import TemplateResponseMixin
from django.http import HttpResponse
def content_to_pdf(content, dest, encoding='utf-8', **kwargs):
"""
Write into *dest* file object the given html *content*.
Return True if the operation completed successfully.
"""
from xhtml2pdf import pisa
src = StringIO(content.encode(encoding))
pdf = pisa.pisaDocument(src, dest, encoding=encoding, **kwargs)
return not pdf.err
def content_to_response(content, filename=None):
"""
Return a pdf response using given *content*.
"""
response = HttpResponse(content, mimetype='application/pdf')
if filename is not None:
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
def render_to_pdf(request, template, context, filename=None, encoding='utf-8',
**kwargs):
"""
Render a pdf response using given *request*, *template* and *context*.
"""
if not isinstance(context, Context):
context = RequestContext(request, context)
content = loader.render_to_string(template, context)
buffer = StringIO()
succeed = content_to_pdf(content, buffer, encoding, **kwargs)
if succeed:
return content_to_response(buffer.getvalue(), filename)
return HttpResponse('Errors rendering pdf:<pre>%s</pre>' % escape(content))
class PDFTemplateResponseMixin(TemplateResponseMixin):
"""
Mixin for Django class based views.
Switch normal and pdf template based on request.
The switch is made when the request has a particular querydict, e.g.::
http://www.example.com?format=pdf
The key and value of the querydict can be overridable using *as_view()*.
That pdf url will be present in the context as *pdf_url*.
For example it is possible to define a view like this::
from django.views.generic import View
class MyView(PDFTemplateResponseMixin, View):
template_name = 'myapp/myview.html'
pdf_filename = 'report.pdf'
The pdf generation is automatically done by *xhtml2pdf* using
the *myapp/myview_pdf.html* template.
Note that the pdf template takes the same context as the normal template.
"""
pdf_template_name = None
pdf_template_name_suffix = '_pdf'
pdf_querydict_key = 'format'
pdf_querydict_value = 'pdf'
pdf_encoding = 'utf-8'
pdf_filename = None
pdf_url_varname = 'pdf_url'
pdf_kwargs = {}
def is_pdf(self):
value = self.request.REQUEST.get(self.pdf_querydict_key, '')
return value.lower() == self.pdf_querydict_value.lower()
def _get_pdf_template_name(self, name):
base, ext = os.path.splitext(name)
return '%s%s%s' % (base, self.pdf_template_name_suffix, ext)
def get_pdf_template_names(self):
"""
If the template name is not given using the class attribute
*pdf_template_name*, then it is obtained using normal template
names, appending *pdf_template_name_suffix*, e.g.::
path/to/detail.html -> path/to/detail_pdf.html
"""
if self.pdf_template_name is None:
names = super(PDFTemplateResponseMixin, self).get_template_names()
return map(self._get_pdf_template_name, names)
return [self.pdf_template_name]
def get_pdf_filename(self):
"""
Return the pdf attachment filename.
If the filename is None, the pdf will not be an attachment.
"""
return self.pdf_filename
def get_pdf_url(self):
"""
This method is used to put the pdf url in the context.
"""
querydict = self.request.GET.copy()
querydict[self.pdf_querydict_key] = self.pdf_querydict_value
return '%s?%s' % (self.request.path, querydict.urlencode())
def get_pdf_response(self, context, **response_kwargs):
return render_to_pdf(
request=self.request,
template=self.get_pdf_template_names(),
context=context,
encoding=self.pdf_encoding,
filename=self.get_pdf_filename(),
**self.pdf_kwargs
)
def render_to_response(self, context, **response_kwargs):
if self.is_pdf():
from django.conf import settings
context['STATIC_ROOT'] = settings.STATIC_ROOT
return self.get_pdf_response(context, **response_kwargs)
context[self.pdf_url_varname] = self.get_pdf_url()
return super(PDFTemplateResponseMixin, self).render_to_response(
context, **response_kwargs)
|
Comments