===========================
Outputting PDFs with Django
===========================

This document explains how to output PDF files dynamically using Django views.
This is made possible by the excellent, open-source ReportLab_ Python PDF
library.

The advantage of generating PDF files dynamically is that you can create
customized PDFs for different purposes -- say, for different users or different
pieces of content.

For example, Django was used at kusports.com_ to generate customized,
printer-friendly NCAA tournament brackets, as PDF files, for people
participating in a March Madness contest.

.. _ReportLab: https://www.reportlab.com/opensource/
.. _kusports.com: http://www.kusports.com/

Install ReportLab
=================

The ReportLab library is `available on PyPI`_. A `user guide`_ (not
coincidentally, a PDF file) is also available for download.
You can install ReportLab with ``pip``:

.. console::

    $ pip install reportlab

Test your installation by importing it in the Python interactive interpreter::

    >>> import reportlab

If that command doesn't raise any errors, the installation worked.

.. _available on PyPI: https://pypi.org/project/reportlab/
.. _user guide: https://www.reportlab.com/docs/reportlab-userguide.pdf

Write your view
===============

The key to generating PDFs dynamically with Django is that the ReportLab API
acts on file-like objects, and Django's :class:`~django.http.FileResponse`
objects accept file-like objects.

Here's a "Hello World" example::

    import io
    from django.http import FileResponse
    from reportlab.pdfgen import canvas

    def some_view(request):
        # Create a file-like buffer to receive PDF data.
        buffer = io.BytesIO()

        # Create the PDF object, using the buffer as its "file."
        p = canvas.Canvas(buffer)

        # Draw things on the PDF. Here's where the PDF generation happens.
        # See the ReportLab documentation for the full list of functionality.
        p.drawString(100, 100, "Hello world.")

        # Close the PDF object cleanly, and we're done.
        p.showPage()
        p.save()

        # FileResponse sets the Content-Disposition header so that browsers
        # present the option to save the file.
        buffer.seek(0)
        return FileResponse(buffer, as_attachment=True, filename='hello.pdf')

The code and comments should be self-explanatory, but a few things deserve a
mention:

* The response will automatically set the MIME type :mimetype:`application/pdf`
  based on the filename extension. This tells browsers that the document is a
  PDF file, rather than an HTML file or a generic `application/octet-stream`
  binary content.

* When ``as_attachment=True`` is passed to ``FileResponse``, it sets the
  appropriate ``Content-Disposition`` header and that tells Web browsers to
  pop-up a dialog box prompting/confirming how to handle the document even if a
  default is set on the machine. If the ``as_attachment`` parameter is omitted,
  browsers will handle the PDF using whatever program/plugin they've been
  configured to use for PDFs.

* You can provide an arbitrary ``filename`` parameter. It'll be used by browsers
  in the "Save as..." dialog.

* Hooking into the ReportLab API is easy: The same buffer passed as the first
  argument to ``canvas.Canvas`` can be fed to the
  :class:`~django.http.FileResponse` class.

* Note that all subsequent PDF-generation methods are called on the PDF
  object (in this case, ``p``) -- not on ``buffer``.

* Finally, it's important to call ``showPage()`` and ``save()`` on the PDF
  file.

.. note::

    ReportLab is not thread-safe. Some of our users have reported odd issues
    with building PDF-generating Django views that are accessed by many people
    at the same time.

Other formats
=============

Notice that there isn't a lot in these examples that's PDF-specific -- just the
bits using ``reportlab``. You can use a similar technique to generate any
arbitrary format that you can find a Python library for. Also see
:doc:`/howto/outputting-csv` for another example and some techniques you can use
when generated text-based formats.

.. seealso::

    Django Packages provides a `comparison of packages
    <https://djangopackages.org/grids/g/pdf/>`_ that help generate PDF files
    from Django.
