Django: Render HTML as PDF to the browser
Tags: python, django, pdf
Published: 2019-09-29 20:01:08

---
title: Django: Render HTML as PDF to the browser
published: true
description: This document descrives an approach to rendering a html document as in PDF format and being able to serve it to the browser through the Django framework. 
tags: Python,Django,PDF
---

The main part of every business application or CRM is to print out some documents like reports, invoices, etc and PDF is the most popular format. As this being something that usual, it is also surprising how complicated can be sometimes to perform such a task.

I have been involved in a small Django project, where definitely printing PDF documents was required as basic functionality. After a little of search, I found a Python adaption of an already known library from my days in PHP world,[FPDF](http://www.fpdf.org/) , and in this case, [PyFDPF](https://github.com/reingart/pyfpdf) is the one that I used.

But let us don't miss more time and get our hands dirty so to produce a PDF file in Django and display it in the browser.

First let's install the library, this can we done using pip:
```bash
pip install fpdf
```
but it will be better including it the requirements.txt, as recommended by the *Best Practices for Dependency Management*:
```bash
fpdf==1.7.2
```

We will create a template file, which it will contain the HTML to be rendered in the pdf. I like to place these files in a specific folder and therefore keep my templates organized.

```
.
└── main
|    └── ...
|    └── urls.py
└── my_app
     └── ...
     └── pdfs
     └── templates
          └── __init__.py
          └── pdf
               └── example.html
```

Below you can find an example of HTML code that I picked up from the document of the library and to test the elements. Including the Django built-in *static* template tag allows us to include pictures from your static folder in the HTML.
```html
{% load static %}

<H1 align="center">Example</H1>
<h2>Basic usage</h2>
<p>You can now easily print text mixing different
    styles : <B>bold</B>, <I>italic</I>, <U>underlined</U>, or
    <B><I><U>all at once</U></I></B>!<BR>You can also insert links
    on text, such as <A HREF="http://www.fpdf.org">www.fpdf.org</A>,
    or on an image: click on the logo.<br>
    <h3>Sample List</h3>
    <ul>
        <li>option 1</li>
        <ol>
            <li>option 2</li>
        </ol>
        <li>option 3</li>
    </ul>

    <table border="0" align="center" width="50%">
        <thead>
            <tr>
                <th width="30%">Header 1</th>
                <th width="70%">header 2</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>cell 1</td>
                <td>cell 2</td>
            </tr>
            <tr>
                <td>cell 2</td>
                <td>cell 3</td>
            </tr>
        </tbody>
    </table>
</p>
```

So we are ready to create the method to render that HTML in a PDF document and deliverit to the browser for visualization, wrapping it into an HTTPResponse. 

```python
from django.shortcuts import HttpResponse
from django.template.loader import get_template, render_to_string

from fpdf import FPDF, HTMLMixin


class HtmlPdf(FPDF, HTMLMixin):
    pass


def print_pdf(request):    
    pdf = HtmlPdf()
    pdf.add_page()
    pdf.write_html(render_to_string('pdf/example.html'))
  
    response = HttpResponse(pdf.output(dest='S').encode('latin-1'))
    response['Content-Type'] = 'application/pdf'

    return response
```

Creating a class with the objects FPDF and HTMLMixin will make the trick so that the *write_html* method can be used within the FPDF logic. This method requires a string containing the HTML code, therefore, we use Django's *render_to_string* to convert into a string the Template object open by the *loader*. 

As the documentation states, when using Python 3.x you need to add ```.encode('latin-1') ``` to the method output in order to render correctly.

We are just missing a way to call *print_pdf* and we can do that through a new entry in the urls.py

```python
from django.urls import path

from myapp import views as web_views

urlpatterns = [
    path(
        "pdf/example",
        web_views.print_pdf,
        name="print_pdf",
    ),
]
```

Well if you have arrived this far and have a running Django instance, for example using Django's functionality to run a server, you will be able to access the pdf calling:
```bash
http://127.0.0.1:8000/pdf/example
```

As said before this is a basic example that can used to render a HTML into a PDF and display it in the browser, however, it is possible to save the file locally, just changing the pdf.output syntaxis:
```python
target = os.path.join(BASE_DIR, 'my_app', 'pdfs')
pdf.output(target + '/example.pdf', 'F')
```

##Notes:
You still have a long journey to create a full pdf file that can be presented as a report from your application, for that reason I would suggest you to dive into the documentation of [PyFDPF](https://github.com/reingart/pyfpdf) to discover more functionality.