We will learn how to make a PDF file using the Python library ReportLab.
Practice setting up canvas coordinates, drawing text, line grids, adding watermark transforms, custom page dimensions, and letterhead templates interactively in Google Colab.
View on GitHubAt your terminal or command prompt, install the package via pip:
pip install reportlab
Create your first PDF file:
Create PDF files with dynamic data taken from SQLite database in Colab.
Canvas is like a blank white paper where the (x, y) coordinates start by default from the bottom-left corner.
The x-coordinate increases towards the right, and the y-coordinate increases upwards. The bottomup argument configures the coordinate origin (setting bottomup=0 flips the origin to top-left, whereas bottomup=1 is bottom-left).
Import canvas from ReportLab. The code below writes "Hello World" at x=200, y=200 on the canvas page.
from pathlib import Path
from reportlab.pdfgen import canvas
output_dir = Path('output')
output_dir.mkdir(exist_ok=True)
my_path = output_dir / 'my_pdf.pdf'
c = canvas.Canvas(str(my_path), bottomup=0)
c.drawString(200, 200, "Hello World") # write text in page
c.showPage() # saves current page
c.save() # stores the file and closes canvas
print(f"PDF generated: {my_path}")
We can manage font size and font colour prior to invoking drawString():
c.setFont("Helvetica", 20) # font family and size
# c.setFillColorCMYK(.2, .2, 0, .39) # font colour as CMYK
# c.setFillColorRGB(0, 0, 1) # font colour as RGB
c.setFillColor('lightblue') # named color
c.drawString(200, 200, "Hello World")
ReportLab includes built-in core font families. Inspect registered fonts using getAvailableFonts():
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
output_dir = Path('output')
my_path = output_dir / 'font_list.pdf'
c = canvas.Canvas(str(my_path), pagesize=letter)
c.translate(inch, inch) # 1 inch margin
c.setFillColorRGB(0, 0, 1)
l1 = c.getAvailableFonts()
j = 1
for my_font in l1:
c.setFont(my_font, 16)
c.drawString(inch, j * inch * 0.5, my_font)
j += 1
c.showPage()
c.save()
Learn how to embed and register custom TrueType (.ttf) fonts in ReportLab to display specialized typography in your PDF outputs.
Adding different font support in ReportlabUtilize unit helper tools like inch and cm alongside page definitions (letter, A4) to manage precise page dimensions:
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter, A4
c.translate(inch, inch) # offset origin by 1 inch
Specify starting (x1, y1) and ending (x2, y2) coordinates to draw lines. Configure stroke color with setStrokeColorRGB() and line width with setLineWidth():
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
output_dir = Path('output')
my_path = output_dir / 'line_drawing.pdf'
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=letter)
c.translate(inch, inch)
c.setStrokeColorRGB(1, 0, 0) # red line
c.setLineWidth(10)
c.line(0, 8 * inch, 7 * inch, 8 * inch)
c.showPage()
c.save()
Construct grids easily using coordinate sequence lists for x and y boundaries:
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
output_dir = Path('output')
my_path = output_dir / 'grid_layout.pdf'
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=A4)
w, h = A4
xlist = [20, 70, 120, 170]
ylist = [h - 20, h - 70, h - 120, h - 170]
c.grid(xlist, ylist)
c.drawString(70, h - 70, "plus2net")
c.save()
Add local photo assets or web images using drawImage():
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
output_dir = Path('output')
my_path = output_dir / 'image_example.pdf'
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=letter)
c.translate(inch, inch)
c.drawImage('https://www.plus2net.com/images/top2.jpg', -0.8 * inch, 9.3 * inch)
c.showPage()
c.save()
Rotate the coordinate space using rotate(angle) and restore origin alignment by reversing the rotation angle offset:
c.rotate(-45)
c.drawImage('logo.jpg', 0, 0)
c.rotate(45)
Draw vector shapes with stroke borders and fill colors using rect():
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
output_dir = Path('output')
my_path = output_dir / 'rectangle.pdf'
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=letter)
c.translate(inch, inch)
c.setLineWidth(10)
c.setStrokeColor('yellow')
c.setFillColor('lightgreen')
c.rect(1 * inch, 2 * inch, 3.5 * inch, 5 * inch, fill=1)
c.showPage()
c.save()
Define custom dimensions by supplying a (width, height) point tuple (where 72 points = 1 inch):
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
output_dir = Path('output')
my_path = output_dir / 'custom_size.pdf'
custom_size = (595, 841)
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=custom_size)
c.translate(inch, inch)
c.setLineWidth(10)
c.setStrokeColor('yellow')
c.setFillColor('lightgreen')
c.rect(1 * inch, 2 * inch, 3.5 * inch, 5 * inch, fill=1)
c.showPage()
c.save()
Combine rotated text with subtle CMYK opacity styling to render security background watermarks:
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
output_dir = Path('output')
my_path = output_dir / 'watermarked.pdf'
c = canvas.Canvas(str(my_path), bottomup=1, pagesize=letter)
c.translate(inch, inch)
c.setLineWidth(10)
c.setStrokeColor('yellow')
c.setFillColor('lightgreen')
c.rect(1 * inch, 2 * inch, 3.5 * inch, 5 * inch, fill=1)
# Watermark execution
c.rotate(45)
c.setFillColorCMYK(0, 0, 0, 0.08)
c.setFont("Helvetica", 100)
c.drawString(2 * inch, 1 * inch, "SAMPLE")
c.rotate(-45)
c.setFont("Helvetica", 10)
c.setFillColorRGB(1, 0, 0)
c.drawString(0, -0.9 * inch, "\u00A9 plus2net.com")
c.showPage()
c.save()
Modularize page designs by encapsulating letterheads and headers in a template module (temp1.py) and calling it inside your primary builder script (main.py).
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
from temp1 import my_temp
output_dir = Path('output')
output_dir.mkdir(exist_ok=True)
my_path = output_dir / 'template_output.pdf'
c = canvas.Canvas(str(my_path), pagesize=letter)
c = my_temp(c)
c.setFillColorRGB(0, 0, 1)
c.setFont("Helvetica", 16)
c.drawString(2 * inch, 4 * inch, 'This is my product')
c.showPage()
c.save()
from reportlab.lib.units import inch
from datetime import date
def my_temp(c):
c.translate(inch, inch)
c.setFont("Helvetica", 14)
c.setStrokeColorRGB(0.1, 0.8, 0.1)
c.setFillColorRGB(0, 0, 1)
c.drawImage('https://www.plus2net.com/images/top2.jpg', -0.8 * inch, 9.3 * inch)
c.drawString(0, 9 * inch, "Shop No : 1234, ABCD Road")
c.drawString(0, 8.7 * inch, "City Name: Mycity, ZIP : 12345")
c.setFillColorRGB(0, 0, 0)
c.line(0, 8.6 * inch, 6.8 * inch, 8.6 * inch)
c.drawString(5.6 * inch, 9.5 * inch, 'Bill No :# 1234')
dt = date.today().strftime('%d-%b-%Y')
c.drawString(5.6 * inch, 9.3 * inch, dt)
c.setFont("Helvetica", 8)
c.drawString(3 * inch, 9.6 * inch, 'Tax No :# ABC1234')
c.line(0, -0.7 * inch, 6.8 * inch, -0.7 * inch)
c.setFillColorRGB(1, 0, 0)
c.drawString(0, -0.9 * inch, "\u00A9 plus2net.com")
c.rotate(45)
c.setFillColorCMYK(0, 0, 0, 0.08)
c.setFont("Helvetica", 100)
c.drawString(2 * inch, 1 * inch, "SAMPLE")
c.rotate(-45)
return c
Secure documents against unauthorized opening using the pdfencrypt module:
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib import pdfencrypt
output_dir = Path('output')
my_path = output_dir / 'protected_hello.pdf'
pw = pdfencrypt.StandardEncryption('test') # password is 'test'
c = canvas.Canvas(str(my_path), bottomup=0, encrypt=pw)
c.drawString(200, 200, 'Hello world')
c.showPage()
c.save()
bottomup coordinate setting.pathlib.Path variables to strings when initializing canvas.Canvas(str(path)).c.showPage() before c.save() to ensure all canvas draw commands render onto the final page.Q: How do I change the default unit from points to inches or centimeters?
A: Import inch or cm from reportlab.lib.units and multiply coordinates (e.g. 2 * inch).
Q: How do I handle multi-page canvas documents?
A: Call c.showPage() after finishing each page to start a new page on the same canvas instance.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.