· tutorials · 9 min read
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.

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.
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
pip install fpdf2
Example Usage
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 imagepdf.image("logo.png", x=10, y=30, w=50)
# HTML support is now built-inhtml = """<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
pip install reportlab
Example Usage
from reportlab.lib.pagesizes import letterfrom reportlab.lib import colorsfrom reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraphfrom reportlab.lib.styles import getSampleStyleSheet
# Create a PDF documentdoc = SimpleDocTemplate("reportlab_advanced.pdf", pagesize=letter)elements = []
# Add styled paragraphstyles = getSampleStyleSheet()elements.append(Paragraph("ReportLab Example with Tables", styles["Heading1"]))
# Create table datadata = [ ["Name", "Age", "Location"], ["John Doe", "34", "New York"], ["Jane Smith", "28", "San Francisco"], ["Robert Brown", "45", "Chicago"]]
# Create and style the tabletable = 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
pip install playwrightplaywright install
Example Usage
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
pip install pyppeteer
Example Usage
import asynciofrom 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 functionasyncio.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
pip install py3-wkhtmltopdf
Example Usage
from wkhtmltopdf import wkhtmltopdf
# From URLwkhtmltopdf(url='https://example.com', output_file='wkhtml_url.pdf')
# From HTML stringhtml_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
pip install pdfkit
Example Usage
import pdfkit
# Configuration (especially important for Windows)config = pdfkit.configuration(wkhtmltopdf='/path/to/wkhtmltopdf')
# From URLpdfkit.from_url('https://example.com', 'pdfkit_url.pdf', configuration=config)
# From filepdfkit.from_file('input.html', 'pdfkit_file.pdf', configuration=config)
# From string with optionsoptions = { '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
pip install weasyprint
Example Usage
from weasyprint import HTML, CSSfrom weasyprint.text.fonts import FontConfiguration
# Basic usageHTML('https://example.com').write_pdf('weasyprint_basic.pdf')
# Advanced usage with custom CSSfont_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
pip install borb
Example Usage
from borb.pdf import Documentfrom borb.pdf import Pagefrom borb.pdf import PDFfrom borb.pdf import SingleColumnLayoutfrom borb.pdf import Paragraphfrom borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFontfrom borb.pdf.canvas.color.color import HexColorfrom decimal import Decimal
# Create documentpdf = Document()
# Add pagepage = Page()pdf.add_page(page)
# Use a layoutlayout = SingleColumnLayout(page)
# Add elementslayout.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 tablefrom borb.pdf.canvas.layout.table.flexible_column_width_table import FlexibleColumnWidthTablefrom 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 filewith 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
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:
Simple API Integration
import requestsapi_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)Enterprise Features
- 99.9% uptime with automatic scaling
- Advanced security and usage analytics
- No infrastructure maintenance required
Getting Started
- Sign up for a free account
- Design templates in the visual editor
- 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 today and transform your PDF generation workflow!
Automate your content with Templated