Usage in web APIs¶
Note that FPDF
instance objects are not designed to be reusable: content cannot be added once output()
has been called.
Hence, even if the FPDF
class should be thread-safe, we recommend that you either create an instance for every request, or if you want to use a global / shared object, to only store the bytes returned from output()
.
Django¶
Django is:
a high-level Python web framework that encourages rapid development and clean, pragmatic design
There is how you can return a PDF document from a Django view:
from django.http import HttpResponse
from fpdf import FPDF
def report(request):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.cell(text="hello world")
return HttpResponse(bytes(pdf.output()), content_type="application/pdf")
Flask¶
Flask is a micro web framework written in Python.
The following code can be placed in a app.py
file and launched using flask run
:
from flask import Flask, make_response
from fpdf import FPDF
app = Flask(__name__)
@app.route("/")
def hello_world():
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.cell(text="hello world")
response = make_response(pdf.output())
response.headers["Content-Type"] = "application/pdf"
return response
gunicorn¶
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX.
The following code can be placed in a gunicorn_fpdf2.py
file and launched using gunicorn -w 4 gunicorn_fpdf2:app
:
from fpdf import FPDF
def app(environ, start_response):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=12)
pdf.cell(text="Hello world!")
data = bytes(pdf.output())
start_response("200 OK", [
("Content-Type", "application/pdf"),
("Content-Length", str(len(data)))
])
return iter([data])
AWS lambda¶
The following code demonstrates some minimal AWS lambda handler function that returns a PDF file as binary output:
from base64 import b64encode
from fpdf import FPDF
def handler(event, context):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.cell(text="hello world")
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
},
'body': b64encode(pdf.output()).decode('utf-8'),
'isBase64Encoded': True
}
This AWS lambda function can then be linked to a HTTP endpoint using API Gateway, or simply exposed as a Lambda Function URL. More information on those pages:
For reference, the test lambda function was initiated using the following AWS CLI commands:
Creating & uploading a lambda layer
pyv=3.8
pip${pyv} install fpdf2 -t python/lib/python${pyv}/site-packages/
# We use a distinct layer for Pillow:
rm -r python/lib/python${pyv}/site-packages/{PIL,Pillow}*
zip -r fpdf2-deps.zip python > /dev/null
aws lambda publish-layer-version --layer-name fpdf2-deps \
--description "Dependencies for fpdf2 lambda" \
--zip-file fileb://fpdf2-deps.zip --compatible-runtimes python${pyv}
Creating the lambda
AWS_ACCOUNT_ID=...
AWS_REGION=eu-west-3
zip -r fpdf2-test.zip lambda.py
aws lambda create-function --function-name fpdf2-test --runtime python${pyv} \
--zip-file fileb://fpdf2-test.zip --handler lambda.handler \
--role arn:aws:iam::${AWS_ACCOUNT_ID}:role/lambda-fpdf2-role \
--layers arn:aws:lambda:${AWS_REGION}:770693421928:layer:Klayers-python${pyv/./}-Pillow:15 \
arn:aws:lambda:${AWS_REGION}:${AWS_ACCOUNT_ID}:layer:fpdf2-deps:1
aws lambda create-function-url-config --function-name fpdf2-test --auth-type NONE
Those commands do not cover the creation of the lambda-fpdf2-role
role, nor configuring the lambda access permissions, for example with a FunctionURLAllowPublicAccess
resource-based policy.
streamlit¶
streamlit is:
a Python library that makes it easy to create and share custom web apps for data science
The following code demonstrates how to display a PDF and add a button allowing to download it:
from base64 import b64encode
from fpdf import FPDF
import streamlit as st
st.title("Demo of fpdf2 usage with streamlit")
@st.cache
def gen_pdf():
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.cell(text="hello world")
return bytes(pdf.output())
# Embed PDF to display it:
base64_pdf = b64encode(gen_pdf()).decode("utf-8")
pdf_display = f'<embed src="data:application/pdf;base64,{base64_pdf}" width="700" height="400" type="application/pdf">'
st.markdown(pdf_display, unsafe_allow_html=True)
# Add a download button:
st.download_button(
label="Download PDF",
data=gen_pdf(),
file_name="file_name.pdf",
mime="application/pdf",
)
FastAPI¶
FastAPI is:
a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints.
The following code shows how to generate a PDF file via a POST endpoint that receives a JSON object. The JSON object can be used to write into the PDF file. The generated PDF file will be returned back to the user/frontend as the response.
from fastapi import FastAPI, Request, Response, HTTPException, status
from fpdf import FPDF
app = FastAPI()
@app.post("/send_data", status_code=status.HTTP_200_OK)
async def create_pdf(request: Request):
"""
POST endpoint that accepts a JSON object
This endpoint returns a PDF file as the response
"""
try:
# data will read the JSON object and can be accessed like a Python Dictionary
# The contents of the JSON object can be used to write into the PDF file (if needed)
data = await request.json()
# Create a sample PDF file
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.cell(text="hello world")
# pdf.cell(text=data["content"]) # Using the contents of the JSON object to write into the PDF file
# Use str(data["content"]) if the content is non-string type
# Prepare the filename and headers
filename = "<file_name_here>.pdf"
headers = {
"Content-Disposition": f"attachment; filename={filename}"
}
# Return the file as a response
return Response(content=bytes(pdf.output()), media_type="application/pdf", headers=headers)
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
Jupyter¶
Check tutorial/notebook.ipynb
web2py¶
Usage of the original PyFPDF lib with web2py is described here: https://github.com/reingart/pyfpdf/blob/master/docs/Web2Py.md
v1.7.2
of PyFPDF is included in web2py
since release 1.85.2
: https://github.com/web2py/web2py/tree/master/gluon/contrib/fpdf