An Intro to pyfpdf – A Simple Python PDF Generation Library

Posted by Mike on July 10th, 2012 filed in Python

Today we’ll be looking at a simple PDF generation library called pyfpdf, a port of FPDF which is a php library. This is not a replacement for Reportlab, but it does give you more than enough to create simple PDFs and may meet your needs. Let’s take a look and see what it can do!

Installing pyfpdf

Sadly there is no or eggs that allow this library to be easily installed. Instead you’ll have to download it, unzip it and copy the folder into your Python’s site-packages folder. The latest version when unzipped has named the folder “pyfpdf-1.54b”, so you’ll need to rename that to “pyfpdf” if you expect to follow the examples in their tutorial or in this one. You may want to just copy it to a virtualenv instead of putting it in your core Python installation, but that’s up to you.

Taking pyfpdf for a Test Drive

As with any new library, you need to actually write some code to see how it works. Here’s one of the simplest scripts you can make that will create a PDF:

import pyfpdf
pdf = pyfpdf.FPDF(format='letter')
pdf.set_font("Arial", size=12)
pdf.cell(200, 10, txt="Welcome to Python!", align="C")

Note that when we initialize our FPDF object we need to tell it that we want the result to be “letter” size. It defaults to “A4″. Next we need to add a page, set the font and put some text in. The pdf.cell call was a little unintuitive. The first two arguments are width and height and kind of specify the location of the text you pass in. The align parameter only takes single letter abbreviations and you’ll have to look in the source to figure those out. In this case, we’re centering the text by passing it a “C”. The last line accepts two parameters: the pdf name and the destination. If there’s no destination present, then the PDF is output to the same directory that the script is run in.

What if you wanted to add another line? Well you can add more text to the end if you edit how big the cell is and just create another cell. If you need a line break, then you can change the cod to the following:

import pyfpdf
pdf = pyfpdf.FPDF(format='letter')
pdf.set_font("Arial", size=12)
pdf.cell(200, 10, txt="Welcome to Python!", ln=1, align="C")
pdf.cell(200,10,'Powered by FPDF',0,1,'C')

Which results in the following PDF: tutorial.pdf

Adding Bling with Headers, Footers and PageBreaks

The tutorial that is included on pyfpdf’s Google Code site shows how to do headers, footers and page breaks. It doesn’t run due to a method name that must have changed and the code is written using “this” instead of “self”, so I rewrote it and cleaned it up a bit. Here’s the code:

import pyfpdf
class MyPDF(pyfpdf.FPDF):
    def header(self):
        Header on each page
        # insert my logo
        self.image("logo.png", x=10, y=8, w=23)
        # position logo on the right
        # set the font for the header, B=Bold
        self.set_font("Arial", style="B", size=15)
        # page title
        self.cell(40,10, "Python Rules!", border=1, ln=0, align="C")
        # insert a line break of 20 pixels
    def footer(self):
        Footer on each page
        # position footer at 15mm from the bottom
        # set the font, I=italic
        self.set_font("Arial", style="I", size=8)
        # display the page number and center it
        pageNum = "Page %s/{nb}" % self.page_no()
        self.cell(0, 10, pageNum, align="C")
if __name__ == "__main__":
    pdf = MyPDF()
    pdf.set_font("Times", size=12)
    # put some lines on the page
    for i in range(1, 50):
        pdf.cell(0, 10, "Line number %s" % i, border=0, ln=1)

Which creates the following PDF: tutorial2.pdf

This time around, we create a subclass of FPDF and override its header and footer methods as they don’t actually do anything anyway (i.e. they’re stubs). In our header, we create an image object and pass in a logo file as well as the x/y location and the width (23) of the logo. You can also pass a height too if you care about keeping the aspect ratio sane. Then we position it and insert a string of text for the title. Finally, we put in a line break.

The footer is set up to be 15 mm from the bottom. It’s also in 8pt Arial Italic. The error in the official tutorial is that it calls self.PageNo(), but that doesn’t exist. However there is a page_no method that seems to replace it, so I used that. Then at the bottom of the script, we actually create our pdf object and tell it to write a bunch of lines. If you run this script, you should get a 3-page document.

Using pyfpdf to Generate a PDF from HTML

One of my readers pointed out that pyfpdf can also generate a PDF from basic HTML. I don’t know how I missed that, but it’s true! You can! You can read about it on the project’s wiki. I am reproducing a slightly modified example below:

html = """
<H1 align="center">html2fpdf</H1>
<h2>Basic usage</h2>
<p>You can now easily print text while 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 hyperlinks
like this <A HREF="">www.mousevspython.comg</A>,
or include a hyperlink in an image. Just click on the one below.<br>
<A HREF=""><img src="tutorial/logo.png" width="150" height="150"></A>
<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>
<tr><td>cell 1</td><td>cell 2</td></tr>
<tr><td>cell 2</td><td>cell 3</td></tr>
from pyfpdf import FPDF, HTMLMixin
class MyFPDF(FPDF, HTMLMixin):
#First page

Which will create the following PDF: html.pdf

Wrapping Up

There’s one more tutorial on their website that talks about colors and line breaks, but I’ll leave that for you to do as an exercise. I didn’t see anything in here about drawing, inserting tables or graphs, custom font embedding or the many other features that are available in Reportlab, but then again this is supposed to be a simple PDF generation library. If you need more advanced features, then you should certainly look at Reportlab or even some of the offshoot projects that are based on it (rst2pdf or xhtml2pdf come to mind).

Further Reading

Source Code

Print Friendly

  • Kurt

    thank you very muyh for this nice tutorial!

  • Pingback: Visto nel Web – 35 « Ok, panico()

  • Pingback: 七周七语言之Python 课后作业 | TopGeek()

  • hunterji

    Thanks for this. It was really helpful. It looks like you can now install via pip or easy_install.
    “pip install fpdf” did the trick for me.

  • Ramon

    Thanks. I saw that post. I hoped you found a way to use Pyfpdf with Python 3. Any workaround (not using Python 2.X)?

  • Mike Driscoll

    Not that I’m aware of. I would ask the developers of the library when you can expect Py3 support. Mariano and Massimo are pretty receptive guys.

  • mark2741

    Great tutorial! Question: I will have a need soon to dynamically generate hundreds of certificates in PDF format. The data that needs to go onto each PDF (name, class name, date, etc.) will be imported from a csv file. *Ideally* I was hoping that I could use an existing image as a background for the PDF page. The image would just be the certificate (corporate logo, graphics, lines for text), and then just add text cells/fields over top of it so that when each PDF is generated, it is all one single page. Hope that makes sense. I’ll look to try it soon but was hoping you could verify whether that is possible with this library, and if not, recommend another? I’m relatively new to Python.


  • mark2741

    Thanks Mike. I will give it a shot and try ReportLab if I must. ReportLab looks like it will take more up-front study from me to get it to do what I need it to do. But at least I know there is a solution. Thanks again!

  • Leonard Whistler

    I’m using pyfpdf 1.54 to output html tables into a pdf file. Everything is 100% fine except I can’t figure out how to adjust row height or font size inside the table cells.

    Font size is the big issue, if I can make the default font smaller my layout can fit Portrait – instead of Landscape. I have a 10 column layout.

    Thanks for the tutorial.