Django.fun

Reportlab alignment & width issues

I’m having some issues with reportlab and writing a PDF. When the PDF is written it only consumes a little less than 2/3 of the page width (letter). The heading, for example, wraps and never makes it past the halfway point of the document.

I’m at a loss for how to get my tables and paragraph to use the full width of the page.

Any insight is greatly appreciated.

Thank you in advance.


import io
import os
from django.core.files.base import ContentFile
from jsignature.utils import draw_signature
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_RIGHT, TA_CENTER, TA_LEFT
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table
from PIL import Image


# create pdf with table and paragraphs
def create_pdf(participant):
    # create a file-like buffer to receive PDF data
    buffer = io.BytesIO()
    # define styles
    styles = getSampleStyleSheet()
    style_general = ParagraphStyle(
        name='left',
        parent=styles['Normal'],
        fontSize=12,
        fontName='Helvetica',
        alignment=TA_LEFT)
    style_image = ParagraphStyle(
        name='left',
        fontSize=30,
        parent=styles['Normal'],
        alignment=TA_LEFT)
    style_heading = ParagraphStyle(
        name='center',
        fontSize=18,
        fontName='Helvetica-Bold',
        parent=styles['Heading1'],
        leading=18,
        alignment=TA_CENTER)
    # create a simple document with page size in buffer
    doc = SimpleDocTemplate(buffer, pagesize=letter, author='Me')
    # create a list of paragraphs
    AllParagraphs = []
    # convert png image to jpeg
    jpeg_image = get_jpeg_image(participant)
    # add rows and columns so that the data can align
    table_data = [
        [Paragraph("My Heading - It should span the full page width", style_heading)],
        [
            Paragraph('Name:', style_general),
            Paragraph(
                f'{participant.first_name} {participant.middle_initial} {participant.last_name}',
                style_general)
        ],
        [
            Paragraph(f'Signature:', style_general),
            # image height of 30 to prevent overlapping since fontSize is 30,
            # image width of double to maintain aspect ratio
            Paragraph(
                "<img src='{0}' valign='middle' width=60 height=30 />".format(
                    jpeg_image),
                style_image)
        ]
    ]
    # set rows and columns into Table object
    table_element = Table(table_data)
    # add table to list of paragraphs
    AllParagraphs.append(table_element)
    # build document with list of paragraphs
    doc.build(AllParagraphs)

    # get content of buffer
    buffer.seek(0)
    pdf_data = buffer.getvalue()
    # save buffer content to django File object
    file_data = ContentFile(pdf_data)
    # name pdf file
    file_data.name = f'{participant.last_name}.pdf'
    # delete jpeg file
    os.remove(jpeg_image)
    # save pdf file to parent model
    participant.pdf = file_data
    participant.save()



For those interested in the answer: adjusting the table style to span multiple columns was the right approach.

In this case a table is being used to best align the signature elements, so spanning the columns similar to how you would in html or css is the solution.

...
    # existing code for placement reference
    # set rows and columns into Table object
    table_element = Table(table_data)
    # add table to list of paragraphs

    # new code for spanning
    # style table object to make single cells span both columns
    table_element.setStyle(TableStyle([
        ('SPAN', (0, 0), (1, 0)),
    ]))

Tutorials

Константы Python: Улучшение управляемости вашего кода

Современный Python: начинаем проект с pyenv и poetry

Настройка проекта Python — виртуальные среды и управление пакетами

Использование requests в Python — тайм-ауты, повторы, хуки

Понимание декораторов в Python

ProcessPoolExecutor в Python: полное руководство

map() против submit() с ProcessPoolExecutor в Python

Понимание атрибутов, словарей и слотов в Python

Полное руководство по slice в Python

Выпуск Django 4.0

Безопасное развертывание приложения Django с помощью Gunicorn, Nginx и HTTPS

Автоматический повтор невыполненных задач Celery

Django REST Framework и Elasticsearch

Докеризация Django с помощью Postgres, Gunicorn и Nginx

Асинхронные задачи с Django и Celery

Релизы безопасности Django: 3.2.4, 3.1.12 и 2.2.24

Выпуски исправлений ошибок Django: 3.2.3, 3.1.11 и 2.2.23

Эффективное использование сериализаторов Django REST Framework

Выпуски безопасности Django: 3.2.2, 3.1.10 и 2.2.22

Выпущенные релизы безопасности Django: 3.2.1, 3.1.9 и 2.2.21

View all tutorials →