Numbered Lists (sort of) in Reportlab

So, for the first time, I think I may have stumbled upon something I wanted to do in code for which Google searches didn’t give me a quick and easy answer. (When it’s not quick, it generally because I’m slow to understand.)

So, since I worked out a solution for myself and am more than usually proud of it, here’s what I have.

The challenge:
I’m automatically generating worksheets in PDF format. For them, I’d like to have exercises in a nice numbered list. You know the format, it’s pretty easy to do in most software.

  1. This is an exercise.
  2. So is this.
  3. And here is a blank to fill ____________.

However, even though Reportlab includes numbering in the bullets, I spent most of yesterday evening trying to tweak different indentation settings to get it to do what I want. And it didn’t. (Again, maybe I’m slow to understand.)

The problem was that either the text didn’t wrap right (it should wrap to be beneath the beginning of the text, not to beneath the number) or I couldn’t get the spacing between the number and the content to look right.

My solution:
I wound up using a table for this. It’s not what I wanted and I feel like the way I have it is a bit of a hack, with different methods checking the length of the list that feeds the table, so that I can add lines via a method an arbitrary number of times. Nonetheless, I’m pretty happy with how the result looks.

from reportlab.lib.pagesizes import A4
from reportlab.lib.enums import TA_LEFT
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle, ListStyle
from reportlab.lib.units import cm

# We need some text to use. I happen to like this one
lines=['Sing, Goddess, sing of the rage of Achilles, son of Peleus--',
       'that murderous anger which condemned Achaeans',
       'to countless agonies and threw many warrior souls',
       'deep into Hades, leaving their dead bodies',
       'carrion food for dogs and birds--',
       'all in fulfilment of the will of Zeus.']

style=ParagraphStyle(name='Normal', alignment=TA_LEFT, fontSize=12, leading=18, fontName='Helvetica')
doc=SimpleDocTemplate('iliad.pdf', pageSize=A4)

# Add a title, straightforward enough.
document.append(Paragraph('<b>The Iliad</b> by Homer', style))

# Now, add numbered lines
for line in lines:
    epic.append([Paragraph('<b>'+str(len(epic)+1)+'.</b>', style),
                 Paragraph(line, style)])

t=Table(epic, colWidths=(1.2*cm, None))
    ('VALIGN', (0,0), (-1,-1), 'TOP')


That gives you this result: iliad.pdf.

About it
Obviously, it’s a table which is made up of a list of lists. The various ‘cells’ (entries in one list) need to be wrapped in Paragraph objects in order to get the wrapping and formatting right. (It seems the <b>bold</b> tags are processed by the Paragraph object, if you’re curious.)

As for the TableStyle, that sets the vertical alignment to ‘TOP’ as it defaults to middle, which looks odd when the line it’s numbering wraps.

Lastly, it was interesting — and helpful — to me that the table cells could be numbered with the negative notation that is familiar from things like lists and strings. That is to say that (0,0) is the top left and (-1, -1) is the bottom right, because it counts from the end of the list. That was nice. (I was ready to start doing janky things with len(list) to get it right.)

Maybe I overestimate the problem I solved…
I would be surprised if I’m the only one who has done this, but it is the best I could come up with. I’d be happy to know if I overlooked some obvious solution / reference. How have you (or others) solved the same problem?

From Tree To Shining Tree

In Radio Lab’s latest (lastest in my queue? I’m falling behind) episode, you get to hear about trees communicating, sharing, saving, adapting. I don’t think that any of it is anything that I hadn’t heard before in one place or another, but there wasn’t a moment of it I would have cut… and I was sad when it was over.

Give it a listen! The episode is named From Tree To Shining Tree.


OOP, as you probably already know, stands for ‘Object Oriented Programming.’ There are a million people better qualified to define it than I am, but here is how I get it: instead of just writing a program that runs from start to finish, you write a series of ‘objects’ which interact.

Of course, I first encountered it in Game Maker, when I thought about making a new kind of strategy game. (Spoiler: I still think it’s a great idea, but am working towards it.) I don’t know how Game Maker works now, but then I understood it like this: you created indiviual objects (figures in the game), gave them a sprite (graphical representation) and coded how they interacted with the user and with each other.

One of the tenents of Game Maker programming was programming objects to respond to ‘events.’ Which basically meant that you’d write a block of code to indicate what that object should do when it, for example, colided with a wall, or was attacked by another object, or whatever.

Ever since then, I’ve been really attracted to the idea of OOP, because it seems to reflect how I think about the world. The world is full of these things (people, objects, devices) and we seem to think we know how they function and interact… so doesn’t that lead to the kind of thinking that we could write an object for everything in the world, code a sort of ‘universe’ for them to all exist in, and simulate reality?

I don’t know why I want to write all this down, except that I’ve been thinking about how coding changed my thinking, and this comes to mind. I know that Python’s support for OOP is one of the things that I really enjoy about it, and, while I worry that I’m not using objects ‘correctly’ (is there a correct way), they really impact the way I think about coding — and the world.

Thinking like a programmer

So, a while ago, I asked some programmers who were learning English from me what people least understood about programming. (Because, who cares what they could learn from me, I’m about what I can learn from them!)

The answer I got surprised me. They said that people just didn’t get how well you had to understand a subject to write a good program to support it. In their cases, they wrote software to automate some parts of a civil engineer’s job and felt burdened by what they’d learned about civil engineering. “I was never interested in it,” on of them said, “and now, whenever I pull onto the autobahn, I check if the on ramp is banked the way it should be. Because that’s the part of the code I wrote.”

Coding, for them, wound up being like teaching English for me: learning about all kind of interesting things that you wouldn’t have otherwise learned, but not always completely content with the list of things they were learning.

“And it changes the way you think.” Quickly followed that example. “You start seeing everything in algorithms, in steps. And making things efficient.”

“I can’t watch my wife work at home.” The other chimed in, “she does things wrong.” When I gave him a quizzical look, he said “she adds steps to things that she doesn’t need to add steps to, like cooking.”

The conversation that followed was wide-ranging, from three guys in a room talking about their wives (we didn’t say anything bad, honey, I swear!) to the actual topic at hand. My takeaway, though, was that they were so obsessed with brevity and efficiency in their work, that it started leaking out into their day-to-day life.

The idea is simple: they were writing code that had to be executed in ‘real time’ (the user shouldn’t feel like she ever had to wait) and so it had to be as few steps as possible. After all, a lot of what they did was write stuff that would be applied to a lot of different data points. So, if you can skip two or three steps — in a process that’s going to be repeated a thousand or more times each time the user alters her plans — you’re reducing the risk of the software seeming laggy.

So, naturally, opening the coffee and putting the open container next to the coffee maker seemed like an extra step to them: why wouldn’t you first get the filter ready and then skip the steps of putting down and picking up the coffee? If you were making a thousand pots of coffee per morning, you probably would.

I’ve spent quite a bit of time in my Python playground of late, and I’ve enjoyed most of it. In case you’re wondering, I haven’t reached the point in my programming life where my obsession with speed leaves me criticizing my wife. It’s just lead me to the point where I can barely stand to see myself work.

I don’t pretend to be able to point to any recording of the words in quotes above. And nothing said in the conversation I’m writing about would have been said in especially good English. I’m just going for the gist. I doubt that either of the programmers, were they to read this, would feel misrepresented.