Overview

WeasyPrint is a powerful library that converts HTML/CSS into PDF. Combined with Jinja2 (a templating engine for Python), you can dynamically populate HTML templates with data, then export them to high-quality PDF files.

In this tutorial, you’ll learn how to:

  1. Set up a minimal project.
  2. Create a simple Jinja2 HTML template.
  3. Render the template and generate a PDF using WeasyPrint.

1. Install Dependencies

Make sure you have Python 3.7+ installed, then install the following packages:

pip install weasyprint jinja2

Depending on your operating system, WeasyPrint may require additional dependencies:

  • Linux: Make sure libpango, libcairo, librsvg, etc. are installed. For example (Ubuntu/Debian): bashCopysudo apt-get install libpango-1.0-0 libcairo2 libffi-dev shared-mime-info
  • Windows: You may need the MSYS2 environment to install the GTK-based dependencies. Please check the WeasyPrint installation docs for details.

2. Set Up Your Project

Create a simple project structure:

my_weasyprint_project/
├─ templates/
│ └─ example.html
├─ main.py
  • templates/example.html will hold our HTML template.
  • main.py will be our Python script that handles Jinja2 rendering and WeasyPrint PDF generation.

3. Create the Jinja2 HTML Template

In templates/example.html, let’s create a very simple HTML structure:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My PDF</title>
<style>
/* Basic styling to illustrate PDF rendering */
body {
font-family: Arial, sans-serif;
}
.title {
text-align: center;
margin-top: 40px;
}
.content {
margin: 20px;
}
.footer {
position: fixed;
bottom: 10px;
width: 100%;
text-align: center;
font-size: 0.9em;
color: #777;
}
</style>
</head>
<body>
<div class="title">
<h1>Hello, {{ name }}!</h1>
</div>
<div class="content">
<p>This is a simple PDF generated with WeasyPrint and Jinja2.</p>
<p>Today’s date is: {{ date_str }}</p>
</div>

<div class="footer">
<p>Page rendered at {{ render_time }}</p>
</div>
</body>
</html>

This template uses Jinja2 placeholders like {{ name }}, {{ date_str }}, and {{ render_time }}. We’ll supply these variables from our Python code.


4. Render the Template and Generate the PDF

In main.py, add the following code:

import datetime
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML

def generate_pdf(name):
# 1. Load Jinja2 environment
env = Environment(loader=FileSystemLoader('templates'))

# 2. Load the template
template = env.get_template('example.html')

# 3. Prepare context (the data we’ll pass to the template)
current_time = datetime.datetime.now()
context = {
'name': name,
'date_str': current_time.strftime('%Y-%m-%d'),
'render_time': current_time.strftime('%H:%M:%S'),
}

# 4. Render HTML
rendered_html = template.render(context)

# 5. Generate PDF from rendered HTML
pdf_file = HTML(string=rendered_html).write_pdf()

# 6. Save PDF to a file
with open('output.pdf', 'wb') as f:
f.write(pdf_file)

print("PDF generated successfully as output.pdf!")

if __name__ == '__main__':
# Provide a sample name to render
generate_pdf("Alice")

Explanation of Key Steps

  1. Environment & Loader
    We use Environment from Jinja2 to load templates from the templates/ directory.
  2. Template Rendering
    We fetch example.html using get_template('example.html'). Then we call template.render(context) with our data dictionary (context).
  3. WeasyPrint Conversion
    We pass the rendered HTML string to HTML(string=rendered_html).write_pdf(), which returns PDF bytes.
  4. Saving the PDF
    We open a file output.pdf in binary write mode and write the PDF bytes.

5. Run and Test

Run the main.py file:

python main.py

If everything is set up correctly, a new file named output.pdf will appear in your project folder. Open it to see your newly generated PDF!


6. Next Steps and Tips

  • Styling: WeasyPrint supports CSS, including page-break properties, custom fonts, etc.
  • Images: You can embed images by referencing local files (<img src="path/to/image.png">) or using data URLs (base64).
  • More Complex Templates: Jinja2 supports control structures (for, if, macros, etc.). Use these to handle dynamic lists, conditional rendering, and more advanced templating logic.
  • Deployment: If you plan to run this on a serverless platform (e.g., AWS Lambda), ensure that all WeasyPrint dependencies are included in your environment.

Conclusion

With just a few lines of code, you can combine Jinja2 templating and WeasyPrint to dynamically generate PDF documents. This workflow is extremely useful for invoicing, reporting, or any scenario where you need to transform HTML content into a professionally formatted PDF.

Feel free to extend this tutorial by adding:

  • Custom fonts
  • Multi-page layouts
  • Page headers/footers
  • Tables, charts, or images

Happy coding!