Files
2025-11-30 08:30:10 +08:00

10 KiB

Tables Reference

Comprehensive guide to creating and styling tables in ReportLab.

Basic Table Creation

from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors

# Simple data (list of lists or tuples)
data = [
    ['Header 1', 'Header 2', 'Header 3'],
    ['Row 1, Col 1', 'Row 1, Col 2', 'Row 1, Col 3'],
    ['Row 2, Col 1', 'Row 2, Col 2', 'Row 2, Col 3'],
]

# Create table
table = Table(data)

# Add to story
story.append(table)

Table Constructor

table = Table(
    data,                    # Required: list of lists/tuples
    colWidths=None,          # List of column widths or single value
    rowHeights=None,         # List of row heights or single value
    style=None,              # TableStyle object
    splitByRow=1,            # Split across pages by rows (not columns)
    repeatRows=0,            # Number of header rows to repeat
    repeatCols=0,            # Number of header columns to repeat
    rowSplitRange=None,      # Tuple (start, end) of splittable rows
    spaceBefore=None,        # Space before table
    spaceAfter=None,         # Space after table
    cornerRadii=None,        # [TL, TR, BL, BR] for rounded corners
)

Column Widths

from reportlab.lib.units import inch

# Equal widths
table = Table(data, colWidths=2*inch)

# Different widths per column
table = Table(data, colWidths=[1.5*inch, 2*inch, 1*inch])

# Auto-calculate widths (default)
table = Table(data)

# Percentage-based (of available width)
table = Table(data, colWidths=[None, None, None])  # Equal auto-sizing

Cell Content Types

Text and Newlines

# Newlines work in cells
data = [
    ['Line 1\nLine 2', 'Single line'],
    ['Another\nmulti-line\ncell', 'Text'],
]

Paragraph Objects

from reportlab.platypus import Paragraph
from reportlab.lib.styles import getSampleStyleSheet

styles = getSampleStyleSheet()

data = [
    [Paragraph("Formatted <b>bold</b> text", styles['Normal']),
     Paragraph("More <i>italic</i> text", styles['Normal'])],
]

table = Table(data)

Images

from reportlab.platypus import Image

data = [
    ['Description', Image('logo.png', width=1*inch, height=1*inch)],
    ['Product', Image('product.jpg', width=2*inch, height=1.5*inch)],
]

table = Table(data)

Nested Tables

# Create inner table
inner_data = [['A', 'B'], ['C', 'D']]
inner_table = Table(inner_data)

# Use in outer table
outer_data = [
    ['Label', inner_table],
    ['Other', 'Content'],
]

outer_table = Table(outer_data)

TableStyle

Styles are applied using command lists:

from reportlab.platypus import TableStyle
from reportlab.lib import colors

style = TableStyle([
    # Command format: ('COMMAND', (startcol, startrow), (endcol, endrow), *args)
    ('GRID', (0, 0), (-1, -1), 1, colors.black),  # Grid over all cells
    ('BACKGROUND', (0, 0), (-1, 0), colors.grey),  # Header background
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),  # Header text color
])

table = Table(data)
table.setStyle(style)

Cell Coordinate System

  • Columns and rows are 0-indexed: (col, row)
  • Negative indices count from end: -1 is last column/row
  • (0, 0) is top-left cell
  • (-1, -1) is bottom-right cell
# Examples:
(0, 0), (2, 0)      # First three cells of header row
(0, 1), (-1, -1)    # All cells except header
(0, 0), (-1, -1)    # Entire table

Styling Commands

Text Formatting

style = TableStyle([
    # Font name
    ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),

    # Font size
    ('FONTSIZE', (0, 0), (-1, -1), 10),

    # Text color
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
    ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),

    # Combined font command
    ('FONT', (0, 0), (-1, 0), 'Helvetica-Bold', 12),  # name, size
])

Alignment

style = TableStyle([
    # Horizontal alignment: LEFT, CENTER, RIGHT, DECIMAL
    ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
    ('ALIGN', (0, 1), (0, -1), 'LEFT'),      # First column left
    ('ALIGN', (1, 1), (-1, -1), 'RIGHT'),    # Other columns right

    # Vertical alignment: TOP, MIDDLE, BOTTOM
    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
    ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'),   # Header bottom-aligned
])

Cell Padding

style = TableStyle([
    # Individual padding
    ('LEFTPADDING', (0, 0), (-1, -1), 12),
    ('RIGHTPADDING', (0, 0), (-1, -1), 12),
    ('TOPPADDING', (0, 0), (-1, -1), 6),
    ('BOTTOMPADDING', (0, 0), (-1, -1), 6),

    # Or set all at once by setting each
])

Background Colors

style = TableStyle([
    # Solid background
    ('BACKGROUND', (0, 0), (-1, 0), colors.blue),
    ('BACKGROUND', (0, 1), (-1, -1), colors.lightgrey),

    # Alternating row colors
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightblue]),

    # Alternating column colors
    ('COLBACKGROUNDS', (0, 0), (-1, -1), [colors.white, colors.lightgrey]),
])

Gradient Backgrounds

from reportlab.lib.colors import Color

style = TableStyle([
    # Vertical gradient (top to bottom)
    ('BACKGROUND', (0, 0), (-1, 0), colors.blue),
    ('VERTICALGRADIENT', (0, 0), (-1, 0),
     [colors.blue, colors.lightblue]),

    # Horizontal gradient (left to right)
    ('HORIZONTALGRADIENT', (0, 1), (-1, 1),
     [colors.red, colors.yellow]),
])

Lines and Borders

style = TableStyle([
    # Complete grid
    ('GRID', (0, 0), (-1, -1), 1, colors.black),

    # Box/outline only
    ('BOX', (0, 0), (-1, -1), 2, colors.black),
    ('OUTLINE', (0, 0), (-1, -1), 2, colors.black),  # Same as BOX

    # Inner grid only
    ('INNERGRID', (0, 0), (-1, -1), 0.5, colors.grey),

    # Directional lines
    ('LINEABOVE', (0, 0), (-1, 0), 2, colors.black),   # Header border
    ('LINEBELOW', (0, 0), (-1, 0), 1, colors.black),   # Header bottom
    ('LINEBEFORE', (0, 0), (0, -1), 1, colors.black),  # Left border
    ('LINEAFTER', (-1, 0), (-1, -1), 1, colors.black), # Right border

    # Thickness and color
    ('LINEABOVE', (0, 1), (-1, 1), 0.5, colors.grey),  # Thin grey line
])

Cell Spanning

data = [
    ['Spanning Header', '', ''],           # Span will merge these
    ['A', 'B', 'C'],
    ['D', 'E', 'F'],
]

style = TableStyle([
    # Span 3 columns in first row
    ('SPAN', (0, 0), (2, 0)),

    # Center the spanning cell
    ('ALIGN', (0, 0), (2, 0), 'CENTER'),
])

table = Table(data)
table.setStyle(style)

Important: Cells that are spanned over must contain empty strings ''.

Advanced Spanning Examples

# Span multiple rows and columns
data = [
    ['A', 'B', 'B', 'C'],
    ['A', 'D', 'E', 'F'],
    ['A', 'G', 'H', 'I'],
]

style = TableStyle([
    # Span rows in column 0
    ('SPAN', (0, 0), (0, 2)),  # Merge A cells vertically

    # Span columns in row 0
    ('SPAN', (1, 0), (2, 0)),  # Merge B cells horizontally

    ('GRID', (0, 0), (-1, -1), 1, colors.black),
])

Special Commands

Rounded Corners

table = Table(data, cornerRadii=[5, 5, 5, 5])  # [TL, TR, BL, BR]

# Or in style
style = TableStyle([
    ('ROUNDEDCORNERS', [10, 10, 0, 0]),  # Rounded top corners only
])

No Split

Prevent table from splitting at specific locations:

style = TableStyle([
    # Don't split between rows 0 and 2
    ('NOSPLIT', (0, 0), (-1, 2)),
])

Split-Specific Styling

Apply styles only to first or last part when table splits:

style = TableStyle([
    # Style for first part after split
    ('LINEBELOW', (0, 'splitfirst'), (-1, 'splitfirst'), 2, colors.red),

    # Style for last part after split
    ('LINEABOVE', (0, 'splitlast'), (-1, 'splitlast'), 2, colors.blue),
])

Repeating Headers

# Repeat first row on each page
table = Table(data, repeatRows=1)

# Repeat first 2 rows
table = Table(data, repeatRows=2)

Complete Examples

Styled Report Table

from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
from reportlab.lib.units import inch

data = [
    ['Product', 'Quantity', 'Unit Price', 'Total'],
    ['Widget A', '10', '$5.00', '$50.00'],
    ['Widget B', '5', '$12.00', '$60.00'],
    ['Widget C', '20', '$3.00', '$60.00'],
    ['', '', 'Subtotal:', '$170.00'],
]

table = Table(data, colWidths=[2.5*inch, 1*inch, 1*inch, 1*inch])

style = TableStyle([
    # Header row
    ('BACKGROUND', (0, 0), (-1, 0), colors.darkblue),
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
    ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTSIZE', (0, 0), (-1, 0), 12),
    ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
    ('BOTTOMPADDING', (0, 0), (-1, 0), 12),

    # Data rows
    ('BACKGROUND', (0, 1), (-1, -2), colors.beige),
    ('GRID', (0, 0), (-1, -2), 0.5, colors.grey),
    ('ALIGN', (1, 1), (-1, -1), 'RIGHT'),
    ('ALIGN', (0, 1), (0, -1), 'LEFT'),

    # Total row
    ('BACKGROUND', (0, -1), (-1, -1), colors.lightgrey),
    ('LINEABOVE', (0, -1), (-1, -1), 2, colors.black),
    ('FONTNAME', (2, -1), (-1, -1), 'Helvetica-Bold'),
])

table.setStyle(style)

Alternating Row Colors

data = [
    ['Name', 'Age', 'City'],
    ['Alice', '30', 'New York'],
    ['Bob', '25', 'Boston'],
    ['Charlie', '35', 'Chicago'],
    ['Diana', '28', 'Denver'],
]

table = Table(data, colWidths=[2*inch, 1*inch, 1.5*inch])

style = TableStyle([
    # Header
    ('BACKGROUND', (0, 0), (-1, 0), colors.darkslategray),
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
    ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),

    # Alternating rows (zebra striping)
    ('ROWBACKGROUNDS', (0, 1), (-1, -1),
     [colors.white, colors.lightgrey]),

    # Borders
    ('BOX', (0, 0), (-1, -1), 2, colors.black),
    ('LINEBELOW', (0, 0), (-1, 0), 2, colors.black),

    # Padding
    ('LEFTPADDING', (0, 0), (-1, -1), 12),
    ('RIGHTPADDING', (0, 0), (-1, -1), 12),
    ('TOPPADDING', (0, 0), (-1, -1), 6),
    ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
])

table.setStyle(style)

Best Practices

  1. Set colWidths explicitly for consistent layout
  2. Use repeatRows for multi-page tables with headers
  3. Apply padding for better readability (especially LEFTPADDING and RIGHTPADDING)
  4. Use ROWBACKGROUNDS for alternating colors instead of styling each row
  5. Put empty strings in cells that will be spanned
  6. Test page breaks early with realistic data amounts
  7. Use Paragraph objects in cells for complex formatted text
  8. Set VALIGN to MIDDLE for better appearance with varying row heights
  9. Keep tables simple - complex nested tables are hard to maintain
  10. Use consistent styling - define once, apply to all tables