Search code examples
pythonpython-3.xflaskfpdf

PDF response is corrupted in Python 3 but works in Python 2


I wrote a working application in python2.7 and Flask. One of the things it does is download a PDF invoice. It's working fine.

Now I am doing a new app that also allows downloading a PDF invoice, but I am using Python3 this time. I can save the file to the server and get a perfectly working PDF, but if I try to send it to the browser, the resulting file is corrupted.

Here is the download function:

@mod.route('/get_invoice/<invoice_id>')
def get_invoice(invoice_id):
    invoice = Invoices.query.filter_by(id=invoice_id).all()

    pdf_generator = PDFInvoice(invoice)
    pdf = pdf_generator.new()

    response = make_response(pdf.output(dest='S'))
    response.headers['Content-Disposition'] = 'attachment; filename="invoice.pdf"'
    response.headers['Content-Type'] = 'application/pdf'

    return response

This is nearly identical to the working function in the older Python2.7 application.

To expand, it is turning this (working PDF):

x�3R��2�35W(�r
Q�w3T��30P^HISp^M^A�^X^[�^YZ*�^[^Z�^Y�*��(h�e^Vg�(�^V+$�(����e����奖h*�d��^@^@�v^T�

into this (corrupted PDF):

x^Ü3Rðâ2Ð35W(çr
QÐw3T°Ô30P^HISp^M^A^É^X^[è^YZ*^Ø^[^Zê^Y^Û*^Ĥ(h^Äe^Vg^Ö(^Ô^V+$^Ö(^Ô^×^×ëe§æ^Õèå¥^Öh*^Äd^Áô^@^@øv^TÂ

The rest of the pdf's data is text strings, and they appear to be unchanged. So it seems to be an encoding issue someplace.


Solution

  • FPDF outputs a str which in Python 2 is basically equivalent to bytes, but in Python 3 is unicode, not bytes. Straight from the docs:

    If you are using Python 3.x you have to use pdf.output(dest='S').encode('latin-1') in order to get the output, if you don't do so the generated PDF will be invalid