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")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.
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.
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()
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])
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")
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.
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.
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.