Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A multiline(paragraph) footer and header in reportlab

What is a best way to have a footer and header in reportlab, that not just a single line, that can be drawed with canvas.drawString in onPage function. Didn`t find a way to put something like Paragraph into header/footer in onPage function. What is the best way to handle this? Is there a way to put a paragraph into footer ?

like image 989
Aldarund Avatar asked Jan 11 '12 22:01

Aldarund


2 Answers

You can use arbitrary drawing commands in the onPage function, so you can just draw a paragraph (see section 5.3 in the reportlab user guide) from your function.

Here is a complete example:

from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Paragraph

styles = getSampleStyleSheet()
styleN = styles['Normal']
styleH = styles['Heading1']

def footer(canvas, doc):
    canvas.saveState()
    P = Paragraph("This is a multi-line footer.  It goes on every page.  " * 5,
                  styleN)
    w, h = P.wrap(doc.width, doc.bottomMargin)
    P.drawOn(canvas, doc.leftMargin, h)
    canvas.restoreState()

doc = BaseDocTemplate('test.pdf', pagesize=letter)
frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height,
               id='normal')
template = PageTemplate(id='test', frames=frame, onPage=footer)
doc.addPageTemplates([template])

text = []
for i in range(111):
    text.append(Paragraph("This is line %d." % i,
                          styleN))
doc.build(text)
like image 101
jochen Avatar answered Nov 14 '22 22:11

jochen


Jochen's answer is great, but I found it incomplete. It works for footers, but not for headers as Reportlab will draw all the flowables on top of the header. You need to be sure the size of the Frame you create excludes the space taken up by the header so flowabls are not printed on top of the header.

Using jochen's code, here is a complete example for headers:

from reportlab.lib.pagesizes import letter, cm
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Paragraph
from functools import partial

styles = getSampleStyleSheet()
styleN = styles['Normal']
styleH = styles['Heading1']

def header(canvas, doc, content):
    canvas.saveState()
    w, h = content.wrap(doc.width, doc.topMargin)
    content.drawOn(canvas, doc.leftMargin, doc.height + doc.topMargin - h)
    canvas.restoreState()

doc = BaseDocTemplate('test.pdf', pagesize=letter)
frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height-2*cm, id='normal')
header_content = Paragraph("This is a multi-line header.  It goes on every page.  " * 8, styleN)
template = PageTemplate(id='test', frames=frame, onPage=partial(header, content=header_content))
doc.addPageTemplates([template])

text = []
for i in range(111):
    text.append(Paragraph("This is line %d." % i, styleN))
doc.build(text)

Pay attention to the decleration of the Frame, it subtracts 2 cm from the height of the frame to allow room for the header. The flowables will be printed within the frame so you can change the size of the frame to allow for various sizes of headers.

I also find that I usually need to pass variables into the header, so I used a partial function assigned to onPage so that the content of the header can be passed in. This way you can have a variable header based on the content of the page.

like image 13
NateB80 Avatar answered Nov 14 '22 23:11

NateB80