Why You Should Stop Creating Reports Manually
Copying data from Excel, pasting it into Word, and then struggling with margins to export a PDF is an incredibly time-consuming process. I once spent an entire morning just creating a sales report, only to have to start over because of a single typo.
If you’re already using Python to crawl data or run automation scripts, your reporting should be automated as well. ReportLab is the key to solving this problem once and for all.
Unlike HTML-to-PDF libraries that often suffer from formatting issues, ReportLab allows you to “draw” directly onto the page. You have absolute control over every pixel. This library is perfect for generating invoices, certificates, or data analysis reports that require high precision.
Environment Setup
It takes less than 10 seconds to install ReportLab via pip. For the best support in chart rendering and image processing, you should also install the Pillow library:
pip install reportlab pillow
Important Note: By default, ReportLab does not support Vietnamese Unicode. Without font configuration, accented characters will appear as error boxes. You need to have a .ttf font file (e.g., Arial or Roboto) ready in your project directory to integrate into your code.
Configuring Core Components
ReportLab offers two approaches: Canvas (manual drawing using x, y coordinates) and Platypus (a high-level layout framework). I recommend using Platypus. It works like stacking content blocks and automatically handles page breaks when data is too long.
1. Handling Fonts and Styles
The first step is to register the font so the report displays correctly. This step helps you avoid 99% of the most common display errors.
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER
# Register font to display Vietnamese
try:
pdfmetrics.registerFont(TTFont('Arial', 'arial.ttf'))
except:
print("Error: arial.ttf file not found. Please check the directory!")
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontName='Arial',
fontSize=20,
alignment=TA_CENTER,
spaceAfter=30
)
2. Building Flexible Tables
Tables are the most important part of any report. With TableStyle, you can format colors, borders, and alignment as professionally as in Excel.
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
# Sample data for the report
data = [
['No.', 'Product', 'Quantity', 'Revenue'],
['1', 'Management Software', '50', '5,000$'],
['2', 'Python Course', '120', '12,000$'],
['3', 'Cloud Services', '30', '3,000$'],
]
table = Table(data, colWidths=[40, 180, 80, 100])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.dodgerblue),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, -1), 'Arial'),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
]))
3. Drawing Sharp Vector Charts
Instead of inserting pixelated bitmap images, use ReportLab’s graphics module. Charts will be drawn as vectors, keeping the file size small and ensuring they remain sharp when printed.
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
def create_chart():
drawing = Drawing(400, 200)
bc = VerticalBarChart()
bc.x = 50
bc.y = 50
bc.height = 125
bc.width = 300
bc.data = [(50, 120, 30)]
bc.categoryAxis.categoryNames = ['Product A', 'Product B', 'Product C']
bc.valueAxis.valueMin = 0
bc.valueAxis.valueMax = 150
drawing.add(bc)
return drawing
Finalizing the PDF Export Script
Now, let’s assemble the pieces into a complete workflow. You can reuse this template for many different projects.
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
def export_pdf(filename):
doc = SimpleDocTemplate(filename, pagesize=A4)
elements = []
# Add title and content
elements.append(Paragraph("BUSINESS PERFORMANCE REPORT", title_style))
elements.append(Spacer(1, 15))
body_style = ParagraphStyle('Body', fontName='Arial', fontSize=12)
elements.append(Paragraph("Summary data as of the end of the month:", body_style))
elements.append(Spacer(1, 20))
# Insert the created table and chart
elements.append(table)
elements.append(Spacer(1, 30))
elements.append(create_chart())
doc.build(elements)
print(f"Success! File saved at: {filename}")
if __name__ == "__main__":
export_pdf("Monthly_Report.pdf")
Real-world Implementation Tips
To ensure the system runs stably in a production environment, keep these 3 points in mind:
- File Management: Always use
try...exceptwhen writing files. If the PDF file is open in another application, the script will crash immediately. - Image Optimization: If the report includes logos or avatars, resize them to the actual display size. This can reduce the file size from several MBs down to a few dozen KBs.
- Full Automation: Combine this script with a scheduling tool (like a Cron job or APScheduler). The system can automatically send reports at 8 AM every Monday without any manual intervention.
Mastering ReportLab frees you from tedious, repetitive tasks. Although the initial setup takes some time, the result is a professional, accurate, and fast workflow.
