Reputation: 130
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
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
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