How to Repeat Table Column Headings over Page Breaks in PDF output from ReportLab

16,959

Solution 1

From the documentation (yes, I know, but it's sometimes hard to locate this stuff in the manual):

The repeatRows argument specifies the number of leading rows that should be repeated when the Table is asked to split itself.

So when you create the table, this is one of the arguments you can pass, and it will turn the first n rows into header rows that repeat. You'll find this part of the text on page 77, but the section relating to creating a Table starts on page 76.

http://www.reportlab.com/docs/reportlab-userguide.pdf

Solution 2

This is the code I developed, after following Gordon's advice to reconsider using repeatRows, and it works!

from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Frame, Spacer
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import A3, A4, landscape, portrait
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.pdfgen import canvas

pdfReportPages = "C:\\Temp\\test.pdf"
doc = SimpleDocTemplate(pdfReportPages, pagesize=A4)

# container for the "Flowable" objects
elements = []
styles=getSampleStyleSheet()
styleN = styles["Normal"]

# Make heading for each column and start data list
column1Heading = "COLUMN ONE HEADING"
column2Heading = "COLUMN TWO HEADING"
# Assemble data for each column using simple loop to append it into data list
data = [[column1Heading,column2Heading]]
for i in range(1,100):
    data.append([str(i),str(i)])

tableThatSplitsOverPages = Table(data, [6 * cm, 6 * cm], repeatRows=1)
tableThatSplitsOverPages.hAlign = 'LEFT'
tblStyle = TableStyle([('TEXTCOLOR',(0,0),(-1,-1),colors.black),
                       ('VALIGN',(0,0),(-1,-1),'TOP'),
                       ('LINEBELOW',(0,0),(-1,-1),1,colors.black),
                       ('BOX',(0,0),(-1,-1),1,colors.black),
                       ('BOX',(0,0),(0,-1),1,colors.black)])
tblStyle.add('BACKGROUND',(0,0),(1,0),colors.lightblue)
tblStyle.add('BACKGROUND',(0,1),(-1,-1),colors.white)
tableThatSplitsOverPages.setStyle(tblStyle)
elements.append(tableThatSplitsOverPages)

doc.build(elements)

Solution 3

Use the repeatRows=1 when you create the Table...

from reportlab.platypus import Table 
Table(data,repeatRows=1)

I always like to have something you can cut & paste into a .py file to run and test. So here it is...

import os
import pandas as pd
import numpy as np
import reportlab.platypus 
import reportlab.lib.styles
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.lib.pagesizes import letter, landscape

reportoutputfilepath = os.path.join('.\\test.pdf')

pdf_file = reportlab.platypus.SimpleDocTemplate(
                            reportoutputfilepath,
                            pagesize=landscape(letter),
                            rightMargin=10,
                            leftMargin=10,
                            topMargin=38,
                            bottomMargin=23
                    )
ts_tables = [
         ('ALIGN', (4,0), (-1,-1), 'RIGHT'),
         ('LINEBELOW', (0,0), (-1,0), 1, colors.purple),
         ('FONT', (0,0), (-1,0), 'Times-Bold'),
         ('LINEABOVE', (0,-1), (-1,-1), 1, colors.purple),
         ('FONT', (0,-1), (-1,-1), 'Times-Bold'),
         ('BACKGROUND',(1,1),(-2,-2),colors.white),
         ('TEXTCOLOR',(0,0),(1,-1),colors.black),
         ('FONTSIZE', (0,0),(-1,-1), 8), 
         ]

df = pd.DataFrame(np.random.randint(0,1000,size=(1000, 4)), columns=list('ABCD'))
lista = [df.columns[:,].values.astype(str).tolist()] + df.values.tolist()

#Here is where you put repeatRows=1
table = reportlab.platypus.Table(lista, colWidths=(20*mm, 20*mm, 20*mm, 20*mm),repeatRows=1)
table_style = reportlab.platypus.TableStyle(ts_tables)
table.setStyle(table_style)
elements = []
elements.append(table)

# Build the PDF
pdf_file.build(elements)
print reportoutputfilepath
Share:
16,959
PolyGeo
Author by

PolyGeo

Community Moderator for Geographic Information Systems SE and Moderator Pro Tem for Genealogy & Family History SE. Freelance ArcGIS Specialist with 30+ years Esri software experience, working principally with ArcGIS Pro, ArcGIS Desktop, ArcPy/Python, Geoprocessing (including Geoprocessing Services and ModelBuilder), Data Driven Pages, Zoology & Genealogy. Author of: ArcGIS Pro, ArcPy and ArcGIS Desktop eLearning videos which are available via Udemy Introductory to Advanced level ArcPy, ArcGIS Pro and ArcGIS Desktop training courses which are available Instructor-led or Self-paced - see PolyGeo website Available for commercial ArcGIS consulting, training and support assignments, and willing to provide quotations to tackle any intractable ArcPy, ArcGIS Pro and ArcGIS Desktop problems which appear on GIS Stack Exchange when they may be unsuitable to resolving expediently using its focused Question & Answer format.

Updated on June 06, 2022

Comments

  • PolyGeo
    PolyGeo about 2 years

    I'm using ReportLab to write tables in PDF documents and am very pleased with the results (despite not having a total grasp on flowables just yet).

    However, I have not been able to figure out how to make a table that spans a page break have its column headings repeated.

    The code below creates a test.pdf in C:\Temp that has a heading row followed by 99 rows of data.

    The heading row looks great on the first page but I would like that to repeat at the top of the second and third pages.

    I'm keen to hear of any approaches that have been used to accomplish that using the SimpleDocTemplate.

    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Frame, Spacer
    from reportlab.lib import colors
    from reportlab.lib.units import cm
    from reportlab.lib.pagesizes import A3, A4, landscape, portrait
    from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
    from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
    from reportlab.pdfgen import canvas
    
    pdfReportPages = "C:\\Temp\\test.pdf"
    doc = SimpleDocTemplate(pdfReportPages, pagesize=A4)
    
    # container for the "Flowable" objects
    elements = []
    styles=getSampleStyleSheet()
    styleN = styles["Normal"]
    
    # Make heading for each column
    column1Heading = Paragraph("<para align=center>COLUMN ONE HEADING</para>",styles['Normal'])
    column2Heading = Paragraph("<para align=center>COLUMN TWO HEADING</para>",styles['Normal'])
    row_array = [column1Heading,column2Heading]
    tableHeading = [row_array]
    tH = Table(tableHeading, [6 * cm, 6 * cm])            # These are the column widths for the headings on the table
    tH.hAlign = 'LEFT'
    tblStyle = TableStyle([('TEXTCOLOR',(0,0),(-1,-1),colors.black),
                           ('VALIGN',(0,0),(-1,-1),'TOP'),
                           ('BOX',(0,0),(-1,-1),1,colors.black),
                           ('BOX',(0,0),(0,-1),1,colors.black)])
    tblStyle.add('BACKGROUND',(0,0),(-1,-1),colors.lightblue)
    tH.setStyle(tblStyle)
    elements.append(tH)
    
    # Assemble rows of data for each column
    for i in range(1,100):
        column1Data = Paragraph("<para align=center> " + "Row " + str(i) + " Column 1 Data" + "</font> </para>",styles['Normal'])
        column2Data = Paragraph("<para align=center> " + "Row " + str(i) + " Column 2 Data" + "</font> </para>",styles['Normal'])
        row_array = [column1Data,column2Data]
        tableRow = [row_array]
        tR=Table(tableRow, [6 * cm, 6 * cm])   
        tR.hAlign = 'LEFT'
        tR.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),colors.white),
                                ('TEXTCOLOR',(0,0),(-1,-1),colors.black),
                                ('VALIGN',(0,0),(-1,-1),'TOP'),
                                ('BOX',(0,0),(-1,-1),1,colors.black),
                                ('BOX',(0,0),(0,-1),1,colors.black)]))
        elements.append(tR)
        del tR
    
    elements.append(Spacer(1, 0.3 * cm))
    
    doc.build(elements)
    
  • PolyGeo
    PolyGeo over 12 years
    Thanks Gordon - pointing me back at the manual has led me to being able to develop a much simpler and working code example that I will add as an answer below.
  • PolyGeo
    PolyGeo about 11 years
    Thanks for the suggestion but in my case I am not using CSS - unless that is just happening in the background.