PDF Generation
π Step-by-Step Guide to Using WeasyPrint in Django
1οΈβ£ Install WeasyPrint
Before using WeasyPrint, install it using pip
:
pip install weasyprint
WeasyPrint has some system dependencies. If you face installation issues, install the required system packages:
For Ubuntu/Debian:
sudo apt install libpango-1.0-0 libpangocairo-1.0-0 libcairo2 libffi-dev
For MacOS:
brew install pango cairo gdk-pixbuf
For Windows: - Install GTK+ from WeasyPrintβs documentation.
2οΈβ£ Create a Django View to Generate PDFs
We will create a class-based view (WeasyTemplateResponseMixin
) to render a Django template into a PDF.
π Example: Generating a Simple PDF
# views.py
from django.http import HttpResponse
from django.template.loader import get_template
from weasyprint import HTML
import tempfile
def generate_pdf(request):
"""
Generate a simple PDF from an HTML template.
"""
= get_template("pdf_template.html")
template = {"title": "My PDF Report", "content": "This is a dynamically generated PDF."}
context = template.render(context)
html_content
# Create a temporary file
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
=html_content).write_pdf(temp_file.name)
HTML(string0)
temp_file.seek(= temp_file.read()
pdf_data
= HttpResponse(pdf_data, content_type="application/pdf")
response "Content-Disposition"] = 'inline; filename="report.pdf"'
response[return response
3οΈβ£ Create an HTML Template for the PDF
WeasyPrint renders HTML and CSS, so we need to define a well-structured HTML template.
π Example: pdf_template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<style>
body {font-family: Arial, sans-serif;
margin: 20px;
}
h1 {color: #333;
}.content {
margin-top: 20px;
}</style>
</head>
<body>
<h1>{{ title }}</h1>
<div class="content">
<p>{{ content }}</p>
</div>
</body>
</html>
4οΈβ£ Add URL Pattern
Map the PDF generation view in urls.py
:
# urls.py
from django.urls import path
from .views import generate_pdf
= [
urlpatterns "generate-pdf/", generate_pdf, name="generate_pdf"),
path( ]
Now, visiting /generate-pdf/
will return a dynamically generated PDF.
ποΈ Advanced WeasyPrint Usage
1οΈβ£ Generating PDFs with Django Querysets
You can pass database query results into the PDF template.
π Example: Generating a PDF Invoice
# views.py
from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML
from .models import Order
def generate_invoice(request, order_id):
"""
Generate an invoice PDF for a specific order.
"""
= Order.objects.get(id=order_id)
order = {"order": order}
context = render_to_string("invoice_template.html", context)
html_string
= HTML(string=html_string).write_pdf()
pdf_file = HttpResponse(pdf_file, content_type="application/pdf")
response "Content-Disposition"] = f'filename="invoice_{order.id}.pdf"'
response[return response
π Example: invoice_template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoice</title>
<style>
body {font-family: Arial, sans-serif;
padding: 20px;
}
h1 {color: #333;
border-bottom: 2px solid #000;
padding-bottom: 10px;
}
table {width: 100%;
border-collapse: collapse;
margin-top: 20px;
}, td {
thborder: 1px solid #ddd;
padding: 10px;
text-align: left;
}.total {
font-weight: bold;
font-size: 1.2em;
}</style>
</head>
<body>
<h1>Invoice for Order #{{ order.id }}</h1>
<p>Customer: {{ order.customer_name }}</p>
<p>Date: {{ order.date }}</p>
<table>
<thead>
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% for item in order.items.all %}<tr>
<td>{{ item.product_name }}</td>
<td>{{ item.quantity }}</td>
<td>${{ item.price }}</td>
</tr>
{% endfor %}</tbody>
</table>
<p class="total">Total Amount: ${{ order.total_price }}</p>
</body>
</html>
2οΈβ£ Saving PDFs to Django Models
You can store generated PDFs as files inside a Django model.
π Example: Save Invoice as a File
from django.core.files.base import ContentFile
from weasyprint import HTML
from .models import Order
def save_invoice_pdf(order_id):
"""
Generate and save a PDF invoice to a model field.
"""
= Order.objects.get(id=order_id)
order = render_to_string("invoice_template.html", {"order": order})
html_string
= HTML(string=html_string).write_pdf()
pdf_file
# Save to model
f"invoice_{order.id}.pdf", ContentFile(pdf_file), save=True) order.invoice.save(
π Update Order
Model
from django.db import models
class Order(models.Model):
= models.CharField(max_length=100)
customer_name = models.DateField(auto_now_add=True)
date = models.DecimalField(max_digits=10, decimal_places=2)
total_price = models.FileField(upload_to="invoices/", blank=True, null=True) # Store PDF invoice
π¨ Styling PDFs with CSS
WeasyPrint fully supports CSS, including: β
Fonts
β
Page Breaks
β
Headers & Footers
β
Images
π Example: Adding Page Breaks
@page {
size: A4;
margin: 20mm;
}.page-break {
page-break-before: always;
}
π Usage in HTML
<p>Page 1 content</p>
<div class="page-break"></div>
<p>Page 2 content</p>
π Common WeasyPrint Issues & Fixes
Issue | Solution |
---|---|
no such file or directory: cairo |
Install dependencies: sudo apt install libcairo2 |
No module named weasyprint |
Run pip install weasyprint |
Fonts not loading | Use absolute file paths for fonts (file:// URLs) |
β Summary: Key Takeaways
Feature | Django Implementation |
---|---|
β Generate PDFs | HTML(string).write_pdf() |
β Use Templates | render_to_string("template.html", context) |
β Save PDFs to Models | order.invoice.save(...) |
β Style with CSS | Full CSS support, including @page |
π Now you can generate beautifully styled PDFs in Django using WeasyPrint! π