cbirch
cbirch

Reputation: 130

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()



Upvotes: 0

Views: 939

Answers (2)

Shakti Agro
Shakti Agro

Reputation: 1

from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from datetime import date

def create_pdf(data, output_filename="plough_price_list.pdf"):
    """
    Creates an attractive PDF document from the given data.

    Args:
        data (list): A list of lists representing the data to be included in the PDF.
        output_filename (str): The name of the output PDF file.
    """

    doc = SimpleDocTemplate(output_filename, pagesize=letter)
    styles = getSampleStyleSheet()

    # Register a font (replace with your desired font if needed)
    pdfmetrics.registerFont(TTFont('Arial', 'Helvetica'))

    # Title Style
    title_style = ParagraphStyle(
        'Title',
        parent=styles['Title'],
        fontName='Arial',
        fontSize=18,
        alignment=1,  # Center alignment
        spaceAfter=12
    )

    # Header Style
    header_style = ParagraphStyle(
        'Header',
        parent=styles['Normal'],
        fontName='Arial',
        fontSize=12,
        textColor=colors.white,
        alignment=1,
    )

    # Body Style
    body_style = ParagraphStyle(
        'Body',
        parent=styles['Normal'],
        fontName='Arial',
        fontSize=10,
        alignment=1,
    )

    # Condition Style
    condition_style = ParagraphStyle(
        'Condition',
        parent=styles['Normal'],
        fontName='Arial',
        fontSize=10,
        textColor=colors.blue,
        spaceBefore=12,
        spaceAfter=6
    )

    # Date Style
    date_style = ParagraphStyle(
        'Date',
        parent=styles['Normal'],
        fontName='Arial',
        fontSize=10,
        alignment=2,  # Right alignment
        spaceAfter=12
    )

    # Create the title paragraph
    title = Paragraph("Plough Price List", title_style)

    # Create the date paragraph
    today = date.today().strftime("%d-%m-%Y")
    date_paragraph = Paragraph(f"Date: {today}", date_style)

    # Create the table data
    table_data = [
        [Paragraph(cell, header_style) for cell in data[0]]  # Header row
    ]
    for row in data[1:]:
        table_data.append([Paragraph(cell, body_style) for cell in row])

    # Create the table
    table = Table(table_data)

    # Define table style
    table_style = TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4682B4")),  # Header background
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),  # Header text color
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, -1), 'Arial'),
        ('FONTSIZE', (0, 0), (-1, -1), 10),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
        ('BACKGROUND', (0, 1), (-1, -1), colors.beige),  # Body background
        ('GRID', (0, 0), (-1, -1), 1, colors.black)  # Grid lines
    ])

    table.setStyle(table_style)

    # Create condition paragraphs
    conditions = [
        Paragraph("<b>Condition</b>", condition_style),
        Paragraph("* This price include regular hydraulic kit.", condition_style),
        Paragraph("* This price valide for 30 days.", condition_style),
        Paragraph("* This price loading port - Pipavav (India), Destination port - (Romania).", condition_style),
        Paragraph("40 feet container", condition_style),
        Paragraph("plough 45 PIECE IN ONE CONTAINER", condition_style),
        Paragraph("transportation 5000 PORT OT PORT IN USD PER CONTAINER", condition_style),
        Paragraph("CUSTOM CLEARENCE 805 CUSTOMER CLEARENCE IN USD PER CONTAINER", condition_style)
    ]

    # Build the PDF
    elements = [date_paragraph, title, table] + conditions
    doc.build(elements)

if __name__ == "__main__":
    # Sample data (replace with your actual data)
    data = [
        ["Sr", "Model", "Weight", "TRACTOR HP", "SOIL CONDITION", "TYRE SIZE", "FOB Value USD", "CIF IN USD"],
        ["1", "1F 350", "349", "35-40", "HARD/MEDIUM", "13/14", "701", "812"],
        ["4", "2F 390-10", "383", "40-45", "SOFT/MEDIUM", "13/14", "744", "855"],
        ["5", "2F 395-12", "386", "40-45", "SOFT/MEDIUM", "14/16", "747", "858"],
        ["6", "2F 415-10", "418", "40-45", "MEDIUM/HARD", "13/14", "773", "884"],
        ["7", "2F 420-12", "421", "40-45", "MEDIUM/HARD", "14/16", "776", "887"],
        ["8", "2F 460-10", "464", "45-50", "MEDIUM/HARD", "13/14", "821", "932"],
        ["9", "2F 465-12", "467", "45-50", "MEDIUM/HARD", "14/16", "824", "935"],
        ["10", "2F 500-10", "498", "55-60", "HARD", "14", "857", "968"],
        ["11", "2F 505-12", "501", "55-60", "HARD", "16", "860", "971"],
        ["13", "3F 415-10.5", "415", "40-45", "SOFT/MEDIUM", "13/14", "777", "888"],
        ["14", "3F 455-10.5", "455", "45-50", "MEDIUM/HARD", "13/14/16", "819", "930"],
        ["15", "3F 525-11", "525", "55-60", "MEDIUM/HARD", "13/14/16", "892", "1003"],
        ["1", "2F 405-11 H-54\"", "405", "40-45", "MEDIUM/HARD", "13/14", "770", "881"],
        ["4", "2F 485-10 Mount 64\"", "485", "50-55", "MEDIUM/HARD", "14/16", "855", "966"],
        ["5", "2F 490-12 Mount 64\"", "488", "50-55", "MEDIUM/HARD", "14/16", "858", "969"],
        ["6", "2F 525-10 F-1.25 H REG", "525", "55-60", "MEDIUM/HARD", "14/16", "898", "1009"],
        ["7", "2F 530-12 F-1.25 H REG", "528", "55-60", "MEDIUM/HARD", "14/16", "901", "1012"],
        ["11

Upvotes: -1

cbirch
cbirch

Reputation: 130

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)),
    ]))

Upvotes: 0

Related Questions