Online ReportLab Compiler

Create PDF files from Python in your browser, with paragraphs, styled tables, charts, page numbers and canvas drawing.

Python
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, Image
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
import matplotlib.pyplot as plt

# create a plot
plt.plot([1,2,3],[2,4,6])
plt.title("Amplification")
plt.savefig("plot.png")
plt.close()

styles = getSampleStyleSheet()

data = [
    ["Unknown 1", "Unknown 2", "ΔCq"],
    ["3.71", "3.54", "0.17"]
]

table = Table(data)

elements = [
    Paragraph("CFX Qualification Plate Report", styles['Title']),
    Spacer(1,20),
    table,
    Spacer(1,20),
    Image("plot.png", width=5*inch, height=3.75*inch)
]

doc = SimpleDocTemplate("report.pdf")
doc.build(elements)
print(f"Saved report.pdf with {doc.page} page")
Saved report.pdf with 1 page

ReportLab generates PDF files from Python. People use it for invoices, reports and certificates: documents produced again and again from data. It has two layers: Platypus, which flows paragraphs, tables and images onto pages for you, and a canvas for drawing at exact positions. This page is an online ReportLab compiler: the code runs in your browser, so you can try it without installing anything. Run the example first, then paste any snippet below into a new cell to try it.

What the example does

The code first saves a matplotlib line chart as plot.png. getSampleStyleSheet() returns ready-made paragraph styles such as "Title", "Heading1" and "Normal". The report is a list of flowables: a Paragraph for the title, a Table built from a list of rows, the chart as an Image, and Spacers that add 20 points of space between them. SimpleDocTemplate("report.pdf") sets up the page, and doc.build(elements) places the flowables from top to bottom, starting a new page when one fills up, and writes the file. doc.page is the last page number.

Draw with the canvas

canvas.Canvas draws text and shapes where you tell it. Positions are in points, 72 to an inch, measured from the bottom-left corner of the page. showPage() finishes a page and save() writes the file:

from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas

width, height = letter
c = canvas.Canvas("canvas.pdf", pagesize=letter)
c.setFont("Helvetica-Bold", 24)
c.drawString(inch, height - inch, "Invoice 1024")
c.setFont("Helvetica", 12)
c.drawString(inch, height - 1.4 * inch, "Due within 30 days")
c.setStrokeColorRGB(0.2, 0.4, 0.8)
c.line(inch, height - 1.6 * inch, width - inch, height - 1.6 * inch)
c.rect(inch, inch, 2 * inch, inch)
c.drawRightString(width - inch, inch, "Page 1")
c.showPage()
c.save()

Style a table

A Table has no lines or colours until you give it a TableStyle. Each command covers a range of cells from one (column, row) corner to another, and -1 means the last column or row:

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

rows = [["Quarter", "Sales"], ["Q1", 120], ["Q2", 135], ["Q3", 150]]
table = Table(rows, colWidths=[100, 80])
table.setStyle(TableStyle([
    ("GRID", (0, 0), (-1, -1), 0.5, colors.grey),
    ("BACKGROUND", (0, 0), (-1, 0), colors.lightblue),
    ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
    ("ALIGN", (1, 1), (1, -1), "RIGHT"),
]))
SimpleDocTemplate("table.pdf").build([table])

Add page numbers

build() calls onFirstPage and onLaterPages once per page with the canvas, so they can draw a header or footer:

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

def footer(canvas, doc):
    canvas.setFont("Helvetica", 9)
    canvas.drawRightString(doc.pagesize[0] - 72, 40, f"Page {doc.page}")

styles = getSampleStyleSheet()
story = [Paragraph(f"Paragraph {i}. " + "Some text. " * 40, styles["Normal"])
         for i in range(1, 30)]
doc = SimpleDocTemplate("long.pdf")
doc.build(story, onFirstPage=footer, onLaterPages=footer)
print(doc.page, "pages")

Use other alphabets

The built-in fonts handle Western European text and Greek letters such as the Δ in the example, but print Cyrillic, Chinese, and letters such as the Polish ł or Turkish ş as black squares. Register a TrueType font that has them. DejaVu Sans comes with matplotlib, so it is already here:

import os
import matplotlib
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import SimpleDocTemplate, Paragraph

font_path = os.path.join(matplotlib.get_data_path(), "fonts", "ttf", "DejaVuSans.ttf")
pdfmetrics.registerFont(TTFont("DejaVu", font_path))

style = ParagraphStyle("unicode", fontName="DejaVu", fontSize=14)
SimpleDocTemplate("unicode.pdf").build([Paragraph("Привет, Łódź, İstanbul", style)])

DejaVu Sans has no Chinese, Japanese or Korean characters.

Getting the file

Files your code saves, like report.pdf, appear in the sidebar's Data tab. Running code and inspecting the result is free. Downloading the files requires a Premium plan.

Good to know

  • SimpleDocTemplate uses A4 paper with 1-inch margins unless you pass pagesize=letter from reportlab.lib.pagesizes.
  • Image stretches the picture to the width and height you give. The example uses a 4:3 box because matplotlib saves 640 by 480 pixels.
  • Paragraph text is markup: <b>, <i> and <font color="red"> work, and an unknown tag is dropped without an error, so "x <y> z" prints as "x z". Pass text from your data through xml.sax.saxutils.escape() first.