Python ReportLab: Create PDFs with Text, Tables, and Images
python reportlab create pdf text tables and images: Learn how to generate PDFs with ReportLab in Python: adding text, tables, and images, controlling layout, and handl...
When you need to generate PDF documents programmatically in Python, ReportLab is a common choice. This article shows how to use python reportlab create pdf text tables and images effectively. You'll learn the core flowables—Paragraph, Table, and Image—and how to combine them into a single PDF with proper layout control.
Creating a PDF Document with ReportLab
The quickest way to start is with SimpleDocTemplate from reportlab.platypus. It handles page size, margins, and pagination automatically. Here's a minimal script that creates a PDF with a single paragraph:
from reportlab.lib.pagesizes import A4 from reportlab.platypus import SimpleDocTemplate, Paragraph from reportlab.lib.styles import getSampleStyleSheet doc = SimpleDocTemplate("output.pdf", pagesize=A4) styles = getSampleStyleSheet() story = [] story.append(Paragraph("Hello, ReportLab!", styles["Title"])) doc.build(story)
SimpleDocTemplate accepts a filename and optional pagesize. The story list holds flowables, which are laid out in order. getSampleStyleSheet() provides default styles like Title, Normal, and Heading1. You can customize these later.
Adding Text with Paragraph and Styles
Paragraph is the main flowable for text. It supports inline markup like <b>, <i>, and <font>. For more control, define your own styles with ParagraphStyle:
from reportlab.lib.styles import ParagraphStyle from reportlab.lib.units import inch custom_style = ParagraphStyle( name="Body", fontName="Helvetica", fontSize=10, leading=14, spaceAfter=6, leftIndent=0.5 * inch, ) story.append(Paragraph("This is a <b>bold</b> paragraph with custom spacing.", custom_style))
leading controls line spacing. spaceAfter adds space below the paragraph. Use fontName to switch fonts; ReportLab bundles standard PDF fonts like Helvetica, Times-Roman, and Courier. For non-Latin text, you'll need to register TrueType fonts.
Building Tables with Table and TableStyle
The Table flowable creates a grid from a list of lists. Combine it with TableStyle to control borders, alignment, and background colors:
from reportlab.platypus import Table, TableStyle from reportlab.lib import colors data = [ ["Product", "Price", "Quantity"], ["Widget", "$10.00", 5], ["Gadget", "$24.99", 2], ["Gizmo", "$7.50", 8], ] table = Table(data) table.setStyle(TableStyle([ ("BACKGROUND", (0, 0), (-1, 0), colors.grey), ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), ("ALIGN", (1, 0), (-1, -1), "CENTER"), ("GRID", (0, 0), (-1, -1), 0.5, colors.black), ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ])) story.append(table)
The TableStyle tuple format is (command, start_cell, end_cell, *args). (-1, -1) means the last row and column. Use SPAN to merge cells, VALIGN for vertical alignment, and ROWBACKGROUNDS for alternating row colors. Tables automatically size to content, but you can set colWidths explicitly.
Inserting Images and Controlling Size
The Image flowable requires a path, URL, or file-like object. You must specify width and height; otherwise, ReportLab uses the image's intrinsic size, which may overflow the page. Use reportlab.lib.utils.ImageReader to inspect dimensions before drawing:
from reportlab.platypus import Image from reportlab.lib.utils import ImageReader img = ImageReader("chart.png") iw, ih = img.getSize() # Scale to fit within 6 inches width while preserving aspect ratio max_width = 6 * inch scale = max_width / iw image = Image("chart.png", width=iw * scale, height=ih * scale) story.append(image)
For network images, pass a URL directly, but note that ReportLab will fetch it at build time. If the image fails to load, the build raises an exception. You can also use Image with a file object for streaming data.
Combining Text, Tables, and Images in a Flow
All flowables live in the same story list. ReportLab lays them out sequentially, breaking pages as needed. You can insert Spacer to add vertical space:
from reportlab.platypus import Spacer story.append(Paragraph("Sales Summary", styles["Heading1"])) story.append(table) story.append(Spacer(1, 12)) story.append(Paragraph("Chart below shows quarterly trends.", styles["Normal"])) story.append(image)
Because SimpleDocTemplate uses the platypus engine, it automatically handles page breaks when content exceeds the page height. However, large images or tables may not split gracefully. For tables, you can enable repeatRows=1 to repeat the header row on each page.
Handling Page Breaks and Flowables
To force a page break, add PageBreak() to the story. This is useful when you want a new section to start on a fresh page:
from reportlab.platypus import PageBreak story.append(PageBreak()) story.append(Paragraph("Appendix", styles["Heading1"]))
For more granular control, use Frame and PageTemplate from reportlab.platypus to define multiple layouts. For example, you can create a template with a footer or a two-column layout. This is more advanced but necessary for complex documents like reports with headers and footers.
Performance and Memory Considerations
SimpleDocTemplate builds the entire PDF in memory. For very large documents (hundreds of pages), this can consume significant RAM. If memory is a concern, use BaseDocTemplate with canvasmaker or write directly to a canvas object. However, for typical reports, SimpleDocTemplate is sufficient.
Images are the biggest memory consumers. Embedding a high-resolution photo at full size can bloat both memory and file size. Always scale images to the required display size. If you only need a thumbnail, resize the image before passing it to ReportLab rather than relying on the width and height parameters, which scale the rendered size but not the underlying pixel data.
Common Pitfalls and How to Avoid Them
One frequent issue is forgetting to set width and height on Image, causing an AttributeError or a layout overflow. Always specify dimensions. Another pitfall is using Table with inconsistent row lengths; ReportLab raises an error if rows have different numbers of cells. Ensure all rows have the same column count.
Paragraph text that contains ampersands or angle brackets must be escaped. Use &, <, and > in your strings. Alternatively, use the xml module to escape automatically. For user-generated content, sanitize input to avoid malformed XML.
Finally, when combining multiple flowables, remember that Spacer and PageBreak are flowables too. If you insert a PageBreak inside a table cell, it will not work as expected. Keep flowables at the top level of the story list.
For production use, consider wrapping your build logic in a function that accepts a list of flowables. This makes testing easier and allows you to reuse the same document structure with different data. You can also use reportlab.platypus.doctemplate to handle page templates for headers and footers, which is essential for professional reports.