ActiveState Code

Recipe 546511: "page x of y" with reportlab


This approach at the common "page x of y" problem avoids a double pass (creating the pdf document twice). This is achieved by a customized canvas class which saves the codes into a list instead of creating postscript on pagebreak (showPage). When the document should be saved, we recall each page and draw the pageinfo. The trick is that you can modify the canvas of each page until it is saved (meaning you already know the total pagecount).

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate

class NumberedCanvas(canvas.Canvas):
    def __init__(self, *args, **kwargs):
        canvas.Canvas.__init__(self, *args, **kwargs)
        self._codes = []
    def showPage(self):
        self._codes.append({'code': self._code, 'stack': self._codeStack})
        self._startPage()
    def save(self):
        """add page info to each page (page x of y)"""
        # reset page counter 
        self._pageNumber = 0
        for code in self._codes:
            # recall saved page
            self._code = code['code']
            self._codeStack = code['stack']
            self.setFont("Helvetica", 7)
            self.drawRightString(200*mm, 20*mm,
                "page %(this)i of %(total)i" % {
                   'this': self._pageNumber+1,
                   'total': len(self._codes),
                }
            )
            canvas.Canvas.showPage(self)

# build doc
doc = SimpleDocTemplate("filename.pdf")
... # add your report definition here
doc.build(elements, canvasmaker=NumberedCanvas)

Discussion

We were not satisfied with the solutions we found (e.g. http://eccentric.cx/wordpress/2006/04/04/using-reportlab-forms-for-page-numbers/). It is suboptimal to draw the pageinfo at different times, mainly because you don't know how much space the total pagecount will use up. That makes it difficult to align the pageinfo (particulary on the right side).

Comments

  1. 1. At 3:47 a.m. on 25 nov 2008, breakz.breakz said:

    This is the nicest and cleanest solution I've come across for this type of pagination. The only problem I'm having is that the png image I'm inserting displays when no canvasmaker argument is supplied to doc.build, but doesn't display when I use this canvas. I'm not terribly familiar with ReportLab so any help is appreciated.

    Thanks, Patrick

  2. 2. At 8:58 a.m. on 3 dec 2008, franco said:

    The nicest solution I've found but:

    I've tried this code but I can't see the resulting pdf. Can you post a complete example with pdf output ?

    Thanks Franco

  3. 3. At 2:24 a.m. on 10 dec 2008, Artur said:

    @franco

    Put this line at the end of save() method outside for loop. This actually saves pdf to file.

    self._doc.SaveToFile(self._filename, self)

    Artur

  4. 4. At 7:16 a.m. on 6 jul 2009, Hasnain Lakhani said:

    I'm having the same problem as the first poster. When I use image flowables with this canvas, the space is left for the image (i.e. there is a gap of the proper size), but the image itself is not shown. Does anyone have a fix for that?

  5. 5. At 9:48 a.m. on 6 jul 2009, Vinay Sajip said:

    See the improved Recipe 576832 which should work with images (a test is included as part of the recipe).

Sign in to comment