| 1 |
=========================== |
|---|
| 2 |
Outputting PDFs with Django |
|---|
| 3 |
=========================== |
|---|
| 4 |
|
|---|
| 5 |
This document explains how to output PDF files dynamically using Django views. |
|---|
| 6 |
This is made possible by the excellent, open-source ReportLab_ Python PDF |
|---|
| 7 |
library. |
|---|
| 8 |
|
|---|
| 9 |
The advantage of generating PDF files dynamically is that you can create |
|---|
| 10 |
customized PDFs for different purposes -- say, for different users or different |
|---|
| 11 |
pieces of content. |
|---|
| 12 |
|
|---|
| 13 |
For example, Django was used at kusports.com_ to generate customized, |
|---|
| 14 |
printer-friendly NCAA tournament brackets, as PDF files, for people |
|---|
| 15 |
participating in a March Madness contest. |
|---|
| 16 |
|
|---|
| 17 |
.. _ReportLab: http://www.reportlab.org/rl_toolkit.html |
|---|
| 18 |
.. _kusports.com: http://www.kusports.com/ |
|---|
| 19 |
|
|---|
| 20 |
Install ReportLab |
|---|
| 21 |
================= |
|---|
| 22 |
|
|---|
| 23 |
Download and install the ReportLab library from http://www.reportlab.org/downloads.html. |
|---|
| 24 |
The `user guide`_ (not coincidentally, a PDF file) explains how to install it. |
|---|
| 25 |
|
|---|
| 26 |
Test your installation by importing it in the Python interactive interpreter:: |
|---|
| 27 |
|
|---|
| 28 |
>>> import reportlab |
|---|
| 29 |
|
|---|
| 30 |
If that command doesn't raise any errors, the installation worked. |
|---|
| 31 |
|
|---|
| 32 |
.. _user guide: http://www.reportlab.org/rsrc/userguide.pdf |
|---|
| 33 |
|
|---|
| 34 |
Write your view |
|---|
| 35 |
=============== |
|---|
| 36 |
|
|---|
| 37 |
The key to generating PDFs dynamically with Django is that the ReportLab API |
|---|
| 38 |
acts on file-like objects, and Django's ``HttpResponse`` objects are file-like |
|---|
| 39 |
objects. |
|---|
| 40 |
|
|---|
| 41 |
.. admonition:: Note |
|---|
| 42 |
|
|---|
| 43 |
For more information on ``HttpResponse`` objects, see |
|---|
| 44 |
`Request and response objects`_. |
|---|
| 45 |
|
|---|
| 46 |
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/ |
|---|
| 47 |
|
|---|
| 48 |
Here's a "Hello World" example:: |
|---|
| 49 |
|
|---|
| 50 |
from reportlab.pdfgen import canvas |
|---|
| 51 |
from django.http import HttpResponse |
|---|
| 52 |
|
|---|
| 53 |
def some_view(request): |
|---|
| 54 |
# Create the HttpResponse object with the appropriate PDF headers. |
|---|
| 55 |
response = HttpResponse(mimetype='application/pdf') |
|---|
| 56 |
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf' |
|---|
| 57 |
|
|---|
| 58 |
# Create the PDF object, using the response object as its "file." |
|---|
| 59 |
p = canvas.Canvas(response) |
|---|
| 60 |
|
|---|
| 61 |
# Draw things on the PDF. Here's where the PDF generation happens. |
|---|
| 62 |
# See the ReportLab documentation for the full list of functionality. |
|---|
| 63 |
p.drawString(100, 100, "Hello world.") |
|---|
| 64 |
|
|---|
| 65 |
# Close the PDF object cleanly, and we're done. |
|---|
| 66 |
p.showPage() |
|---|
| 67 |
p.save() |
|---|
| 68 |
return response |
|---|
| 69 |
|
|---|
| 70 |
The code and comments should be self-explanatory, but a few things deserve a |
|---|
| 71 |
mention: |
|---|
| 72 |
|
|---|
| 73 |
* The response gets a special mimetype, ``application/pdf``. This tells |
|---|
| 74 |
browsers that the document is a PDF file, rather than an HTML file. If |
|---|
| 75 |
you leave this off, browsers will probably interpret the output as HTML, |
|---|
| 76 |
which would result in ugly, scary gobbledygook in the browser window. |
|---|
| 77 |
|
|---|
| 78 |
* The response gets an additional ``Content-Disposition`` header, which |
|---|
| 79 |
contains the name of the PDF file. This filename is arbitrary: Call it |
|---|
| 80 |
whatever you want. It'll be used by browsers in the "Save as..." |
|---|
| 81 |
dialogue, etc. |
|---|
| 82 |
|
|---|
| 83 |
* The ``Content-Disposition`` header starts with ``'attachment; '`` in this |
|---|
| 84 |
example. This forces Web browsers to pop-up a dialog box |
|---|
| 85 |
prompting/confirming how to handle the document even if a default is set |
|---|
| 86 |
on the machine. If you leave off ``'attachment;'``, browsers will handle |
|---|
| 87 |
the PDF using whatever program/plugin they've been configured to use for |
|---|
| 88 |
PDFs. Here's what that code would look like:: |
|---|
| 89 |
|
|---|
| 90 |
response['Content-Disposition'] = 'filename=somefilename.pdf' |
|---|
| 91 |
|
|---|
| 92 |
* Hooking into the ReportLab API is easy: Just pass ``response`` as the |
|---|
| 93 |
first argument to ``canvas.Canvas``. The ``Canvas`` class expects a |
|---|
| 94 |
file-like object, and ``HttpResponse`` objects fit the bill. |
|---|
| 95 |
|
|---|
| 96 |
* Note that all subsequent PDF-generation methods are called on the PDF |
|---|
| 97 |
object (in this case, ``p``) -- not on ``response``. |
|---|
| 98 |
|
|---|
| 99 |
* Finally, it's important to call ``showPage()`` and ``save()`` on the PDF |
|---|
| 100 |
file. |
|---|
| 101 |
|
|---|
| 102 |
Complex PDFs |
|---|
| 103 |
============ |
|---|
| 104 |
|
|---|
| 105 |
If you're creating a complex PDF document with ReportLab, consider using the |
|---|
| 106 |
cStringIO_ library as a temporary holding place for your PDF file. The |
|---|
| 107 |
cStringIO library provides a file-like object interface that is particularly |
|---|
| 108 |
efficient. Here's the above "Hello World" example rewritten to use |
|---|
| 109 |
``cStringIO``:: |
|---|
| 110 |
|
|---|
| 111 |
from cStringIO import StringIO |
|---|
| 112 |
from reportlab.pdfgen import canvas |
|---|
| 113 |
from django.http import HttpResponse |
|---|
| 114 |
|
|---|
| 115 |
def some_view(request): |
|---|
| 116 |
# Create the HttpResponse object with the appropriate PDF headers. |
|---|
| 117 |
response = HttpResponse(mimetype='application/pdf') |
|---|
| 118 |
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf' |
|---|
| 119 |
|
|---|
| 120 |
buffer = StringIO() |
|---|
| 121 |
|
|---|
| 122 |
# Create the PDF object, using the StringIO object as its "file." |
|---|
| 123 |
p = canvas.Canvas(buffer) |
|---|
| 124 |
|
|---|
| 125 |
# Draw things on the PDF. Here's where the PDF generation happens. |
|---|
| 126 |
# See the ReportLab documentation for the full list of functionality. |
|---|
| 127 |
p.drawString(100, 100, "Hello world.") |
|---|
| 128 |
|
|---|
| 129 |
# Close the PDF object cleanly. |
|---|
| 130 |
p.showPage() |
|---|
| 131 |
p.save() |
|---|
| 132 |
|
|---|
| 133 |
# Get the value of the StringIO buffer and write it to the response. |
|---|
| 134 |
pdf = buffer.getvalue() |
|---|
| 135 |
buffer.close() |
|---|
| 136 |
response.write(pdf) |
|---|
| 137 |
return response |
|---|
| 138 |
|
|---|
| 139 |
.. _cStringIO: http://www.python.org/doc/current/lib/module-cStringIO.html |
|---|
| 140 |
|
|---|
| 141 |
Further resources |
|---|
| 142 |
================= |
|---|
| 143 |
|
|---|
| 144 |
* PDFlib_ is another PDF-generation library that has Python bindings. To |
|---|
| 145 |
use it with Django, just use the same concepts explained in this article. |
|---|
| 146 |
* HTMLdoc_ is a command-line script that can convert HTML to PDF. It |
|---|
| 147 |
doesn't have a Python interface, but you can escape out to the shell |
|---|
| 148 |
using ``system`` or ``popen`` and retrieve the output in Python. |
|---|
| 149 |
* `forge_fdf in Python`_ is a library that fills in PDF forms. |
|---|
| 150 |
|
|---|
| 151 |
.. _PDFlib: http://www.pdflib.org/ |
|---|
| 152 |
.. _HTMLdoc: http://www.htmldoc.org/ |
|---|
| 153 |
.. _forge_fdf in Python: http://www.accesspdf.com/article.php/20050421092951834 |
|---|