This content is also available in: French

Warning, this article is technical… If you don’t care about python development, skip it :)

You want to create pdf reports, send letters or any other action needing pdf documents ?

If you are a python expert, you’ll have surely heard about ReportLab. Yeah, I know, to use it, you need to code it, and when you change your template you have to do everything again.

A solution to that, is to use RML, an xml language done by the company that created reportlab. RML permits to use stylesheets, and to have an xml document that generates the pdf (just like html).

There is a problem with RML. The reference handler, (Reportlab RML) isn’t free (neither as in beer nor as in speech)… But the fabulous guys at Z3C did an open source handler for RML, z3c.rml !

Just one problem : when you create big documents, the process will crawl, take all your ram and die badly. The solution to that aspect ? using PyPDF to join pdf files into one (one template per document part).

Now, here comes the real deal : we will template that xml, just like we would do for html, using an xml templating language : genshi.

To ease that task, I’ve made a little python lib that handles the templating, the merging of various documents into one and much more.

For the most impatient, head directly to the examples ! :)

How to use it ?

Example case : a data table

First, let’s initialize a factory (a class that allows to create various documents and join them in one pdf):

from pyjon.reports import ReportFactory
factory = ReportFactory()

Now let’s create a test.xml file containing our template:

<?xml version="1.0" encoding="iso-8859-1" standalone="no" ?>
<!DOCTYPE document SYSTEM "rml_1_0.dtd">
<document xmlns:py="http://genshi.edgewall.org/">
<template pageSize="(595, 842)" leftMargin="72" showBoundary="0">
  <pageTemplate id="main">
    <frame id="first" x1="1in" y1="1in" width="6.27in" height="9.69in"/>
  </pageTemplate>
</template>
<stylesheet>
  <blockTableStyle id="mynicetable" spaceBefore="12">
    <lineStyle kind="OUTLINE" colorName="black" thickness="0.5"/>
    <blockFont name="Times-Bold" size="6" leading="7" start="0,0" stop="-1,0"/>
    <blockBottomPadding length="1"/>
    <blockBackground colorName="0xD0D0D0" start="0,0" stop="-1,0"/>
    <lineStyle kind="LINEBELOW" colorName="black" start="0,0" stop="-1,0" thickness="0.5"/>
    <!--body section-->
    <blockFont name="Times-Roman" size="6" leading="7" start="0,1" stop="-1,-1"/>
    <blockTopPadding length="1" start="0,1" stop="-1,-1"/>
    <blockBackground colorsByRow="0xD0FFD0;None" start="0,1" stop="-1,-1"/>
    <blockAlignment value="right" start="1,1" stop="-1,-1"/>

    <!-- closing the table when restarting it on next page -->
    <lineStyle kind="LINEBELOW" colorName="black" start="0,splitlast" stop="-1,splitlast" thickness="0.5"/>
  </blockTableStyle>
</stylesheet>
<story>
  <h1>$title</h1>
  <blockTable repeatRows="1" style="mynicetable">
    <tr><td py:for="i in range(10)">Row ${i}</td></tr>
    <tr py:for="line in data"><td py:for="col in line" py:content="col" /></tr>
  </blockTable>
  <para py:content="dummy" />
</story>
</document>

Let’s have a look at this template:

  • The template node permits to choose the document size
  • The stylesheet node permits to define the styling of the document
  • The story nodes contains the document content (just like body in html)
  • Like in html, h1 defines a level 1 title (we print the title var inside)
  • A para is like an html p (a paragraph)
  • The blockTable is the table tag containing rows and columns (td and tr). The py:for attributes define the iterations over list vars
  • The first one (py:for=”i in range(10)”) defines the columns (Row 0 to Row 9)
  • The second (py:for=”line in data”) shows the whole content of the data var (item per item)
  • The last one (py:for=”col in line”) shows the content of each line (with a py:content printing it inside the tag)

Now let’s generate a document with this template:

template = 'test.xml'
testdata = [range(10)] * 100

factory.render_template(
        template_file=template,
        title=u'THE TITLE',
        data=testdata,
        dummy='foo')

We passed various variables and a data list (testdata) containing the numbers 0 to 9, 100 times: [[0, ..., 9], …, [0, ..., 9]].
Please note that all keyword arguments passed to the function (beside template_file or template_string) are passed to the genshi template, and accessible as root variables there.

Needing to add another secion in the document with the same template ? No problem !

factory.render_template(
        template_file=template,
        title=u'THE TITLE 2!',
        data=testdata,
        dummy='bar'
        )

Eventually, we will create a single pdf files, with all the content, finalising the factory (cleaning it):

factory.render_document('test.pdf')
factory.cleanup()

And voilà! It’s as simple as that.

How to install it ?

To install pyjon.reports it’s very simple: just type easy_install pyjon.reports in your console (in a virtual env or globally), and you’ll be able to generate pdf documents in your applications in less than 5 minutes.

If you want to see the source code or contribute, see the bitbucket page: http://bitbucket.org/jon1012/pyjonreports

For more info: