Skip to main content

Generate PDFs in Python Using Pyppeteer

· 10 min read
Michał Szymanowski
Michał Szymanowski
PDFBolt Co-Founder

Illustration of generating PDF files using Pyppeteer

In this comprehensive guide, we'll show you how to create a dynamic certificate PDF using Pyppeteer and Mako templates for HTML population. As demand for professional certificate generation grows – whether for online courses, digital awards, or corporate recognition – this step-by-step tutorial is designed to help you transform your HTML content into high-quality PDFs effortlessly. Discover how Pyppeteer's headless browser capabilities, paired with the flexibility of Mako's templating engine, can streamline your certificate PDF generation process and meet modern web standards.

What is Pyppeteer?

Pyppeteer is a Python library offering a high-level API to control Chromium-based browsers in headless mode for seamless HTML to PDF conversion. Much like Puppeteer for Node.js, Pyppeteer empowers developers to render dynamic HTML, execute complex JavaScript, and generate high-quality PDFs or screenshots with ease. This powerful tool is perfect for converting custom-designed HTML templates into professional-grade PDF documents, making it an essential solution for efficient PDF generation and enhancing your web application's document workflows.

Differences Between Puppeteer and Pyppeteer

Differences Between Puppeteer and Pyppeteer

While this guide focuses on using Pyppeteer to generate certificates in Python, it's helpful to understand how it differs from its JavaScript counterpart, Puppeteer. Below are some key distinctions.

Language and API Design

  • Puppeteer is built for Node.js and uses JavaScript’s asynchronous model with Promises.
  • Pyppeteer adapts this functionality for Python, offering a more Pythonic API that accepts both dictionaries and keyword arguments, making it more intuitive for Python developers.

Options Passing

  • Puppeteer: Uses JavaScript objects (e.g., launch({ headless: true })).
  • Pyppeteer: Supports both dictionaries and keyword arguments (e.g., launch(headless=True)).

Element Selection

  • Puppeteer: Employs concise methods like $(), $$(), and $x().
  • Pyppeteer: Uses more descriptive methods (querySelector(), querySelectorAll(), and xpath()) along with shorthands (J(), JJ(), and Jx()).

JavaScript Evaluation

  • Both libraries use evaluate(), but Pyppeteer may need force_expr=True to correctly interpret expressions.

These differences ensure that Pyppeteer integrates naturally into Python projects while providing functionality similar to Puppeteer.

Step-by-Step Guide: Creating a Certificate PDF with Pyppeteer

Below is a comprehensive guide to set up your environment, design a certificate template using Mako and generate a PDF with Pyppeteer.

Step 1: Set Up Your Environment

Prerequisites: Before you begin, make sure your Python development environment is ready for seamless PDF generation with Pyppeteer.

RequirementRecommendation and Download Links
PythonPython 3.6 or higher installed. If not, download from Python.org.
Package ManagerUse pip (included with Python) or pipenv for dependency management. Learn more in the pip documentation.
IDEUse PyCharm – my personal recommendation – or alternatives like VS Code or Atom.
  1. Then create a new project directory and navigate into it:
mkdir certificate-generation
cd certificate-generation
  1. Install the necessary packages.

Use pip to install Pyppeteer (for generating PDFs) and Mako (for templating):

pip install pyppeteer mako

Step 2: Set Up Your Project Directory Structure

Organize your project files in a clear and logical hierarchy. This structure helps keep your project organized and simplifies managing your certificate generation process.

Recommended layout:

certificate-generation/
├── data/ # Directory for your JSON data files
│ └── certificate_data.json # File containing certificate details
├── templates/ # Directory for your HTML templates
│ └── certificate_template.html # Mako template for the certificate design
└── generate_certificate.py # Python script for generating the PDF

Step 3: Create Your Certificate Template with Mako

  • Design your certificate using HTML and CSS, and use Mako templating syntax to dynamically insert data.
  • Save the template as certificate_template.html in your templates directory.
  • This template should include placeholders that will be dynamically populated with your certificate data.
View Certificate Template (HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Certificate</title>
<style>
body, html {
margin: 0;
padding: 0;
}

body {
font-family: Georgia, serif;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}

.container {
width: 100%;
aspect-ratio: 1050 / 742;
border: 20px solid #c9aa81;
padding: 30px;
box-sizing: border-box;
display: flex;
flex-direction: column;
background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
}

.header {
color: #b59467;
margin: 50px 0 30px;
text-align: center;
}

.title {
font-size: 78px;
letter-spacing: 4px;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}

.subtitle {
font-size: 30px;
letter-spacing: 2px;
margin-top: 5px;
}

.person-description {
text-align: center;
font-size: 24px;
margin: 10px 0;
}

.person {
font-size: 62px;
font-style: italic;
margin: 20px auto;
text-align: center;
color: #333;
}

.course {
text-align: center;
margin: 20px 0;
}

.course-description {
font-size: 24px;
margin-bottom: 20px;
}

.course-name {
font-size: 30px;
color: #333;
}

.footer-details {
display: flex;
justify-content: space-between;
margin-top: auto;
padding-top: 15px;
}

.footer-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 10px;
}

.footer-item .label {
font-size: 18px;
text-align: center;
margin-top: 5px;
}

.value {
font-size: 24px;
font-family: 'Satisfy', cursive;
font-style: italic;
}

.divider {
width: 60%;
border: 0;
border-top: 1px solid #7c5f38;
margin: 10px auto;
}

@media (max-width: 600px) {
.title {
font-size: 36px;
}

.person {
font-size: 24px;
}

.person-description,
.course-description {
font-size: 18px;
}

.course-name {
font-size: 28px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Certificate</h1>
<h2 class="subtitle">of Completion</h2>
</div>
<div class="person-description">This certificate is proudly presented to</div>
<div class="person">${recipient_name}</div>
<div class="course">
<div class="course-description">
For successfully completing the course:
</div>
<div class="course-name">${course_name}</div>
</div>
<div class="footer-details">
<div class="footer-item">
<div class="value">${date}</div>
<hr class="divider">
<div class="label">Date</div>
</div>
<div class="footer-item">
<div class="value">${instructor}</div>
<hr class="divider">
<div class="label">Instructor</div>
</div>
</div>
</div>
</body>
</html>

Step 4: Prepare Your Certificate Data

Next, create a JSON file named certificate_data.json in your data directory with the dynamic values for your certificate.

View Certificate Data (JSON)
{
"recipient_name": "Jane Doe",
"course_name": "Digital Marketing Strategies",
"date": "2025-03-09",
"instructor": "John Smith"
}

Step 5: Write the PDF Generation Script

Now, create a Python script called generate_certificate.py in the root of your project. This script will load the certificate data, render the HTML using the Mako template, and generate a PDF using Pyppeteer.

View Complete Script
import os
import json
import asyncio
from mako.template import Template
import datetime
import random

# Set the desired Chromium revision for pyppeteer
PYPPETEER_CHROMIUM_REVISION = '1263111'
os.environ['PYPPETEER_CHROMIUM_REVISION'] = PYPPETEER_CHROMIUM_REVISION

from pyppeteer import launch

async def generate_pdf(html_content, output_path):
# Launch a headless Chromium browser
browser = await launch()
page = await browser.newPage()

# Set the HTML content for the page and wait for all resources to load
await page.setContent(html_content)

# Generate the PDF in A4 landscape mode with background graphics printed
await page.pdf({
'path': output_path,
'format': 'A4',
'printBackground': True,
'landscape': True
})

# Close the browser
await browser.close()


def render_template(template_path, data):
# Load the Mako template from file
with open(template_path, 'r', encoding='utf-8') as file:
template_content = file.read()
template = Template(template_content)
# Render the template with provided data
return template.render(**data)


def load_data(json_path):
# Load data from a JSON file
with open(json_path, 'r', encoding='utf-8') as file:
data = json.load(file)
return data


async def main():
# Define file paths
json_path = os.path.join('data', 'certificate_data.json')
template_path = os.path.join('templates', 'certificate_template.html')

# Create a unique output file name using a timestamp and a random number
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
output_pdf = f'certificate_{timestamp}.pdf'

# Load certificate data and render the HTML template
data = load_data(json_path)
rendered_html = render_template(template_path, data)

# Generate the PDF
await generate_pdf(rendered_html, output_pdf)
print(f'PDF generated: {output_pdf}')


if __name__ == '__main__':
asyncio.run(main())

Step 6: Run the Script

With everything in place, navigate to your certificate-generation directory and run your script from the terminal:

python generate_certificate.py

Step 7: Verify Your Certificate PDF

After running the script, open the generated certificate_<timestamp>.pdf file to ensure that your certificate’s design and dynamic content are rendered perfectly. This final check confirms that your PDF generation workflow is functioning as intended.

Certificate PDF Preview:

Example of a generated certificate PDF using Pyppeteer

Bonus: Generating a Web Page PDF

In addition to creating PDFs from HTML, you can also generate a PDF directly from an existing web page. This is useful if you need to archive a webpage or create a snapshot of a live site.

Below is a quick example of how to do it using Pyppeteer.

View Code for Generating PDF from URL
import asyncio
import os

# Set the desired Chromium revision for pyppeteer
PYPPETEER_CHROMIUM_REVISION = '1263111'
os.environ['PYPPETEER_CHROMIUM_REVISION'] = PYPPETEER_CHROMIUM_REVISION

from pyppeteer import launch

async def generate_pdf_from_webpage(target_url, output_file):
# Initialize a headless Chromium browser instance
browser = await launch()
page = await browser.newPage()

# Navigate to the target URL and wait until network activity calms down
await page.goto(target_url, {'waitUntil': 'networkidle2'})

# Create a PDF file in A4 format, ensuring background graphics are included
await page.pdf({
'path': output_file,
'format': 'A4',
'printBackground': True
})

# Close the browser to free up resources
await browser.close()

# Example usage: Generate a PDF snapshot of a live webpage
asyncio.run(generate_pdf_from_webpage('https://example.com', 'webpage.pdf'))

Simplified Approach: Using PDF Templates

While Pyppeteer provides excellent control over PDF generation, managing the entire stack can add complexity to your project. If you're looking for a simpler approach that separates design from code, PDFBolt Templates offer an effective alternative.

Why Consider Templates?

Instead of handling browser automation, dependency management, and HTML rendering in your Python code, templates let you:

  • Design once, use everywhere: Create reusable layouts through a visual designer, then populate them with JSON data.
  • Eliminate dependencies: No need to manage Chromium installations, browser lifecycles, or headless browser configurations.
  • Simplify deployment: API-based generation removes server-side browser requirements and memory overhead.
  • Improve maintainability: Update designs through the web interface with version control, without touching your application code.

Certificate Generation via Templates

Below is the process for generating our certificate through PDFBolt's template system.

Step 1: Create Your Template

  1. Sign up or log into your account.
  2. Navigate to TemplatesCreate Template.
  3. Choose Create from Scratch or select a certificate from the gallery and customize it to your needs.
Template Resources
  1. Design your certificate using HTML/CSS with Handlebars syntax:
Certificate Template with Handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Certificate</title>
<style>
body, html {
margin: 0;
padding: 0;
}

body {
font-family: Georgia, serif;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}

.container {
width: 100%;
aspect-ratio: 1050 / 742;
border: 20px solid #c9aa81;
padding: 30px;
box-sizing: border-box;
display: flex;
flex-direction: column;
background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
}

.header {
color: #b59467;
margin: 50px 0 30px;
text-align: center;
}

.title {
font-size: 78px;
letter-spacing: 4px;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}

.subtitle {
font-size: 30px;
letter-spacing: 2px;
margin-top: 5px;
}

.person-description {
text-align: center;
font-size: 24px;
margin: 10px 0;
}

.person {
font-size: 62px;
font-style: italic;
margin: 20px auto;
text-align: center;
color: #333;
}

.course {
text-align: center;
margin: 20px 0;
}

.course-description {
font-size: 24px;
margin-bottom: 20px;
}

.course-name {
font-size: 30px;
color: #333;
}

.footer-details {
display: flex;
justify-content: space-between;
margin-top: auto;
padding-top: 15px;
}

.footer-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 10px;
}

.footer-item .label {
font-size: 18px;
text-align: center;
margin-top: 5px;
}

.value {
font-size: 24px;
font-family: 'Satisfy', cursive;
font-style: italic;
}

.divider {
width: 60%;
border: 0;
border-top: 1px solid #7c5f38;
margin: 10px auto;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Certificate</h1>
<h2 class="subtitle">of Completion</h2>
</div>
<div class="person-description">This certificate is proudly presented to</div>
<div class="person">{{recipient_name}}</div>
<div class="course">
<div class="course-description">
For successfully completing the course:
</div>
<div class="course-name">{{course_name}}</div>
</div>
<div class="footer-details">
<div class="footer-item">
<div class="value">{{date}}</div>
<hr class="divider">
<div class="label">Date</div>
</div>
<div class="footer-item">
<div class="value">{{instructor}}</div>
<hr class="divider">
<div class="label">Instructor</div>
</div>
</div>
</div>
</body>
</html>

Step 2: Test and Publish

  1. Add sample data in the DATA tab to preview your template:
{
"recipient_name": "Jane Doe",
"course_name": "Digital Marketing Strategies",
"date": "2025-03-09",
"instructor": "John Smith"
}
  1. Use Real PDF Preview to generate an actual PDF and verify the output.
  2. Click Publish to make your template available via API.
  3. Copy your template ID for integration or access ready-to-use code snippets for multiple programming languages through the Get API Code button.

Step 3: Generate Certificates from Python

Here's the equivalent Python code using PDFBolt's API – notice how much simpler it becomes:

import requests
import json

url = "https://api.pdfbolt.com/v1/direct"
headers = {
"API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"Content-Type": "application/json"
}

data_json = '''{
"templateId": "your-certificate-template-id",
"templateData": {
"recipient_name":"Jane Doe",
"course_name":"Digital Marketing Strategies",
"date":"2025-03-09",
"instructor":"John Smith"
}
}'''

data = json.loads(data_json)

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

with open('pdfbolt_certificate_example.pdf', 'wb') as f:
f.write(response.content)
print("PDF generated successfully")

except requests.exceptions.HTTPError as e:
print(f"HTTP {response.status_code}")
print(f"Error Message: {response.text}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")

PDF Configuration Options

You can configure PDF options like format, landscape, printBackground, and other parameters in two ways:

  1. In the Template Designer: Set options in the OPTIONS tab when creating your template.
  2. In API Requests: Include parameters directly in your API request payload:
{
"templateId": "your-certificate-template-id",
"templateData": { ... },
"format": "A4",
"landscape": true,
"printBackground": true
}

🎊 You're All Set!

With your template, you can now generate professional certificates for any recipient by simply changing the JSON data. The same template handles all variations while maintaining consistent design and formatting.

Additional Sources

Beyond templates, PDFBolt also supports:

  • URL to PDF: Convert any web page directly to PDF.
  • HTML Content: Send raw HTML content for PDF generation.

Conclusion

Using Pyppeteer with Mako templates offers a powerful solution for dynamic PDF generation in Python. This guide covered environment setup, designing a custom certificate template, populating it with dynamic data, and converting it to professional PDFs.

We also explored PDFBolt Templates as an alternative that simplifies development by eliminating browser dependencies and providing visual design tools with significantly less code and infrastructure management.

Both approaches provide seamless HTML to PDF conversion and high-quality results. Choose Pyppeteer when you need complete control over the rendering process, or opt for templates when you want to focus on application logic rather than PDF infrastructure.

Leverage these techniques to streamline your document creation workflow and choose the approach that best fits your project's needs.

Enjoy your smooth journey to professional PDF generation!