A multiline(paragraph) footer and header in reportlab

前端 未结 4 1740
一向
一向 2020-12-15 20:38

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

相关标签:
4条回答
  • 2020-12-15 20:54

    I know this is a bit old but I have encountered this problem and was able to solve it. When you have more than one page in your PDF and want to have the footer/header on every page, You have to use NextPageTemplate('template_id'). I am only writing the relevant code as the rest is shown in @jochen example above.

    In my case, I was using PageBreak() and it took me a while to understand why I was only getting the footer in the first page.

    from reportlab.platypus import Paragraph, PageBreak, PageTemplate, Frame, NextPageTemplate
    
    frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
    template = PageTemplate(id='footer', onPage=footer, frames=[frame])
    
    
    # add a NextPageTemplate before a PageBreak to have the footer in the next page
    
    text.append(Paragraph('some text', style)),
    text.append(NextPageTemplate('footer')), # this will make the footer to be on the next page if exists
    text.append(PageBreak())
    doc.build(text)
    
    0 讨论(0)
  • 2020-12-15 20:55

    Additional Approach for adding the header or footer on all pages: there are arguments for the build method to do this.

    Do not use the frame and template in the answer by jochen. In the last line, use

    doc.build(text, onFirstPage=footer, onLaterPages=footer)
    

    the rest of the approach will be the same as from jochen.

    0 讨论(0)
  • 2020-12-15 20:59

    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)
    
    0 讨论(0)
  • 2020-12-15 21:01

    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.

    0 讨论(0)
提交回复
热议问题