Create PDF Files with Python and FPDF

Published on: July 10, 2026
Reading time: 4 minutes
Geração de arquivos PDF usando biblioteca FPDF em Python

Learning how to create PDF files with Python is useful when you need to automate invoices, certificates, reports, receipts, labels, or personalized documents. The FPDF family of libraries provides a direct way to build a PDF page by page with text, images, lines, tables, headers, and footers.

This beginner guide uses the modern fpdf2 package, which is imported with from fpdf import FPDF. You will install the library, create a first document, control fonts and page layout, add images, build tables, handle page breaks, generate reports from data, and avoid common encoding and file-path errors.

Basic knowledge of Python functions, pathlib, and loops will make the examples easier to follow.

What Is FPDF?

FPDF is a lightweight approach to PDF generation. Your program defines the document size, margins, fonts, and content, and the library writes a PDF file. The current fpdf2 documentation includes the complete API, tutorials, and layout examples. The pathlib documentation is also useful for reliable output paths.

PDF generation is different from editing an existing document. FPDF is strongest when your application creates a new file from structured data.

Install fpdf2

Create a virtual environment and install the package:

python -m pip install fpdf2

Then confirm the import:

from fpdf import FPDF

print("FPDF is ready")

The package name is fpdf2, but the Python import remains fpdf. The virtual environment guide explains how to isolate dependencies for each project.

Create Your First PDF

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=16)
pdf.cell(text="Hello from Python!")
pdf.output("hello.pdf")

The basic sequence is simple:

  1. Create an FPDF object.
  2. Add at least one page.
  3. Select a font.
  4. Add content.
  5. Write the document with output().

Choose Page Size and Orientation

from fpdf import FPDF

pdf = FPDF(orientation="P", unit="mm", format="A4")
pdf.add_page()

P means portrait and L means landscape. Millimeters are convenient for print documents, while points may be useful when matching design specifications.

Add a Title and Paragraphs

cell() is useful for short lines. multi_cell() wraps longer text automatically.

from fpdf import FPDF

pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()

pdf.set_font("Helvetica", style="B", size=20)
pdf.cell(0, 12, "Monthly Report", new_x="LMARGIN", new_y="NEXT", align="C")

pdf.ln(6)
pdf.set_font("Helvetica", size=11)
pdf.multi_cell(
    0,
    6,
    "This report was generated automatically with Python. "
    "Long text is wrapped across multiple lines by multi_cell().",
)

pdf.output("monthly-report.pdf")

Automatic page breaking prevents content from continuing below the printable area.

Use Margins and Spacing

pdf.set_margins(left=18, top=18, right=18)
pdf.set_auto_page_break(auto=True, margin=18)

Use ln() to create vertical space, and avoid positioning every element with hard-coded coordinates unless the document requires a fixed form layout.

Add Headers and Footers

Create a subclass when every page needs repeated content:

from fpdf import FPDF


class ReportPDF(FPDF):
    def header(self):
        self.set_font("Helvetica", style="B", size=10)
        self.cell(0, 8, "Academify Report", align="C")
        self.ln(12)

    def footer(self):
        self.set_y(-15)
        self.set_font("Helvetica", style="I", size=9)
        self.cell(0, 10, f"Page {self.page_no()}", align="C")

The library calls header() and footer() automatically as pages are created.

Add an Image

from pathlib import Path
from fpdf import FPDF

logo = Path("assets") / "logo.png"

pdf = FPDF()
pdf.add_page()

if logo.exists():
    pdf.image(str(logo), x=15, y=12, w=35)

pdf.set_y(55)
pdf.set_font("Helvetica", size=12)
pdf.cell(0, 8, "Document with an image", new_x="LMARGIN", new_y="NEXT")
pdf.output("document-with-image.pdf")

Always verify that an image exists before inserting it. The pathlib guide covers portable paths and file checks.

Create a Simple Table

from fpdf import FPDF

rows = [
    ("Product", "Quantity", "Price"),
    ("Keyboard", "2", "$90.00"),
    ("Mouse", "3", "$60.00"),
    ("Monitor", "1", "$240.00"),
]

pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=10)

widths = [80, 40, 50]

for row_index, row in enumerate(rows):
    if row_index == 0:
        pdf.set_font("Helvetica", style="B", size=10)
    else:
        pdf.set_font("Helvetica", size=10)

    for value, width in zip(row, widths):
        pdf.cell(width, 9, value, border=1)
    pdf.ln()

pdf.output("products.pdf")

zip() combines each value with its column width. See the Python zip guide for more examples.

Generate a PDF from a List of Dictionaries

from fpdf import FPDF

sales = [
    {"customer": "Ana", "total": 120.50},
    {"customer": "Jordan", "total": 89.90},
    {"customer": "Sam", "total": 215.00},
]

pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", style="B", size=18)
pdf.cell(0, 12, "Sales Summary", new_x="LMARGIN", new_y="NEXT")

pdf.set_font("Helvetica", size=11)
for sale in sales:
    line = f"Customer: {sale['customer']} | Total: ${sale['total']:.2f}"
    pdf.cell(0, 8, line, new_x="LMARGIN", new_y="NEXT")

pdf.output("sales-summary.pdf")

This pattern works well when data comes from a CSV file, database, API, or Pandas DataFrame. The CSV guide and Pandas guide explain common data sources.

Create a Reusable Report Function

from pathlib import Path
from fpdf import FPDF


def create_report(title: str, lines: list[str], output: Path) -> Path:
    output.parent.mkdir(parents=True, exist_ok=True)

    pdf = FPDF()
    pdf.set_auto_page_break(auto=True, margin=15)
    pdf.add_page()
    pdf.set_font("Helvetica", style="B", size=18)
    pdf.cell(0, 12, title, new_x="LMARGIN", new_y="NEXT")
    pdf.ln(4)

    pdf.set_font("Helvetica", size=11)
    for line in lines:
        pdf.multi_cell(0, 6, line)
        pdf.ln(2)

    pdf.output(str(output))
    return output


created_file = create_report(
    "Project Notes",
    ["First item", "Second item", "Third item"],
    Path("output") / "notes.pdf",
)
print(created_file.resolve())

A focused function is easier to test and reuse. The functions guide explains parameters, return values, and clean function design.

Handle Unicode and Custom Fonts

Built-in PDF fonts do not cover every Unicode character. For multilingual text, register a Unicode font file that your project is licensed to distribute.

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.add_font("DejaVu", fname="fonts/DejaVuSans.ttf")
pdf.set_font("DejaVu", size=12)
pdf.multi_cell(0, 7, "Text with international characters: café, ação, 日本語")
pdf.output("unicode.pdf")

Keep font files inside a predictable project directory and check the path before generating the document. Do not assume a font installed on your computer exists on a server.

Build an Invoice

A basic invoice normally contains a seller, customer, item rows, totals, date, and payment information. Separate calculation from presentation so the PDF layer receives already validated values.

items = [
    {"description": "Consulting", "quantity": 3, "unit_price": 80.0},
    {"description": "Support", "quantity": 2, "unit_price": 45.0},
]

total = sum(item["quantity"] * item["unit_price"] for item in items)
print(f"Invoice total: ${total:.2f}")

Then render the values into rows. This separation makes calculations testable with Pytest.

Handle Errors Clearly

from pathlib import Path

output = Path("reports") / "result.pdf"

try:
    output.parent.mkdir(parents=True, exist_ok=True)
    pdf.output(str(output))
except PermissionError:
    print("The PDF cannot be written to this folder.")
except OSError as error:
    print(f"Could not create the PDF: {error}")

Typical failures include missing images, missing font files, invalid output folders, permissions, and unsupported characters. Catch only errors you can explain or recover from.

Common Mistakes

  • Forgetting to call add_page().
  • Writing text before selecting a font.
  • Using a font that does not contain required characters.
  • Assuming relative paths point to the script directory.
  • Creating fixed coordinates that overlap when text wraps.
  • Not enabling automatic page breaks.
  • Mixing calculations and PDF layout in one long function.
  • Overwriting an existing document without checking.

Frequently Asked Questions

What should I install: fpdf or fpdf2?

For modern projects, install fpdf2. The import statement remains from fpdf import FPDF.

Can FPDF edit an existing PDF?

It is primarily designed to generate new PDFs. Editing or merging existing documents may require a different PDF library.

Can I add charts?

Yes. Save a chart as an image and insert it with image().

Can I create multi-page reports?

Yes. Enable automatic page breaks and use headers and footers for repeated content.

Does it support accented and international text?

Yes, when you register a Unicode font that contains the needed characters.

Conclusion

FPDF provides a practical way to create PDF files with Python. Start with a page, font, and a few cells, then add multi-line text, images, tables, headers, footers, and automatic page breaks.

For reliable applications, keep calculations separate from layout, use pathlib for file locations, validate external assets, and register an appropriate font for Unicode text. These habits turn a small PDF script into a reusable reporting system.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    representação de automação com ícones de engrenagens, gráficos e pessoas no background
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Automate Everyday Tasks with Python

    Learn how to automate everyday tasks with Python: organize files, rename batches, process CSV data, create reports, schedule scripts, and

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    logo do BeautifulSoup em um fundo branco
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Web Scraping with BeautifulSoup and Requests

    Learn web scraping with Requests and BeautifulSoup: fetch pages, parse HTML, select elements, clean data, follow pagination, handle errors, respect

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    Criação de bot Telegram com Python
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Build a Telegram Bot with Python

    Build a Telegram bot with Python step by step: create a bot token, handle commands and messages, add buttons, store

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Automação web com Selenium usando Python
    Automation and ScriptsWeb Development
    Foto de perfil de Leandro Hirt da Academify

    Selenium with Python: Complete Web Automation Guide

    Learn Selenium with Python from setup to browser control, element locators, waits, forms, screenshots, page objects, testing, and reliable web

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Logos do Python e Excel lado a lado representando a importação de dados do Excel para Python.
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Import Excel Data into Python with Pandas

    Learn how to import Excel data into Python with Pandas and openpyxl, select sheets and columns, clean values, handle dates

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Python e ícone de e-mail sobre teclado de notebook
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Automate Emails with Python: Complete Guide

    Learn how to automate emails with Python using EmailMessage and smtplib, environment variables, HTML, attachments, multiple recipients, scheduling, and error

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026