DEV Community

Kyle Rhodelander
Kyle Rhodelander

Posted on

Best Python Libraries for Automating PDF Reports in 2026 (With Code Examples)

Best Python Libraries for Automating PDF Reports in 2026 (With Code Examples)

If you've ever sat through the soul-crushing process of manually generating the same PDF report week after week, you already know the pain. Whether you're building financial summaries, analytics dashboards, or client-facing documents, automating PDF generation with Python can save hours of repetitive work and eliminate human error.

In 2026, the Python ecosystem for PDF automation has matured significantly. There are now libraries for everything from pixel-perfect templating to data-heavy report generation. This guide covers the best options, with honest assessments and working code examples so you can pick the right tool for your use case.


Why Automate PDF Reports with Python?

Before diving into libraries, let's be clear about what "automating PDF reports" actually means in practice:

  • Scheduled reports: Generate and email PDFs every Monday morning from live database data
  • On-demand generation: A user clicks "Export to PDF" in your web app and gets a polished document
  • Bulk document creation: Create thousands of personalized invoices or certificates from a data source
  • Data pipelines: Include PDF export as the final stage of an ETL or analytics workflow

Python is a natural fit for all of these. It integrates easily with databases, APIs, pandas DataFrames, and scheduling tools like Airflow or cron.


The Top Python PDF Libraries in 2026

1. ReportLab — The Veteran Workhorse

Best for: Highly customized, pixel-perfect PDFs where you need full layout control

ReportLab has been around for over two decades and is still one of the most powerful options available. It gives you low-level control over every element on the page. The learning curve is steeper than newer alternatives, but nothing beats it when you need precise positioning.

The open-source version (reportlab) handles most use cases. The commercial ReportLab PLUS product adds a visual design tool and enhanced chart components for teams that need enterprise features.

from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet

def generate_sales_report(data, filename="sales_report.pdf"):
    doc = SimpleDocTemplate(filename, pagesize=letter)
    styles = getSampleStyleSheet()
    elements = []

    # Title
    title = Paragraph("Monthly Sales Report — June 2026", styles["Title"])
    elements.append(title)

    # Table data
    table_data = [["Product", "Units Sold", "Revenue"]]
    for row in data:
        table_data.append([row["product"], str(row["units"]), f"${row['revenue']:,.2f}"])

    table = Table(table_data)
    table.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#2E4057")),
        ("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor("#F0F4F8")]),
        ("GRID", (0, 0), (-1, -1), 0.5, colors.grey),
        ("PADDING", (0, 0), (-1, -1), 8),
    ]))
    elements.append(table)
    doc.build(elements)
    print(f"Report saved to {filename}")

# Sample usage
sales_data = [
    {"product": "Widget A", "units": 1240, "revenue": 62000},
    {"product": "Widget B", "units": 875, "revenue": 43750},
    {"product": "Widget C", "units": 2100, "revenue": 105000},
]
generate_sales_report(sales_data)
Enter fullscreen mode Exit fullscreen mode

Pros: Extremely powerful, excellent for tables and charts, large community

Cons: Verbose API, no HTML/CSS templating, steep initial learning curve


2. WeasyPrint — HTML/CSS to PDF

Best for: Web developers who want to design reports in HTML/CSS and export to PDF

WeasyPrint is a game-changer if your team already knows HTML and CSS. You design the report like a webpage, then WeasyPrint renders it to PDF with high fidelity. This is especially useful when your report templates are already living as Jinja2 HTML files.

from weasyprint import HTML, CSS
from jinja2 import Environment, FileSystemLoader
import pandas as pd

def generate_html_report(dataframe, output_path="report.pdf"):
    env = Environment(loader=FileSystemLoader("templates"))
    template = env.get_template("report_template.html")

    # Convert DataFrame to list of dicts for templating
    rows = dataframe.to_dict(orient="records")
    totals = {
        "units": dataframe["units"].sum(),
        "revenue": dataframe["revenue"].sum()
    }

    html_content = template.render(rows=rows, totals=totals, title="Q2 2026 Report")

    HTML(string=html_content).write_pdf(
        output_path,
        stylesheets=[CSS(string="@page { size: A4; margin: 2cm; }")]
    )
    print(f"PDF written to {output_path}")
Enter fullscreen mode Exit fullscreen mode

And your templates/report_template.html might look like:

<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial, sans-serif; color: #333; }
        h1 { color: #2E4057; border-bottom: 3px solid #2E4057; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th { background: #2E4057; color: white; padding: 10px; text-align: left; }
        td { padding: 8px 10px; border-bottom: 1px solid #ddd; }
        tr:nth-child(even) { background: #f9f9f9; }
    </style>
</head>
<body>
    <h1>{{ title }}</h1>
    <table>
        <tr><th>Product</th><th>Units</th><th>Revenue</th></tr>
        {% for row in rows %}
        <tr>
            <td>{{ row.product }}</td>
            <td>{{ row.units }}</td>
            <td>${{ "%.2f"|format(row.revenue) }}</td>
        </tr>
        {% endfor %}
        <tr><td><strong>Total</strong></td>
            <td><strong>{{ totals.units }}</strong></td>
            <td><strong>${{ "%.2f"|format(totals.revenue) }}</strong></td>
        </tr>
    </table>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Pros: Designer-friendly, easy templating with Jinja2, great for complex layouts

Cons: Requires system-level dependencies (libpango, libcairo), not ideal for embedded environments


3. fpdf2 — Lightweight and Modern

Best for: Simple to medium-complexity reports where you want minimal dependencies

fpdf2 is the actively maintained fork of PyFPDF and strikes a great balance between simplicity and capability. It's pure Python, requires no external system libraries, and supports Unicode, images, cell layouts, and multi-page documents with ease.

from fpdf import FPDF
import datetime

class SalesReportPDF(FPDF):
    def header(self):
        self.set_font("Helvetica", "B", 16)
        self.set_fill_color(46, 64, 87)
        self.set_text_color(255, 255, 255)
        self.cell(0, 14, "Automated Sales Report", align="C", fill=True, new_x="LMARGIN", new_y="NEXT")
        self.set_text_color(0, 0, 0)
        self.ln(4)

    def footer(self):
        self.set_y(-15)
        self.set_font("Helvetica", "I", 8)
        self.set_text_color(150, 150, 150)
        self.cell(0, 10, f"Generated on {datetime.date.today()} | Page {self.page_no()}", align="C")

    def add_table(self, headers, rows):
        self.set_font("Helvetica", "B", 10)
        self.set_fill_color(220, 230, 241)
        col_width = self.epw / len(headers)
        for header in headers:
            self.cell(col_width, 10, header, border=1, fill=True, align="C")
        self.ln()

        self.set_font("Helvetica", size=10)
        for i, row in enumerate(rows):
            fill = i % 2 == 0
            self.set_fill_color(245, 248, 252) if fill else self.set_fill_color(255, 255, 255)
            for cell in row:
                self.cell(col_width, 9, str(cell), border=1, fill=True, align="C")
            self.ln()

pdf = SalesReportPDF()
pdf.add_page()
pdf.add_table(
    headers=["Product", "Units Sold", "Revenue ($)"],
    rows=[
        ["Widget A", "1,240", "62,000.00"],
        ["Widget B", "875", "43,750.00"],
        ["Widget C", "2,100", "105,000.00"],
    ]
)
pdf.output("fpdf2_report.pdf")
Enter fullscreen mode Exit fullscreen mode

Pros: Pure Python, easy to learn, active development, good Unicode support

Cons: Less powerful than ReportLab for complex chart work, no HTML rendering


4. Plotly + kaleido — Data Visualization in PDFs

Best for: Reports that need embedded charts and graphs from live data

If your PDF reports are heavy on data visualization, combine plotly with kaleido to render charts as static images, then embed them using any of the libraries above.

import plotly.graph_objects as go
import plotly.io as pio

def create_revenue_chart(products, revenues, output_path="chart.png"):
    fig = go.Figure(data=[
        go.Bar(
            x=products,
            y=revenues,
            marker_color=["#2E4057", "#048A81", "#54C6EB"],
            text=[f"${v:,}" for v in revenues],
            textposition="outside"
        )
    ])
    fig.update_layout(
        title="Revenue by Product — Q2 2026",
        xaxis_title="Product",
        yaxis_title="Revenue ($)",
        plot_bgcolor="white",
        paper_bgcolor="white",
        font=dict(family="Arial", size=13),
        height=400
    )
    # kaleido must be installed: pip install kaleido
    pio.write_image(fig, output_path, format="png", width=700, height=400, scale=2)
    return output_path

# Then embed the image in your fpdf2 or ReportLab document
products = ["Widget A", "Widget B", "Widget C"]
revenues = [62000, 43750, 105000]
chart_path = create_revenue_chart(products, revenues)

# In fpdf2:
# pdf.image(chart_path, x=10, y=60, w=180)
Enter fullscreen mode Exit fullscreen mode

Pros: Beautiful, interactive-quality charts rendered to static images, huge chart library

Cons: kaleido can be finicky on some environments; adds dependencies


5. PyMuPDF (fitz) — Reading, Editing, and Merging PDFs

Best for: When you need to manipulate existing PDFs, fill form fields, or merge documents

Not every PDF workflow starts from scratch. Sometimes you need to inject data into existing templates, merge multiple reports, or extract content. PyMuPDF (also installable as pymupdf) is the fastest and most capable library for this.

import fitz  # PyMuPDF

def stamp_pdf_with_metadata(input_path, output_path, company_name, report_date):
    doc = fitz.open(input_path)
    for page in doc:
        # Add a watermark-style stamp on every page
        rect = fitz.Rect(400, 20, 590, 50)
        page.draw_rect(rect, color=(0.18, 0.25, 0.34), fill=(0.18, 0.25, 0.34))
        page.insert_text(
            (405, 40),
            f"{company_name} | {report_date}",
            fontsize=9,
            color=(1, 1, 1)
        )
    doc.save(output_path)
    print(f"Stamped PDF saved: {output_path}")

stamp_pdf_with_metadata("report_draft.pdf", "report_final.pdf", "Acme Corp", "June 2026")
Enter fullscreen mode Exit fullscreen mode

Pros: Extremely fast, can read/edit/merge/annotate existing PDFs, extracts text and images

Cons: Not ideal for building reports from scratch, licensing is AGPL (commercial license available)


Choosing the Right Library

Use Case Recommended Library
Full layout control from scratch ReportLab
HTML/CSS-based templates WeasyPrint
Simple reports, minimal dependencies fpdf2
Data-heavy charts Plotly + kaleido
Edit or merge existing PDFs PyMuPDF
Complex reports with charts + tables fpdf2 or ReportLab + Plotly

For most automation pipelines in 2026, a combination of fpdf2 + Plotly covers 80% of real-world use cases with the least friction. If your team has web design skills, WeasyPrint + Jinja2 is often the most maintainable long-term choice.


Scheduling Your Reports Automatically

Building the PDF is only half the job. Here's a minimal example using Python's schedule library to run report generation every Monday morning:

import schedule
import time
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

def run_weekly_report():
    # Generate your PDF here (call your function)
    generate_sales_report(fetch_data_from_db())
    send_email_with_attachment("report.pdf", recipient="manager@company.com")
    print("Weekly report sent.")

schedule.every().monday.at("08:00").do(run_weekly_report)

while True:
    schedule.run_pending()
    time.sleep(60)
Enter fullscreen mode Exit fullscreen mode

For production workloads, consider Apache Airflow or Prefect for orchestration with proper logging, retries, and alerting.


Recommended Tools and Resources

To round out your PDF automation stack, these tools pair well with the libraries above:

  • Jinja2 — Templating engine for HTML-based reports with WeasyPrint
  • pandas — Data manipulation before feeding into your PDF generator
  • Pillow — Image preprocessing before embedding in PDFs
  • Amazon SES — Reliable bulk email delivery for distributing generated reports

For those building SaaS applications around PDF generation, the ReportLab commercial license and PDFShift API are worth exploring for hosted, scalable HTML-to-PDF rendering.


Final Thoughts

PDF report automation is one of those investments that pays dividends immediately and compounds over time. An hour spent building an automated report pipeline can save dozens of hours per year and reduce costly manual errors.

Start simple: pick fpdf2 if you want to get something working today with minimal setup, or WeasyPrint if you have existing HTML skills and want beautiful, maintainable templates. Add Plotly charts when your reports need visual impact, and reach for PyMuPDF when you need to work with existing documents.

The code examples above are production-ready starting points, not toy demos. Clone them, adapt them to your data sources, and you'll have your first automated PDF pipeline running before end of day.


Ready to Automate Your First Report?

Try this today: Install fpdf2 with pip install fpdf2, copy the SalesReportPDF example above, replace the sample data with a CSV read via pandas, and you'll have a working automated report in under 30 minutes.

If you found this guide useful, share it with your team — chances are someone else is still manually copying data into Word documents every Friday. And if you're building something more complex, drop a comment below describing your use case. I'm happy to point you toward the right combination of tools.

Have questions about a specific library or use case? Leave a comment or reach out directly.

Top comments (0)