I'm trying to serve a dynamic PDF to my users through Flask. They fill a form to get some calculations presented, but they would like to auto-fill the values to a PDF-template and then print it on paper.
I got it working locally, just filling the values to a PDF-template, but I have no clue on how I'd work it into my Flask app. What I got locally is I've got a template PDF-file, a dictionary with the values, a function to fill the values to the template and write it out to a new PDF-file. Thou when hosting it with Flask, I want it to dynamically serve them the new PDF created by the function instead of writing it to a file.
I'm using the pdfrw library for the pdf-modification, I realise I might need to use make_reponse or IO or a combination. Just dont know how to get the result from pdfrw NOT to a file. Or where to start.
Here's the function:
import os
import pdfrw
data_dict = #just a plain dict corresponding with the temp.pdf keys
PDF_TEMPLATE_PATH = 'temp.pdf'
PDF_OUT_PATH = 'out.pdf'
ANNOT_KEY = '/Annots'
ANNOT_FIELD_KEY = '/T'
ANNOT_VAL_KEY = '/V'
ANNOT_RECT_KEY = '/Rect'
SUBTYPE_KEY = '/Subtype'
WIDGET_SUBTYPE_KEY = '/Widget'
def write_fillable_pdf(input_pdf_path, output_pdf_path, data_dict):
template_pdf = pdfrw.PdfReader(input_pdf_path)
annotations = template_pdf.pages[0][ANNOT_KEY]
for annotation in annotations:
if annotation[SUBTYPE_KEY] == WIDGET_SUBTYPE_KEY:
if annotation[ANNOT_FIELD_KEY]:
key = annotation[ANNOT_FIELD_KEY][1:-1]
if key in data_dict.keys():
annotation.update(
pdfrw.PdfDict(V='{}'.format(data_dict[key]))
)
pdfrw.PdfWriter().write(output_pdf_path, template_pdf)
if __name__ == '__main__':
write_fillable_pdf(PDF_TEMPLATE_PATH, PDF_OUT_PATH, data_dict)
So I guess, how do I get this line
pdfrw.PdfWriter().write(output_pdf_path, template_pdf)
to be able to serve with eg. make_reponse or IO or likely? I'll just make a button-href to a /pdf route in flask for this when I get it working.
Sorry I'm rather new to this, trying over my head to meet the wishes of my users.
Thank you for any guidance or direction.
Assuming you don't want to save the PDF to the server, and just keep it in memory, then serve it with Flask's send_file
function, you could modify your write_fillable_pdf
function to instead return a bytes type object. So instead of:
pdfrw.PdfWriter().write(output_pdf_path, template_pdf)
You could do the following:
buf = io.BytesIO()
pdfrw.PdfWriter().write(buf, template_pdf)
buf.seek(0)
return buf
remembering to import io
at the top of the file!
Then in your flask code, the return value of write_fillable_pdf
could be passed directly to the send file function:
from flask import send_file
data = write_fillable_pdf() # With some arguments
return send_file(data, mimetype='application/pdf')
You'd probably want to modifiy the arguments write_fillable_pdf
accepts, as you no longer wish to supply the output_pdf_path
. You'd also want to pass this your data_dict
which could be constructed based on values supplied from the user or somewhere else.