Content description:
Reportlab - generating a PDF on the server
Solved issue: How to generate a pdf file with order details and reservation barcode without saving the file to the server.
from io import BytesIO
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib.pagesizes import A4
buffer = BytesIO()
canvas = Canvas(buffer, pagesize=A4)
To use non-standard characters, register appropriate fonts that support the required characters. In the sample pdf file I use the Vera font.
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('Vera', 'Vera.ttf'))
The registered font can now be used:
canvas.setFont("Vera", size=10)
I place the images generated on the basis of data from the application (in this case the reservation barcode) in the created document using the drawImage() method, which takes the ImageReader object as the first argument.
im = ImageReader(image)
canvas.drawImage(im, x=0, y=-5*cm, width=150, height=100)
A list of text strings can be added to a document using a text object.
txt_obj = canvas.beginText(14, -6.5 * cm)
txt_lst = ["line of text 1", "line of text 2", "line of text 3"]
for line in txt_lst:
txt_obj.textOut(line)
txt_obj.moveCursor(0, 16)
canvas.drawText(txt_obj)
You can add a table to a document as an object of the Table class. I separately define a list containing the data of individual table rows (table_data variable). I also define styles applicable to all or part of the table.
t = Table(table_data, colWidths=[60, 230, 70, 60, 50], rowHeights=30)
style = [('BACKGROUND',(0,0),(-1,-2),colors.lightblue),
('ALIGN',(0,-1),(-1,-1),'CENTER'),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('FONTSIZE', (0,0), (-1,-1), 10),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE')]
t.setStyle(tblstyle=style)
t.wrapOn(canvas, 10, 10)
t.drawOn(canvas=canvas, x=20, y=-22*cm)
I then save the created Canvas object and return the resulting buffer to the controller. To send a pdf file based on data from the buffer I use Flask's send_file() function.
from flask.helpers import send_file
@bp.route('/get-pdf/<int:id>', methods=['GET'])
@login_required
def get_pdf(id):
contract = Contract.query.get(id)
contractor = User.query.get(contract.contractor_id)
booking = Booking.query.filter_by(contract_id=contract.id).first()
pdf = create_pdf(booking_no=booking.id,
contractor=contractor.username,
contractor_no=contractor.id,
truck_plate=booking.truck_reg_number,
warehouse=contract.warehouse,
date=contract.date_of_delivery,
time=booking.booking_time,
pallets_pos=contract.pallets_position,
pallets=contract.pallets_actual)
pdf.seek(0)
return send_file(pdf, as_attachment=True, mimetype='application/pdf',
attachment_filename='booking.pdf', cache_timeout=0)