Skip to content

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