Welcome, guest | Sign In | My Account | Store | Cart

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, 31 lines
 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)

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).

6 comments

breakz.breakz 15 years, 4 months ago  # | flag

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

franco 15 years, 3 months ago  # | flag

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

Artur Przybyla 15 years, 3 months ago  # | flag

@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

Hasnain Lakhani 14 years, 8 months ago  # | flag

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?

Vinay Sajip 14 years, 8 months ago  # | flag

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

Prabhakar 7 years, 4 months ago  # | flag

I can create a pdf with clickable bookmarks in the table of contents (toc), or I can create a pdf with "x of y" page numbering, but table of contents clickable appears to break when I try to do both.

Created by hermes tresmegistos on Tue, 12 Feb 2008 (PSF)
Python recipes (4591)
hermes tresmegistos's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks