Render HTML to PDF in Django site
Solution 1
Try the solution from Reportlab.
Download it and install it as usual with python setup.py install
You will also need to install the following modules: xhtml2pdf, html5lib, pypdf with easy_install.
Here is an usage example:
First define this function:
import cStringIO as StringIO
from xhtml2pdf import pisa
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
from cgi import escape
def render_to_pdf(template_src, context_dict):
template = get_template(template_src)
context = Context(context_dict)
html = template.render(context)
result = StringIO.StringIO()
pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
Then you can use it like this:
def myview(request):
#Retrieve data or whatever you need
return render_to_pdf(
'mytemplate.html',
{
'pagesize':'A4',
'mylist': results,
}
)
The template:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>My Title</title>
<style type="text/css">
@page {
size: {{ pagesize }};
margin: 1cm;
@frame footer {
-pdf-frame-content: footerContent;
bottom: 0cm;
margin-left: 9cm;
margin-right: 9cm;
height: 1cm;
}
}
</style>
</head>
<body>
<div>
{% for item in mylist %}
RENDER MY CONTENT
{% endfor %}
</div>
<div id="footerContent">
{%block page_foot%}
Page <pdf:pagenumber>
{%endblock%}
</div>
</body>
</html>
Hope it helps.
Solution 2
Try wkhtmltopdf with either one of the following wrappers
django-wkhtmltopdf or python-pdfkit
This worked great for me,supports javascript and css or anything for that matter which a webkit browser supports.
For more detailed tutorial please see this blog post
Solution 3
https://github.com/nigma/django-easy-pdf
Template:
{% extends "easy_pdf/base.html" %}
{% block content %}
<div id="content">
<h1>Hi there!</h1>
</div>
{% endblock %}
View:
from easy_pdf.views import PDFTemplateView
class HelloPDFView(PDFTemplateView):
template_name = "hello.html"
If you want to use django-easy-pdf on Python 3 check the solution suggested here.
Solution 4
I just whipped this up for CBV. Not used in production but generates a PDF for me. Probably needs work for the error reporting side of things but does the trick so far.
import StringIO
from cgi import escape
from xhtml2pdf import pisa
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.views.generic import TemplateView
class PDFTemplateResponse(TemplateResponse):
def generate_pdf(self, retval):
html = self.content
result = StringIO.StringIO()
rendering = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
if rendering.err:
return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
else:
self.content = result.getvalue()
def __init__(self, *args, **kwargs):
super(PDFTemplateResponse, self).__init__(*args, mimetype='application/pdf', **kwargs)
self.add_post_render_callback(self.generate_pdf)
class PDFTemplateView(TemplateView):
response_class = PDFTemplateResponse
Used like:
class MyPdfView(PDFTemplateView):
template_name = 'things/pdf.html'
Solution 5
After trying to get this to work for too many hours, I finally found this: https://github.com/vierno/django-xhtml2pdf
It's a fork of https://github.com/chrisglass/django-xhtml2pdf that provides a mixin for a generic class-based view. I used it like this:
# views.py
from django_xhtml2pdf.views import PdfMixin
class GroupPDFGenerate(PdfMixin, DetailView):
model = PeerGroupSignIn
template_name = 'groups/pdf.html'
# templates/groups/pdf.html
<html>
<style>
@page { your xhtml2pdf pisa PDF parameters }
</style>
</head>
<body>
<div id="header_content"> (this is defined in the style section)
<h1>{{ peergroupsignin.this_group_title }}</h1>
...
Use the model name you defined in your view in all lowercase when populating the template fields. Because its a GCBV, you can just call it as '.as_view' in your urls.py:
# urls.py (using url namespaces defined in the main urls.py file)
url(
regex=r"^(?P<pk>\d+)/generate_pdf/$",
view=views.GroupPDFGenerate.as_view(),
name="generate_pdf",
),
Admin
Updated on August 21, 2021Comments
-
Admin over 2 years
For my django powered site, I am looking for an easy solution to convert dynamic html pages to pdf.
Pages include HTML and charts from Google visualization API (which is javascript based, yet including those graphs is a must).
-
arcanum over 14 years+1 I've been using this solution for a year and its great. PISA can even create barcodes with a simple tag, plus much more. And its easy.
-
Andriy Drozdyuk almost 13 yearsMan, reportlab is pita to install on windows 7 64bit, python2.7 64bit. Still trying...
-
Andriy Drozdyuk almost 13 yearsThanks to this great site with precompiled 64-bit distribution of common python libs I was able to get things going: lfd.uci.edu/~gohlke/pythonlibs
-
Andriy Drozdyuk almost 13 yearsWould be nice if there was some reference to how include media like css/images. This guy had this question, just like me: stackoverflow.com/questions/2179958/…
-
Javier over 12 years@drozzy, to use CSS I had to include all the css code IN the Html HEAD, inside a <style> tag.
-
dfrankow about 12 yearsDoesn't seem to run Javascript.
-
Mutant over 11 yearsAny suggestion on how Javascript call can be incorporated?
-
codeVerine about 11 yearsIs it able to generate multicolored PDFs with complete CSS ?
-
fixmycode almost 11 yearspisa is now distributed as xhtml2pdf
-
laycat almost 11 yearsCan't seem to find PisaDocument API, htmltopdf.org/doc/pisa-en.html seems to be down as well. Any other sources of documentation?
-
laycat almost 11 yearsHow can I force a next page?
-
laycat almost 11 yearshow would I disable footer from page 1?
-
Furbeenator over 10 yearsThis works great, but I cannot seem to render HTML with an
<ol></ol>
tag that useslist-style-type: upper-alpha;
, I cannot get the OL to use letters instead of numbers when rendered to PDF using Pisa and cStringIO. It renders fine when using Django'srender_to_response
, but not when I render to PDF. Any ideas on how to get this method to respect the alphabetic Ordered List (ol) type? -
David Hofmann over 9 yearsI gues that the right thing to do is to make browsers produce the pdf becuase they are the only ones doing proper html/js/css rendering. see this question stackoverflow.com/q/25574082/39998
-
vinyll over 9 yearsThis worked almost straight forward for me. The only thing was to replace
html.encode("ISO-8859-1")
byhtml.decode("utf-8")
-
normic over 9 yearsI've changed the code as @vinyll mentioned and additionally had to add a line to the class PDFTemplateView:
content_type = "application/pdf"
-
Jay Modi about 9 yearsIn the above answer section "First define this function:", How to get only pdf file in response? I want to save PDF file in my file system.
-
The NetYeti over 8 yearsThis is the easiest one to implement of the options I have tried so far. For my needs (generating a pdf report from an html version) this just simply works. Thanks!
-
tom over 8 yearsIt works only for the static content in html content,what about the dynamic content like (charts,graphs) which are generated to javascript !!
-
digz6666 over 8 years@alejoss You should use inline styles instead of CSS.
-
mehmet about 8 yearsHow about svg embedded within html, is that also supported?
-
mehmet about 8 yearsDoes it support svg graphics embedded within html?
-
jithin about 8 years@mmatt Yes it supports svg .See this stackoverflow.com/questions/12395541/… and this github.com/wkhtmltopdf/wkhtmltopdf/issues/1964
-
mehmet about 8 yearsJust be careful, webkit does not support everything chrome/firefox does: webkit.org/status
-
mehmet about 8 yearsdjango-wkhtmltopdf did wonders for me! also be sure to turn off all the animations your javascript/charting engine does.
-
Sebastien almost 8 yearsIn python3, except the conversion
cStringIO.StringIO
toio.StringIO
, we must defineresult
asresult = io.BytesIO()
instead ofresult = StringIO
. -
Ivan Borshchov over 7 years@Johan Looks like now only experimental version of
xhtml2pdf
supportspython3
. See here github.com/xhtml2pdf/xhtml2pdf#installation. So you need to reinstallpip uninstall xhtml2pdf
andpip install --pre xhtml2pdf
. Also use the @Sebastien advice to correctly work withStringIO
in python3 . -
Ivan Borshchov over 7 years@Johan But after some tries today I still have several problems with
xhtml2pdf
onpython3
, may be they are related to unstable release. Now I switched topdfkit
maketips.net/tip/72/html-to-pdf-in-django . It works perfect on Python3, without any problems supports styles and images and it is based on lightweightwkhtmltopdf
binary . -
Manish Ojha over 6 years@mehmet it didnot support my simple bar-chart js. I got lots of errors. Can you help me with it??
-
jithin over 6 years@ManishOjha simple bar chart is based on canvas not svg
-
jithin over 6 years@sam did you check the blog post?
-
Manish Ojha over 6 years@jithin thanks for the advice. But the graph loaded but not completely. I have kept
'javascript-delay': 1000
. What will be the best way? -
jithin over 6 years@ManishOjha did you try increasing the delay?
-
Manish Ojha over 6 years@jithin yes I tried putting
'javascript-delay': 2000
but it throws errorreturned non-zero exit status -11
-
jithin over 6 years@ManishOjha i can't comment without more information.The html file generated can be located in appdata or temp folder.Please open the file in browser to check for errors.Also try running by adding ignore errors flag.
-
Manish Ojha over 6 years@jithin can you reply here stackoverflow.com/questions/47327278/…
-
sam over 6 years@jithin, yes i did check the blog post, having issues with rendering the css and an SVG, and making the pdf longer than 1 page.
-
Ahmad Ayyaz over 4 yearsHow I can download multiple files with this approach?
-
David almost 4 yearsThis solution may not work straight away for django 3.0 as django-utils-six is removed but the easy_pdf depends on that.