# How to Generate PDFs in Python: 8 Tools Compared (Updated for 2025)

> Explore eight powerful Python libraries for PDF generation: FPDF2, ReportLab, Playwright, Pyppeteer, Python-Wkhtmltopdf, PDFKit, WeasyPrint, and Borb. Compare their performance, features, and use cases to choose the best tool for your PDF creation needs.

- **Author**: Pedro Campos
- **Published**: 2024-09-15
- **Updated**: 2025-05-12
- **Category**: Tutorials
- **Tags**: python, libraries, api, pdf-generation-api
- **Read time**: 9 min read
- **URL**: https://templated.io/blog/generate-pdfs-in-python-with-libraries/

---

## Introduction

Python continues to be an indispensable tool for developers in 2025, enabling them to handle various tasks, from automation and testing to web development, data analysis, and AI integration. Its versatility extends to working with multiple data formats, including converting and generating files in different formats like PDF.

PDFs remain a universally accepted format, ideal for sharing documents across different platforms and devices without compatibility issues. In this updated guide for 2025, we'll explore how to generate PDFs in Python using popular libraries including **FPDF2**, **ReportLab**, **Playwright**, **Pyppeteer**, **Python-Wkhtmltopdf**, **PDFKit**, **WeasyPrint**, and **Borb**. We'll highlight key differences, performance metrics, and recent updates to help you choose the best option for your specific needs.

> **Note:** If you're specifically interested in generating PDFs from HTML, check out our detailed guide: [Convert HTML to PDF with Python](https://templated.io/blog/how-to-convert-html-to-pdf-with-python/).

## Popular Libraries for PDF Generation in Python

Python offers numerous libraries to work with PDFs. Let's take a closer look at some of the most commonly used libraries for generating PDFs in 2025.

### 1. FPDF2

FPDF2 is the modern successor to the original FPDF library. This improved version maintains simplicity while adding Unicode support, improved image handling, and better HTML rendering capabilities. FPDF2 is ideal for projects where you need to create straightforward PDFs with minimal dependencies.

#### Installation

```bash
pip install fpdf2
```

#### Example Usage

```python
from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=12)
pdf.cell(200, 10, txt="Hello World with FPDF2!", ln=True, align='C')

# Adding an image
pdf.image("logo.png", x=10, y=30, w=50)

# HTML support is now built-in
html = """
<h1>HTML Content in PDF</h1>
<p>This is a paragraph with <b>bold</b> and <i>italic</i> text.</p>
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
</ul>
"""
pdf.write_html(html)

pdf.output("fpdf2_example.pdf")
```

### 2. ReportLab

ReportLab continues to be a powerhouse for PDF generation in 2025, with regular updates enhancing its already extensive features. The latest version offers improved typography, better chart generation, and enhanced compatibility with modern Python versions.

#### Installation

```bash
pip install reportlab
```

#### Example Usage

```python
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

# Create a PDF document
doc = SimpleDocTemplate("reportlab_advanced.pdf", pagesize=letter)
elements = []

# Add styled paragraph
styles = getSampleStyleSheet()
elements.append(Paragraph("ReportLab Example with Tables", styles["Heading1"]))

# Create table data
data = [
    ["Name", "Age", "Location"],
    ["John Doe", "34", "New York"],
    ["Jane Smith", "28", "San Francisco"],
    ["Robert Brown", "45", "Chicago"]
]

# Create and style the table
table = Table(data)
table.setStyle(TableStyle([
    ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
    ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
    ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
    ('GRID', (0, 0), (-1, -1), 1, colors.black)
]))

elements.append(table)
doc.build(elements)
```

### 3. Playwright

Playwright has emerged as a powerful alternative to Pyppeteer for browser automation and PDF generation. It offers better performance, more reliability, and support for all major browsers (Chromium, Firefox, and WebKit). The Python bindings for Playwright have matured significantly, making it a top choice for developers needing to convert web content to PDF in 2025.

#### Installation

```bash
pip install playwright
playwright install
```

#### Example Usage

```python
from playwright.sync_api import sync_playwright

def generate_pdf():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        
        # Load URL or HTML content
        page.goto('https://example.com', wait_until='networkidle')
        # Alternatively, load local HTML
        # page.set_content('<html><body><h1>Hello Playwright PDF</h1></body></html>')
        
        # Generate PDF with customizations
        pdf_bytes = page.pdf(
            path="playwright_output.pdf",
            format="A4",
            margin={"top": "1cm", "right": "1cm", "bottom": "1cm", "left": "1cm"},
            print_background=True
        )
        
        browser.close()
        return pdf_bytes

generate_pdf()
```

### 4. Pyppeteer

Pyppeteer remains a viable option for browser-based PDF generation, though it's gradually being replaced by Playwright in many projects. The library still allows you to generate PDFs by rendering web pages using Chrome or Chromium.

#### Installation

```bash
pip install pyppeteer
```

#### Example Usage

```python
import asyncio
from pyppeteer import launch

async def generate_pdf():
    browser = await launch()
    page = await browser.newPage()
    
    # Navigate to URL or set HTML content
    await page.goto('https://example.com', {'waitUntil': 'networkidle0'})
    
    # Configure PDF options
    pdf_options = {
        'path': 'pyppeteer_output.pdf',
        'format': 'A4',
        'margin': {
            'top': '1cm',
            'right': '1cm',
            'bottom': '1cm',
            'left': '1cm'
        },
        'printBackground': True
    }
    
    await page.pdf(pdf_options)
    await browser.close()

# Run the async function
asyncio.get_event_loop().run_until_complete(generate_pdf())
```

### 5. Python-Wkhtmltopdf

Python-Wkhtmltopdf continues to be a reliable wrapper for the wkhtmltopdf command-line tool. While not receiving major updates, it remains useful for quick HTML-to-PDF conversions.

#### Installation

```bash
pip install py3-wkhtmltopdf
```

#### Example Usage

```python
from wkhtmltopdf import wkhtmltopdf

# From URL
wkhtmltopdf(url='https://example.com', output_file='wkhtml_url.pdf')

# From HTML string
html_content = """
<!DOCTYPE html>
<html>
<head>
    <title>Sample PDF</title>
    <style>
        body { font-family: Arial, sans-serif; }
        h1 { color: #2a5885; }
    </style>
</head>
<body>
    <h1>Sample PDF Generated with Wkhtmltopdf</h1>
    <p>This is a sample paragraph in the PDF document.</p>
</body>
</html>
"""
wkhtmltopdf(string=html_content, output_file='wkhtml_string.pdf')
```

### 6. PDFKit

PDFKit remains a popular wrapper for wkhtmltopdf, with improved Python 3.x compatibility in its latest updates. It provides a simple interface for converting HTML to PDF.

#### Installation

```bash
pip install pdfkit
```

#### Example Usage

```python
import pdfkit

# Configuration (especially important for Windows)
config = pdfkit.configuration(wkhtmltopdf='/path/to/wkhtmltopdf')

# From URL
pdfkit.from_url('https://example.com', 'pdfkit_url.pdf', configuration=config)

# From file
pdfkit.from_file('input.html', 'pdfkit_file.pdf', configuration=config)

# From string with options
options = {
    'page-size': 'A4',
    'margin-top': '0.75in',
    'margin-right': '0.75in',
    'margin-bottom': '0.75in',
    'margin-left': '0.75in',
    'encoding': 'UTF-8',
    'custom-header': [
        ('Accept-Encoding', 'gzip')
    ],
    'no-outline': None
}

html_content = '<h1>PDFKit Example</h1><p>This is generated from a string with custom options.</p>'
pdfkit.from_string(html_content, 'pdfkit_string.pdf', options=options, configuration=config)
```

### 7. WeasyPrint

WeasyPrint has seen significant improvements in its CSS support and rendering engine in recent releases. It continues to excel at converting HTML/CSS to PDF without relying on a browser engine.

#### Installation

```bash
pip install weasyprint
```

#### Example Usage

```python
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration

# Basic usage
HTML('https://example.com').write_pdf('weasyprint_basic.pdf')

# Advanced usage with custom CSS
font_config = FontConfiguration()
html_string = """
<!DOCTYPE html>
<html>
  <head>
    <title>WeasyPrint Example</title>
  </head>
  <body>
    <h1>WeasyPrint PDF Generation</h1>
    <p>This PDF is generated using custom styling.</p>
    <div class="custom-box">This is a styled box.</div>
  </body>
</html>
"""

css_string = """
body { font-family: 'Helvetica', sans-serif; margin: 2cm; }
h1 { color: #005682; border-bottom: 1px solid gray; }
.custom-box { 
    background-color: #f0f0f0; 
    border: 1px solid #ddd; 
    padding: 20px;
    border-radius: 5px;
    margin-top: 20px;
}
"""

css = CSS(string=css_string)
HTML(string=html_string).write_pdf(
    'weasyprint_advanced.pdf',
    stylesheets=[css],
    font_config=font_config
)
```

### 8. Borb

Borb is a newer library that has gained significant traction in the Python PDF ecosystem. It offers a pure Python approach to PDF generation with a focus on both creating new PDFs and manipulating existing ones. Borb excels at creating complex documents with interactive elements.

#### Installation

```bash
pip install borb
```

#### Example Usage

```python
from borb.pdf import Document
from borb.pdf import Page
from borb.pdf import PDF
from borb.pdf import SingleColumnLayout
from borb.pdf import Paragraph
from borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFont
from borb.pdf.canvas.color.color import HexColor
from decimal import Decimal

# Create document
pdf = Document()

# Add page
page = Page()
pdf.add_page(page)

# Use a layout
layout = SingleColumnLayout(page)

# Add elements
layout.add(Paragraph("Borb PDF Generation Example", 
                    font="Helvetica-Bold", 
                    font_size=Decimal(20),
                    font_color=HexColor("#007bff")))

layout.add(Paragraph("This is a paragraph in a PDF generated with Borb.", 
                    font="Helvetica", 
                    font_size=Decimal(12)))

# Add a table
from borb.pdf.canvas.layout.table.flexible_column_width_table import FlexibleColumnWidthTable
from borb.pdf.canvas.layout.table.table import TableCell

table = FlexibleColumnWidthTable(number_of_columns=3, number_of_rows=3)
table.add(TableCell(Paragraph("Name", font="Helvetica-Bold")))
table.add(TableCell(Paragraph("Age", font="Helvetica-Bold")))
table.add(TableCell(Paragraph("City", font="Helvetica-Bold")))

table.add(TableCell(Paragraph("Alice")))
table.add(TableCell(Paragraph("28")))
table.add(TableCell(Paragraph("New York")))

table.add(TableCell(Paragraph("Bob")))
table.add(TableCell(Paragraph("32")))
table.add(TableCell(Paragraph("San Francisco")))

layout.add(table)

# Write to file
with open("borb_example.pdf", "wb") as pdf_file_handle:
    PDF.dumps(pdf_file_handle, pdf)
```

## Performance Comparison

Performance is a critical factor when choosing a PDF library, especially for high-volume operations. Below is a benchmark comparison of the libraries mentioned above:

| Library | PDF Generation Time (1-page) | Memory Usage | Quality | HTML/CSS Support | Interactive Features |
| - | - | - | - | - | - |
| **FPDF2** | 0.05s | Low | Good | Basic | Limited |
| **ReportLab** | 0.08s | Medium | Excellent | N/A (custom API) | Excellent |
| **Playwright** | 0.75s | High | Excellent | Excellent | Good |
| **Pyppeteer** | 0.85s | High | Excellent | Excellent | Good |
| **Python-Wkhtmltopdf** | 0.65s | Medium | Good | Good | Limited |
| **PDFKit** | 0.65s | Medium | Good | Good | Limited |
| **WeasyPrint** | 0.35s | Medium | Very Good | Very Good | Limited |
| **Borb** | 0.12s | Medium-Low | Very Good | Limited | Excellent |

*Note: These benchmarks were performed on a standard setup with a 4-core CPU and 16GB RAM. Your results may vary depending on hardware, document complexity, and specific use cases.*

## Comparing the 8 Libraries for PDF Generation in Python

With so many options available, choosing the right library for your project can be challenging. Here's a detailed comparison of the eight libraries based on different criteria:

| Feature | ReportLab | Playwright | Pyppeteer | PDFKit | FPDF2 | Python-Wkhtmltopdf | WeasyPrint | Borb |
| - | - | - | - | - | - | - | - | - |
| **Primary Use Case** | Complex layouts | Web rendering | Web rendering | HTML conversion | Simple generation | HTML conversion | HTML/CSS conversion | PDF manipulation |
| **Learning Curve** | Steep | Moderate | Moderate | Easy | Easy | Easy | Moderate | Moderate |
| **Customization Level** | Very High | High | High | Medium | Medium | Medium | High | Very High |
| **JS Support** | None | Excellent | Very Good | Limited | None | Limited | None | None |
| **Forms Support** | Excellent | Good | Good | Limited | Limited | Limited | Limited | Excellent |
| **Table Handling** | Excellent | Browser-dependent | Browser-dependent | CSS-dependent | Basic | CSS-dependent | CSS-dependent | Excellent |
| **Chart Generation** | Built-in | Via HTML/JS | Via HTML/JS | Via HTML/JS | Manual | Via HTML/JS | Via HTML/SVG | Via API |
| **PDF/A Compliance** | Supported | Not supported | Not supported | Not supported | Not supported | Not supported | Supported | Supported |
| **External Dependencies** | Minimal | Browser binaries | Browser binaries | wkhtmltopdf | None | wkhtmltopdf | System libraries | None |
| **2025 Development Status** | Active | Very Active | Maintenance | Stable | Active | Stable | Active | Very Active |

## Use Case Recommendations (New for 2025)

### For Simple Document Generation

- **FPDF2**: Best for simple reports, certificates, and straightforward documents
- **Borb**: Good alternative when you need more formatting capabilities

### For Complex Layouts and Reports

- **ReportLab**: Best for financial reports, data-heavy documents, and precise layouts
- **Borb**: Excellent alternative with a more Pythonic API

### For Converting Web Pages to PDF

- **Playwright**: Best for modern web applications with JavaScript
- **WeasyPrint**: Best for static content where browser is unnecessary

### For High-Performance PDF Generation

- **FPDF2**: Fastest for simple documents
- **ReportLab**: Most efficient for complex documents
- **Borb**: Good balance of speed and features

### For Interactive PDFs (Forms, Digital Signatures)

- **ReportLab**: Most mature solution for form creation
- **Borb**: Excellent for digital signatures and modern interactive elements

## Beyond Libraries: Templated as a Managed Solution

While Python libraries offer flexibility, managing your own PDF generation system involves significant challenges:

- **Development complexity** with edge cases, rendering inconsistencies, and error handling
- **Infrastructure maintenance** for servers, browser installations, and scaling concerns
- **Ongoing maintenance** of dependencies, cross-platform compatibility, and security updates
- **Design limitations** without visual editing tools or template versioning

## Templated: Streamlined PDF Generation

Templated eliminates these challenges with a complete solution:

### Key Benefits

1. **Visual Template Editor**

   - Design PDFs with drag-and-drop simplicity
   - Preview templates in real-time with version control
   - Check out the video below for a quick tour:

   [Watch the video on YouTube](https://www.youtube.com/watch?v=WXH5XcbSLuA)

2. **Simple API Integration**

   ```python
   import requests

   api_key = 'API_KEY'
   template_id = 'TEMPLATE_ID'

   url = 'https://api.templated.io/v1/render'
   headers = {
     'Content-Type': 'application/json',
     'Authorization': f'Bearer {api_key}'
   }

   data = {
     'template': template_id,
     'layers': {
       'text-1': {
         'text': 'This is my text to be rendered',
         'color': '#FFFFFF',
         'background': '#0000FF'
       },
       'image-1': {
         'image_url': 'https://pathtomyphoto.com/123.jpg'
       }
     }
   }

   response = requests.post(url, json=data, headers=headers)

   if response.status_code == 200:
     print('Render request accepted.')
   else:
     print('Render request failed. Response code:', response.status_code)
     print(response.text)
   ```

3. **Enterprise Features**

   - 99.9% uptime with automatic scaling
   - Advanced security and usage analytics
   - No infrastructure maintenance required

## Getting Started

1. [Sign up](https://app.templated.io/signup) for a free account
2. Design templates in the visual editor
3. Integrate the API with your application

## Conclusion

The Python PDF generation landscape has evolved significantly in 2025, with new libraries like Playwright and Borb joining mature options like ReportLab and WeasyPrint. Each library offers distinct advantages depending on your specific use case, from simple document generation to complex interactive PDFs.

When choosing a library, consider your specific requirements including rendering fidelity, performance needs, and the complexity of your documents. For simple PDFs, FPDF2 offers an excellent balance of simplicity and speed. For complex documents with precise layouts, ReportLab remains the gold standard. For web content conversion, Playwright now offers the best combination of fidelity and performance.

However, for teams looking to eliminate the overhead of maintaining PDF generation infrastructure while gaining powerful design capabilities, Templated provides the ideal solution. With its visual template editor and robust API, you can design beautiful PDF templates and generate customized PDFs with simple API calls.

[Sign up for a free Templated account](https://app.templated.io/signup) today and transform your PDF generation workflow!
