Render HTML to PDF in Django site

117,105

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",
       ),
Share:
117,105
Admin
Author by

Admin

Updated on August 21, 2021

Comments

  • Admin
    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
    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
    Andriy Drozdyuk almost 13 years
    Man, reportlab is pita to install on windows 7 64bit, python2.7 64bit. Still trying...
  • Andriy Drozdyuk
    Andriy Drozdyuk almost 13 years
    Thanks 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
    Andriy Drozdyuk almost 13 years
    Would 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
    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
    dfrankow about 12 years
    Doesn't seem to run Javascript.
  • Mutant
    Mutant over 11 years
    Any suggestion on how Javascript call can be incorporated?
  • codeVerine
    codeVerine about 11 years
    Is it able to generate multicolored PDFs with complete CSS ?
  • fixmycode
    fixmycode almost 11 years
    pisa is now distributed as xhtml2pdf
  • laycat
    laycat almost 11 years
    Can't seem to find PisaDocument API, htmltopdf.org/doc/pisa-en.html seems to be down as well. Any other sources of documentation?
  • laycat
    laycat almost 11 years
    How can I force a next page?
  • laycat
    laycat almost 11 years
    how would I disable footer from page 1?
  • Furbeenator
    Furbeenator over 10 years
    This works great, but I cannot seem to render HTML with an <ol></ol> tag that uses list-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's render_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
    David Hofmann over 9 years
    I 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
    vinyll over 9 years
    This worked almost straight forward for me. The only thing was to replace html.encode("ISO-8859-1") by html.decode("utf-8")
  • normic
    normic over 9 years
    I've changed the code as @vinyll mentioned and additionally had to add a line to the class PDFTemplateView: content_type = "application/pdf"
  • Jay Modi
    Jay Modi about 9 years
    In 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
    The NetYeti over 8 years
    This 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
    tom over 8 years
    It works only for the static content in html content,what about the dynamic content like (charts,graphs) which are generated to javascript !!
  • digz6666
    digz6666 over 8 years
    @alejoss You should use inline styles instead of CSS.
  • mehmet
    mehmet about 8 years
    How about svg embedded within html, is that also supported?
  • mehmet
    mehmet about 8 years
    Does it support svg graphics embedded within html?
  • jithin
    jithin about 8 years
  • mehmet
    mehmet about 8 years
    Just be careful, webkit does not support everything chrome/firefox does: webkit.org/status
  • mehmet
    mehmet about 8 years
    django-wkhtmltopdf did wonders for me! also be sure to turn off all the animations your javascript/charting engine does.
  • Sebastien
    Sebastien almost 8 years
    In python3, except the conversion cStringIO.StringIO to io.StringIO, we must define result as result = io.BytesIO() instead of result = StringIO.
  • Ivan Borshchov
    Ivan Borshchov over 7 years
    @Johan Looks like now only experimental version of xhtml2pdf supports python3. See here github.com/xhtml2pdf/xhtml2pdf#installation. So you need to reinstall pip uninstall xhtml2pdf and pip install --pre xhtml2pdf. Also use the @Sebastien advice to correctly work with StringIO in python3 .
  • Ivan Borshchov
    Ivan Borshchov over 7 years
    @Johan But after some tries today I still have several problems with xhtml2pdf on python3, may be they are related to unstable release. Now I switched to pdfkit 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 lightweight wkhtmltopdf binary .
  • Manish Ojha
    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
    jithin over 6 years
    @ManishOjha simple bar chart is based on canvas not svg
  • jithin
    jithin over 6 years
    @sam did you check the blog post?
  • Manish Ojha
    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
    jithin over 6 years
    @ManishOjha did you try increasing the delay?
  • Manish Ojha
    Manish Ojha over 6 years
    @jithin yes I tried putting 'javascript-delay': 2000 but it throws error returned non-zero exit status -11
  • jithin
    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
    Manish Ojha over 6 years
    @jithin can you reply here stackoverflow.com/questions/47327278/…
  • sam
    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
    Ahmad Ayyaz over 4 years
    How I can download multiple files with this approach?
  • David
    David almost 4 years
    This solution may not work straight away for django 3.0 as django-utils-six is removed but the easy_pdf depends on that.