ReportLab: Generating PDF reports from Python

The Python community offers dozens of libraries designed to generate graphics, reports, PDF files, images, and charts. It can be somewhat overwhelming choosing which tool is appropriate for the job. In this section, we will experiment with the ReportLab toolkit, which is a Python module that allows us to create PDF files. ReportLab can be integrated with Django to generate dynamic PDFs on-the-fly for the data stored in our Django models.

ReportLab is an open source project available at http://www.reportlab.org. It is a very mature tool and includes binaries for several platforms as well as source code. It also contains extension code written in C, so it's relatively fast. It is possible for ReportLab to insert PNG and GIF image files into PDF output, but in order to do so we must have the Python Imaging Library (PIL) installed. We will not require this functionality in this book, but if you need it for a future project, see the PIL documentation for setup instructions.

The starting point in the ReportLab API is drawing to canvas objects. Canvas objects are exactly what they sound like: blank slates that are accessible using various drawing commands that paint graphics, images, and words. This is sometimes referred to as a low-level drawing interface because generating output is often tedious. If we were creating anything beyond basic reporting output, we would likely want to build our own framework or set of routines on top of these low-level drawing functions.

Drawing to a canvas object is a lot like working with the old LOGO programming language. It has a cursor that we can move around and draw points from one position to another. Mostly, these drawing functions work with two-dimensional (x, y) coordinates to specify starting and ending positions.

This two-dimensional coordinate system in ReportLab is different from the typical system used in many graphics applications. It is the standard Cartesian plane, whose origin (x and y coordinates both equal to 0) begins in the lower-left hand corner instead of the typical upper-right hand corner. This coordinate system is used in most mathematics courses, but computer graphics tools, including HTML and CSS layouts, typically use a different coordinate system, where the origin is in the upper-left.

ReportLab: Generating PDF reports from Python

ReportLab's low-level interface also includes functions to render text to a canvas. This includes support for different fonts and colors. The text routines we will see, however, may surprise you with their relative crudeness. For example, word-wrapping and other typesetting operations are not automatically implemented. ReportLab includes a more advanced set of routines called PLATYPUS, which can handle page layout and typography. Most low-level drawing tools do not include this functionality by default (hence the name "low-level").

This low-level drawing interface is called pdfgen and is located in the reportlab.pdfgen module. The ReportLab User's Guide includes extensive information about its use and a separate API reference is also available.

The ReportLab canvas object is designed to work directly on files. We can create a new canvas from an existing open file object or by simply passing in a file name. The canvas constructor takes as its first argument the filename or an open file object. For example:

from reportlab.pdfgen import canvas

c = canvas.Canvas("myreport.pdf")

Once we obtained a canvas object, we can access the drawing routines as methods on the instance. To draw some text, we can call the drawString method:

c.drawString(250, 250, "Ecommerce in Django")

This command moves the cursor to coordinates (250, 250) and draws the string "Ecommerce in Django". In addition to drawing strings, the canvas object includes methods to create rectangles, lines, circles, and other shapes.

Because PDF was originally designed for printed output, consideration needs to be made for page size. Page size refers to the size of the PDF document if it were to be output to paper. By default, ReportLab uses the A4 standard, but it supports most popular page sizes, including letter, the typical size used in the US. Various page sizes are defined in reportlab.lib.pagesizes. To change this setting for our canvas object, we pass in the pagesize keyword argument to the canvas constructor.

from reportlab.lib.pagesizes import letter 

c = canvas.Canvas('myreport.pdf', pagesize=letter)

Because the units passed to our drawing functions, like rect, will vary according to what page size we're using, we can use ReportLab's units module to precisely control the output of our drawing methods. Units are stored in reportlab.lib.units. We can use the inch unit to draw shapes of a specific size:

from reportlab.lib.units import inch

c.rect(1*inch, 1*inch, 0.5*inch, 1*inch)

The above code fragment draws a rectangle, starting one inch from the bottom and one inch from the left of the page, with sides that are length 0.5 inches and one inch, as shown in the following screenshot:

ReportLab: Generating PDF reports from Python

Not particularly impressive is it? As you can see, using the low-level library routines require a lot of work to generate very little results. Using these routines directly is tedious. They are certainly useful and required for some tasks. They can also act as building blocks for your own, more sophisticated routines.

Building our own library of routines would still be a lot of work. Fortunately ReportLab includes a built-in high-level interface for creating sophisticated report documents quickly. These routines are called PLATYPUS; we mentioned them earlier when talking about typesetting text, but they can do much more.

PLATYPUS is an acronym for "Page Layout and Typography Using Scripts". The PLATYPUS code is located in the reportlab.platypus module. It allows us to create some very sophisticated documents suitable for reporting systems in an e-commerce application.

Using PLATYPUS means we don't have to worry about things such as page margins, font sizes, and word-wrapping. The bulk of this heavy lifting is taken care of by the high-level routines. We can, if we wish, access the low-level canvas routines. Instead we can build a document from a template object, defining and adding the elements (such as paragraphs, tables, spacers, and images) to the document container.

The following example generates a PDF report listing all the products in our Product inventory:

from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.platypus import Paragraph, Spacer
from reportlab.lib import sytles

doc = SimpleDocTemplate("products.pdf")
Catalog = []
header = Paragraph("Product Inventory", styles['Heading1'])    Catalog.append(header) 
style = styles['Normal'] 
for product in Product.objects.all():
    for product in Product.objects.all():
        p = Paragraph("%s" % product.name, style) 
        Catalog.append(p) 
        s = Spacer(1, 0.25*inch)
        Catalog.append(s)
doc.build(Catalog)
ReportLab: Generating PDF reports from Python

The previous code generates a PDF file called products.pdf that contains a header and a list of our product inventory. The output is displayed in the accompanying screenshot.

The document template used above, SimpleDocTemplate, is a class derived from ReportLab's BaseDocTemplate class. It provides for management of the page, the PDF metadata such as document title and author, and can even be used for PDF encryption. By inheriting from BaseDocTemplate, we could create our own document templates that correspond to specific reports or types of report we might generate on a regular basis (a product deals flyer, for example).

In addition to the paragraph and spacer elements we saw in the previous example, documents also support very sophisticated table definitions. We can render the same product inventory in a table format using ReportLab's Table class. Tables can be styled in myriad ways, including setting background and line colors around specific cells, rows or columns, drawing boxes around subsets of cells, and adding images to specific cells. A very simple demonstration is as follows:

doc = SimpleDocTemplate("products.pdf")
Catalog = []
header = Paragraph("Product Inventory", styles['Heading1'])    
Catalog.append(header)
style = styles['Normal']
headings = ('Product Name', 'Product Description')
allproducts = [(p.name, p.description) for p in Product.objects.all()]
t = Table([headings] + allproducts)
t.setStyle(TableStyle(
                   [('GRID', (0,0), (1,-1), 2, colors.black), 
                    ('LINEBELOW', (0,0), (-1,0), 2, colors.red), 
                    ('BACKGROUND', (0, 0), (-1, 0), colors.pink)]))
Catalog.append(t) doc.build(Catalog)

This code defines a two-column table with headings Product Name and Product Description, and then renders the product inventory into each row in the table. We set some style rules for the Table element, including a black grid throughout the table, a red line beneath the headers, and a light pink background on the header row. The PDF output looks like this:

ReportLab: Generating PDF reports from Python

Notice how the padding and centering of the table is handled automatically by the ReportLab package. Now we're getting more sophisticated, but there is a lot more to the ReportLab library. We will end our tour by looking at ReportLab's chart functions.

Creating charts in ReportLab is straightforward, but very powerful. One aspect of the charting module worth mentioning is that it does not have to be output to PDF files, but is general purpose enough to render to other formats. PostScript and bitmap output is available, as well as support for the SVG XML format is now supported by most browsers.

To create charts we need to use ReportLab's Drawing object. This is what ReportLab calls a "Flowable". We've already seen several examples of flowable objects: Paragraph, Spacer, and Table. Flowables can be added to a document template as we've already seen. In addition to the Drawing object, ReportLab provides several classes that represent different kinds of charts: line charts, bar charts, pie charts, and so on. We instantiate an object from one of these classes and manipulate it by adding our data, our axis labels, and so on. Once our chart object is set up, we can add it to our Drawing flowable and then add the Drawing to our document template.

The following example renders a simple line chart with fictitious sales data for our CranStore.com website. It shows sales of cranberry sauce through the months of October, November, and December. The code is as follows:

from reportlab.graphics.charts.linecharts import HorizontalLineChart
from reportlab.graphics.shapes import Drawing
sales = [ (23, 74, 93) ]
months = ['Oct', 'Nov', 'Dec']

doc = SimpleDocTemplate("products.pdf")
Catalog = []
style = styles['Normal']
p = Paragraph("Cranberry Sauce Sales", styles['Heading1']) Catalog.append(p) 
d = Drawing(100, 100) 
cht = HorizontalLineChart()
cht.data = sales
cht.categoryAxis.categoryNames = months
d.add(cht)
Catalog.append(d)
doc.build(Catalog)    

The output in PDF format appears as follows:

ReportLab: Generating PDF reports from Python

Notice that our sales data is a list of tuples. This list could contain more than one element. If it did, then our horizontal line chart would contain two lines: one for each data series defined in the sales list. Most of ReportLab's line chart classes allow for this type of data definition.

Generating a pie chart is equally as simple. We can even add a fancy pop-out on a specific pie slice:

doc = SimpleDocTemplate("products.pdf")
Catalog = []
style = styles['Normal']
p = Paragraph("Cranberry Sauce Sales", styles['Heading1']) Catalog.append(p) 
d = Drawing(100, 125) 
cht = Pie() 
cht.data = sales[0]
cht.labels = months
cht.slices[0].popout = 10
d.add(cht)
Catalog.append(d)
doc.build(Catalog)

When rendered the pie chart appears as follows:

ReportLab: Generating PDF reports from Python

As you can see, ReportLab allows us to quickly generate interesting report output in PDF and other formats. As it is a Python library, we can easily integrate it with our existing Django code. But what if we want to publish our report information to the Web? The next section will explain how to write Django views that generate custom, dynamic PDF output.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset