2025, Oct 29 23:00

How to Resize a Logo on Each PDF Page in ReportLab: Use drawImage with Explicit Width and Height

Fix oversized logos in ReportLab PDFs: scale images on each page by passing width and height to drawImage, preserve aspect ratio, and ensure consistent layout.

Adding a small logo or badge to every page of a PDF is a common pattern when generating reports. The friction starts when the source PNG is large and a naive attempt to resize it inside the drawing call has no effect, so each page is stamped with a full-size image instead of a scaled one.

Example that illustrates the issue

The following shows how the logo is added on each page, but attempts to constrain its size don’t stick. The image keeps rendering at its original dimensions.

def paint_sheet(sheet, dossier, pagesize=A4):
    pg_no = sheet.getPageNumber()
    emblem = Image('/opt/rspro/home/e8409/projects/CRAMM logo.png')
    emblem._restrictSize(1 * inch, 2 * inch)
    sheet.drawImage(emblem, 0, 0)
    sheet.showPage()
from reportlab.platypus import PageTemplate
upright_tpl = PageTemplate(
  id='upright', 
  frames=upright_frame,
  onPage=paint_sheet, 
  pagesize=A4)
from reportlab.platypus import BaseDocTemplate
pdf_doc = BaseDocTemplate(
  'report.pdf',
  pageTemplates=[
    upright_tpl
  ]
)

What’s going on

The resizing attempt is made via a size constraint, but the image is drawn using a method that doesn’t inherit that constraint. The draw call respects the original image dimensions unless explicit width and height are supplied. As a result, the logo reappears at full size on every page.

The fix

Specify the target dimensions directly in the drawing call. If needed, read the image first and inspect its dimensions; then pass the desired size when rendering so it’s scaled as intended.

from reportlab.lib.utils import ImageReader
def render_stamp(canvas_ctx, doc_ctx, pagesize=A4):
    logo_path = '/opt/rspro/home/e8409/projects/CRAMM logo.png'
    img_reader = ImageReader(logo_path)
    orig_w, orig_h = img_reader.getSize()
    target_w = desired_width
    target_h = desired_height
    canvas_ctx.drawImage(
        logo_path,
        0,
        0,
        width=target_w,
        height=target_h,
        preserveAspectRatio=True
    )

This approach keeps the image handling in the same flow where the PDF is drawn. There’s no need to preprocess the PNG elsewhere. One additional observation from practice: if you are running this in an environment with executable cells, running steps out of order can mask the effect of your changes.

Why this matters

Consistent, predictable scaling is essential when placing branding elements or any fixed graphics across multiple pages. Without explicit sizing, a large source asset will dominate the canvas, breaking page layout and pushing content around. Controlling dimensions at the drawing call ensures repeatable output for every page of the report.

Conclusion

When placing repeating images on a PDF, rely on the drawing method’s width and height parameters to control scale, and keep aspect ratio if needed. Read the image if you want to inspect its native size, then render it once per page with explicit dimensions. If something still looks off, verify that the relevant code paths are executed in the correct order.

The article is based on a question from StackOverflow by vashts85 and an answer by RCDevs Security.