Skip to main content

Posts about rst2pdf (old posts, page 1)

rst2pdf: release fever!

I did a release yesterday, and another today of my rst-to-pdf-without-latex tool. What's new? Here's an incomplete list:

New in 0,4

  • Fixed bullet and item lists indentation/nesting.

  • Implemented citations

  • Working links between footnotes and its references

  • Justification enabled by default

  • Fixed table bug (demo.txt works now)

  • Title and author support in PDF properties

  • Support for document title in header/footer

  • Custom page sizes in stylesheet

New in 0.3

  • Font embedding (use any True Type font in your PDFs)

  • Syntax highlighter using Pygments

  • User's manual

  • External/custom stylesheets

  • Support for page numbers in header/footer

Of course, since I said I would release something every friday, this means I need to find something else to release? ;-)

This friday will see a new rst2pdf release

Following my new policy of one release every friday, in 6 days you will see a rst2pdf release. But not any release: a great release.

What will be new?

  • Support for page number/section names/section numbers in headers and footers.

  • Custom interpreted text roles (that means inline styling ;-)

  • Stylesheets defined in external files. The syntax is JSON which may look a bit strange, but it works great.

  • A Manual!

  • Easy True Type font embedding.

  • Maybe: syntax highlighting directive via pygments. I know I could make it work using the ImageFormatter, but then you can't copy the code. There is a docutils-sandbox project that does exactly what I want.

I intend to call this release 0.3.0, but maybe I will jump higher, since there is not much more left to implement.

Some more rst2pdf love, time-based releases of my code

Since revision #17_ you can display Page numbers in headers and footers (only!) by using this syntax:

.. header::

   This is the header. Page ###Page###

This is the content

.. footer::

   This is the footer. Page ###Page###

It has some issues if your page number is bigger than 99999999999 or your header/footer is a little longer than one line when using the placeholder, because the space required is calculated with the placeholder instead of with the number, but those are really marginal cases.

Next in line, a decent way to define custom stylesheets.

As for "time-based releases", I intend to release a new version of something every friday.

Since I have about a dozen projects in different stages of usability, I expect this will push me a bit more towards showing this stuff instead of it rotting in my hard drive and unknown svn repos.

Creating PDF Reports with Python and Restructured Text

This article is inspired by a thread in the PyAr mailing list. Here´s the original question (translated):

From: Daniel Padula

I need some advice. I need to create an application for schools that takes student data (personal information, subjects, grades, etc) and produces their grade report. I need to create a printed copy, and keep a historic record.

As a first step, I thought on generating them in PDF via reportlab, but I want opinions. For example, I can generate the PDF, print it and regenerate it if I need to reprint it. What other optins do you see? It's basically text with tables. Reportlab? LaTeX? Some other tool?

To this I replied I suggested Restructured Text which if you follow my blog should surprise noone at all ;-)

In this story I will try to bring together all the pieces to turn a chunk of python data into a nice PDF report. Hope it´s useful for someone!

Why not use reportlab directly?

Here's an example I posted in that thread: how to create a PDF with two paragraphs, using restructured text:

This is a paragraph. It has several lines, but what it says does not matter.
I can press enter anywhere, because
it ends only on a blank
line. Like this.

This is another paragraph.

And here's what you need to do in reportlab:

# -*- coding: utf-8 -*-
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
styles = getSampleStyleSheet()
def go():
  doc = SimpleDocTemplate("phello.pdf")
  Story = [Spacer(1,2*inch)]
  style = styles["Normal"]
  p = Paragraph('''This is a paragraph. It has several lines, but what it says does not matter.
I can press enter anywhere, because
it ends when the string ends.''', style)
  p = Paragraph('''This is another paragraph.''', style)


Of course, you could write a program that takes text separated in paragraphs as its input, and creates the reportlab Paragraph elements, puts them in the Story and builds the document.... but then you are reinventing the restructured text parser, only worse!

Restructured text is data. Reportlab programs are code. Data is easier to generate than code.

So, how do you do a report?

You create a file with the data in it, process it via one of the many rst->pdf paths (I suggest my rst2pdf script, but feel free to use the other 9 alternatives).

Suppose you have the following data:

frobtimes = [[1,3],[3,5],[9,8]]

And you want to produce this report:

Frobniz performance

* 1 frobniz: 3 seconds

* 3 frobniz: 5 seconds

* 9 frobniz: 8 seconds

You could do it this way:

print '''Frobniz performance

for ft in frobtimes:
  print '* %d frobniz: %d seconds\n'%(ft[0],ft[1])
And it will work. However, this means you are writing code again! This time, you are reinventing templating


What you want is to use, say, Mako (or whatever). It's going to be better than your homebrew solution anyway. Here's the template for the report:

${title('Frobniz Performance')}

% for ft in frobtimes:
* ${ft[0]} frobniz: $ft[1] seconds

% endfor

This uses a function title defined thus:

title=lambda(text): text+'\n'+'='\*len(text)+'\n\n'

You could generalize it to support multiple heading levels:

title=lambda(text,level): text+'\n'+'=-~_#%^'[level]*len(text)+'\n\n'

Trickier: tables

One very common feature of reports is tables. In fact, it would be more natural to present our frobniz report as a table. The bad news is how tables look like in restructured text:

| Frobniz | Time (seconds) |
|        1|              3 |
|        3|              5 |
|        9|              8 |

Which is very pretty, but not exactly trivial to generate. But don't worry, there is a simple solution for this, too: CSV tables:

.. csv-table:: Frobniz time measurements
   :header: Frobniz,Time(seconds)


Produces this:

Frobniz time measurements









And of course, there is python's csv module if you want to be fancy and avoid trouble with delimiters, escaping and so on:

def table(title,header,data):
  csv_writer = csv.writer(head, dialect='excel')

  head=´:header: %s´head.getvalue()

  csv_writer = csv.writer(body, dialect='excel')
  for row in data:

  return '''.. csv-table:: %s
     :header: %s


will produce neat, ready for use, csv table directives for restructured text.

How would it work?

This python program is really generic. All you need is for it to match a template (an external text file), with data in the form of a bunch of python variables.

But how do we get the data? Well, from a database, usually. But it can come from anywhere. You could be making a report about your bookmarks, or about files in a folder, this is really generic stuff.

What would I use to get the data? I would use JSON in the middle. I would make my report generator take the following arguments:

  1. A mako template name.

  2. A JSON data file.

That way, the program will be completely generic.

So, put all this together, and there's the superduper magical report generator.

Once you get rst, pass it through something to create PDFs, but store only the rst, which is (almost) plain text, searchable, easy to store, and much smaller.

I don't expect such a report generator to be over 50 lines of code, including comments.

Missing pieces

  • While restructured text is almost plain text, there are special characters, which you should escape. That is left as an exercise to the reader ;-)

  • Someone should really write this thing ;-)

Giving rst2pdf some love

Because of a thread in the PyAr list about generating reports from Python, I suggested using ReST and my rst2pdf script.

This caused a few things:

  1. I decided it's a pretty decent piece of code, and it deserves a release. Making a release means I needed to fix the most embarrasing pieces of it. So...

  2. Implemented the class directive, so it can have custom paragraph styles with very little effort.

  3. Did proper command line parsing.

  4. Did proper setuptools script

  5. Uploaded to PyPI

  6. Created a release in Google Code.

So, if you want the simplest way to generate PDF files from a program in the entire pythonic universe... give it a look.

rst2pdf: New and improved

My rst2pdf script has had several things happen to it.

  1. It got another guy working on it: Christoph Zwerschke

  2. It's on googlecode now:

  3. Christoph made a number of improvements:

    • bulleted and enumerated list simplified, use same font as text for bullets and numbers

    • links in table of contents work

    • compress literal sections horizontally so that they always fit on the page

  4. I have integrated hyphenation using wordaxe (works only with reportlab 2.1)

The output using hyphenation is really ugly right now (for example, I get a black square instead of an hyphen) but it's a small step forward.

rst2pdf again

I did a little (very little) more work on rst2pdf 1


  • Headers

  • Footers

  • Footnotes (as endnotes, real footnotes are too much work because you have to reflow the text)

  • External links (http, email)

  • Some styling improvements (meaning: the output is not so painful in the eyes)

  • Paper size support

  • Real separators

Then there are some things you just get because it's done using reportlab:

  • TrueType font embedding (check the example below, and look at the monospaced font)

  • No hyphenation (although there is a project on the web that claims to have done it, I should check it out)

Still broken:

  • No way to put things like page numbers or section names in headers/footers

  • The footnotes are not linked to their contents and viceversa

  • In fact, no internal links work, including title references

  • Tables are pretty broken

And here is the usual version of the rst demo showing improvements.


My tool to convert Rstructured Text to PDF

Done with rst2pdf for now

It works much better than it did last night.

The main missing/broken things are:

  • Tables

  • Links

  • Footnotes/citations

  • Headers/Footers

  • References

  • Table HEaders/Spanning cells

  • Lists that don't start at 1

The rest seems to be in working order and producing decent output already. It can process the ReST demo and it doesn't look bad: check it out

I say, good for half a day of hacking, and will now stop, because it's a weekend and I have a family :-)

But that doesn't mean you can't try it for yourself. Just run it thus:

python myrstfile.txt

And you will get (hopefully) a myrstfile.txt.pdf

Let me know how it works, and if it doesn't and it's not related to one of the things I mentioned above as broken, send me a test file!

Generating PDFs from Restructured text

This has always been possible, going via LaTeX.

However, LaTeX being what it is, you either need to learn it, or you end up with rather plain-looking documents.

While that's ok for a manual, I want to use Restructured Text for everything involving documents.

So, I looked for another solution. Sadly, I could not make the existing rlpdf writer work, sooooo I decided to write my own tool.

Since I intensely dislike the Visitor pattern involved in writing a regular docutils writer, I adapted my old and ended with which just traverses the tree recursively and writes the PDF using ReportLab.

And it took me about 3 hours to make it work:

  • For a limited subset of RST (no footnotes, no links, no decoration)

  • For some subset of tables (no col/row spanning)

  • With limited "styling" (it's mostly there, but I need to write a lot of ReportLab styles.

How well does it work... rather well.

Here's a generated PDF of The RestructuredText Primer

Ignore aesthetics, and consider function, it's pretty good.