Ответ Йохена великолепен, но я нашел его неполным. Он работает для нижних колонтитулов, но не для верхних колонтитулов, так как Reportlab будет рисовать все текущие элементы поверх верхнего колонтитула. Необходимо убедиться, что размер создаваемого фрейма исключает пространство, занимаемое заголовком, чтобы надписи заголовка не печатались поверх заголовка.
Используя код Йохена, вот полный пример для заголовков:
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)
Обратите внимание на декляцию фрейма, он вычитает 2 см от высоты фрейма, чтобы освободить место для заголовка. Потоки будут напечатаны внутри фрейма, поэтому вы можете изменить размер фрейма, чтобы учесть различные размеры заголовков.
Я также нахожу, что мне обычно нужно передавать переменные в заголовок, поэтому я использовал частичную функцию, назначенную onPage, чтобы можно было передавать содержимое заголовка. Таким образом, вы можете иметь переменный заголовок на основе содержимого страницы.