Search code examples
pythonreportlab

Add Page Break before adding a Split with Flowable


I have an application that is using reportlab to build a document of tables. What I want to happen is when a flowable (in this case, always a Table) needs to split across pages, it should first add a page break. Thus, a Table should be allowed to split, but any table that is split should always start on a new page. There are multiple Tables in the same document, and if two can fit on the same page without splitting, there should not be a page break.

The closest I have gotten to this is to set allowSplitting to False when initializing the Document. However the issue is when a table exceeds the amount of space it has to fit, it will just fail. If instead of failing it will then wrap, this is what I am looking for.

For instance, this will fail with an error about not having enough space:

from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, inch
from reportlab.platypus import SimpleDocTemplate, Table
from reportlab.lib.styles import getSampleStyleSheet

doc = SimpleDocTemplate("simple_table_grid.pdf", pagesize=letter, allowSplitting=False)
# container for the 'Flowable' objects
elements = []
data2 = []

data = [['00', '01', '02', '03', '04'],
       ['10', '11', '12', '13', '14'],
       ['20', '21', '22', '23', '24'],
       ['30', '31', '32', '33', '34']]

for i in range(100):
  data2.append(['AA', 'BB', 'CC', 'DD', 'EE'])

t1 = Table(data)
t2 = Table(data2)

elements.append(t1)
elements.append(t2)
doc.build(elements)

The first table (t1) will fit fine, however t2 does not. If the allowSplitting is left off, it will fit everything in the doc, however t1 and t2 are on the same page. Because t2 is longer than one page, I would like it to add a page break before it starts, and then to split on the following pages where needed.


Solution

  • One option is to make use of the document height and table height to calculate the correct placement of PageBreak() elements. Document height can be obtained from the SimpleDocTemplate object and the table height can be calculated with the wrap() method.

    The example below inserts a PageBreak() if the available height is less than table height. It then recalculates the available height for the next table.

    from reportlab.lib.pagesizes import letter
    from reportlab.platypus import SimpleDocTemplate, Table, PageBreak
    
    doc = SimpleDocTemplate("simple_table_grid.pdf", pagesize=letter)
    
    # Create multiple tables of various lengths.
    tables = []
    for rows in [10, 10, 30, 50, 30, 10]:
        data = [[0, 1, 2, 3, 4] for _ in range(rows)]
        tables.append(Table(data, style=[('BOX', (0, 0), (-1, -1), 2, (0, 0, 0))]))
    
    # Insert PageBreak() elements at appropriate positions.
    elements = []
    available_height = doc.height
    for table in tables:
        table_height = table.wrap(0, available_height)[1]
        if available_height < table_height:
            elements.extend([PageBreak(), table])
            if table_height < doc.height:
                available_height = doc.height - table_height
            else:
                available_height = table_height % doc.height
        else:
            elements.append(table)
            available_height = available_height - table_height
    
    doc.build(elements)