How to Generate PDFs from Jinja2 Templates Using Python
In this tutorial, we will show you how to use a Python script to generate beautiful PDFs from Jinja2 templates. Whether you're creating invoices, reports, or any custom documents, this method allows you to automate the generation of PDFs directly from templates. We will also discuss the importance of using a local Python environment and good practices to keep your projects organized and manageable.
Prerequisites
Before we start, ensure that you have the following tools and libraries installed:
•Python: The programming language for this script.
•Jinja2: A templating engine used to generate HTML from data.
•pdfkit: A Python wrapper for wkhtmltopdf, which converts HTML to PDF.
•wkhtmltopdf: A command-line tool for rendering HTML into PDFs.
Install the necessary libraries using the following commands:
pip install jinja2 pdfkit
Additionally, ensure that wkhtmltopdf is installed on your system:
sudo apt-get install wkhtmltopdf
Setting Up the Virtual Environment
It's a good practice to use a local Python environment for your projects. This helps keep your dependencies isolated and ensures that your project is not affected by global Python packages. Follow these steps to set up the environment:
# Create a new directory for your project if you haven't already
mkdir ~/my_project
cd ~/my_project
# Create a virtual environment in ~/bin/env
python3 -m venv ~/bin/env
# Activate the environment
source ~/bin/env/bin/activate
# Install the necessary Python dependencies
pip install jinja2 pdfkit
Once the environment is set up, you can simply activate it by running:
source ~/bin/env/bin/activate
Creating the Template
Next, create a Jinja2 template to define the structure and design of your PDF. In this example, we’ll create a simple template with a title, content, a list of items, and an image.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<!-- Bootstrap CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
/* Custom styles for PDF generation */
@page {
size: A4;
margin: 1cm;
}
body {
font-family: 'Arial', sans-serif;
line-height: 1.5;
margin: 0;
}
.header, .footer {
background-color: #f8f9fa;
padding: 10px;
text-align: center;
color: #333;
}
.header h1 {
margin: 0;
font-size: 1.8rem;
color: #007bff;
}
.footer p {
margin: 0;
font-size: 0.9rem;
}
.content {
padding: 20px;
}
.content h2 {
color: #28a745;
margin-top: 20px;
}
.content p {
text-align: justify;
}
.content blockquote {
background-color: #f1f3f4;
border-left: 4px solid #007bff;
padding: 10px 15px;
font-style: italic;
color: #555;
margin: 20px 0;
}
.content .table {
margin-top: 20px;
}
.content .btn {
display: inline-block;
margin-top: 15px;
}
.highlight {
color: #dc3545;
font-weight: bold;
}
.list-group-item {
background-color: #f9f9f9;
color: #333;
}
.list-group-item:hover {
background-color: #007bff;
color: #fff;
}
.img-container {
text-align: center;
margin-top: 20px;
}
.img-container img {
max-width: 50%;
height: auto;
}
</style>
</head>
<body>
<div class="header">
<h1>{{ title }}</h1>
<p>PDF generated using Jinja2 and PDFKit</p>
</div>
<div class="content container">
<h2>Introduction</h2>
<p>{{ content }}</p>
<h2>Highlighted Text</h2>
<p>You can use <span class="highlight">colors and bold text</span> to draw attention to key points in your PDF.</p>
<h2>Table Example</h2>
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Example 1</td>
<td>This is the first example item.</td>
<td>$10</td>
</tr>
<tr>
<td>Example 2</td>
<td>This is the second example item.</td>
<td>$20</td>
</tr>
</tbody>
</table>
<h2>List Example</h2>
<ul class="list-group">
{% for item in items %}
<li class="list-group-item">{{ item }}</li>
{% endfor %}
</ul>
<h2>Blockquote Example</h2>
<blockquote>
"This is a beautifully styled blockquote to emphasize key ideas."
</blockquote>
<h2>Image Example</h2>
<div class="img-container">
<img src="{{ image_path }}" alt="Example Image">
</div>
<h2>Button Example</h2>
<a href="https://mosaid.xyz" class="btn btn-primary">Click Me</a>
</div>
<div class="footer">
<p>{{ footer }}</p>
</div>
<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.4.4/dist/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>
Save this file as template.html in your project directory.
Generating the PDF
Now let’s use a Python script to generate the PDF from the Jinja2 template. Below is the script that takes the template, renders it with data, and generates the PDF:
import pdfkit
from jinja2 import Environment, FileSystemLoader
import os
import sys
from datetime import datetime
def generate_pdf(template_file, output_dir="./output"):
# Create output directory if it doesn't exist
os.makedirs(output_dir, exist_ok=True)
# Extract the directory and filename from the template file
template_dir = os.path.dirname(template_file)
template_name = os.path.basename(template_file)
# Set up Jinja2 environment
env = Environment(loader=FileSystemLoader(template_dir))
template = env.get_template(template_name)
# Define data to be rendered in the template
data = {
"title": "Jinja2 to PDF Example",
"content": "This is a PDF generated using Jinja2 and pdfkit.",
"items": ["Introduction", "Body Content", "Conclusion"],
"footer": "Generated by Radouan MOSAID",
"image_path": "/path/to/image.jpg", # Absolute path
}
# Render the template with the data
rendered_html = template.render(data)
# Get the current date string in YYYY-MM-DD format
date_str = datetime.now().strftime("%Y-%m-%d")
# Output PDF file path with the date appended to make it unique
output_pdf_path = os.path.join(output_dir, f"output_{date_str}.pdf")
# Define pdfkit options to enable local file access
options = {
'enable-local-file-access': '', # Allow access to local files
}
# Convert the rendered HTML to a PDF with options
pdfkit.from_string(rendered_html, output_pdf_path, options=options)
print(f"PDF has been created successfully: {output_pdf_path}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <template_file>")
sys.exit(1)
# Get the template file from the command-line arguments
template_file = sys.argv[1]
# Check if the file exists
if not os.path.isfile(template_file):
print(f"Error: Template file '{template_file}' not found.")
sys.exit(1)
# Generate PDF
generate_pdf(template_file)
This script loads the template, renders it with the provided data, and outputs a PDF to the specified directory. The path to the image is provided as an absolute path to avoid errors.
Running the Script
To run the script, simply execute it from your terminal while in the project directory:
python script.py template.html
This will generate a PDF file with the rendered content. The output PDF will be saved in the output directory with a unique name based on the current date.
Handling Errors
Sometimes, you might encounter issues like missing image files or incorrect file paths. Make sure to:
• Check the file path of the image used in the template.
• Ensure that --enable-local-file-access
is set in the pdfkit
options to allow local file access.
• Use absolute paths for local files to avoid any issues related to relative paths.
Customizing Your PDF
You can customize the PDF output by modifying the template, adding more dynamic data to the Jinja2 context, and adjusting the CSS styling. For example, you could change the page layout, font styles, or add additional sections like tables or charts.
Conclusion
In this tutorial, we’ve shown how to create PDFs using Jinja2 templates and Python. By organizing your code into a local Python environment and following best practices for managing dependencies, you can easily generate custom documents automatically. This process can be extended and customized for various use cases, making it an efficient way to automate document generation.
0 Comments, latest